You are an expert software engineer specialised in Next.js 14 with TypeScript. I am using TailwindCSS for styling. You utilise or modern techniques and best practices in crafting your solutions. I need you to build me a component that allows me to display children components in a wrapping fashion while ensuring space-between behaviour for each row. It takes in two inputs, the child component which will be displayed, and an array of properties which will be fed into the child component using destructive means, and finally a keyPrefix to allow for unique definition of keys. There is also an input whereby I specify the number of children component in each row. The purpose of this is to be responsive depending on the screen width. It takes in TailwindCSS breakpoints. For example, if I specify base as 1, and “md” as 2, and “xl” as 3, then it means for sizes below “md” each row will have one child component. for sizes above “md” and up to “xl” breakpoint, there will be two children component. And above “xl”, there will be three children components. Use my `useScreenWidth` hook where necessary. Specify the default gap as `gap-8` both between rows and between children components. However, by children components are justified between (spaced between). I also will input an invisible component which you will use to fill in the remaining empty spots on a row. For example, if the row has a capacity for three children components, and there’s only two children components, one invisible component will be added. The invisible component will have the same sizing as the parent component to make sure spacing is done correctly.

Current Implementation to extend:

`components/c-ui/container.tsx`

```
import React from "react";
import useScreenWidth from "../../hooks/screen-width.hook";

/**
 * Defines the possible TailwindCSS breakpoints.
 */
type Breakpoint = "base" | "sm" | "md" | "lg" | "xl" | "2xl";

/**
 * Props interface for the Container component.
 *
 * @template P - The type of the props expected by the child component.
 */
interface ContainerProps<P> {
  /**
   * The child component to be rendered within the container.
   */
  childComponent: React.ComponentType<P>;

  /**
   * An array of props that will be passed to each child component.
   */
  propsArray: P[];

  /**
   * A prefix string to ensure unique keys for each child component.
   */
  keyPrefix: string;

  /**
   * An object specifying the number of children per row for each TailwindCSS breakpoint.
   * Example:
   * {
   *   base: 1,
   *   md: 2,
   *   xl: 3
   * }
   */
  childrenPerRow: Partial<Record<Breakpoint, number>>;
}

/**
 * Container component to display child components in a responsive, wrapping layout.
 * Ensures space-between behavior for each row with customizable gaps.
 *
 * @template P - The type of the props expected by the child component.
 *
 * @param {ContainerProps<P>} props - The props for the Container component.
 *
 * @returns {JSX.Element} The rendered Container component.
 */
const Container = <P,>({ childComponent: ChildComponent, propsArray, keyPrefix, childrenPerRow }: ContainerProps<P>): JSX.Element => {
  // Determine screen size using the useScreenWidth hook for each breakpoint
  const isSm = useScreenWidth("sm");
  const isMd = useScreenWidth("md");
  const isLg = useScreenWidth("lg");
  const isXl = useScreenWidth("xl");
  const is2xl = useScreenWidth("2xl");

  /**
   * Determines the current number of children per row based on the active breakpoint.
   * It checks from the largest breakpoint to the smallest to find the highest matching breakpoint.
   */
  let numberOfChildrenPerRow = childrenPerRow["base"] ?? 1; // Default to 1 if 'base' is not specified

  if (is2xl && childrenPerRow["2xl"]) {
    numberOfChildrenPerRow = childrenPerRow["2xl"]!;
  } else if (isXl && childrenPerRow["xl"]) {
    numberOfChildrenPerRow = childrenPerRow["xl"]!;
  } else if (isLg && childrenPerRow["lg"]) {
    numberOfChildrenPerRow = childrenPerRow["lg"]!;
  } else if (isMd && childrenPerRow["md"]) {
    numberOfChildrenPerRow = childrenPerRow["md"]!;
  } else if (isSm && childrenPerRow["sm"]) {
    numberOfChildrenPerRow = childrenPerRow["sm"]!;
  }

  /**
   * Groups the propsArray into chunks based on the numberOfChildrenPerRow.
   * Each chunk represents a row of child components.
   */
  const rows: P[][] = [];
  for (let i = 0; i < propsArray.length; i += numberOfChildrenPerRow) {
    rows.push(propsArray.slice(i, i + numberOfChildrenPerRow));
  }

  return (
    <div className="flex flex-col gap-8">
      {rows.map((row, rowIndex) => (
        <div key={`${keyPrefix}-row-${rowIndex}`} className="flex justify-between gap-8">
          {row.map((childProps, childIndex) => (
            <ChildComponent key={`${keyPrefix}-${rowIndex}-${childIndex}`} {...childProps} />
          ))}
        </div>
      ))}
    </div>
  );
};

export default Container;
```

Code Implementations:

`hooks/screen-width.hook.ts`

```tsx
import { useState, useEffect } from "react";

/**
 * TailwindCSS default breakpoints in pixels.
 */
const tailwindBreakpoints: Record<string, number> = { sm: 640, md: 768, lg: 1024, xl: 1280, "2xl": 1536 };

/**
 * Sorted array of breakpoint names based on their min-width.
 */
const sortedBreakpoints = Object.keys(tailwindBreakpoints).sort((a, b) => tailwindBreakpoints[a] - tailwindBreakpoints[b]) as Array<
  keyof typeof tailwindBreakpoints
>;

/**
 * Custom hook to determine if the current screen size matches a TailwindCSS breakpoint.
 *
 * @param size - The TailwindCSS breakpoint to check (e.g., 'sm', 'md', 'lg', etc.).
 * @param strict - If true, the hook checks if the screen width is within the exact range of the breakpoint.
 *                 If false, it checks if the screen width is equal to or greater than the breakpoint's min-width.
 *                 Defaults to false.
 * @returns A boolean indicating whether the current screen size matches the criteria.
 */
function useScreenWidth(size: keyof typeof tailwindBreakpoints, strict: boolean = false): boolean {
  const [matches, setMatches] = useState<boolean>(false);

  useEffect(() => {
    if (typeof window === "undefined") {
      // If window is undefined (e.g., during SSR), default to false
      return;
    }

    const minWidth = tailwindBreakpoints[size];
    let maxWidth: number | undefined;

    if (strict) {
      const currentIndex = sortedBreakpoints.indexOf(size);
      if (currentIndex === -1) {
        console.warn(`Invalid Tailwind breakpoint: ${size}`);
        return;
      }
      if (currentIndex < sortedBreakpoints.length - 1) {
        const nextSize = sortedBreakpoints[currentIndex + 1];
        maxWidth = tailwindBreakpoints[nextSize] - 1;
      }
    }

    let query = `(min-width: ${minWidth}px)`;
    if (strict && maxWidth !== undefined) {
      query += ` and (max-width: ${maxWidth}px)`;
    }

    const mediaQueryList = window.matchMedia(query);

    const updateMatch = (event: MediaQueryListEvent) => {
      setMatches(event.matches);
    };

    // Initial check
    setMatches(mediaQueryList.matches);

    // Add listener using modern `addEventListener`
    mediaQueryList.addEventListener("change", updateMatch);

    // Cleanup listener on unmount
    return () => {
      mediaQueryList.removeEventListener("change", updateMatch);
    };
  }, [size, strict]);

  return matches;
}

export default useScreenWidth;
```