@kushagradhawan/kookie-blocks 0.1.5 → 0.1.6
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.
- package/dist/cjs/components/code/CodeBlock.d.ts.map +1 -1
- package/dist/cjs/components/code/CodeBlock.js +1 -1
- package/dist/cjs/components/code/CodeBlock.js.map +3 -3
- package/dist/esm/components/code/CodeBlock.d.ts.map +1 -1
- package/dist/esm/components/code/CodeBlock.js +1 -1
- package/dist/esm/components/code/CodeBlock.js.map +3 -3
- package/package.json +1 -1
- package/src/components/code/CodeBlock.tsx +74 -25
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeBlock.d.ts","sourceRoot":"","sources":["../../../../src/components/code/CodeBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyE,MAAM,OAAO,CAAC;AAK9F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"CodeBlock.d.ts","sourceRoot":"","sources":["../../../../src/components/code/CodeBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyE,MAAM,OAAO,CAAC;AAK9F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAoY9C,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,QAAe,EACf,YAAmB,EACnB,UAAU,EACV,SAAS,EACT,UAAU,EACV,eAAe,GAChB,EAAE,cAAc,qBAyBhB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
"use strict";var
|
|
1
|
+
"use strict";var B=Object.create;var k=Object.defineProperty;var j=Object.getOwnPropertyDescriptor;var I=Object.getOwnPropertyNames;var $=Object.getPrototypeOf,A=Object.prototype.hasOwnProperty;var M=(r,t)=>{for(var n in t)k(r,n,{get:t[n],enumerable:!0})},L=(r,t,n,i)=>{if(t&&typeof t=="object"||typeof t=="function")for(let s of I(t))!A.call(r,s)&&s!==n&&k(r,s,{get:()=>t[s],enumerable:!(i=j(t,s))||i.enumerable});return r};var D=(r,t,n)=>(n=r!=null?B($(r)):{},L(t||!r||!r.__esModule?k(n,"default",{value:r,enumerable:!0}):n,r)),_=r=>L(k({},"__esModule",{value:!0}),r);var Q={};M(Q,{CodeBlock:()=>q});module.exports=_(Q);var e=D(require("react")),o=require("@kushagradhawan/kookie-ui"),b=require("@hugeicons/react"),g=require("@hugeicons/core-free-icons"),E=require("shiki");const C=360,J="one-light",G="one-dark-pro";function O({children:r,background:t="none",backgroundProps:n={}}){const{dotSize:i=24,color:s="var(--gray-10)",backgroundColor:l="var(--gray-2)",height:a,width:d="100%",radius:u="3"}=n;if(t==="none")return e.default.createElement(o.Card,{size:"1",variant:"soft"},e.default.createElement(o.Flex,{justify:"center",align:"center",py:"4"},e.default.createElement(o.Theme,{fontFamily:"sans"},r)));if(t==="dots"){const f={backgroundImage:`radial-gradient(circle, ${s} 1px, transparent 1px)`,borderRadius:`var(--radius-${u})`,backgroundSize:`${i}px ${i}px`,backgroundPosition:"center",backgroundColor:l,width:d,...a&&{height:a}};return e.default.createElement(o.Card,{size:"1",variant:"soft"},e.default.createElement(o.Flex,{justify:"center",align:"center",py:"4",style:f},e.default.createElement(o.Theme,{fontFamily:"sans"},r)))}const c={backgroundImage:`url(${t})`,backgroundSize:"cover",backgroundPosition:"center",backgroundRepeat:"no-repeat",borderRadius:`var(--radius-${u})`,width:d,...a&&{height:a}};return e.default.createElement(o.Card,{size:"1",variant:"soft"},e.default.createElement(o.Flex,{justify:"center",align:"center",py:"4",style:c},e.default.createElement(o.Theme,{fontFamily:"sans"},r)))}const U=(0,e.memo)(function({code:t,language:n,showCopy:i=!0,showLanguage:s=!0,lightTheme:l=J,darkTheme:a=G}){const[d,u]=(0,e.useState)(null),[c,f]=(0,e.useState)(!1),[p,y]=(0,e.useState)(C),[S,T]=(0,e.useState)(!1),m=(0,e.useRef)(null),h=(0,e.useRef)(null),v=p>C;(0,e.useEffect)(()=>{let x=!1;return(0,E.codeToHtml)(t,{lang:n,themes:{light:l,dark:a},defaultColor:!1}).then(z=>{x||u(z)}).catch(()=>{x||u(null)}),()=>{x=!0}},[t,n,l,a]),(0,e.useEffect)(()=>{m.current&&y(m.current.scrollHeight)},[d]),(0,e.useEffect)(()=>()=>{h.current&&clearTimeout(h.current)},[]);const w=(0,e.useCallback)(async()=>{if(t.trim())try{await navigator.clipboard.writeText(t),T(!0),h.current&&clearTimeout(h.current),h.current=setTimeout(()=>T(!1),2e3)}catch{}},[t]),P=n==="text"?"plaintext":n,H=(0,e.useCallback)(()=>{f(x=>!x)},[]),F={maxHeight:c?`${p}px`:`${C}px`};return e.default.createElement(o.Box,{position:"relative"},e.default.createElement(o.Card,{size:"1",variant:"soft"},e.default.createElement(o.Flex,{direction:"column",gap:"3"},e.default.createElement(o.Flex,{gap:"2",justify:"between",align:"start"},s&&e.default.createElement(o.Code,{size:"1",color:"gray",highContrast:!0},P),e.default.createElement(o.Flex,{align:"center",gap:"2",className:"code-action-buttons"},v&&e.default.createElement(o.Button,{size:"2",variant:"ghost",color:"gray",onClick:H,tooltip:c?"Collapse":"Expand","aria-label":c?"Collapse code":"Expand code"},e.default.createElement(b.HugeiconsIcon,{icon:g.ArrowDown01Icon,style:{transform:c?"rotate(180deg)":"rotate(0deg)"},className:"code-chevron"})),i&&e.default.createElement(o.Button,{size:"2",variant:"ghost",color:"gray",onClick:w,tooltip:S?"Copied!":"Copy","aria-label":S?"Copied!":"Copy code"},e.default.createElement(b.HugeiconsIcon,{icon:S?g.Tick01Icon:g.Copy01Icon})," Copy"))),e.default.createElement(o.Box,{ref:m,style:F,className:"code-content"},d?e.default.createElement(o.Box,{className:"code-block-content",width:"100%",style:{minWidth:0},dangerouslySetInnerHTML:{__html:d}}):e.default.createElement("pre",{className:"code-block-content"},e.default.createElement(o.Code,{size:"3"},t))),v&&!c&&e.default.createElement(o.Box,{className:"code-scroll-shadow visible"}))))});function X(r){const t=n=>{if(typeof n=="string")return n;if(typeof n=="number")return String(n);if(!n)return"";if(Array.isArray(n))return n.map(t).join("");if(typeof n=="object"&&"props"in n){const i=n.props;if(i?.children)return t(i.children)}return""};return t(r)}function N(r){const t=n=>{if(!n)return null;if(typeof n=="object"&&"props"in n){const i=n.props,s=i?.className||i?.class||"";if(typeof s=="string"){const l=s.match(/language-([\w-]+)/i);if(l)return l[1]}if(i?.children)if(Array.isArray(i.children))for(const l of i.children){const a=t(l);if(a)return a}else return t(i.children)}return null};return t(r)||"text"}function K(r){return{tsx:"TSX",ts:"TS",jsx:"JSX",js:"JS",javascript:"JS",typescript:"TS",css:"CSS",html:"HTML",json:"JSON",bash:"SH",sh:"SH",shell:"SH",text:"plaintext"}[r.toLowerCase()]||r.toLowerCase()}const W=(0,e.memo)(function({children:t,showCopy:n=!0,showLanguage:i=!0}){const[s,l]=(0,e.useState)(!1),[a,d]=(0,e.useState)(C),[u,c]=(0,e.useState)(!1),f=(0,e.useRef)(null),p=(0,e.useRef)(null),y=X(t),S=N(t),T=K(S),m=a>C;(0,e.useEffect)(()=>{f.current&&d(f.current.scrollHeight)},[t]),(0,e.useEffect)(()=>()=>{p.current&&clearTimeout(p.current)},[]);const h=(0,e.useCallback)(async()=>{if(y.trim())try{await navigator.clipboard.writeText(y),c(!0),p.current&&clearTimeout(p.current),p.current=setTimeout(()=>c(!1),2e3)}catch{}},[y]),v={maxHeight:s?`${a}px`:`${C}px`},w=(0,e.useCallback)(()=>{l(H=>!H)},[]);return e.default.createElement(o.Box,{position:"relative"},e.default.createElement(o.Card,{size:"1",variant:"soft"},e.default.createElement(o.Flex,{direction:"column",gap:"3"},e.default.createElement(o.Flex,{gap:"2",justify:"between",align:"start"},i&&e.default.createElement(o.Code,{size:"1",color:"gray",highContrast:!0},T),e.default.createElement(o.Flex,{align:"center",gap:"2",className:"code-action-buttons"},m&&e.default.createElement(o.Button,{size:"2",variant:"ghost",color:"gray",onClick:w,tooltip:s?"Collapse":"Expand","aria-label":s?"Collapse code":"Expand code"},e.default.createElement(b.HugeiconsIcon,{icon:g.ArrowDown01Icon,style:{transform:s?"rotate(180deg)":"rotate(0deg)"},className:"code-chevron"})),n&&e.default.createElement(o.Button,{size:"2",variant:"ghost",color:"gray",onClick:h,tooltip:u?"Copied!":"Copy","aria-label":u?"Copied!":"Copy code"},e.default.createElement(b.HugeiconsIcon,{icon:u?g.Tick01Icon:g.Copy01Icon})," Copy"))),e.default.createElement(o.Box,{ref:f,style:v,className:"code-content"},e.default.createElement("div",{className:"code-block-content"},t)),m&&!s&&e.default.createElement(o.Box,{className:"code-scroll-shadow visible"}))))});function q({children:r,code:t,language:n,preview:i,showCopy:s=!0,showLanguage:l=!0,lightTheme:a,darkTheme:d,background:u,backgroundProps:c}){const f=t||r&&e.default.Children.count(r)>0,p=n||N(r)||"text";return e.default.createElement(o.Box,{className:"docs-code-block",mt:"6",mb:"8"},e.default.createElement(o.Flex,{direction:"column",gap:"2"},i&&e.default.createElement(O,{background:u,backgroundProps:c},i),t&&e.default.createElement(U,{code:t,language:p,showCopy:s,showLanguage:l,lightTheme:a,darkTheme:d}),r&&!t&&e.default.createElement(W,{showCopy:s,showLanguage:l},r)))}
|
|
2
2
|
//# sourceMappingURL=CodeBlock.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/code/CodeBlock.tsx"],
|
|
4
|
-
"sourcesContent": ["import React, { useState, useRef, useEffect, useCallback, memo, type ReactNode } from \"react\";\nimport { Box, Card, Flex, Button, Code, Theme } from \"@kushagradhawan/kookie-ui\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { Copy01Icon, Tick01Icon } from \"@hugeicons/core-free-icons\";\nimport { codeToHtml } from \"shiki\";\nimport type { CodeBlockProps } from \"./types\";\n\nconst COLLAPSED_HEIGHT = 360;\nconst DEFAULT_LIGHT_THEME = \"one-light\";\nconst DEFAULT_DARK_THEME = \"one-dark-pro\";\n\n// ============================================\n// Preview Section\n// ============================================\n\ninterface PreviewSectionProps {\n children: ReactNode;\n background?: \"none\" | \"dots\" | string;\n backgroundProps?: {\n dotSize?: number;\n color?: string;\n backgroundColor?: string;\n height?: string;\n width?: string;\n radius?: string;\n };\n}\n\nfunction PreviewSection({ children, background = \"none\", backgroundProps = {} }: PreviewSectionProps) {\n const { dotSize = 24, color = \"var(--gray-10)\", backgroundColor = \"var(--gray-2)\", height, width = \"100%\", radius = \"3\" } = backgroundProps;\n\n if (background === \"none\") {\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\">\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n }\n\n if (background === \"dots\") {\n const dotsStyle: React.CSSProperties = {\n backgroundImage: `radial-gradient(circle, ${color} 1px, transparent 1px)`,\n borderRadius: `var(--radius-${radius})`,\n backgroundSize: `${dotSize}px ${dotSize}px`,\n backgroundPosition: \"center\",\n backgroundColor,\n width,\n ...(height && { height }),\n };\n\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\" style={dotsStyle}>\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n }\n\n const imageStyle: React.CSSProperties = {\n backgroundImage: `url(${background})`,\n backgroundSize: \"cover\",\n backgroundPosition: \"center\",\n backgroundRepeat: \"no-repeat\",\n borderRadius: `var(--radius-${radius})`,\n width,\n ...(height && { height }),\n };\n\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\" style={imageStyle}>\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n}\n\n// ============================================\n// Code Section (for runtime highlighting)\n// ============================================\n\ninterface CodeSectionProps {\n code: string;\n language: string;\n showCopy?: boolean;\n showLanguage?: boolean;\n lightTheme?: string;\n darkTheme?: string;\n}\n\nconst CodeSection = memo(function CodeSection({\n code,\n language,\n showCopy = true,\n showLanguage = true,\n lightTheme = DEFAULT_LIGHT_THEME,\n darkTheme = DEFAULT_DARK_THEME,\n}: CodeSectionProps) {\n const [highlighted, setHighlighted] = useState<string | null>(null);\n const [isExpanded, setIsExpanded] = useState(false);\n const [contentHeight, setContentHeight] = useState(COLLAPSED_HEIGHT);\n const [copied, setCopied] = useState(false);\n const contentRef = useRef<HTMLDivElement>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const shouldShowToggle = contentHeight > COLLAPSED_HEIGHT;\n\n useEffect(() => {\n let cancelled = false;\n codeToHtml(code, {\n lang: language,\n themes: { light: lightTheme, dark: darkTheme },\n defaultColor: false,\n })\n .then((html) => {\n if (!cancelled) setHighlighted(html);\n })\n .catch(() => {\n if (!cancelled) setHighlighted(null);\n });\n return () => {\n cancelled = true;\n };\n }, [code, language, lightTheme, darkTheme]);\n\n useEffect(() => {\n if (contentRef.current) {\n setContentHeight(contentRef.current.scrollHeight);\n }\n }, [highlighted]);\n\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n };\n }, []);\n\n const handleCopy = useCallback(async () => {\n if (!code.trim()) return;\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n resetTimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n } catch {\n // Silently fail\n }\n }, [code]);\n\n const displayLanguage = language === \"text\" ? \"plaintext\" : language;\n\n const contentStyle: React.CSSProperties = {\n maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,\n };\n\n return (\n <Box position=\"relative\">\n <Card size=\"1\" variant=\"soft\">\n <Flex direction=\"column\" gap=\"3\">\n <Flex gap=\"2\" justify=\"between\" align=\"start\">\n {showLanguage && (\n <Code size=\"1\" color=\"gray\" highContrast>\n {displayLanguage}\n </Code>\n )}\n <Flex align=\"center\" gap=\"2\" className=\"code-action-buttons\">\n {showCopy && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleCopy}\n tooltip={copied ? \"Copied!\" : \"Copy\"}\n aria-label={copied ? \"Copied!\" : \"Copy code\"}\n >\n <HugeiconsIcon icon={copied ? Tick01Icon : Copy01Icon} /> Copy\n </Button>\n )}\n </Flex>\n </Flex>\n\n <Box ref={contentRef} style={contentStyle} className=\"code-content\">\n {highlighted ? (\n <Box className=\"code-block-content\" width=\"100%\" style={{ minWidth: 0 }} dangerouslySetInnerHTML={{ __html: highlighted }} />\n ) : (\n <pre className=\"code-block-content\">\n <Code size=\"3\">{code}</Code>\n </pre>\n )}\n </Box>\n\n {shouldShowToggle && !isExpanded && <Box className=\"code-scroll-shadow visible\" />}\n </Flex>\n </Card>\n </Box>\n );\n});\n\n// ============================================\n// Children Code Section (for pre-highlighted MDX)\n// ============================================\n\ninterface ChildrenCodeSectionProps {\n children: ReactNode;\n showCopy?: boolean;\n showLanguage?: boolean;\n}\n\nfunction extractCodeFromChildren(children?: ReactNode): string {\n if (!children) return \"\";\n if (typeof children === \"object\" && children !== null && \"props\" in children) {\n const childProps = (children as any).props;\n if (childProps?.children && typeof childProps.children === \"object\") {\n const codeProps = (childProps.children as any).props;\n const codeChildren = codeProps?.children;\n if (typeof codeChildren === \"string\") return codeChildren;\n }\n if (typeof childProps?.children === \"string\") return childProps.children;\n }\n if (typeof children === \"string\") return children;\n return \"\";\n}\n\nfunction extractLanguageFromChildren(children?: ReactNode): string {\n if (!children) return \"text\";\n if (typeof children === \"object\" && children !== null && \"props\" in children) {\n const childProps = (children as any).props;\n if (childProps?.children && typeof childProps.children === \"object\") {\n const codeProps = (childProps.children as any).props;\n const className = codeProps?.className || \"\";\n const match = className.match(/language-([\\w-]+)/i);\n if (match) return match[1];\n }\n const className = childProps?.className || \"\";\n const match = className.match(/language-([\\w-]+)/i);\n if (match) return match[1];\n }\n return \"text\";\n}\n\nfunction formatLanguageLabel(lang: string): string {\n const aliasMap: Record<string, string> = {\n tsx: \"TSX\",\n ts: \"TS\",\n jsx: \"JSX\",\n js: \"JS\",\n javascript: \"JS\",\n typescript: \"TS\",\n css: \"CSS\",\n html: \"HTML\",\n json: \"JSON\",\n bash: \"SH\",\n sh: \"SH\",\n shell: \"SH\",\n text: \"plaintext\",\n };\n return aliasMap[lang.toLowerCase()] || lang.toLowerCase();\n}\n\nconst ChildrenCodeSection = memo(function ChildrenCodeSection({ children, showCopy = true, showLanguage = true }: ChildrenCodeSectionProps) {\n const [isExpanded, setIsExpanded] = useState(false);\n const [contentHeight, setContentHeight] = useState(COLLAPSED_HEIGHT);\n const [copied, setCopied] = useState(false);\n const contentRef = useRef<HTMLDivElement>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const code = extractCodeFromChildren(children);\n const language = extractLanguageFromChildren(children);\n const displayLanguage = formatLanguageLabel(language);\n\n const shouldShowToggle = contentHeight > COLLAPSED_HEIGHT;\n\n useEffect(() => {\n if (contentRef.current) {\n setContentHeight(contentRef.current.scrollHeight);\n }\n }, [children]);\n\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n };\n }, []);\n\n const handleCopy = useCallback(async () => {\n if (!code.trim()) return;\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n resetTimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n } catch {\n // Silently fail\n }\n }, [code]);\n\n const contentStyle: React.CSSProperties = {\n maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,\n };\n\n return (\n <Box position=\"relative\">\n <Card size=\"1\" variant=\"soft\">\n <Flex direction=\"column\" gap=\"3\">\n <Flex gap=\"2\" justify=\"between\" align=\"start\">\n {showLanguage && (\n <Code size=\"1\" color=\"gray\" highContrast>\n {displayLanguage}\n </Code>\n )}\n <Flex align=\"center\" gap=\"2\" className=\"code-action-buttons\">\n {showCopy && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleCopy}\n tooltip={copied ? \"Copied!\" : \"Copy\"}\n aria-label={copied ? \"Copied!\" : \"Copy code\"}\n >\n <HugeiconsIcon icon={copied ? Tick01Icon : Copy01Icon} /> Copy\n </Button>\n )}\n </Flex>\n </Flex>\n\n <Box ref={contentRef} style={contentStyle} className=\"code-content\">\n <div className=\"code-block-content\">{children}</div>\n </Box>\n\n {shouldShowToggle && !isExpanded && <Box className=\"code-scroll-shadow visible\" />}\n </Flex>\n </Card>\n </Box>\n );\n});\n\n// ============================================\n// Main CodeBlock Component\n// ============================================\n\nexport function CodeBlock({\n children,\n code,\n language,\n preview,\n showCopy = true,\n showLanguage = true,\n lightTheme,\n darkTheme,\n background,\n backgroundProps,\n}: CodeBlockProps) {\n const hasCode = code || (children && React.Children.count(children) > 0);\n const displayLanguage = language || extractLanguageFromChildren(children) || \"text\";\n\n return (\n <Box className=\"docs-code-block\" mt=\"6\" mb=\"8\">\n <Flex direction=\"column\" gap=\"2\">\n {preview && (\n <PreviewSection background={background} backgroundProps={backgroundProps}>\n {preview}\n </PreviewSection>\n )}\n\n {code && (\n <CodeSection code={code} language={displayLanguage} showCopy={showCopy} showLanguage={showLanguage} lightTheme={lightTheme} darkTheme={darkTheme} />\n )}\n\n {children && !code && (\n <ChildrenCodeSection showCopy={showCopy} showLanguage={showLanguage}>\n {children}\n </ChildrenCodeSection>\n )}\n </Flex>\n </Box>\n );\n}\n"],
|
|
5
|
-
"mappings": "0jBAAA,IAAAA,EAAA,GAAAC,EAAAD,EAAA,eAAAE,IAAA,eAAAC,EAAAH,GAAA,IAAAI,EAAsF,oBACtFC,EAAqD,qCACrDD,EAA8B,4BAC9BE,
|
|
6
|
-
"names": ["CodeBlock_exports", "__export", "CodeBlock", "__toCommonJS", "import_react", "import_kookie_ui", "import_core_free_icons", "import_shiki", "COLLAPSED_HEIGHT", "DEFAULT_LIGHT_THEME", "DEFAULT_DARK_THEME", "PreviewSection", "children", "background", "backgroundProps", "dotSize", "color", "backgroundColor", "height", "width", "radius", "React", "dotsStyle", "imageStyle", "CodeSection", "code", "language", "showCopy", "showLanguage", "lightTheme", "darkTheme", "highlighted", "setHighlighted", "isExpanded", "setIsExpanded", "contentHeight", "setContentHeight", "copied", "setCopied", "contentRef", "resetTimeoutRef", "shouldShowToggle", "cancelled", "html", "handleCopy", "displayLanguage", "contentStyle", "extractCodeFromChildren", "
|
|
4
|
+
"sourcesContent": ["import React, { useState, useRef, useEffect, useCallback, memo, type ReactNode } from \"react\";\nimport { Box, Card, Flex, Button, Code, Theme } from \"@kushagradhawan/kookie-ui\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { Copy01Icon, Tick01Icon, ArrowDown01Icon } from \"@hugeicons/core-free-icons\";\nimport { codeToHtml } from \"shiki\";\nimport type { CodeBlockProps } from \"./types\";\n\nconst COLLAPSED_HEIGHT = 360;\nconst DEFAULT_LIGHT_THEME = \"one-light\";\nconst DEFAULT_DARK_THEME = \"one-dark-pro\";\n\n// ============================================\n// Preview Section\n// ============================================\n\ninterface PreviewSectionProps {\n children: ReactNode;\n background?: \"none\" | \"dots\" | string;\n backgroundProps?: {\n dotSize?: number;\n color?: string;\n backgroundColor?: string;\n height?: string;\n width?: string;\n radius?: string;\n };\n}\n\nfunction PreviewSection({ children, background = \"none\", backgroundProps = {} }: PreviewSectionProps) {\n const { dotSize = 24, color = \"var(--gray-10)\", backgroundColor = \"var(--gray-2)\", height, width = \"100%\", radius = \"3\" } = backgroundProps;\n\n if (background === \"none\") {\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\">\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n }\n\n if (background === \"dots\") {\n const dotsStyle: React.CSSProperties = {\n backgroundImage: `radial-gradient(circle, ${color} 1px, transparent 1px)`,\n borderRadius: `var(--radius-${radius})`,\n backgroundSize: `${dotSize}px ${dotSize}px`,\n backgroundPosition: \"center\",\n backgroundColor,\n width,\n ...(height && { height }),\n };\n\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\" style={dotsStyle}>\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n }\n\n const imageStyle: React.CSSProperties = {\n backgroundImage: `url(${background})`,\n backgroundSize: \"cover\",\n backgroundPosition: \"center\",\n backgroundRepeat: \"no-repeat\",\n borderRadius: `var(--radius-${radius})`,\n width,\n ...(height && { height }),\n };\n\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\" style={imageStyle}>\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n}\n\n// ============================================\n// Code Section (for runtime highlighting)\n// ============================================\n\ninterface CodeSectionProps {\n code: string;\n language: string;\n showCopy?: boolean;\n showLanguage?: boolean;\n lightTheme?: string;\n darkTheme?: string;\n}\n\nconst CodeSection = memo(function CodeSection({\n code,\n language,\n showCopy = true,\n showLanguage = true,\n lightTheme = DEFAULT_LIGHT_THEME,\n darkTheme = DEFAULT_DARK_THEME,\n}: CodeSectionProps) {\n const [highlighted, setHighlighted] = useState<string | null>(null);\n const [isExpanded, setIsExpanded] = useState(false);\n const [contentHeight, setContentHeight] = useState(COLLAPSED_HEIGHT);\n const [copied, setCopied] = useState(false);\n const contentRef = useRef<HTMLDivElement>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const shouldShowToggle = contentHeight > COLLAPSED_HEIGHT;\n\n useEffect(() => {\n let cancelled = false;\n codeToHtml(code, {\n lang: language,\n themes: { light: lightTheme, dark: darkTheme },\n defaultColor: false,\n })\n .then((html) => {\n if (!cancelled) setHighlighted(html);\n })\n .catch(() => {\n if (!cancelled) setHighlighted(null);\n });\n return () => {\n cancelled = true;\n };\n }, [code, language, lightTheme, darkTheme]);\n\n useEffect(() => {\n if (contentRef.current) {\n setContentHeight(contentRef.current.scrollHeight);\n }\n }, [highlighted]);\n\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n };\n }, []);\n\n const handleCopy = useCallback(async () => {\n if (!code.trim()) return;\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n resetTimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n } catch {\n // Silently fail\n }\n }, [code]);\n\n const displayLanguage = language === \"text\" ? \"plaintext\" : language;\n\n const handleToggle = useCallback(() => {\n setIsExpanded((prev) => !prev);\n }, []);\n\n const contentStyle: React.CSSProperties = {\n maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,\n };\n\n const chevronStyle: React.CSSProperties = {\n transform: isExpanded ? \"rotate(180deg)\" : \"rotate(0deg)\",\n };\n\n return (\n <Box position=\"relative\">\n <Card size=\"1\" variant=\"soft\">\n <Flex direction=\"column\" gap=\"3\">\n <Flex gap=\"2\" justify=\"between\" align=\"start\">\n {showLanguage && (\n <Code size=\"1\" color=\"gray\" highContrast>\n {displayLanguage}\n </Code>\n )}\n <Flex align=\"center\" gap=\"2\" className=\"code-action-buttons\">\n {shouldShowToggle && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleToggle}\n tooltip={isExpanded ? \"Collapse\" : \"Expand\"}\n aria-label={isExpanded ? \"Collapse code\" : \"Expand code\"}\n >\n <HugeiconsIcon icon={ArrowDown01Icon} style={chevronStyle} className=\"code-chevron\" />\n </Button>\n )}\n {showCopy && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleCopy}\n tooltip={copied ? \"Copied!\" : \"Copy\"}\n aria-label={copied ? \"Copied!\" : \"Copy code\"}\n >\n <HugeiconsIcon icon={copied ? Tick01Icon : Copy01Icon} /> Copy\n </Button>\n )}\n </Flex>\n </Flex>\n\n <Box ref={contentRef} style={contentStyle} className=\"code-content\">\n {highlighted ? (\n <Box className=\"code-block-content\" width=\"100%\" style={{ minWidth: 0 }} dangerouslySetInnerHTML={{ __html: highlighted }} />\n ) : (\n <pre className=\"code-block-content\">\n <Code size=\"3\">{code}</Code>\n </pre>\n )}\n </Box>\n\n {shouldShowToggle && !isExpanded && <Box className=\"code-scroll-shadow visible\" />}\n </Flex>\n </Card>\n </Box>\n );\n});\n\n// ============================================\n// Children Code Section (for pre-highlighted MDX)\n// ============================================\n\ninterface ChildrenCodeSectionProps {\n children: ReactNode;\n showCopy?: boolean;\n showLanguage?: boolean;\n}\n\nfunction extractCodeFromChildren(children?: ReactNode): string {\n const extractText = (node: any): string => {\n if (typeof node === \"string\") return node;\n if (typeof node === \"number\") return String(node);\n if (!node) return \"\";\n if (Array.isArray(node)) return node.map(extractText).join(\"\");\n if (typeof node === \"object\" && \"props\" in node) {\n const props = node.props;\n if (props?.children) return extractText(props.children);\n }\n return \"\";\n };\n return extractText(children);\n}\n\nfunction extractLanguageFromChildren(children?: ReactNode): string {\n const findLanguage = (node: any): string | null => {\n if (!node) return null;\n if (typeof node === \"object\" && \"props\" in node) {\n const props = node.props;\n const className = props?.className || props?.class || \"\";\n if (typeof className === \"string\") {\n const match = className.match(/language-([\\w-]+)/i);\n if (match) return match[1];\n }\n if (props?.children) {\n if (Array.isArray(props.children)) {\n for (const child of props.children) {\n const lang = findLanguage(child);\n if (lang) return lang;\n }\n } else {\n return findLanguage(props.children);\n }\n }\n }\n return null;\n };\n return findLanguage(children) || \"text\";\n}\n\nfunction formatLanguageLabel(lang: string): string {\n const aliasMap: Record<string, string> = {\n tsx: \"TSX\",\n ts: \"TS\",\n jsx: \"JSX\",\n js: \"JS\",\n javascript: \"JS\",\n typescript: \"TS\",\n css: \"CSS\",\n html: \"HTML\",\n json: \"JSON\",\n bash: \"SH\",\n sh: \"SH\",\n shell: \"SH\",\n text: \"plaintext\",\n };\n return aliasMap[lang.toLowerCase()] || lang.toLowerCase();\n}\n\nconst ChildrenCodeSection = memo(function ChildrenCodeSection({ children, showCopy = true, showLanguage = true }: ChildrenCodeSectionProps) {\n const [isExpanded, setIsExpanded] = useState(false);\n const [contentHeight, setContentHeight] = useState(COLLAPSED_HEIGHT);\n const [copied, setCopied] = useState(false);\n const contentRef = useRef<HTMLDivElement>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const code = extractCodeFromChildren(children);\n const language = extractLanguageFromChildren(children);\n const displayLanguage = formatLanguageLabel(language);\n\n const shouldShowToggle = contentHeight > COLLAPSED_HEIGHT;\n\n useEffect(() => {\n if (contentRef.current) {\n setContentHeight(contentRef.current.scrollHeight);\n }\n }, [children]);\n\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n };\n }, []);\n\n const handleCopy = useCallback(async () => {\n if (!code.trim()) return;\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n resetTimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n } catch {\n // Silently fail\n }\n }, [code]);\n\n const contentStyle: React.CSSProperties = {\n maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,\n };\n\n const handleToggle = useCallback(() => {\n setIsExpanded((prev) => !prev);\n }, []);\n\n const chevronStyle: React.CSSProperties = {\n transform: isExpanded ? \"rotate(180deg)\" : \"rotate(0deg)\",\n };\n\n return (\n <Box position=\"relative\">\n <Card size=\"1\" variant=\"soft\">\n <Flex direction=\"column\" gap=\"3\">\n <Flex gap=\"2\" justify=\"between\" align=\"start\">\n {showLanguage && (\n <Code size=\"1\" color=\"gray\" highContrast>\n {displayLanguage}\n </Code>\n )}\n <Flex align=\"center\" gap=\"2\" className=\"code-action-buttons\">\n {shouldShowToggle && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleToggle}\n tooltip={isExpanded ? \"Collapse\" : \"Expand\"}\n aria-label={isExpanded ? \"Collapse code\" : \"Expand code\"}\n >\n <HugeiconsIcon icon={ArrowDown01Icon} style={chevronStyle} className=\"code-chevron\" />\n </Button>\n )}\n {showCopy && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleCopy}\n tooltip={copied ? \"Copied!\" : \"Copy\"}\n aria-label={copied ? \"Copied!\" : \"Copy code\"}\n >\n <HugeiconsIcon icon={copied ? Tick01Icon : Copy01Icon} /> Copy\n </Button>\n )}\n </Flex>\n </Flex>\n\n <Box ref={contentRef} style={contentStyle} className=\"code-content\">\n <div className=\"code-block-content\">{children}</div>\n </Box>\n\n {shouldShowToggle && !isExpanded && <Box className=\"code-scroll-shadow visible\" />}\n </Flex>\n </Card>\n </Box>\n );\n});\n\n// ============================================\n// Main CodeBlock Component\n// ============================================\n\nexport function CodeBlock({\n children,\n code,\n language,\n preview,\n showCopy = true,\n showLanguage = true,\n lightTheme,\n darkTheme,\n background,\n backgroundProps,\n}: CodeBlockProps) {\n const hasCode = code || (children && React.Children.count(children) > 0);\n const displayLanguage = language || extractLanguageFromChildren(children) || \"text\";\n\n return (\n <Box className=\"docs-code-block\" mt=\"6\" mb=\"8\">\n <Flex direction=\"column\" gap=\"2\">\n {preview && (\n <PreviewSection background={background} backgroundProps={backgroundProps}>\n {preview}\n </PreviewSection>\n )}\n\n {code && (\n <CodeSection code={code} language={displayLanguage} showCopy={showCopy} showLanguage={showLanguage} lightTheme={lightTheme} darkTheme={darkTheme} />\n )}\n\n {children && !code && (\n <ChildrenCodeSection showCopy={showCopy} showLanguage={showLanguage}>\n {children}\n </ChildrenCodeSection>\n )}\n </Flex>\n </Box>\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["CodeBlock_exports", "__export", "CodeBlock", "__toCommonJS", "import_react", "import_kookie_ui", "import_core_free_icons", "import_shiki", "COLLAPSED_HEIGHT", "DEFAULT_LIGHT_THEME", "DEFAULT_DARK_THEME", "PreviewSection", "children", "background", "backgroundProps", "dotSize", "color", "backgroundColor", "height", "width", "radius", "React", "dotsStyle", "imageStyle", "CodeSection", "code", "language", "showCopy", "showLanguage", "lightTheme", "darkTheme", "highlighted", "setHighlighted", "isExpanded", "setIsExpanded", "contentHeight", "setContentHeight", "copied", "setCopied", "contentRef", "resetTimeoutRef", "shouldShowToggle", "cancelled", "html", "handleCopy", "displayLanguage", "handleToggle", "prev", "contentStyle", "extractCodeFromChildren", "extractText", "node", "props", "extractLanguageFromChildren", "findLanguage", "className", "match", "child", "lang", "formatLanguageLabel", "ChildrenCodeSection", "preview", "hasCode"]
|
|
7
7
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"CodeBlock.d.ts","sourceRoot":"","sources":["../../../../src/components/code/CodeBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyE,MAAM,OAAO,CAAC;AAK9F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;
|
|
1
|
+
{"version":3,"file":"CodeBlock.d.ts","sourceRoot":"","sources":["../../../../src/components/code/CodeBlock.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAyE,MAAM,OAAO,CAAC;AAK9F,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAoY9C,wBAAgB,SAAS,CAAC,EACxB,QAAQ,EACR,IAAI,EACJ,QAAQ,EACR,OAAO,EACP,QAAe,EACf,YAAmB,EACnB,UAAU,EACV,SAAS,EACT,UAAU,EACV,eAAe,GAChB,EAAE,cAAc,qBAyBhB"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import e,{useState as
|
|
1
|
+
import e,{useState as m,useRef as H,useEffect as T,useCallback as P,memo as I}from"react";import{Box as f,Card as v,Flex as p,Button as L,Code as z,Theme as B}from"@kushagradhawan/kookie-ui";import{HugeiconsIcon as N}from"@hugeicons/react";import{Copy01Icon as $,Tick01Icon as A,ArrowDown01Icon as M}from"@hugeicons/core-free-icons";import{codeToHtml as G}from"shiki";const y=360,O="one-light",U="one-dark-pro";function X({children:r,background:t="none",backgroundProps:o={}}){const{dotSize:n=24,color:i="var(--gray-10)",backgroundColor:a="var(--gray-2)",height:s,width:u="100%",radius:c="3"}=o;if(t==="none")return e.createElement(v,{size:"1",variant:"soft"},e.createElement(p,{justify:"center",align:"center",py:"4"},e.createElement(B,{fontFamily:"sans"},r)));if(t==="dots"){const g={backgroundImage:`radial-gradient(circle, ${i} 1px, transparent 1px)`,borderRadius:`var(--radius-${c})`,backgroundSize:`${n}px ${n}px`,backgroundPosition:"center",backgroundColor:a,width:u,...s&&{height:s}};return e.createElement(v,{size:"1",variant:"soft"},e.createElement(p,{justify:"center",align:"center",py:"4",style:g},e.createElement(B,{fontFamily:"sans"},r)))}const l={backgroundImage:`url(${t})`,backgroundSize:"cover",backgroundPosition:"center",backgroundRepeat:"no-repeat",borderRadius:`var(--radius-${c})`,width:u,...s&&{height:s}};return e.createElement(v,{size:"1",variant:"soft"},e.createElement(p,{justify:"center",align:"center",py:"4",style:l},e.createElement(B,{fontFamily:"sans"},r)))}const K=I(function({code:t,language:o,showCopy:n=!0,showLanguage:i=!0,lightTheme:a=O,darkTheme:s=U}){const[u,c]=m(null),[l,g]=m(!1),[d,S]=m(y),[x,k]=m(!1),C=H(null),h=H(null),w=d>y;T(()=>{let b=!1;return G(t,{lang:o,themes:{light:a,dark:s},defaultColor:!1}).then(J=>{b||c(J)}).catch(()=>{b||c(null)}),()=>{b=!0}},[t,o,a,s]),T(()=>{C.current&&S(C.current.scrollHeight)},[u]),T(()=>()=>{h.current&&clearTimeout(h.current)},[]);const E=P(async()=>{if(t.trim())try{await navigator.clipboard.writeText(t),k(!0),h.current&&clearTimeout(h.current),h.current=setTimeout(()=>k(!1),2e3)}catch{}},[t]),j=o==="text"?"plaintext":o,F=P(()=>{g(b=>!b)},[]),_={maxHeight:l?`${d}px`:`${y}px`};return e.createElement(f,{position:"relative"},e.createElement(v,{size:"1",variant:"soft"},e.createElement(p,{direction:"column",gap:"3"},e.createElement(p,{gap:"2",justify:"between",align:"start"},i&&e.createElement(z,{size:"1",color:"gray",highContrast:!0},j),e.createElement(p,{align:"center",gap:"2",className:"code-action-buttons"},w&&e.createElement(L,{size:"2",variant:"ghost",color:"gray",onClick:F,tooltip:l?"Collapse":"Expand","aria-label":l?"Collapse code":"Expand code"},e.createElement(N,{icon:M,style:{transform:l?"rotate(180deg)":"rotate(0deg)"},className:"code-chevron"})),n&&e.createElement(L,{size:"2",variant:"ghost",color:"gray",onClick:E,tooltip:x?"Copied!":"Copy","aria-label":x?"Copied!":"Copy code"},e.createElement(N,{icon:x?A:$})," Copy"))),e.createElement(f,{ref:C,style:_,className:"code-content"},u?e.createElement(f,{className:"code-block-content",width:"100%",style:{minWidth:0},dangerouslySetInnerHTML:{__html:u}}):e.createElement("pre",{className:"code-block-content"},e.createElement(z,{size:"3"},t))),w&&!l&&e.createElement(f,{className:"code-scroll-shadow visible"}))))});function W(r){const t=o=>{if(typeof o=="string")return o;if(typeof o=="number")return String(o);if(!o)return"";if(Array.isArray(o))return o.map(t).join("");if(typeof o=="object"&&"props"in o){const n=o.props;if(n?.children)return t(n.children)}return""};return t(r)}function D(r){const t=o=>{if(!o)return null;if(typeof o=="object"&&"props"in o){const n=o.props,i=n?.className||n?.class||"";if(typeof i=="string"){const a=i.match(/language-([\w-]+)/i);if(a)return a[1]}if(n?.children)if(Array.isArray(n.children))for(const a of n.children){const s=t(a);if(s)return s}else return t(n.children)}return null};return t(r)||"text"}function q(r){return{tsx:"TSX",ts:"TS",jsx:"JSX",js:"JS",javascript:"JS",typescript:"TS",css:"CSS",html:"HTML",json:"JSON",bash:"SH",sh:"SH",shell:"SH",text:"plaintext"}[r.toLowerCase()]||r.toLowerCase()}const Q=I(function({children:t,showCopy:o=!0,showLanguage:n=!0}){const[i,a]=m(!1),[s,u]=m(y),[c,l]=m(!1),g=H(null),d=H(null),S=W(t),x=D(t),k=q(x),C=s>y;T(()=>{g.current&&u(g.current.scrollHeight)},[t]),T(()=>()=>{d.current&&clearTimeout(d.current)},[]);const h=P(async()=>{if(S.trim())try{await navigator.clipboard.writeText(S),l(!0),d.current&&clearTimeout(d.current),d.current=setTimeout(()=>l(!1),2e3)}catch{}},[S]),w={maxHeight:i?`${s}px`:`${y}px`},E=P(()=>{a(F=>!F)},[]);return e.createElement(f,{position:"relative"},e.createElement(v,{size:"1",variant:"soft"},e.createElement(p,{direction:"column",gap:"3"},e.createElement(p,{gap:"2",justify:"between",align:"start"},n&&e.createElement(z,{size:"1",color:"gray",highContrast:!0},k),e.createElement(p,{align:"center",gap:"2",className:"code-action-buttons"},C&&e.createElement(L,{size:"2",variant:"ghost",color:"gray",onClick:E,tooltip:i?"Collapse":"Expand","aria-label":i?"Collapse code":"Expand code"},e.createElement(N,{icon:M,style:{transform:i?"rotate(180deg)":"rotate(0deg)"},className:"code-chevron"})),o&&e.createElement(L,{size:"2",variant:"ghost",color:"gray",onClick:h,tooltip:c?"Copied!":"Copy","aria-label":c?"Copied!":"Copy code"},e.createElement(N,{icon:c?A:$})," Copy"))),e.createElement(f,{ref:g,style:w,className:"code-content"},e.createElement("div",{className:"code-block-content"},t)),C&&!i&&e.createElement(f,{className:"code-scroll-shadow visible"}))))});function oe({children:r,code:t,language:o,preview:n,showCopy:i=!0,showLanguage:a=!0,lightTheme:s,darkTheme:u,background:c,backgroundProps:l}){const g=t||r&&e.Children.count(r)>0,d=o||D(r)||"text";return e.createElement(f,{className:"docs-code-block",mt:"6",mb:"8"},e.createElement(p,{direction:"column",gap:"2"},n&&e.createElement(X,{background:c,backgroundProps:l},n),t&&e.createElement(K,{code:t,language:d,showCopy:i,showLanguage:a,lightTheme:s,darkTheme:u}),r&&!t&&e.createElement(Q,{showCopy:i,showLanguage:a},r)))}export{oe as CodeBlock};
|
|
2
2
|
//# sourceMappingURL=CodeBlock.js.map
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../../../src/components/code/CodeBlock.tsx"],
|
|
4
|
-
"sourcesContent": ["import React, { useState, useRef, useEffect, useCallback, memo, type ReactNode } from \"react\";\nimport { Box, Card, Flex, Button, Code, Theme } from \"@kushagradhawan/kookie-ui\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { Copy01Icon, Tick01Icon } from \"@hugeicons/core-free-icons\";\nimport { codeToHtml } from \"shiki\";\nimport type { CodeBlockProps } from \"./types\";\n\nconst COLLAPSED_HEIGHT = 360;\nconst DEFAULT_LIGHT_THEME = \"one-light\";\nconst DEFAULT_DARK_THEME = \"one-dark-pro\";\n\n// ============================================\n// Preview Section\n// ============================================\n\ninterface PreviewSectionProps {\n children: ReactNode;\n background?: \"none\" | \"dots\" | string;\n backgroundProps?: {\n dotSize?: number;\n color?: string;\n backgroundColor?: string;\n height?: string;\n width?: string;\n radius?: string;\n };\n}\n\nfunction PreviewSection({ children, background = \"none\", backgroundProps = {} }: PreviewSectionProps) {\n const { dotSize = 24, color = \"var(--gray-10)\", backgroundColor = \"var(--gray-2)\", height, width = \"100%\", radius = \"3\" } = backgroundProps;\n\n if (background === \"none\") {\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\">\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n }\n\n if (background === \"dots\") {\n const dotsStyle: React.CSSProperties = {\n backgroundImage: `radial-gradient(circle, ${color} 1px, transparent 1px)`,\n borderRadius: `var(--radius-${radius})`,\n backgroundSize: `${dotSize}px ${dotSize}px`,\n backgroundPosition: \"center\",\n backgroundColor,\n width,\n ...(height && { height }),\n };\n\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\" style={dotsStyle}>\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n }\n\n const imageStyle: React.CSSProperties = {\n backgroundImage: `url(${background})`,\n backgroundSize: \"cover\",\n backgroundPosition: \"center\",\n backgroundRepeat: \"no-repeat\",\n borderRadius: `var(--radius-${radius})`,\n width,\n ...(height && { height }),\n };\n\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\" style={imageStyle}>\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n}\n\n// ============================================\n// Code Section (for runtime highlighting)\n// ============================================\n\ninterface CodeSectionProps {\n code: string;\n language: string;\n showCopy?: boolean;\n showLanguage?: boolean;\n lightTheme?: string;\n darkTheme?: string;\n}\n\nconst CodeSection = memo(function CodeSection({\n code,\n language,\n showCopy = true,\n showLanguage = true,\n lightTheme = DEFAULT_LIGHT_THEME,\n darkTheme = DEFAULT_DARK_THEME,\n}: CodeSectionProps) {\n const [highlighted, setHighlighted] = useState<string | null>(null);\n const [isExpanded, setIsExpanded] = useState(false);\n const [contentHeight, setContentHeight] = useState(COLLAPSED_HEIGHT);\n const [copied, setCopied] = useState(false);\n const contentRef = useRef<HTMLDivElement>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const shouldShowToggle = contentHeight > COLLAPSED_HEIGHT;\n\n useEffect(() => {\n let cancelled = false;\n codeToHtml(code, {\n lang: language,\n themes: { light: lightTheme, dark: darkTheme },\n defaultColor: false,\n })\n .then((html) => {\n if (!cancelled) setHighlighted(html);\n })\n .catch(() => {\n if (!cancelled) setHighlighted(null);\n });\n return () => {\n cancelled = true;\n };\n }, [code, language, lightTheme, darkTheme]);\n\n useEffect(() => {\n if (contentRef.current) {\n setContentHeight(contentRef.current.scrollHeight);\n }\n }, [highlighted]);\n\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n };\n }, []);\n\n const handleCopy = useCallback(async () => {\n if (!code.trim()) return;\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n resetTimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n } catch {\n // Silently fail\n }\n }, [code]);\n\n const displayLanguage = language === \"text\" ? \"plaintext\" : language;\n\n const contentStyle: React.CSSProperties = {\n maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,\n };\n\n return (\n <Box position=\"relative\">\n <Card size=\"1\" variant=\"soft\">\n <Flex direction=\"column\" gap=\"3\">\n <Flex gap=\"2\" justify=\"between\" align=\"start\">\n {showLanguage && (\n <Code size=\"1\" color=\"gray\" highContrast>\n {displayLanguage}\n </Code>\n )}\n <Flex align=\"center\" gap=\"2\" className=\"code-action-buttons\">\n {showCopy && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleCopy}\n tooltip={copied ? \"Copied!\" : \"Copy\"}\n aria-label={copied ? \"Copied!\" : \"Copy code\"}\n >\n <HugeiconsIcon icon={copied ? Tick01Icon : Copy01Icon} /> Copy\n </Button>\n )}\n </Flex>\n </Flex>\n\n <Box ref={contentRef} style={contentStyle} className=\"code-content\">\n {highlighted ? (\n <Box className=\"code-block-content\" width=\"100%\" style={{ minWidth: 0 }} dangerouslySetInnerHTML={{ __html: highlighted }} />\n ) : (\n <pre className=\"code-block-content\">\n <Code size=\"3\">{code}</Code>\n </pre>\n )}\n </Box>\n\n {shouldShowToggle && !isExpanded && <Box className=\"code-scroll-shadow visible\" />}\n </Flex>\n </Card>\n </Box>\n );\n});\n\n// ============================================\n// Children Code Section (for pre-highlighted MDX)\n// ============================================\n\ninterface ChildrenCodeSectionProps {\n children: ReactNode;\n showCopy?: boolean;\n showLanguage?: boolean;\n}\n\nfunction extractCodeFromChildren(children?: ReactNode): string {\n if (!children) return \"\";\n if (typeof children === \"object\" && children !== null && \"props\" in children) {\n const childProps = (children as any).props;\n if (childProps?.children && typeof childProps.children === \"object\") {\n const codeProps = (childProps.children as any).props;\n const codeChildren = codeProps?.children;\n if (typeof codeChildren === \"string\") return codeChildren;\n }\n if (typeof childProps?.children === \"string\") return childProps.children;\n }\n if (typeof children === \"string\") return children;\n return \"\";\n}\n\nfunction extractLanguageFromChildren(children?: ReactNode): string {\n if (!children) return \"text\";\n if (typeof children === \"object\" && children !== null && \"props\" in children) {\n const childProps = (children as any).props;\n if (childProps?.children && typeof childProps.children === \"object\") {\n const codeProps = (childProps.children as any).props;\n const className = codeProps?.className || \"\";\n const match = className.match(/language-([\\w-]+)/i);\n if (match) return match[1];\n }\n const className = childProps?.className || \"\";\n const match = className.match(/language-([\\w-]+)/i);\n if (match) return match[1];\n }\n return \"text\";\n}\n\nfunction formatLanguageLabel(lang: string): string {\n const aliasMap: Record<string, string> = {\n tsx: \"TSX\",\n ts: \"TS\",\n jsx: \"JSX\",\n js: \"JS\",\n javascript: \"JS\",\n typescript: \"TS\",\n css: \"CSS\",\n html: \"HTML\",\n json: \"JSON\",\n bash: \"SH\",\n sh: \"SH\",\n shell: \"SH\",\n text: \"plaintext\",\n };\n return aliasMap[lang.toLowerCase()] || lang.toLowerCase();\n}\n\nconst ChildrenCodeSection = memo(function ChildrenCodeSection({ children, showCopy = true, showLanguage = true }: ChildrenCodeSectionProps) {\n const [isExpanded, setIsExpanded] = useState(false);\n const [contentHeight, setContentHeight] = useState(COLLAPSED_HEIGHT);\n const [copied, setCopied] = useState(false);\n const contentRef = useRef<HTMLDivElement>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const code = extractCodeFromChildren(children);\n const language = extractLanguageFromChildren(children);\n const displayLanguage = formatLanguageLabel(language);\n\n const shouldShowToggle = contentHeight > COLLAPSED_HEIGHT;\n\n useEffect(() => {\n if (contentRef.current) {\n setContentHeight(contentRef.current.scrollHeight);\n }\n }, [children]);\n\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n };\n }, []);\n\n const handleCopy = useCallback(async () => {\n if (!code.trim()) return;\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n resetTimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n } catch {\n // Silently fail\n }\n }, [code]);\n\n const contentStyle: React.CSSProperties = {\n maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,\n };\n\n return (\n <Box position=\"relative\">\n <Card size=\"1\" variant=\"soft\">\n <Flex direction=\"column\" gap=\"3\">\n <Flex gap=\"2\" justify=\"between\" align=\"start\">\n {showLanguage && (\n <Code size=\"1\" color=\"gray\" highContrast>\n {displayLanguage}\n </Code>\n )}\n <Flex align=\"center\" gap=\"2\" className=\"code-action-buttons\">\n {showCopy && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleCopy}\n tooltip={copied ? \"Copied!\" : \"Copy\"}\n aria-label={copied ? \"Copied!\" : \"Copy code\"}\n >\n <HugeiconsIcon icon={copied ? Tick01Icon : Copy01Icon} /> Copy\n </Button>\n )}\n </Flex>\n </Flex>\n\n <Box ref={contentRef} style={contentStyle} className=\"code-content\">\n <div className=\"code-block-content\">{children}</div>\n </Box>\n\n {shouldShowToggle && !isExpanded && <Box className=\"code-scroll-shadow visible\" />}\n </Flex>\n </Card>\n </Box>\n );\n});\n\n// ============================================\n// Main CodeBlock Component\n// ============================================\n\nexport function CodeBlock({\n children,\n code,\n language,\n preview,\n showCopy = true,\n showLanguage = true,\n lightTheme,\n darkTheme,\n background,\n backgroundProps,\n}: CodeBlockProps) {\n const hasCode = code || (children && React.Children.count(children) > 0);\n const displayLanguage = language || extractLanguageFromChildren(children) || \"text\";\n\n return (\n <Box className=\"docs-code-block\" mt=\"6\" mb=\"8\">\n <Flex direction=\"column\" gap=\"2\">\n {preview && (\n <PreviewSection background={background} backgroundProps={backgroundProps}>\n {preview}\n </PreviewSection>\n )}\n\n {code && (\n <CodeSection code={code} language={displayLanguage} showCopy={showCopy} showLanguage={showLanguage} lightTheme={lightTheme} darkTheme={darkTheme} />\n )}\n\n {children && !code && (\n <ChildrenCodeSection showCopy={showCopy} showLanguage={showLanguage}>\n {children}\n </ChildrenCodeSection>\n )}\n </Flex>\n </Box>\n );\n}\n"],
|
|
5
|
-
"mappings": "AAAA,OAAOA,GAAS,YAAAC,EAAU,UAAAC,EAAQ,aAAAC,EAAW,eAAAC,EAAa,QAAAC,MAA4B,QACtF,OAAS,OAAAC,EAAK,QAAAC,EAAM,QAAAC,EAAM,UAAAC,EAAQ,QAAAC,EAAM,SAAAC,MAAa,4BACrD,OAAS,iBAAAC,MAAqB,mBAC9B,OAAS,cAAAC,EAAY,cAAAC,
|
|
6
|
-
"names": ["React", "useState", "useRef", "useEffect", "useCallback", "memo", "Box", "Card", "Flex", "Button", "Code", "Theme", "HugeiconsIcon", "Copy01Icon", "Tick01Icon", "codeToHtml", "COLLAPSED_HEIGHT", "DEFAULT_LIGHT_THEME", "DEFAULT_DARK_THEME", "PreviewSection", "children", "background", "backgroundProps", "dotSize", "color", "backgroundColor", "height", "width", "radius", "dotsStyle", "imageStyle", "CodeSection", "code", "language", "showCopy", "showLanguage", "lightTheme", "darkTheme", "highlighted", "setHighlighted", "isExpanded", "setIsExpanded", "contentHeight", "setContentHeight", "copied", "setCopied", "contentRef", "resetTimeoutRef", "shouldShowToggle", "cancelled", "html", "handleCopy", "displayLanguage", "contentStyle", "extractCodeFromChildren", "
|
|
4
|
+
"sourcesContent": ["import React, { useState, useRef, useEffect, useCallback, memo, type ReactNode } from \"react\";\nimport { Box, Card, Flex, Button, Code, Theme } from \"@kushagradhawan/kookie-ui\";\nimport { HugeiconsIcon } from \"@hugeicons/react\";\nimport { Copy01Icon, Tick01Icon, ArrowDown01Icon } from \"@hugeicons/core-free-icons\";\nimport { codeToHtml } from \"shiki\";\nimport type { CodeBlockProps } from \"./types\";\n\nconst COLLAPSED_HEIGHT = 360;\nconst DEFAULT_LIGHT_THEME = \"one-light\";\nconst DEFAULT_DARK_THEME = \"one-dark-pro\";\n\n// ============================================\n// Preview Section\n// ============================================\n\ninterface PreviewSectionProps {\n children: ReactNode;\n background?: \"none\" | \"dots\" | string;\n backgroundProps?: {\n dotSize?: number;\n color?: string;\n backgroundColor?: string;\n height?: string;\n width?: string;\n radius?: string;\n };\n}\n\nfunction PreviewSection({ children, background = \"none\", backgroundProps = {} }: PreviewSectionProps) {\n const { dotSize = 24, color = \"var(--gray-10)\", backgroundColor = \"var(--gray-2)\", height, width = \"100%\", radius = \"3\" } = backgroundProps;\n\n if (background === \"none\") {\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\">\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n }\n\n if (background === \"dots\") {\n const dotsStyle: React.CSSProperties = {\n backgroundImage: `radial-gradient(circle, ${color} 1px, transparent 1px)`,\n borderRadius: `var(--radius-${radius})`,\n backgroundSize: `${dotSize}px ${dotSize}px`,\n backgroundPosition: \"center\",\n backgroundColor,\n width,\n ...(height && { height }),\n };\n\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\" style={dotsStyle}>\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n }\n\n const imageStyle: React.CSSProperties = {\n backgroundImage: `url(${background})`,\n backgroundSize: \"cover\",\n backgroundPosition: \"center\",\n backgroundRepeat: \"no-repeat\",\n borderRadius: `var(--radius-${radius})`,\n width,\n ...(height && { height }),\n };\n\n return (\n <Card size=\"1\" variant=\"soft\">\n <Flex justify=\"center\" align=\"center\" py=\"4\" style={imageStyle}>\n <Theme fontFamily=\"sans\">{children}</Theme>\n </Flex>\n </Card>\n );\n}\n\n// ============================================\n// Code Section (for runtime highlighting)\n// ============================================\n\ninterface CodeSectionProps {\n code: string;\n language: string;\n showCopy?: boolean;\n showLanguage?: boolean;\n lightTheme?: string;\n darkTheme?: string;\n}\n\nconst CodeSection = memo(function CodeSection({\n code,\n language,\n showCopy = true,\n showLanguage = true,\n lightTheme = DEFAULT_LIGHT_THEME,\n darkTheme = DEFAULT_DARK_THEME,\n}: CodeSectionProps) {\n const [highlighted, setHighlighted] = useState<string | null>(null);\n const [isExpanded, setIsExpanded] = useState(false);\n const [contentHeight, setContentHeight] = useState(COLLAPSED_HEIGHT);\n const [copied, setCopied] = useState(false);\n const contentRef = useRef<HTMLDivElement>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const shouldShowToggle = contentHeight > COLLAPSED_HEIGHT;\n\n useEffect(() => {\n let cancelled = false;\n codeToHtml(code, {\n lang: language,\n themes: { light: lightTheme, dark: darkTheme },\n defaultColor: false,\n })\n .then((html) => {\n if (!cancelled) setHighlighted(html);\n })\n .catch(() => {\n if (!cancelled) setHighlighted(null);\n });\n return () => {\n cancelled = true;\n };\n }, [code, language, lightTheme, darkTheme]);\n\n useEffect(() => {\n if (contentRef.current) {\n setContentHeight(contentRef.current.scrollHeight);\n }\n }, [highlighted]);\n\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n };\n }, []);\n\n const handleCopy = useCallback(async () => {\n if (!code.trim()) return;\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n resetTimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n } catch {\n // Silently fail\n }\n }, [code]);\n\n const displayLanguage = language === \"text\" ? \"plaintext\" : language;\n\n const handleToggle = useCallback(() => {\n setIsExpanded((prev) => !prev);\n }, []);\n\n const contentStyle: React.CSSProperties = {\n maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,\n };\n\n const chevronStyle: React.CSSProperties = {\n transform: isExpanded ? \"rotate(180deg)\" : \"rotate(0deg)\",\n };\n\n return (\n <Box position=\"relative\">\n <Card size=\"1\" variant=\"soft\">\n <Flex direction=\"column\" gap=\"3\">\n <Flex gap=\"2\" justify=\"between\" align=\"start\">\n {showLanguage && (\n <Code size=\"1\" color=\"gray\" highContrast>\n {displayLanguage}\n </Code>\n )}\n <Flex align=\"center\" gap=\"2\" className=\"code-action-buttons\">\n {shouldShowToggle && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleToggle}\n tooltip={isExpanded ? \"Collapse\" : \"Expand\"}\n aria-label={isExpanded ? \"Collapse code\" : \"Expand code\"}\n >\n <HugeiconsIcon icon={ArrowDown01Icon} style={chevronStyle} className=\"code-chevron\" />\n </Button>\n )}\n {showCopy && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleCopy}\n tooltip={copied ? \"Copied!\" : \"Copy\"}\n aria-label={copied ? \"Copied!\" : \"Copy code\"}\n >\n <HugeiconsIcon icon={copied ? Tick01Icon : Copy01Icon} /> Copy\n </Button>\n )}\n </Flex>\n </Flex>\n\n <Box ref={contentRef} style={contentStyle} className=\"code-content\">\n {highlighted ? (\n <Box className=\"code-block-content\" width=\"100%\" style={{ minWidth: 0 }} dangerouslySetInnerHTML={{ __html: highlighted }} />\n ) : (\n <pre className=\"code-block-content\">\n <Code size=\"3\">{code}</Code>\n </pre>\n )}\n </Box>\n\n {shouldShowToggle && !isExpanded && <Box className=\"code-scroll-shadow visible\" />}\n </Flex>\n </Card>\n </Box>\n );\n});\n\n// ============================================\n// Children Code Section (for pre-highlighted MDX)\n// ============================================\n\ninterface ChildrenCodeSectionProps {\n children: ReactNode;\n showCopy?: boolean;\n showLanguage?: boolean;\n}\n\nfunction extractCodeFromChildren(children?: ReactNode): string {\n const extractText = (node: any): string => {\n if (typeof node === \"string\") return node;\n if (typeof node === \"number\") return String(node);\n if (!node) return \"\";\n if (Array.isArray(node)) return node.map(extractText).join(\"\");\n if (typeof node === \"object\" && \"props\" in node) {\n const props = node.props;\n if (props?.children) return extractText(props.children);\n }\n return \"\";\n };\n return extractText(children);\n}\n\nfunction extractLanguageFromChildren(children?: ReactNode): string {\n const findLanguage = (node: any): string | null => {\n if (!node) return null;\n if (typeof node === \"object\" && \"props\" in node) {\n const props = node.props;\n const className = props?.className || props?.class || \"\";\n if (typeof className === \"string\") {\n const match = className.match(/language-([\\w-]+)/i);\n if (match) return match[1];\n }\n if (props?.children) {\n if (Array.isArray(props.children)) {\n for (const child of props.children) {\n const lang = findLanguage(child);\n if (lang) return lang;\n }\n } else {\n return findLanguage(props.children);\n }\n }\n }\n return null;\n };\n return findLanguage(children) || \"text\";\n}\n\nfunction formatLanguageLabel(lang: string): string {\n const aliasMap: Record<string, string> = {\n tsx: \"TSX\",\n ts: \"TS\",\n jsx: \"JSX\",\n js: \"JS\",\n javascript: \"JS\",\n typescript: \"TS\",\n css: \"CSS\",\n html: \"HTML\",\n json: \"JSON\",\n bash: \"SH\",\n sh: \"SH\",\n shell: \"SH\",\n text: \"plaintext\",\n };\n return aliasMap[lang.toLowerCase()] || lang.toLowerCase();\n}\n\nconst ChildrenCodeSection = memo(function ChildrenCodeSection({ children, showCopy = true, showLanguage = true }: ChildrenCodeSectionProps) {\n const [isExpanded, setIsExpanded] = useState(false);\n const [contentHeight, setContentHeight] = useState(COLLAPSED_HEIGHT);\n const [copied, setCopied] = useState(false);\n const contentRef = useRef<HTMLDivElement>(null);\n const resetTimeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);\n\n const code = extractCodeFromChildren(children);\n const language = extractLanguageFromChildren(children);\n const displayLanguage = formatLanguageLabel(language);\n\n const shouldShowToggle = contentHeight > COLLAPSED_HEIGHT;\n\n useEffect(() => {\n if (contentRef.current) {\n setContentHeight(contentRef.current.scrollHeight);\n }\n }, [children]);\n\n useEffect(() => {\n return () => {\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n };\n }, []);\n\n const handleCopy = useCallback(async () => {\n if (!code.trim()) return;\n try {\n await navigator.clipboard.writeText(code);\n setCopied(true);\n if (resetTimeoutRef.current) clearTimeout(resetTimeoutRef.current);\n resetTimeoutRef.current = setTimeout(() => setCopied(false), 2000);\n } catch {\n // Silently fail\n }\n }, [code]);\n\n const contentStyle: React.CSSProperties = {\n maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,\n };\n\n const handleToggle = useCallback(() => {\n setIsExpanded((prev) => !prev);\n }, []);\n\n const chevronStyle: React.CSSProperties = {\n transform: isExpanded ? \"rotate(180deg)\" : \"rotate(0deg)\",\n };\n\n return (\n <Box position=\"relative\">\n <Card size=\"1\" variant=\"soft\">\n <Flex direction=\"column\" gap=\"3\">\n <Flex gap=\"2\" justify=\"between\" align=\"start\">\n {showLanguage && (\n <Code size=\"1\" color=\"gray\" highContrast>\n {displayLanguage}\n </Code>\n )}\n <Flex align=\"center\" gap=\"2\" className=\"code-action-buttons\">\n {shouldShowToggle && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleToggle}\n tooltip={isExpanded ? \"Collapse\" : \"Expand\"}\n aria-label={isExpanded ? \"Collapse code\" : \"Expand code\"}\n >\n <HugeiconsIcon icon={ArrowDown01Icon} style={chevronStyle} className=\"code-chevron\" />\n </Button>\n )}\n {showCopy && (\n <Button\n size=\"2\"\n variant=\"ghost\"\n color=\"gray\"\n onClick={handleCopy}\n tooltip={copied ? \"Copied!\" : \"Copy\"}\n aria-label={copied ? \"Copied!\" : \"Copy code\"}\n >\n <HugeiconsIcon icon={copied ? Tick01Icon : Copy01Icon} /> Copy\n </Button>\n )}\n </Flex>\n </Flex>\n\n <Box ref={contentRef} style={contentStyle} className=\"code-content\">\n <div className=\"code-block-content\">{children}</div>\n </Box>\n\n {shouldShowToggle && !isExpanded && <Box className=\"code-scroll-shadow visible\" />}\n </Flex>\n </Card>\n </Box>\n );\n});\n\n// ============================================\n// Main CodeBlock Component\n// ============================================\n\nexport function CodeBlock({\n children,\n code,\n language,\n preview,\n showCopy = true,\n showLanguage = true,\n lightTheme,\n darkTheme,\n background,\n backgroundProps,\n}: CodeBlockProps) {\n const hasCode = code || (children && React.Children.count(children) > 0);\n const displayLanguage = language || extractLanguageFromChildren(children) || \"text\";\n\n return (\n <Box className=\"docs-code-block\" mt=\"6\" mb=\"8\">\n <Flex direction=\"column\" gap=\"2\">\n {preview && (\n <PreviewSection background={background} backgroundProps={backgroundProps}>\n {preview}\n </PreviewSection>\n )}\n\n {code && (\n <CodeSection code={code} language={displayLanguage} showCopy={showCopy} showLanguage={showLanguage} lightTheme={lightTheme} darkTheme={darkTheme} />\n )}\n\n {children && !code && (\n <ChildrenCodeSection showCopy={showCopy} showLanguage={showLanguage}>\n {children}\n </ChildrenCodeSection>\n )}\n </Flex>\n </Box>\n );\n}\n"],
|
|
5
|
+
"mappings": "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",
|
|
6
|
+
"names": ["React", "useState", "useRef", "useEffect", "useCallback", "memo", "Box", "Card", "Flex", "Button", "Code", "Theme", "HugeiconsIcon", "Copy01Icon", "Tick01Icon", "ArrowDown01Icon", "codeToHtml", "COLLAPSED_HEIGHT", "DEFAULT_LIGHT_THEME", "DEFAULT_DARK_THEME", "PreviewSection", "children", "background", "backgroundProps", "dotSize", "color", "backgroundColor", "height", "width", "radius", "dotsStyle", "imageStyle", "CodeSection", "code", "language", "showCopy", "showLanguage", "lightTheme", "darkTheme", "highlighted", "setHighlighted", "isExpanded", "setIsExpanded", "contentHeight", "setContentHeight", "copied", "setCopied", "contentRef", "resetTimeoutRef", "shouldShowToggle", "cancelled", "html", "handleCopy", "displayLanguage", "handleToggle", "prev", "contentStyle", "extractCodeFromChildren", "extractText", "node", "props", "extractLanguageFromChildren", "findLanguage", "className", "match", "child", "lang", "formatLanguageLabel", "ChildrenCodeSection", "CodeBlock", "preview", "hasCode"]
|
|
7
7
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { useState, useRef, useEffect, useCallback, memo, type ReactNode } from "react";
|
|
2
2
|
import { Box, Card, Flex, Button, Code, Theme } from "@kushagradhawan/kookie-ui";
|
|
3
3
|
import { HugeiconsIcon } from "@hugeicons/react";
|
|
4
|
-
import { Copy01Icon, Tick01Icon } from "@hugeicons/core-free-icons";
|
|
4
|
+
import { Copy01Icon, Tick01Icon, ArrowDown01Icon } from "@hugeicons/core-free-icons";
|
|
5
5
|
import { codeToHtml } from "shiki";
|
|
6
6
|
import type { CodeBlockProps } from "./types";
|
|
7
7
|
|
|
@@ -152,10 +152,18 @@ const CodeSection = memo(function CodeSection({
|
|
|
152
152
|
|
|
153
153
|
const displayLanguage = language === "text" ? "plaintext" : language;
|
|
154
154
|
|
|
155
|
+
const handleToggle = useCallback(() => {
|
|
156
|
+
setIsExpanded((prev) => !prev);
|
|
157
|
+
}, []);
|
|
158
|
+
|
|
155
159
|
const contentStyle: React.CSSProperties = {
|
|
156
160
|
maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,
|
|
157
161
|
};
|
|
158
162
|
|
|
163
|
+
const chevronStyle: React.CSSProperties = {
|
|
164
|
+
transform: isExpanded ? "rotate(180deg)" : "rotate(0deg)",
|
|
165
|
+
};
|
|
166
|
+
|
|
159
167
|
return (
|
|
160
168
|
<Box position="relative">
|
|
161
169
|
<Card size="1" variant="soft">
|
|
@@ -167,6 +175,18 @@ const CodeSection = memo(function CodeSection({
|
|
|
167
175
|
</Code>
|
|
168
176
|
)}
|
|
169
177
|
<Flex align="center" gap="2" className="code-action-buttons">
|
|
178
|
+
{shouldShowToggle && (
|
|
179
|
+
<Button
|
|
180
|
+
size="2"
|
|
181
|
+
variant="ghost"
|
|
182
|
+
color="gray"
|
|
183
|
+
onClick={handleToggle}
|
|
184
|
+
tooltip={isExpanded ? "Collapse" : "Expand"}
|
|
185
|
+
aria-label={isExpanded ? "Collapse code" : "Expand code"}
|
|
186
|
+
>
|
|
187
|
+
<HugeiconsIcon icon={ArrowDown01Icon} style={chevronStyle} className="code-chevron" />
|
|
188
|
+
</Button>
|
|
189
|
+
)}
|
|
170
190
|
{showCopy && (
|
|
171
191
|
<Button
|
|
172
192
|
size="2"
|
|
@@ -210,35 +230,44 @@ interface ChildrenCodeSectionProps {
|
|
|
210
230
|
}
|
|
211
231
|
|
|
212
232
|
function extractCodeFromChildren(children?: ReactNode): string {
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
if (
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
233
|
+
const extractText = (node: any): string => {
|
|
234
|
+
if (typeof node === "string") return node;
|
|
235
|
+
if (typeof node === "number") return String(node);
|
|
236
|
+
if (!node) return "";
|
|
237
|
+
if (Array.isArray(node)) return node.map(extractText).join("");
|
|
238
|
+
if (typeof node === "object" && "props" in node) {
|
|
239
|
+
const props = node.props;
|
|
240
|
+
if (props?.children) return extractText(props.children);
|
|
220
241
|
}
|
|
221
|
-
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
return "";
|
|
242
|
+
return "";
|
|
243
|
+
};
|
|
244
|
+
return extractText(children);
|
|
225
245
|
}
|
|
226
246
|
|
|
227
247
|
function extractLanguageFromChildren(children?: ReactNode): string {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
248
|
+
const findLanguage = (node: any): string | null => {
|
|
249
|
+
if (!node) return null;
|
|
250
|
+
if (typeof node === "object" && "props" in node) {
|
|
251
|
+
const props = node.props;
|
|
252
|
+
const className = props?.className || props?.class || "";
|
|
253
|
+
if (typeof className === "string") {
|
|
254
|
+
const match = className.match(/language-([\w-]+)/i);
|
|
255
|
+
if (match) return match[1];
|
|
256
|
+
}
|
|
257
|
+
if (props?.children) {
|
|
258
|
+
if (Array.isArray(props.children)) {
|
|
259
|
+
for (const child of props.children) {
|
|
260
|
+
const lang = findLanguage(child);
|
|
261
|
+
if (lang) return lang;
|
|
262
|
+
}
|
|
263
|
+
} else {
|
|
264
|
+
return findLanguage(props.children);
|
|
265
|
+
}
|
|
266
|
+
}
|
|
236
267
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
}
|
|
241
|
-
return "text";
|
|
268
|
+
return null;
|
|
269
|
+
};
|
|
270
|
+
return findLanguage(children) || "text";
|
|
242
271
|
}
|
|
243
272
|
|
|
244
273
|
function formatLanguageLabel(lang: string): string {
|
|
@@ -301,6 +330,14 @@ const ChildrenCodeSection = memo(function ChildrenCodeSection({ children, showCo
|
|
|
301
330
|
maxHeight: isExpanded ? `${contentHeight}px` : `${COLLAPSED_HEIGHT}px`,
|
|
302
331
|
};
|
|
303
332
|
|
|
333
|
+
const handleToggle = useCallback(() => {
|
|
334
|
+
setIsExpanded((prev) => !prev);
|
|
335
|
+
}, []);
|
|
336
|
+
|
|
337
|
+
const chevronStyle: React.CSSProperties = {
|
|
338
|
+
transform: isExpanded ? "rotate(180deg)" : "rotate(0deg)",
|
|
339
|
+
};
|
|
340
|
+
|
|
304
341
|
return (
|
|
305
342
|
<Box position="relative">
|
|
306
343
|
<Card size="1" variant="soft">
|
|
@@ -312,6 +349,18 @@ const ChildrenCodeSection = memo(function ChildrenCodeSection({ children, showCo
|
|
|
312
349
|
</Code>
|
|
313
350
|
)}
|
|
314
351
|
<Flex align="center" gap="2" className="code-action-buttons">
|
|
352
|
+
{shouldShowToggle && (
|
|
353
|
+
<Button
|
|
354
|
+
size="2"
|
|
355
|
+
variant="ghost"
|
|
356
|
+
color="gray"
|
|
357
|
+
onClick={handleToggle}
|
|
358
|
+
tooltip={isExpanded ? "Collapse" : "Expand"}
|
|
359
|
+
aria-label={isExpanded ? "Collapse code" : "Expand code"}
|
|
360
|
+
>
|
|
361
|
+
<HugeiconsIcon icon={ArrowDown01Icon} style={chevronStyle} className="code-chevron" />
|
|
362
|
+
</Button>
|
|
363
|
+
)}
|
|
315
364
|
{showCopy && (
|
|
316
365
|
<Button
|
|
317
366
|
size="2"
|