Mdx.tsx
1 import { 2 Match, 3 Switch, 4 children, 5 createSignal, 6 onMount, 7 splitProps, 8 type ParentProps, 9 } from "solid-js"; 10 import { A } from "@solidjs/router"; 11 import { QuickLinks, type QuickLinksProps } from "../ingredients/quick-link"; 12 import { Emph, type EmphProps } from "../ingredients/emph"; 13 import cfg from "../constant"; 14 import Reveal from "~/ingredients/rand-reveal"; 15 import { twMerge } from "tailwind-merge"; 16 import Comment from "~/ingredients/comment"; 17 18 const cstomLink = (props: ParentProps & { href: string }) => { 19 const [, rest] = splitProps(props, ["children"]); 20 const resolved = children(() => props.children); 21 22 const [childRef, setChildRef] = createSignal<HTMLDivElement>(); 23 const [inlineAClass, setInlineAClass] = createSignal(""); 24 25 onMount(() => { 26 if (childRef && childRef()?.parentElement) { 27 if (childRef()!.parentElement?.tagName.startsWith("H")) { 28 setInlineAClass(`anchor no-underline`); 29 } else { 30 setInlineAClass(`anchor decoration-dotted underline-offset-4`); 31 } 32 } 33 }); 34 35 if (props.href.startsWith("#")) { 36 37 return ( 38 <A ref={setChildRef} class={inlineAClass()} {...rest} noScroll={true}> 39 {resolved()} 40 </A> 41 ); 42 } 43 44 return ( 45 <A 46 class="underline-offset-4 decoration-2 hover:underline-offset-2 transition-all inline items-center space-x-px group break-all pr-0.5 font-normal" 47 target="_blank" 48 rel="noopener noreferrer nofollow" 49 {...rest} 50 > 51 <span>{resolved()}</span> 52 </A> 53 ); 54 }; 55 56 const imgContent = ( 57 props: ParentProps & { 58 class: string; 59 src: string; 60 alt: string; 61 title: string; 62 ref: (el: HTMLVideoElement) => void | undefined; 63 }, 64 ) => ( 65 <Switch 66 fallback={ 67 <img 68 class={`w-full ${props.class}`} 69 src={props.src} 70 alt={props.alt} 71 loading="lazy" 72 /> 73 } 74 > 75 <Match when={!props.src.startsWith("http")}> 76 <img 77 class={`w-full ${props.class}`} 78 src={cfg.obj_store + "/" + props.src} 79 alt={props.alt} 80 loading="lazy" 81 /> 82 </Match> 83 84 <Match when={props.src.endsWith(".webm")}> 85 <video 86 ref={props.ref} 87 src={props.src} 88 class={`w-full squircle rounded-md ${props.class}`} 89 controls={props.title === "controls"} 90 muted={props.title !== "controls"} 91 autoplay={props.title !== "controls"} 92 loop={props.title !== "controls"} 93 preload="metadata" 94 playsinline 95 > 96 <source src={props.src} type="video/webm" /> 97 </video> 98 </Match> 99 </Switch> 100 ); 101 102 const components = { 103 p: (props: ParentProps) => <p {...props} class="leading-relaxed font-sans text-md">{props.children}</p>, 104 nav: (props: ParentProps) => <nav {...props}>{props.children}</nav>, 105 TesterComponent: () => ( 106 <p> 107 Remove This Now!!! If you see this it means that markdown custom 108 components does work 109 </p> 110 ), 111 hr: (props: ParentProps) => { 112 return <hr {...props} class="bg-sprout-100 rounded-xl h-0.5 w-full" />; 113 }, 114 pre: (props: ParentProps) => { 115 const [codeBlockRef, setCodeBlockRef] = createSignal< 116 HTMLDivElement | undefined 117 >(); 118 const [copied, setCopied] = createSignal(false); 119 const copyToClipboard = () => { 120 if (codeBlockRef()) { 121 const codeContent = codeBlockRef()!.innerText; // loaded 122 123 if (codeContent) { 124 navigator.clipboard.writeText(codeContent).then(() => { 125 setCopied(true); 126 setTimeout(() => setCopied(false), 2000); 127 }); 128 } 129 } 130 }; 131 return ( 132 <div class="relative group w-full"> 133 <pre 134 {...props} 135 class="w-full border bg-[#f9f9f9] px-4 py-2.5 overflow-auto scrollbar scrollbar-rounded" 136 ref={setCodeBlockRef} 137 > 138 {props.children} 139 </pre> 140 <button 141 class="absolute bg-transparent right-2 top-2 h-8 w-8 justify-center items-center flex rounded-md hover:bg-sprout-100 transition-all" 142 onClick={copyToClipboard} 143 > 144 <div 145 class={twMerge( 146 "transition-all duration-400 group-hover:text-sprout-500", 147 copied() ? "group-hover:i-ci:check" : "group-hover:i-ci:copy", 148 )} 149 /> 150 </button> 151 </div> 152 ); 153 }, 154 code: (props: ParentProps) => { 155 const [childRef, setChildRef] = createSignal<HTMLDivElement>(); 156 const [inlineCodeClass, setInlineCodeClass] = createSignal(""); 157 158 onMount(() => { 159 if (childRef && childRef()?.parentElement) { 160 // console.log('Parent tag name:', childRef()!.parentElement?.tagName); 161 if (childRef()!.parentElement?.tagName != "PRE") { 162 setInlineCodeClass("bg-sprout-100 px-1 text-sprout-950 rounded-sm inline flex-none font-medium leading-tight max-w-full whitespace-normal break-normal"); 163 } 164 } 165 }); 166 167 return <code ref={setChildRef} class={inlineCodeClass()}>{props.children}</code>; 168 }, 169 170 response: (props: ParentProps) => { 171 return <span>{props.children}</span>; 172 }, 173 void: (props: ParentProps) => { 174 return <span>{props.children}</span>; 175 }, 176 unknown: (props: ParentProps) => { 177 return <span>{props.children}</span>; 178 }, 179 180 QuickLinks: (props: QuickLinksProps) => ( 181 <QuickLinks {...props}>{props.children}</QuickLinks> 182 ), 183 184 Emph: (props: EmphProps) => <Emph type={props.type}>{props.children}</Emph>, 185 186 Reveal: (props: ParentProps) => <Reveal>{props.children}</Reveal>, 187 188 C: (props: ParentProps) => <span class="inline-flex px-0.5"> 189 <Comment> 190 {props.children} 191 </Comment> 192 </span>, 193 194 h1: (props: ParentProps) => ( 195 <div> 196 <h1 {...props} class="prose-h1 flex justify-start items-center font-sans"> 197 <div class="rounded-sm bg-sprout-300 w-5 h-5 mr-2 shadow-md" /> 198 {props.children} 199 </h1> 200 </div> 201 ), 202 h2: (props: ParentProps) => { 203 return ( 204 <> 205 <h2 {...props} class="prose-h2 flex justify-start items-center font-sans"> 206 <div class="rounded-sm bg-sprout-300 w-4.5 h-4.5 mr-2 mb-0.5 shadow-md" /> 207 {props.children} 208 </h2> 209 </> 210 ); 211 }, 212 h3: (props: ParentProps) => { 213 return ( 214 <h3 {...props} class="prose-h3 flex justify-start items-center font-sans"> 215 <div class="rounded-sm bg-sprout-300 w-4 h-4 mr-1.5 mb-px shadow-md" /> 216 {props.children} 217 </h3> 218 ); 219 }, 220 h4: (props: ParentProps) => { 221 return ( 222 <h4 {...props} class="prose-h4 flex justify-start items-center font-sans"> 223 <div class="rounded-sm bg-sprout-300 w-3.5 h-3.5 mr-1.5 mb-px shadow-sm" /> 224 {props.children} 225 </h4> 226 ); 227 }, 228 h5: (props: ParentProps) => { 229 return ( 230 <h5 {...props} class="prose-h5 flex justify-start items-center my-1 font-sans"> 231 <div class="px-2 py-2 mr-1 rounded-sm border border-2 border-sprout-200 shadow-md" /> 232 {props.children} 233 </h5> 234 ); 235 }, 236 h6: (props: ParentProps) => ( 237 <h6 {...props} class="prose-h6 flex justify-start items-center my-1 font-sans"> 238 <div class="px-1.5 py-1.5 mr-1 rounded-sm border border-2 border-sprout-200 shadow-md" /> 239 {props.children} 240 </h6> 241 ), 242 blockquote: (props: ParentProps) => ( 243 <blockquote class="flex flex-col items-center text-base not-italic font-normal text-zinc-500 my-3"> 244 <div class="flex justify-start h-4 w-full"> 245 <div class="text-3xl text-sprout-400 leading-none">"</div> 246 </div> 247 <div class="mx-2.5 not-prose !leading-none">{props.children}</div> 248 <div class="relative flex justify-end h-4 w-full"> 249 <div class="absolute -top-4 right-4 text-3xl text-sprout-400 leading-none">"</div> 250 </div> 251 </blockquote> 252 ), 253 254 a: cstomLink, 255 strong: (props: ParentProps) => <strong class="font-bold" {...props} />, 256 257 img: imgContent, 258 table: (props: ParentProps) => <table>{props.children}</table>, 259 li: (props: ParentProps) => ( 260 <li {...props} class="mb-2 marker:text-sprout-400 whitespace-normal break-words"> 261 {props.children} 262 </li> 263 ), 264 ul: (props: ParentProps) => ( 265 <ul 266 {...props} 267 class="pl-6 mb-2 list-disc decoration-sprout-300 marker:text-sprout-400" 268 > 269 {props.children} 270 </ul> 271 ), 272 ol: (props: ParentProps) => ( 273 <ol {...props} 274 class="pl-6 mb-2 list-decimal decoration-sprout-300 marker:text-sprout-400"> 275 {props.children} 276 </ol> 277 ), 278 }; 279 280 export default components;