[
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release\n\non:\n  workflow_dispatch:\n    inputs:\n      version:\n        description: 'Version to release (e.g., 1.0.0)'\n        required: true\n        type: string\n\njobs:\n  release:\n    runs-on: ubuntu-latest\n    steps:\n      - name: Checkout\n        uses: actions/checkout@v4\n        with:\n          fetch-depth: 0\n          token: ${{ secrets.GITHUB_TOKEN }}\n\n      - uses: pnpm/action-setup@v3\n        with:\n          version: 8\n\n      - name: Setup Node.js\n        uses: actions/setup-node@v4\n        with:\n          node-version: 20\n          registry-url: 'https://registry.npmjs.org'\n\n      - name: Install dependencies\n        run: pnpm i\n\n      - name: Run Tests\n        run: pnpm run --filter react-plock test\n\n      - name: Build\n        run: pnpm run --filter react-plock build\n\n      - name: Manual Release\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}\n          VERSION: ${{ inputs.version }}\n        run: |\n          git config --global user.email \"github-actions[bot]@users.noreply.github.com\"\n          git config --global user.name \"github-actions[bot]\"\n\n          # Change directory to the package\n          cd libs/react-plock\n\n          # Update version in package.json\n          npm version $VERSION --no-git-tag-version\n\n          # Return to root\n          cd ../../\n\n          # Commit the version change\n          git add .\n          git commit -m \"chore: release v${VERSION}\"\n\n          # Push the version change commit\n          git push\n\n          # Publish to npm\n          pnpm --filter react-plock publish\n\n      - name: Generate Changelog\n        id: changelog\n        run: |\n          PREVIOUS_TAG=$(git describe --tags --abbrev=0 HEAD^)\n          REPO=\"${GITHUB_REPOSITORY}\"\n          {\n            echo \"CHANGELOG<<EOF\"\n            echo \"# Changelog\"\n            echo \"\"\n            git log --pretty=format:\"- %s (${REPO}@%h)\" ${PREVIOUS_TAG}..HEAD\n            echo \"\"\n            echo \"EOF\"\n          } >> $GITHUB_ENV\n\n      - name: Create GitHub Release\n        uses: actions/create-release@v1\n        env:\n          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}\n        with:\n          tag_name: v${{ inputs.version }}\n          release_name: v${{ inputs.version }}\n          body: ${{ env.CHANGELOG }}\n          draft: false\n          prerelease: false\n"
  },
  {
    "path": ".github/workflows/stale.yml",
    "content": "name: Mark stale issues and pull requests\n\non:\n  schedule:\n  - cron: '29 18 * * *'\n\njobs:\n  stale:\n\n    runs-on: ubuntu-latest\n    permissions:\n      issues: write\n      pull-requests: write\n\n    steps:\n    - uses: actions/stale@v5\n      with:\n        repo-token: ${{ secrets.GITHUB_TOKEN }}\n        stale-issue-message: 'This issue has been automatically closed due to inactivity. If you still have further updates, please feel free to reopen or create a new issue.'\n        stale-pr-message: 'This pull request has been automatically closed due to inactivity. If you still intend to contribute, please feel free to reopen the PR or submit a new one.'\n        stale-issue-label: 'no-issue-activity'\n        stale-pr-label: 'no-pr-activity'\n"
  },
  {
    "path": ".github/workflows/testing.yml",
    "content": "name: Testing CI\n\non:\n  pull_request:\n    branches:\n      - main\n      - dev\n\njobs:\n  main:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n        with:\n          fetch-depth: 0\n\n      - uses: pnpm/action-setup@v3\n        with:\n          version: 8\n\n      - run: pnpm install\n      - run: pnpm run --filter react-plock test\n"
  },
  {
    "path": ".gitignore",
    "content": "# See http://help.github.com/ignore-files/ for more about ignoring files.\n\n# compiled output\n/**/dist\n/tmp\n/out-tsc\n\n# dependencies\nnode_modules\n\n# IDEs and editors\n/.idea\n.project\n.classpath\n.c9/\n*.launch\n.settings/\n*.sublime-workspace\n\n# IDE - VSCode\n.vscode/*\n!.vscode/settings.json\n!.vscode/tasks.json\n!.vscode/launch.json\n!.vscode/extensions.json\n\n# misc\n/.sass-cache\n/connect.lock\n/coverage\n/libpeerconnection.log\nnpm-debug.log\nyarn-error.log\ntestem.log\n/typings\n\n# System Files\n.DS_Store\nThumbs.db\n"
  },
  {
    "path": ".prettierrc",
    "content": "{\n  \"singleQuote\": true\n}\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2023 Renato Pozzi\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "![Plock Logo](./assets/cover.png)\n\nReact Plock is a tree-shakeable **ultra small** npm package (**less than 1kB gzipped**) that allows you to create amazing masonry layouts with an amazing developer experience. With React Plock, you can easily create responsive and customizable layouts that adapt to different screen sizes and devices.\n\n### Features\n\n- **Masonry Layout**: Create beautiful masonry layouts with ease.\n- **Balanced Layout**: Create a more visually harmonious masonry grid by considering the height of each item.\n- **Responsive**: Automatically adapts to different screen sizes and devices.\n- **Customizable**: Customize the layout to match your needs.\n- **TypeScript Ready**: Get the strength of type-safe languages.\n- **Amazing DX**: Easy to use and well-documented.\n\n### Examples\n\n- Using Next.js 14 (Server Components) [See Working Demo](https://react-plock-with-nextjs.vercel.app/)\n- Using ViteJS [See Working Demo](https://react-plock-with-vite.vercel.app/)\n\n### Installation\n\n```bash\nnpm install react-plock\n```\n\n### Usage\n\nUsing Plock with the new v3 APIs it's a piece of cake. Here's an example of how can you create a masonry grid. You can even see a demo of this example by clicking [here](https://react-plock-with-vite.vercel.app/).\n\n```tsx\nimport { Masonry } from 'react-plock';\n\nconst ImagesMasonry = () => {\n  const items = [...imageUrls];\n\n  return (\n    <Masonry\n      items={items}\n      config={{\n        columns: [1, 2, 3],\n        gap: [24, 12, 6],\n        media: [640, 768, 1024],\n      }}\n      render={(item, idx) => (\n        <img key={idx} src={item} style={{ width: '100%', height: 'auto' }} />\n      )}\n    />\n  );\n};\n```\n\n### Balanced Layout\n\nReact Plock offers a balanced layout option that creates a more visually harmonious masonry grid by considering the height of each item. When enabled with `useBalancedLayout: true`, the layout algorithm distributes items across columns while attempting to minimize height differences between columns.\n\nThis is particularly useful for content with varying heights, such as images or cards, where a traditional masonry layout might create uneven columns.\n\nUnlike the default layout which distributes items sequentially, the balanced layout dynamically measures and adjusts item placement to create a more aesthetically pleasing result.\n\n```tsx\n<Masonry\n  items={items}\n  config={{\n    columns: [2, 3, 4],\n    gap: [16, 16, 16],\n    media: [640, 768, 1024],\n    useBalancedLayout: true, // Enable balanced layout\n  }}\n  render={(item) => (\n    <img src={item.url} alt={item.alt} style={{ width: '100%' }} />\n  )}\n/>\n```\n\n### API Reference\n\nHere's the TypeScript definition for the Masonry Component, below you can find a more detailed explanation.\n\n```ts\nexport type MasonryProps<T> = React.ComponentPropsWithoutRef<'div'> & {\n  items: T[];\n  render: (item: T, idx: number) => React.ReactNode;\n  config: {\n    columns: number | number[];\n    gap: number | number[];\n    media?: number[];\n    useBalancedLayout?: boolean;\n  };\n  as?: React.ElementType;\n};\n```\n\n#### Items\n\nThis prop accepts a generic array of elements, each one will be passed to the **render** property.\n\n#### Render\n\nThe masonry render prop. Here's where you define the styles of every tile of the grid, the function takes the current looping item and the relative index.\n\n#### Config\n\nA configuration object that is used to define the number of columns, media queries and gaps between items.\n\n#### Other Props\n\nAs you can see, by using `React.ComponentPropsWithoutRef<\"div\">` you can simply pass every available property to the div, some examples are **id** and **className**. The only one property that will be overwritten will be the `style` because is used internally for the masonry generation.\n\n### Important Note\n\nPlease, note that in case you are passing an array to the columns attribute of the config property, the number of elements **MUST** be equal to the number of media AND gap breakpoints provided!\n\n```tsx\n// Correct: This will be responsive with 3 breakpoints.\n<Masonry\n  {...otherProps}\n  config={{\n    columns: [1, 2, 3],\n    gap: [12, 16, 20],\n    media: [640, 768, 1024],\n  }}\n/>\n\n// Correct: This will be responsive with 2 breakpoints.\n<Masonry\n  {...otherProps}\n  config={{\n    columns: [1, 2],\n    gap: [2, 4],\n    media: [640, 1024],\n  }}\n/>\n\n// Correct: This will be fixed with 4 columns in every screen size.\n<Masonry\n  {...otherProps}\n  config={{\n    columns: 4,\n    gap: 8\n  }}\n/>\n\n// NOT Correct: This will cause trouble in rendering.\n<Masonry\n  {...otherProps}\n  config={{\n    columns: [4],\n    media: [640, 768],\n  }}\n/>\n```\n"
  },
  {
    "path": "assets/data/images.ts",
    "content": "export const images = [\n  \"https://images.unsplash.com/photo-1606542758304-820b04394ac2?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NDQy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1611419010196-a360856fc42f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUzMjg2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1530919424169-4b95f917e937?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NDQ2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1634703080363-98f94e5a1076?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NDUw&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1588007374946-c79543903e8a?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUxNTk1&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1561411996-3794338f63cc?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUzNzIy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1560052775-e4f689f06f07?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NDYw&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1604818659463-34304eab8e70?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUxNjgz&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1520563683082-7ef74b616a89?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NDY4&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1573537805874-4cedc5d389ce?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NDI0&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1534817043788-41286c872b7b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUxNjcz&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1586461715699-1e192dcd04c6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NDc5&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1580428180163-76ab1efe2aed?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NDgz&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1595271444083-08084c6857c7?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NDg2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1604818659418-1c53672b00f6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NDg4&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1602136773736-34d445b989cb?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUxNDA0&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1555086156-e6c7353d283f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyNzAw&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1530692228265-084b21566b12?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTA0&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1547434836-398fc5a73e91?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTA1&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1626759486966-c067e3f79982?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTEw&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1611419010019-550124aef004?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTE2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1560671021-cb36f70ce82d?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTE3&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1574357265250-10c88f63ebfd?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyNTAx&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/flagged/photo-1579451442952-f0365f3f0aed?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTIy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1619796404374-aff912b43cd2?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTIz&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1582472978953-12929ab18f3e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTIz&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1594886801338-b81548345f77?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTI5&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1539606328118-80c679838702?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTMw&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1573455494057-12684d151bf4?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTMx&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1557515126-1bf9ada5cb93?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTMy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1536890992765-f42a1ee1e2a8?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTQy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1586754102101-36b67e4c5bcf?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyNzg5&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1547355253-ff0740f6e8c1?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTU0&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1555679025-2b0c57d18992?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTU4&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1608687087357-845abfade367?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTYw&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/flagged/photo-1579451443170-44b3963c3341?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUxMjgx&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1535237654113-09c1e5d6e622?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTY4&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1503187685617-d78d295f163e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTcx&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1628753254988-da6979f94173?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTc1&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1585731833344-88bf310c0db6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTc2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1579169703977-e4575236583c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTc2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1622099330140-69125ac0d6a9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUzNjQ3&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1576075796033-848c2a5f3696?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyNTE3&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1573501815578-6252ee088c47?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTg2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1553227957-454e04fa8472?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyNDAw&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1564836663277-c4aa761b9882?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTk2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1607419674405-256ed5bc8f69?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NTk3&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1535737005411-82def79a9038?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyODY2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1582701973975-0fff9b4c06e6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0MDQz&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1548387834-7bf05019e89b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjA2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1598933385397-43259d8c1554?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjA3&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1606152538442-6764e7a61d3d?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjEy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1529641484336-ef35148bab06?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyNzE1&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1585732436715-4c25f4ba85f5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjE2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1625632019469-108132f8fc60?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjE4&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1574357278720-2809ce8065db?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjIw&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1582150800250-347e369a020c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjIx&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1595353447630-3b8758e6a386?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjI2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1563964040780-8605906e3eb6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUxMjg3&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1620215175664-cb9a6f5b6103?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyMDI2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1564736676781-d0f57b29f67a?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjM4&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1556015174-ac6f87f53456?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUxMTMy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1498736297812-3a08021f206f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjQ0&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1588007374916-76ab3ff82a78?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjQ3&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1563679200937-f4266649d170?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjU0&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1559828801-04565cd31e27?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUxODIy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1575290904798-0f89bf0297d5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjYw&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1556367713-029b57f4a20e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjYx&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1610802752018-795027c7eca9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjY0&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1581250190370-6368e32dbcb5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjY5&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1579024567508-3cbf27eec69b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Njcy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1534946366195-7bf1dfc2fbd1?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Njcz&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1628174383885-642404980686?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Njc4&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1612974904144-5c725b5c6e98?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Njg2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1638621029425-6c77f2219438?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Njg2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1574391884720-bbc3740c59d1?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUzOTMx&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1608558259020-0ba7302dc3d1?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Njkw&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1600382224527-3ab1474fbb2a?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Njk0&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1588007375246-3ee823ef4851?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyMDc1&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1622407132338-48135fd10360?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Njk5&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1498036882173-b41c28a8ba34?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NjMx&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1568838572861-3d9cc2724435?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzA4&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1582150809510-8098a39b1ab9?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzEw&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1573455494060-c5595004fb6c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUzNzQ5&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1515036551567-bf1198cccc35?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzEy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1502211261676-a07824b55355?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzEz&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1607817895500-6edefa86bdb7?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzE3&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1626848810124-aa1bf62c0230?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUxNjUw&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1571182160015-2169f6e1aa5f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUzODQ5&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1536901766856-5d45744cd180?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzI1&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1555852224-2a3e675fc47e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzI4&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1594162958229-f7d5c3bf33d7?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzI4&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1578608738964-cd27acd5af2c?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyNTc5&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1626339277573-ff8da132c10b?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzQy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1612781367540-433dff529376?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzQ2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1485163819542-13adeb5e0068?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyNzE5&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1598476902279-11952e5a21b8?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzYz&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1615006801329-249dcef6aa0f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzY3&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1610802711091-89aaa0ce4bc5?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0NzY5&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1604604994333-f1b0e9471186?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyOTEz&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1594974027866-46b2413fb07d?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Nzc0&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1601601083968-da6bc248246f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0MDMy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1583430999549-6813db72eb6e?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyMTcz&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1575907199965-cf4c0ea4092d?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Nzgy&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1536098561742-ca998e48cbcc?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUyNzQ2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1578608724117-4e61ac0ff89f?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Nzg1&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1476445704028-a36e0c798192?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODUzMTE4&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1571942662090-0d81d067ca19?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Nzk1&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1558961910-90e0503c1064?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Nzk2&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1626111416202-4afbfda51ce3?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0Nzk3&ixlib=rb-4.0.3&q=80&w=720\",\n  \"https://images.unsplash.com/photo-1574357273651-588a9228b9a6?crop=entropy&cs=tinysrgb&fit=max&fm=jpg&ixid=MnwxMjA3fDB8MXxhbGx8fHx8fHx8fHwxNjc3ODU0ODAw&ixlib=rb-4.0.3&q=80&w=720\",\n];\n"
  },
  {
    "path": "examples/with-nextjs/.eslintrc.json",
    "content": "{\n  \"extends\": \"next/core-web-vitals\"\n}\n"
  },
  {
    "path": "examples/with-nextjs/.gitignore",
    "content": "# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.\n\n# dependencies\n/node_modules\n/.pnp\n.pnp.js\n.yarn/install-state.gz\n\n# testing\n/coverage\n\n# next.js\n/.next/\n/out/\n\n# production\n/build\n\n# misc\n.DS_Store\n*.pem\n\n# debug\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\n\n# local env files\n.env*.local\n\n# vercel\n.vercel\n\n# typescript\n*.tsbuildinfo\nnext-env.d.ts\n"
  },
  {
    "path": "examples/with-nextjs/README.md",
    "content": "This is a [Next.js](https://nextjs.org/) project bootstrapped with [`create-next-app`](https://github.com/vercel/next.js/tree/canary/packages/create-next-app).\n\n## Getting Started\n\nFirst, run the development server:\n\n```bash\nnpm run dev\n# or\nyarn dev\n# or\npnpm dev\n# or\nbun dev\n```\n\nOpen [http://localhost:3000](http://localhost:3000) with your browser to see the result.\n\nYou can start editing the page by modifying `app/page.tsx`. The page auto-updates as you edit the file.\n\nThis project uses [`next/font`](https://nextjs.org/docs/basic-features/font-optimization) to automatically optimize and load Inter, a custom Google Font.\n\n## Learn More\n\nTo learn more about Next.js, take a look at the following resources:\n\n- [Next.js Documentation](https://nextjs.org/docs) - learn about Next.js features and API.\n- [Learn Next.js](https://nextjs.org/learn) - an interactive Next.js tutorial.\n\nYou can check out [the Next.js GitHub repository](https://github.com/vercel/next.js/) - your feedback and contributions are welcome!\n\n## Deploy on Vercel\n\nThe easiest way to deploy your Next.js app is to use the [Vercel Platform](https://vercel.com/new?utm_medium=default-template&filter=next.js&utm_source=create-next-app&utm_campaign=create-next-app-readme) from the creators of Next.js.\n\nCheck out our [Next.js deployment documentation](https://nextjs.org/docs/deployment) for more details.\n"
  },
  {
    "path": "examples/with-nextjs/next.config.mjs",
    "content": "/** @type {import('next').NextConfig} */\nconst nextConfig = {\n  images: {\n    remotePatterns: [\n      {\n        protocol: \"https\",\n        hostname: \"images.unsplash.com\",\n        port: \"\",\n        pathname: \"/**/**\",\n      },\n    ],\n  },\n};\n\nexport default nextConfig;\n"
  },
  {
    "path": "examples/with-nextjs/package.json",
    "content": "{\n  \"name\": \"with-nextjs\",\n  \"version\": \"1.0.0\",\n  \"private\": true,\n  \"scripts\": {\n    \"dev\": \"next dev --turbopack\",\n    \"build\": \"next build\",\n    \"start\": \"next start\",\n    \"lint\": \"next lint\"\n  },\n  \"dependencies\": {\n    \"react\": \"19.2.0\",\n    \"react-dom\": \"19.2.0\",\n    \"next\": \"15.5.9\",\n    \"react-plock\": \"workspace:*\"\n  },\n  \"devDependencies\": {\n    \"typescript\": \"^5\",\n    \"@types/node\": \"^20\",\n    \"@types/react\": \"19.2.2\",\n    \"@types/react-dom\": \"19.2.1\",\n    \"autoprefixer\": \"^10.0.1\",\n    \"postcss\": \"^8\",\n    \"tailwindcss\": \"^3.3.0\",\n    \"eslint\": \"^8\",\n    \"eslint-config-next\": \"15.5.4\"\n  },\n  \"pnpm\": {\n    \"overrides\": {\n      \"@types/react\": \"19.2.2\",\n      \"@types/react-dom\": \"19.2.1\"\n    }\n  }\n}\n"
  },
  {
    "path": "examples/with-nextjs/postcss.config.js",
    "content": "module.exports = {\n  plugins: {\n    tailwindcss: {},\n    autoprefixer: {},\n  },\n};\n"
  },
  {
    "path": "examples/with-nextjs/src/app/globals.css",
    "content": "body {\n  margin: 0;\n}\n"
  },
  {
    "path": "examples/with-nextjs/src/app/layout.tsx",
    "content": "import type { Metadata } from \"next\";\nimport { Inter } from \"next/font/google\";\nimport \"./globals.css\";\n\nconst inter = Inter({ subsets: [\"latin\"] });\n\nexport const metadata: Metadata = {\n  title: \"Create Next App\",\n  description: \"Generated by create next app\",\n};\n\nexport default function RootLayout({\n  children,\n}: Readonly<{\n  children: React.ReactNode;\n}>) {\n  return (\n    <html lang=\"en\">\n      <body className={inter.className}>{children}</body>\n    </html>\n  );\n}\n"
  },
  {
    "path": "examples/with-nextjs/src/app/page.tsx",
    "content": "'use client';\n\nimport { useState } from 'react';\nimport { Masonry } from 'react-plock';\nimport { Button } from './ui/Button';\n\nconst items = [\n  { height: 600, color: '#D32F2F', id: 1 }, // Extra Tall - Darker Red\n  { height: 200, color: '#00796B', id: 2 }, // Short - Darker Teal\n  { height: 400, color: '#1976D2', id: 3 }, // Tall - Darker Blue\n  { height: 300, color: '#2E7D32', id: 4 }, // Medium - Darker Green\n  { height: 500, color: '#F57F17', id: 5 }, // Very Tall - Darker Yellow\n  { height: 250, color: '#C2185B', id: 6 }, // Medium-Short - Darker Pink\n  { height: 450, color: '#4527A0', id: 7 }, // Tall - Darker Purple\n  { height: 200, color: '#1565C0', id: 8 }, // Short - Darker Blue\n  { height: 350, color: '#D84315', id: 9 }, // Medium-Tall - Darker Orange\n  { height: 550, color: '#1B5E20', id: 10 }, // Very Tall - Darker Green\n  { height: 150, color: '#B71C1C', id: 11 }, // Very Short - Darker Red\n  { height: 400, color: '#311B92', id: 12 }, // Tall - Darker Purple\n  { height: 300, color: '#00695C', id: 13 }, // Medium - Darker Turquoise\n  { height: 250, color: '#E65100', id: 14 }, // Medium-Short - Darker Orange\n  { height: 500, color: '#880E4F', id: 15 }, // Very Tall - Darker Pink\n  { height: 200, color: '#0D47A1', id: 16 }, // Short - Darker Blue\n  { height: 450, color: '#3E2723', id: 17 }, // Tall - Darker Brown\n  { height: 350, color: '#4A148C', id: 18 }, // Medium-Tall - Darker Purple\n  { height: 500, color: '#01579B', id: 19 }, // Medium - Darker Blue\n  { height: 400, color: '#AD1457', id: 20 }, // Tall - Darker Pink\n  { height: 300, color: '#E65100', id: 21 }, // Medium-Short - Darker Orange\n];\n\nexport default function Home() {\n  const [isBalanced, setIsBalanced] = useState(false);\n\n  return (\n    <div style={{ padding: '16px', fontFamily: 'system-ui' }}>\n      <fieldset style={{ padding: 16 }}>\n        <legend>Settings</legend>\n\n        <Button onClick={() => setIsBalanced(!isBalanced)}>\n          {isBalanced ? 'Balanced Layout' : 'Default Layout'}\n        </Button>\n      </fieldset>\n\n      <div style={{ paddingTop: '16px' }} />\n\n      <Masonry\n        items={items}\n        config={{\n          columns: [1, 2, 3],\n          gap: [24, 12, 6],\n          media: [640, 1024, 1280],\n          useBalancedLayout: isBalanced,\n        }}\n        render={(item) => (\n          <div\n            style={{\n              height: item.height,\n              backgroundColor: item.color,\n              width: '100%',\n              display: 'flex',\n              alignItems: 'center',\n              justifyContent: 'center',\n              color: '#FFF',\n              fontSize: '22px',\n              fontWeight: 'bold',\n            }}\n          >\n            {`${item.height}px - #${item.id}`}\n          </div>\n        )}\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "examples/with-nextjs/src/app/ui/Button.tsx",
    "content": "import { ButtonHTMLAttributes, forwardRef } from 'react';\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {}\n\nconst Button = forwardRef<HTMLButtonElement, ButtonProps>(\n  ({ className, style, ...props }, ref) => {\n    return <button className={className} ref={ref} {...props} />;\n  }\n);\n\nButton.displayName = 'Button';\n\nexport { Button };\n"
  },
  {
    "path": "examples/with-nextjs/tailwind.config.ts",
    "content": "import type { Config } from \"tailwindcss\";\n\nconst config: Config = {\n  content: [\n    \"./src/pages/**/*.{js,ts,jsx,tsx,mdx}\",\n    \"./src/components/**/*.{js,ts,jsx,tsx,mdx}\",\n    \"./src/app/**/*.{js,ts,jsx,tsx,mdx}\",\n  ],\n  theme: {\n    extend: {\n      backgroundImage: {\n        \"gradient-radial\": \"radial-gradient(var(--tw-gradient-stops))\",\n        \"gradient-conic\":\n          \"conic-gradient(from 180deg at 50% 50%, var(--tw-gradient-stops))\",\n      },\n    },\n  },\n  plugins: [],\n};\nexport default config;\n"
  },
  {
    "path": "examples/with-nextjs/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"lib\": [\"dom\", \"dom.iterable\", \"esnext\"],\n    \"allowJs\": true,\n    \"skipLibCheck\": true,\n    \"strict\": true,\n    \"noEmit\": true,\n    \"esModuleInterop\": true,\n    \"module\": \"esnext\",\n    \"moduleResolution\": \"bundler\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"jsx\": \"preserve\",\n    \"incremental\": true,\n    \"plugins\": [\n      {\n        \"name\": \"next\"\n      }\n    ],\n    \"paths\": {\n      \"@/*\": [\"./src/*\"],\n      \"@assets/*\": [\"../../assets/*\"]\n    },\n    \"target\": \"ES2017\"\n  },\n  \"include\": [\"next-env.d.ts\", \"**/*.ts\", \"**/*.tsx\", \".next/types/**/*.ts\"],\n  \"exclude\": [\"node_modules\"]\n}\n"
  },
  {
    "path": "examples/with-vite/.gitignore",
    "content": "# Logs\nlogs\n*.log\nnpm-debug.log*\nyarn-debug.log*\nyarn-error.log*\npnpm-debug.log*\nlerna-debug.log*\n\nnode_modules\ndist\ndist-ssr\n*.local\n\n# Editor directories and files\n.vscode/*\n!.vscode/extensions.json\n.idea\n.DS_Store\n*.suo\n*.ntvs*\n*.njsproj\n*.sln\n*.sw?\n"
  },
  {
    "path": "examples/with-vite/index.html",
    "content": "<!DOCTYPE html>\n<html lang=\"en\">\n  <head>\n    <meta charset=\"UTF-8\" />\n    <link rel=\"icon\" type=\"image/svg+xml\" href=\"/vite.svg\" />\n    <meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n    <title>Vite + React + TS</title>\n  </head>\n  <body>\n    <div id=\"root\"></div>\n    <script type=\"module\" src=\"/src/main.tsx\"></script>\n  </body>\n</html>\n"
  },
  {
    "path": "examples/with-vite/package.json",
    "content": "{\n  \"name\": \"with-vite\",\n  \"private\": true,\n  \"version\": \"1.0.0\",\n  \"type\": \"module\",\n  \"scripts\": {\n    \"dev\": \"vite\",\n    \"build\": \"tsc && vite build\",\n    \"preview\": \"vite preview\"\n  },\n  \"dependencies\": {\n    \"react\": \"^18.2.0\",\n    \"react-dom\": \"^18.2.0\",\n    \"react-plock\": \"workspace:*\",\n    \"vite-tsconfig-paths\": \"^4.3.1\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^18.0.27\",\n    \"@types/react-dom\": \"^18.0.10\",\n    \"@vitejs/plugin-react\": \"^3.1.0\",\n    \"typescript\": \"^5.3.3\",\n    \"vite\": \"^4.1.0\"\n  }\n}\n"
  },
  {
    "path": "examples/with-vite/src/ReactPlock.tsx",
    "content": "import { Masonry } from 'react-plock';\nimport { useState } from 'react';\nimport { Button } from './ui/Button';\n\nconst items = [\n  { height: 600, color: '#D32F2F', id: 1 }, // Extra Tall - Darker Red\n  { height: 200, color: '#00796B', id: 2 }, // Short - Darker Teal\n  { height: 400, color: '#1976D2', id: 3 }, // Tall - Darker Blue\n  { height: 300, color: '#2E7D32', id: 4 }, // Medium - Darker Green\n  { height: 500, color: '#F57F17', id: 5 }, // Very Tall - Darker Yellow\n  { height: 250, color: '#C2185B', id: 6 }, // Medium-Short - Darker Pink\n  { height: 450, color: '#4527A0', id: 7 }, // Tall - Darker Purple\n  { height: 200, color: '#1565C0', id: 8 }, // Short - Darker Blue\n  { height: 350, color: '#D84315', id: 9 }, // Medium-Tall - Darker Orange\n  { height: 550, color: '#1B5E20', id: 10 }, // Very Tall - Darker Green\n  { height: 150, color: '#B71C1C', id: 11 }, // Very Short - Darker Red\n  { height: 400, color: '#311B92', id: 12 }, // Tall - Darker Purple\n  { height: 300, color: '#00695C', id: 13 }, // Medium - Darker Turquoise\n  { height: 250, color: '#E65100', id: 14 }, // Medium-Short - Darker Orange\n  { height: 500, color: '#880E4F', id: 15 }, // Very Tall - Darker Pink\n  { height: 200, color: '#0D47A1', id: 16 }, // Short - Darker Blue\n  { height: 450, color: '#3E2723', id: 17 }, // Tall - Darker Brown\n  { height: 350, color: '#4A148C', id: 18 }, // Medium-Tall - Darker Purple\n  { height: 500, color: '#01579B', id: 19 }, // Medium - Darker Blue\n  { height: 400, color: '#AD1457', id: 20 }, // Tall - Darker Pink\n  { height: 300, color: '#E65100', id: 21 }, // Medium-Short - Darker Orange\n];\n\nexport function ReactPlock() {\n  const [isBalanced, setIsBalanced] = useState(false);\n\n  return (\n    <div style={{ padding: '16px', fontFamily: 'system-ui' }}>\n      <fieldset style={{ padding: 16 }}>\n        <legend>Settings</legend>\n\n        <Button onClick={() => setIsBalanced(!isBalanced)}>\n          {isBalanced ? 'Balanced Layout' : 'Default Layout'}\n        </Button>\n      </fieldset>\n\n      <div style={{ paddingTop: '16px' }} />\n\n      <Masonry\n        items={items}\n        config={{\n          columns: [1, 2, 3],\n          gap: [24, 12, 6],\n          media: [640, 1024, 1280],\n          useBalancedLayout: isBalanced,\n        }}\n        render={(item) => (\n          <div\n            style={{\n              height: item.height,\n              backgroundColor: item.color,\n              width: '100%',\n              display: 'flex',\n              alignItems: 'center',\n              justifyContent: 'center',\n              color: '#FFF',\n              fontSize: '22px',\n              fontWeight: 'bold',\n            }}\n          >\n            {`${item.height}px - #${item.id}`}\n          </div>\n        )}\n      />\n    </div>\n  );\n}\n"
  },
  {
    "path": "examples/with-vite/src/main.tsx",
    "content": "import React from 'react';\nimport ReactDOM from 'react-dom/client';\nimport { ReactPlock } from './ReactPlock';\n\nimport './styles/index.css';\n\nReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(\n  <React.StrictMode>\n    <ReactPlock />\n  </React.StrictMode>\n);\n"
  },
  {
    "path": "examples/with-vite/src/styles/index.css",
    "content": "body {\n  margin: 0;\n}\n"
  },
  {
    "path": "examples/with-vite/src/ui/Button.tsx",
    "content": "import { ButtonHTMLAttributes, forwardRef } from 'react';\n\nexport interface ButtonProps extends ButtonHTMLAttributes<HTMLButtonElement> {}\n\nconst Button = forwardRef<HTMLButtonElement, ButtonProps>(\n  ({ className, style, ...props }, ref) => {\n    return <button className={className} ref={ref} {...props} />;\n  }\n);\n\nButton.displayName = 'Button';\n\nexport { Button };\n"
  },
  {
    "path": "examples/with-vite/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"baseUrl\": \".\",\n    \"target\": \"ESNext\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"DOM\", \"DOM.Iterable\", \"ESNext\"],\n    \"allowJs\": false,\n    \"skipLibCheck\": true,\n    \"esModuleInterop\": false,\n    \"allowSyntheticDefaultImports\": true,\n    \"strict\": true,\n    \"forceConsistentCasingInFileNames\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n    \"paths\": {\n      \"@assets/*\": [\"../../assets/*\"]\n    }\n  },\n  \"include\": [\"src\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "examples/with-vite/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"Node\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "examples/with-vite/vite.config.ts",
    "content": "import react from \"@vitejs/plugin-react\";\nimport { defineConfig } from \"vite\";\nimport tsconfigPaths from \"vite-tsconfig-paths\";\n\n// https://vitejs.dev/config/\nexport default defineConfig({\n  plugins: [react(), tsconfigPaths()],\n});\n"
  },
  {
    "path": "libs/react-plock/package.json",
    "content": "{\n  \"name\": \"react-plock\",\n  \"version\": \"3.6.1\",\n  \"type\": \"module\",\n  \"description\": \"The 1kB Masonry Grid for React\",\n  \"author\": \"Renato Pozzi <askides@proton.me>\",\n  \"homepage\": \"https://github.com/askides/react-plock#readme\",\n  \"license\": \"ISC\",\n  \"main\": \"dist/index.es.js\",\n  \"module\": \"dist/index.es.js\",\n  \"types\": \"dist/index.d.ts\",\n  \"exports\": {\n    \".\": {\n      \"types\": \"./dist/index.d.ts\",\n      \"import\": \"./dist/index.es.js\",\n      \"require\": \"./dist/index.cjs.js\"\n    }\n  },\n  \"directories\": {\n    \"dist\": \"dist\"\n  },\n  \"files\": [\n    \"dist\"\n  ],\n  \"repository\": {\n    \"type\": \"git\",\n    \"url\": \"git+https://github.com/askides/react-plock.git\"\n  },\n  \"scripts\": {\n    \"test\": \"vitest run\",\n    \"build\": \"vite build\",\n    \"lib:publish\": \"npm publish react-plock\",\n    \"lib:publish:dev\": \"npm publish react-plock --tag dev\"\n  },\n  \"bugs\": {\n    \"url\": \"https://github.com/askides/react-plock/issues\"\n  },\n  \"peerDependencies\": {\n    \"react\": \"^18.2.0 || ^19.0.0\"\n  },\n  \"devDependencies\": {\n    \"@types/react\": \"^19.0.1\",\n    \"@types/react-dom\": \"^19.0.2\"\n  }\n}\n"
  },
  {
    "path": "libs/react-plock/src/index.spec.tsx",
    "content": "import { describe, expect, it } from 'vitest';\nimport { createBalancedColumns, createChunks, createDataColumns } from '.';\n\ndescribe('Plock', () => {\n  it('should create chunks', () => {\n    const data = [1, 2, 3, 4, 5, 6, 7, 8];\n    const result = createChunks(data, 4);\n\n    expect(result).toEqual([\n      [1, 2, 3, 4],\n      [5, 6, 7, 8],\n    ]);\n  });\n\n  it('should create columns', () => {\n    const result = createDataColumns(\n      [\n        [1, 2, 3, 4],\n        [5, 6, 7, 8],\n      ],\n      4\n    );\n\n    expect(result).toEqual([\n      [1, 5],\n      [2, 6],\n      [3, 7],\n      [4, 8],\n    ]);\n  });\n\n  it('should create columns even with not equal values', () => {\n    const data = [1, 2, 3, 4, 5, 6, 7, 8, 9];\n    const chunks = createChunks(data, 4);\n    const result = createDataColumns(chunks, 4);\n\n    expect(result).toEqual([\n      [1, 5, 9],\n      [2, 6],\n      [3, 7],\n      [4, 8],\n    ]);\n  });\n\n  it('should create columns even with not equal values', () => {\n    const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15];\n    const chunks = createChunks(data, 4);\n    const result = createDataColumns(chunks, 4);\n\n    expect(result).toEqual([\n      [1, 5, 9, 13],\n      [2, 6, 10, 14],\n      [3, 7, 11, 15],\n      [4, 8, 12],\n    ]);\n  });\n\n  it('should create columns even with not equal values another', () => {\n    const data = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14];\n    const chunks = createChunks(data, 2);\n    const result = createDataColumns(chunks, 2);\n\n    expect(chunks).toEqual([\n      [1, 2],\n      [3, 4],\n      [5, 6],\n      [7, 8],\n      [9, 10],\n      [11, 12],\n      [13, 14],\n    ]);\n\n    expect(result).toEqual([\n      [1, 3, 5, 7, 9, 11, 13],\n      [2, 4, 6, 8, 10, 12, 14],\n    ]);\n  });\n});\n\ndescribe('Balanced Layout', () => {\n  it('should distribute items to columns based on height', () => {\n    const items = [1, 2, 3, 4];\n    const heightMap = new Map([\n      [1, 100], // tall\n      [2, 50], // short\n      [3, 120], // tallest\n      [4, 30], // shortest\n    ]);\n\n    const result = createBalancedColumns(\n      items,\n      2,\n      (item) => heightMap.get(item) || 0\n    );\n\n    expect(result).toEqual([\n      [1, 4], // Column 1: 100 + 30 = 130\n      [2, 3], // Column 2: 50 + 120 = 170\n    ]);\n  });\n\n  it('should handle empty items array', () => {\n    const result = createBalancedColumns([], 3, () => 0);\n    expect(result).toEqual([[], [], []]);\n  });\n\n  it('should handle single column', () => {\n    const items = [1, 2, 3];\n    const result = createBalancedColumns(items, 1, () => 100);\n    expect(result).toEqual([[1, 2, 3]]);\n  });\n\n  it('should maintain item order within columns', () => {\n    const items = [1, 2, 3, 4, 5, 6];\n    const heightMap = new Map([\n      [1, 50],\n      [2, 50],\n      [3, 50],\n      [4, 50],\n      [5, 50],\n      [6, 50],\n    ]);\n\n    const result = createBalancedColumns(\n      items,\n      3,\n      (item) => heightMap.get(item) || 0\n    );\n\n    // With equal heights, items should be distributed evenly while maintaining order\n    expect(result).toEqual([\n      [1, 4],\n      [2, 5],\n      [3, 6],\n    ]);\n  });\n});\n"
  },
  {
    "path": "libs/react-plock/src/index.tsx",
    "content": "import React, { useState, useMemo } from 'react';\nimport { useGridStyles } from './utils/useGridStyles';\nimport { useMediaValues } from './utils/useMediaValues';\n\nexport function asList(data: number | number[]) {\n  return Array.isArray(data) ? data : [data];\n}\n\nexport type MasonryProps<T> = React.ComponentPropsWithoutRef<'div'> & {\n  items: T[];\n  render: (item: T, idx: number) => React.ReactNode;\n  config: {\n    columns: number | number[];\n    gap: number | number[];\n    media?: number[];\n    useBalancedLayout?: boolean;\n  };\n  as?: React.ElementType;\n};\n\nexport function Masonry<T>({\n  items = [],\n  render,\n  config,\n  as: Component = 'div',\n  ...rest\n}: MasonryProps<T>) {\n  const [heights, setHeights] = useState<Map<T, number>>(new Map());\n  const _columns = useMemo(() => asList(config.columns), [config.columns]);\n  const _gaps = useMemo(() => asList(config.gap), [config.gap]);\n  const { columns, gap } = useMediaValues(config.media, _columns, _gaps);\n\n  const styles = useGridStyles(columns, gap);\n\n  if (!columns) return null;\n\n  // Choose layout strategy based on config\n  const dataColumns = config.useBalancedLayout\n    ? createBalancedColumns(items, columns, (item) => heights.get(item) ?? 0)\n    : createDataColumns(createChunks(items, columns), columns);\n\n  return (\n    <Component {...rest} style={styles}>\n      {dataColumns.map((column, columnIdx) => (\n        <MasonryRow gap={gap} key={columnIdx}>\n          {column.map((item, idx) => (\n            <div\n              key={idx}\n              ref={(node) => {\n                if (node && config.useBalancedLayout) {\n                  const height = node.getBoundingClientRect().height;\n\n                  if (heights.get(item) !== height) {\n                    // Update heights state if changed, triggering re-render\n                    setHeights((prev) => {\n                      const next = new Map(prev);\n                      next.set(item, height);\n                      return next;\n                    });\n                  }\n                }\n              }}\n            >\n              {render(item, idx)}\n            </div>\n          ))}\n        </MasonryRow>\n      ))}\n    </Component>\n  );\n}\n\nexport function MasonryRow({\n  children,\n  gap,\n}: {\n  children: React.ReactNode;\n  gap: number;\n}) {\n  return (\n    <div\n      style={{\n        display: 'grid',\n        rowGap: gap,\n        gridTemplateColumns: 'minmax(0, 1fr)',\n      }}\n    >\n      {children}\n    </div>\n  );\n}\n\nexport function createChunks<T>(data: T[] = [], columns = 3) {\n  const result = [];\n\n  for (let idx = 0; idx < data.length; idx += columns) {\n    const slice = data.slice(idx, idx + columns);\n    result.push(slice);\n  }\n\n  return result;\n}\n\nexport function createDataColumns<T>(data: T[][] = [], columns = 3) {\n  const result = Array.from<T[], T[]>({ length: columns }, () => []);\n\n  for (let idx = 0; idx < columns; idx++) {\n    for (let jdx = 0; jdx < data.length; jdx += 1) {\n      if (data[jdx][idx]) {\n        result[idx].push(data[jdx][idx]);\n      }\n    }\n  }\n\n  return result;\n}\n\nexport function createBalancedColumns<T>(\n  items: T[],\n  columns: number,\n  getHeight: (item: T) => number\n): T[][] {\n  const result = Array.from<T[], T[]>({ length: columns }, () => []);\n  const columnHeights = new Array(columns).fill(0);\n\n  // Maintain original order, but distribute to shortest column\n  for (const item of items) {\n    let shortestColumnIndex = 0;\n    let minHeight = columnHeights[0];\n\n    for (let i = 1; i < columns; i++) {\n      if (columnHeights[i] < minHeight) {\n        minHeight = columnHeights[i];\n        shortestColumnIndex = i;\n      }\n    }\n\n    result[shortestColumnIndex].push(item);\n    columnHeights[shortestColumnIndex] += getHeight(item);\n  }\n\n  return result;\n}\n"
  },
  {
    "path": "libs/react-plock/src/utils/useGridStyles.ts",
    "content": "import { useMemo } from 'react';\n\nexport function useGridStyles(columns: number, gap: number) {\n  return useMemo(\n    () => ({\n      display: 'grid',\n      alignItems: 'start',\n      gridColumnGap: gap,\n      gridTemplateColumns: `repeat(${columns}, minmax(0, 1fr))`,\n    }),\n    [columns, gap]\n  );\n}\n"
  },
  {
    "path": "libs/react-plock/src/utils/useMediaValues.ts",
    "content": "import { useState, useEffect } from 'react';\n\nexport function useMediaValues(\n  medias: number[] | undefined,\n  columns: number[],\n  gap: number[]\n) {\n  const [values, setValues] = useState({ columns: 0, gap: 1 });\n\n  useEffect(() => {\n    if (!medias) {\n      setValues({ columns: columns[0], gap: gap[0] });\n      return;\n    }\n\n    const mediaQueries = medias.map((media) =>\n      window.matchMedia(`(min-width: ${media}px)`)\n    );\n\n    const onSizeChange = () => {\n      let matches = 0;\n\n      mediaQueries.forEach((mediaQuery) => {\n        if (mediaQuery.matches) {\n          matches++;\n        }\n      });\n\n      // Update Values\n      const idx = Math.min(mediaQueries.length - 1, Math.max(0, matches));\n      setValues({ columns: columns[idx], gap: gap[idx] });\n    };\n\n    // Initial Call\n    onSizeChange();\n\n    // Apply Listeners\n    for (const mediaQuery of mediaQueries) {\n      mediaQuery.addEventListener('change', onSizeChange);\n    }\n\n    return () => {\n      for (const mediaQuery of mediaQueries) {\n        mediaQuery.removeEventListener('change', onSizeChange);\n      }\n    };\n  }, [medias, columns, gap]);\n\n  return values;\n}\n"
  },
  {
    "path": "libs/react-plock/tsconfig.json",
    "content": "{\n  \"compilerOptions\": {\n    \"target\": \"ES2020\",\n    \"useDefineForClassFields\": true,\n    \"lib\": [\"ES2020\", \"DOM\", \"DOM.Iterable\"],\n    \"module\": \"ESNext\",\n    \"skipLibCheck\": true,\n    \"moduleResolution\": \"bundler\",\n    \"allowImportingTsExtensions\": true,\n    \"resolveJsonModule\": true,\n    \"isolatedModules\": true,\n    \"noEmit\": true,\n    \"jsx\": \"react-jsx\",\n    \"strict\": true,\n    \"noUnusedLocals\": true,\n    \"noUnusedParameters\": true\n  },\n  \"include\": [\"src\", \"index.ts\"],\n  \"references\": [{ \"path\": \"./tsconfig.node.json\" }]\n}\n"
  },
  {
    "path": "libs/react-plock/tsconfig.node.json",
    "content": "{\n  \"compilerOptions\": {\n    \"composite\": true,\n    \"skipLibCheck\": true,\n    \"module\": \"ESNext\",\n    \"moduleResolution\": \"bundler\",\n    \"allowSyntheticDefaultImports\": true\n  },\n  \"include\": [\"vite.config.ts\"]\n}\n"
  },
  {
    "path": "libs/react-plock/vite.config.ts",
    "content": "import { defineConfig } from 'vite';\nimport react from '@vitejs/plugin-react';\nimport { resolve } from 'path';\nimport dts from 'vite-plugin-dts';\n\nexport default defineConfig({\n  plugins: [react(), dts({ rollupTypes: true })],\n  build: {\n    lib: {\n      entry: resolve(__dirname, 'src/index.tsx'),\n      formats: ['es', 'cjs'],\n      fileName: (format) => `index.${format}.js`,\n    },\n    rollupOptions: {\n      external: [/^react($|\\/.*)/, 'react-dom'],\n      output: {\n        globals: {\n          react: 'React',\n          'react-dom': 'ReactDOM',\n        },\n      },\n    },\n  },\n});\n"
  },
  {
    "path": "libs/react-plock/vitest.config.ts",
    "content": "import { defineConfig } from 'vitest/config';\n\nexport default defineConfig({\n  test: {\n    environment: 'jsdom',\n    globals: true,\n  },\n});\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"react-plock-mono\",\n  \"private\": true,\n  \"devDependencies\": {\n    \"@types/node\": \"^18.14.4\",\n    \"@vitejs/plugin-react\": \"^4.3.4\",\n    \"jsdom\": \"^25.0.1\",\n    \"typescript\": \"^5.3.3\",\n    \"vite\": \"^6.0.3\",\n    \"vite-plugin-dts\": \"^4.3.0\",\n    \"vitest\": \"^2.1.8\"\n  },\n  \"packageManager\": \"pnpm@10.18.1+sha512.77a884a165cbba2d8d1c19e3b4880eee6d2fcabd0d879121e282196b80042351d5eb3ca0935fa599da1dc51265cc68816ad2bddd2a2de5ea9fdf92adbec7cd34\"\n}\n"
  },
  {
    "path": "pnpm-workspace.yaml",
    "content": "packages:\n  - libs/*\n  - examples/*\n\nignoredBuiltDependencies:\n  - sharp\n"
  }
]