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. Program your solution in this file `components/c-ui/container.tsx`.

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;
```