[Devlog 1] Code-highlighting, Copy-paste
Nathan Luong | March 12, 2023 |5
Expanding Blogs Functionalities
Add light mode for blogs/*
On the bottom right-hand side, above the back-to-top button, there is an option to toggle light mode in the blog reader. At the time, this mode will only be useable on the blogs/* domain.
Adding light mode on the site is somewhat difficult since I'm using a mix of CSS-modules and tailwind-css on this particular page.
To get around that, I used some ugly fixes that I'm not so proud of.
- First, I refactor the mdBlogs.moudule.cssso that it contains different attributes for dark or light mode.
  .postLight a {
    color: #018fbd;
  }
  .postLight a:hover {
    color: #00c1ff;
  }
  .postDark a {
    color: #018fbd;
  }
  .postDark a:hover {
    color: #00c1ff;
  }
- In the [slug].tsxfile where the blog gets rendered, I need to manually grab the theme from theuse-themehook and conditionally add the className into the components with some additional tailwind styling. This led to an ugly solution.
  <ReactMarkdown
    className={
      theme === "dark"
        ? `${styles.postDark} ${styles.post} w-full z-10 text-lighttext`
        : `${styles.postLight} ${styles.post} w-full z-10 text-darktext`
    }
  >
    ...
  </ReactMarkdown>
Mapping Markdown code into React components
- 
Light theme code syntax highlighting: As seen for all code blocks on the page, after being parsed from a markdown file, it got converted to HTML via react-markdownand syntax-highlighted with therehypeHighlightplugin.
- 
Wrapper for code blocks: With each code block rendered by react-markdown, it got mapped into a custom React component wrapper that provides additional functionality (ie: copy-pasting) and states to handle UI interactions (ie:setTimeout()).
  pre: (element: { children?: any; node?: any }) => {
    const [copied, setCopied] = useState(false);
    return (
      <div>
        <CopyPasteButton .../>
        <pre className="rounded-md code-block">{element.children}</pre>
      </div>
    )
  }
- The copy-paste functionality requires recursively traversing through a React component tree where a node can contain a list of nodes. The flattenDeepfunction fromlodashcan help with this problem.
  // Traverse through the tree recursively
  // retrun a multi-dimentional array containing the code.
  const preProcess = (data: preProcessProps[]) => {
    const result: DeepArray<String> = [];
    data.forEach((item) => {
      if (typeof item === "object") {
        result.push(preProcess(item.props.children));
      } else {
        result.push(item);
      }
    });
    return result;
  };
  // Copy to clipboard the string representing the flattened array
  pre: (element: { children?: any; node?: any }) => {
    // ...
    navigator.clipboard.writeText(
      flattenDeep(
        preProcess(element.children[0].props.children)
      ).join("")
    );
    // ...
  }
- To support such an operation, type DeepArrayneeds to be recursive:
  type DeepArray<T> = T | Array<DeepArray<T>>;
  type dataProps = {
    props: {
      children: dataProps[];
    };
  };
  type preProcessProps = string | dataProps;
Plans for the future
- Make blogs RSS-compatible
- Light mode on /portfoliopage and/linktree
What I learned
- Introduction to lodash.js
- Recursive types in TypeScript
- Theme customization with tailwindCSS