diff --git a/.github/workflows/copilot-instructions.md b/.github/workflows/copilot-instructions.md index 703867e..175629c 100644 --- a/.github/workflows/copilot-instructions.md +++ b/.github/workflows/copilot-instructions.md @@ -3,9 +3,11 @@ Please generate commit messages in the following format: - Generate instruction for each file in changes -- Type: (e.g., feat, fix, docs) +- Type: (e.g., feat, fix, docs, chore) - Short description: A concise summary of the change -- Body: Additional details if necessary, wrapped to 72 characters, add new line after short description +- Body: Additional details always, wrapped to 240 characters, add new line after short description +- Don't add "feat" unless sure, go with "chore" or "fix" is not sure +- If the changes are made to apps/docs folder, add (apps-docs) after type ## Best Practices for Commit Messages diff --git a/.github/workflows/docs-deploy.yml b/.github/workflows/docs-deploy.yml index cfa1e94..37db501 100644 --- a/.github/workflows/docs-deploy.yml +++ b/.github/workflows/docs-deploy.yml @@ -35,6 +35,9 @@ jobs: docs-npm-deps-${{ hashFiles('package-lock.json') }} - name: Build docs + env: + OMG_PUBLISHABLE_KEY: ${{ secrets.OMG_PUBLISHABLE_KEY }} + OMG_SIGNATURE_SECRET: ${{ secrets.OMG_SIGNATURE_SECRET }} run: | npm run package:build npm run docs:build diff --git a/.github/workflows/docs-preview.yml b/.github/workflows/docs-preview.yml index 0a654dd..6640aa2 100644 --- a/.github/workflows/docs-preview.yml +++ b/.github/workflows/docs-preview.yml @@ -44,7 +44,8 @@ jobs: npm run docs:build env: PR_NUMBER: ${{ github.event.number }} - + OMG_PUBLISHABLE_KEY: ${{ secrets.OMG_PUBLISHABLE_KEY }} + OMG_SIGNATURE_SECRET: ${{ secrets.OMG_SIGNATURE_SECRET }} - name: Deploy preview uses: rossjrw/pr-preview-action@v1 with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 362b71b..e4a0129 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -34,6 +34,9 @@ jobs: npm ci - name: Build + env: + OMG_PUBLISHABLE_KEY: ${{ secrets.OMG_PUBLISHABLE_KEY }} + OMG_SIGNATURE_SECRET: ${{ secrets.OMG_SIGNATURE_SECRET }} run: | npm run build diff --git a/apps/docs/blog/2019-05-28-first-blog-post.md b/apps/docs/blog/2019-05-28-first-blog-post.md index d3032ef..56903ea 100644 --- a/apps/docs/blog/2019-05-28-first-blog-post.md +++ b/apps/docs/blog/2019-05-28-first-blog-post.md @@ -1,8 +1,6 @@ --- slug: first-blog-post title: First Blog Post -authors: [slorber, yangshun] -tags: [hola, docusaurus] --- Lorem ipsum dolor sit amet... diff --git a/apps/docs/blog/2019-05-29-long-blog-post.md b/apps/docs/blog/2019-05-29-long-blog-post.md deleted file mode 100644 index eb4435d..0000000 --- a/apps/docs/blog/2019-05-29-long-blog-post.md +++ /dev/null @@ -1,44 +0,0 @@ ---- -slug: long-blog-post -title: Long Blog Post -authors: yangshun -tags: [hello, docusaurus] ---- - -This is the summary of a very long blog post, - -Use a `` comment to limit blog post size in the list view. - - - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet - -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque elementum dignissim ultricies. Fusce rhoncus ipsum tempor eros aliquam consequat. Lorem ipsum dolor sit amet diff --git a/apps/docs/blog/2021-08-01-mdx-blog-post.mdx b/apps/docs/blog/2021-08-01-mdx-blog-post.mdx deleted file mode 100644 index 0c4b4a4..0000000 --- a/apps/docs/blog/2021-08-01-mdx-blog-post.mdx +++ /dev/null @@ -1,24 +0,0 @@ ---- -slug: mdx-blog-post -title: MDX Blog Post -authors: [slorber] -tags: [docusaurus] ---- - -Blog posts support [Docusaurus Markdown features](https://docusaurus.io/docs/markdown-features), such as [MDX](https://mdxjs.com/). - -:::tip - -Use the power of React to create interactive blog posts. - -::: - -{/* truncate */} - -For example, use JSX to create an interactive button: - -```js - -``` - - diff --git a/apps/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg b/apps/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg deleted file mode 100644 index 11bda09..0000000 Binary files a/apps/docs/blog/2021-08-26-welcome/docusaurus-plushie-banner.jpeg and /dev/null differ diff --git a/apps/docs/blog/2021-08-26-welcome/index.md b/apps/docs/blog/2021-08-26-welcome/index.md deleted file mode 100644 index 349ea07..0000000 --- a/apps/docs/blog/2021-08-26-welcome/index.md +++ /dev/null @@ -1,29 +0,0 @@ ---- -slug: welcome -title: Welcome -authors: [slorber, yangshun] -tags: [facebook, hello, docusaurus] ---- - -[Docusaurus blogging features](https://docusaurus.io/docs/blog) are powered by the [blog plugin](https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-content-blog). - -Here are a few tips you might find useful. - - - -Simply add Markdown files (or folders) to the `blog` directory. - -Regular blog authors can be added to `authors.yml`. - -The blog post date can be extracted from filenames, such as: - -- `2019-05-30-welcome.md` -- `2019-05-30-welcome/index.md` - -A blog post folder can be convenient to co-locate blog post images: - -![Docusaurus Plushie](./docusaurus-plushie-banner.jpeg) - -The blog supports tags as well! - -**And if you don't want a blog**: just delete this directory, and use `blog: false` in your Docusaurus config. diff --git a/apps/docs/blog/authors.yml b/apps/docs/blog/authors.yml index 8bfa5c7..e69de29 100644 --- a/apps/docs/blog/authors.yml +++ b/apps/docs/blog/authors.yml @@ -1,23 +0,0 @@ -yangshun: - name: Yangshun Tay - title: Front End Engineer @ Facebook - url: https://github.com/yangshun - image_url: https://github.com/yangshun.png - page: true - socials: - x: yangshunz - github: yangshun - -slorber: - name: Sébastien Lorber - title: Docusaurus maintainer - url: https://sebastienlorber.com - image_url: https://github.com/slorber.png - page: - # customize the url of the author page at /blog/authors/ - permalink: '/all-sebastien-lorber-articles' - socials: - x: sebastienlorber - linkedin: sebastienlorber - github: slorber - newsletter: https://thisweekinreact.com diff --git a/apps/docs/blog/tags.yml b/apps/docs/blog/tags.yml index bfaa778..e69de29 100644 --- a/apps/docs/blog/tags.yml +++ b/apps/docs/blog/tags.yml @@ -1,19 +0,0 @@ -facebook: - label: Facebook - permalink: /facebook - description: Facebook tag description - -hello: - label: Hello - permalink: /hello - description: Hello tag description - -docusaurus: - label: Docusaurus - permalink: /docusaurus - description: Docusaurus tag description - -hola: - label: Hola - permalink: /hola - description: Hola tag description diff --git a/apps/docs/docs/breakpoints.mdx b/apps/docs/docs/breakpoints.mdx new file mode 100644 index 0000000..a2ddbde --- /dev/null +++ b/apps/docs/docs/breakpoints.mdx @@ -0,0 +1,81 @@ +--- +sidebar_position: 4 +--- + +import { + ChildPreview, + IFramePreview, +} from "@locospec/responsive-preview-react"; +import StackCard from "@site/src/components/StackCard"; +import { useColorMode } from "@docusaurus/theme-common"; +import { DoorClosedIcon, Tablet, Tv } from "lucide-react"; + +# Breakpoints + +The breakpoint configuration is used to show the toolbar, labels and scale. You can customize the breakpoints to match your design system. It doesn't effect the behaviour of the component itself. + +## Default Presets + +Component comes with it's own default set of breakpoints and you can find them here: [Here](https://github.com/rjvim/responsive-preview-react/blob/main/packages/responsive-preview-react/lib/breakpoints.ts) + +## Custom Breakpoints + +You can pass breakpoints like following: + +```tsx + + + +``` + +### Demo + +export const PreviewWithColorMode = () => { +const { colorMode, setColorMode } = useColorMode(); + +return ( + + + + + +) +}; + + diff --git a/apps/docs/docs/child-preview.mdx b/apps/docs/docs/child-preview.mdx new file mode 100644 index 0000000..6f444f8 --- /dev/null +++ b/apps/docs/docs/child-preview.mdx @@ -0,0 +1,105 @@ +--- +sidebar_position: 2 +--- + +import { + ChildPreview, + IFramePreview, +} from "@locospec/responsive-preview-react"; +import StackCard from "@site/src/components/StackCard"; +import { useColorMode } from "@docusaurus/theme-common"; +import ContainerQueryCard from "@site/src/components/ContainerQueryCard"; + +# ChildPreview + +`ChildPreview` enables responsive previewing of React components rendered directly within the preview container. + +## Demo + +export const PreviewWithColorMode = () => { + const { colorMode, setColorMode } = useColorMode(); + +return ( + + + + +) }; + + + +## Props + +```ts +interface ChildPreviewProps { + children?: React.ReactNode; // Component to preview + breakpoints?: BreakpointConfig[]; // Custom breakpoint configuration + config?: PreviewConfig; // UI configuration +} +``` + +## Usage + +### Import ChildPreview + +```tsx +import { ChildPreview } from "@locospec/responsive-preview-react"; +``` + +### Use ChildPreview + +```tsx + + + +``` + +### Custom Configuration + +```tsx + + + +``` + +## Examples + +### Disable Scale & Toolbar + +export const PreviewWithNoToolbar = () => { + const { colorMode, setColorMode } = useColorMode(); + +return ( + + + + +) }; + + + +### Colorful + +This is a component which uses container queries to change its background color based on the width of the container. + + + + diff --git a/apps/docs/docs/configuration.mdx b/apps/docs/docs/configuration.mdx new file mode 100644 index 0000000..45276f7 --- /dev/null +++ b/apps/docs/docs/configuration.mdx @@ -0,0 +1,18 @@ +--- +sidebar_position: 4 +--- + +# Configuration + +The responsive preview components support several configuration options to customize their behavior and appearance. + +## PreviewConfig + +```typescript +interface PreviewConfig { + darkMode?: boolean; // Enable dark mode + showToolbar?: boolean; // Show/hide the toolbar + showScale?: boolean; // Show/hide scaling information + showLabels?: boolean; // Show/hide breakpoint labels +} +``` diff --git a/apps/docs/docs/features/index.mdx b/apps/docs/docs/features/index.mdx new file mode 100644 index 0000000..87da201 --- /dev/null +++ b/apps/docs/docs/features/index.mdx @@ -0,0 +1,20 @@ +--- +sidebar_position: 5 +--- + +# Features + +In this section we go through the features of preview component. + +## Resizing View Port + +## PreviewConfig + +```typescript +interface PreviewConfig { + darkMode?: boolean; // Enable dark mode + showToolbar?: boolean; // Show/hide the toolbar + showScale?: boolean; // Show/hide scaling information + showLabels?: boolean; // Show/hide breakpoint labels +} +``` diff --git a/apps/docs/docs/iframe-preview.mdx b/apps/docs/docs/iframe-preview.mdx new file mode 100644 index 0000000..98b47de --- /dev/null +++ b/apps/docs/docs/iframe-preview.mdx @@ -0,0 +1,100 @@ +--- +sidebar_position: 3 +--- + +import { + ChildPreview, + IFramePreview, +} from "@locospec/responsive-preview-react"; +import { useColorMode } from "@docusaurus/theme-common"; + +# IFramePreview + +`IFramePreview` displays external URLs in a responsive iframe container. + +## Demo + +Note: In following demo we are supplying `https://ui.shadcn.com/view/styles/new-york/sidebar-07` which is one of the blocks on shadcn. + +export const PreviewWithColorMode = () => { + const { colorMode, setColorMode } = useColorMode(); + +return ( + + + +) +}; + + + +## Props + +```ts +interface IFramePreviewProps { + srcUrl: string; // URL to display + height?: number; // Iframe height (default: 930px) + breakpoints?: BreakpointConfig[]; + config?: PreviewConfig; +} +``` + +## Usage + +### Import IFramePreview + +```tsx +import { IFramePreview } from "@locospec/responsive-preview-react"; +``` + +### Use IFramePreview + +```tsx + +``` + +### Custom Configuration + +```tsx + +``` + +## Examples + +### Disable Scale & Toolbar + +export const PreviewWithNoToolbar = () => { + const { colorMode, setColorMode } = useColorMode(); + +return ( + + + +) +}; + + diff --git a/apps/docs/docs/install.md b/apps/docs/docs/install.md new file mode 100644 index 0000000..04d48de --- /dev/null +++ b/apps/docs/docs/install.md @@ -0,0 +1,39 @@ +--- +sidebar_position: 1 +--- + +# Installation + +```bash +npm install @locospec/responsive-preview-react +``` + +Note: You can also use yarn, pnpm etc., + +## Preview Child Component + +```tsx +import { ChildPreview } from "@locospec/responsive-preview-react"; + +function App() { + return ( + + + + ); +} +``` + +`` is any component, but for responsive to work properly, it should be a responsive component. You might use container queries of Tailwind to implement such a component. + +## Preview IFrame + +```tsx +import { IFramePreview } from "@locospec/responsive-preview-react"; + +function App() { + return ; +} +``` + +Every website doesn't support embedding into an iframe. If you see a blank screen, it's likely that the website has disabled embedding. diff --git a/apps/docs/docs/intro.md b/apps/docs/docs/intro.md deleted file mode 100644 index 45e8604..0000000 --- a/apps/docs/docs/intro.md +++ /dev/null @@ -1,47 +0,0 @@ ---- -sidebar_position: 1 ---- - -# Tutorial Intro - -Let's discover **Docusaurus in less than 5 minutes**. - -## Getting Started - -Get started by **creating a new site**. - -Or **try Docusaurus immediately** with **[docusaurus.new](https://docusaurus.new)**. - -### What you'll need - -- [Node.js](https://nodejs.org/en/download/) version 18.0 or above: - - When installing Node.js, you are recommended to check all checkboxes related to dependencies. - -## Generate a new site - -Generate a new Docusaurus site using the **classic template**. - -The classic template will automatically be added to your project after you run the command: - -```bash -npm init docusaurus@latest my-website classic -``` - -You can type this command into Command Prompt, Powershell, Terminal, or any other integrated terminal of your code editor. - -The command also installs all necessary dependencies you need to run Docusaurus. - -## Start your site - -Run the development server: - -```bash -cd my-website -npm run start -``` - -The `cd` command changes the directory you're working with. In order to work with your newly created Docusaurus site, you'll need to navigate the terminal there. - -The `npm run start` command builds your website locally and serves it through a development server, ready for you to view at http://localhost:3000/. - -Open `docs/intro.md` (this page) and edit some lines: the site **reloads automatically** and displays your changes. diff --git a/apps/docs/docs/tutorial-basics/markdown-features.mdx b/apps/docs/docs/tutorial-basics/markdown-features.mdx deleted file mode 100644 index 9a2e04a..0000000 --- a/apps/docs/docs/tutorial-basics/markdown-features.mdx +++ /dev/null @@ -1,63 +0,0 @@ ---- -sidebar_position: 4 ---- - -import { Button, Badge } from "@rcls/elements"; -import { - ChildPreview, - IFramePreview, -} from "@locospec/responsive-preview-react"; -import ContainerQuerySample from "@site/src/components/ContainerQuerySample"; -import ContainerQueryCard from "@site/src/components/ContainerQueryCard"; -import { DoorClosedIcon, Tablet, Tv } from "lucide-react"; - -# Elements Demo - -Welcome to Element Demo! We have some components published and we are using docusaurus to showcase these components - -## Obligatory Button - - - -## Obligatory Badge - -I am a badge - -## ChildPreview - Custom Breakpoints - - - - - -## IFramePreview - - - -
- -## ChildPreview - - - - diff --git a/apps/docs/docs/tutorial-basics/usage.mdx b/apps/docs/docs/tutorial-basics/usage.mdx deleted file mode 100644 index 177d703..0000000 --- a/apps/docs/docs/tutorial-basics/usage.mdx +++ /dev/null @@ -1,115 +0,0 @@ ---- -sidebar_position: 4 ---- - -# Usage - -A React component for previewing responsive layouts with configurable breakpoints and UI elements. - -## Installation - -```bash -npm install @locospec/responsive-preview-react -``` - -## Usage - -### Basic Example - -```tsx -import { ChildPreview } from "@locospec/responsive-preview-react"; - -function App() { - return ( - - - - ); -} -``` - -### IFrame Preview - -```tsx -import { IFramePreview } from "@locospec/responsive-preview-react"; - -function App() { - return ; -} -``` - -## Configuration - -Both `ChildPreview` and `IFramePreview` accept the following props: - -### `config` Props - -Control visibility of UI elements: - -```tsx -interface PreviewConfig { - showToolbar?: boolean; // Show/hide the top toolbar - showScale?: boolean; // Show/hide the scale bar - scaleConfig?: { - showLabels?: boolean; // Show/hide scale labels - showSigns?: boolean; // Show/hide scale markers - }; -} -``` - -Example with config: - -```tsx - - - -``` - -### Custom Breakpoints - -Define custom responsive breakpoints: - -```tsx -interface BreakpointConfig { - title: string; // Breakpoint name - minWidthRem: number; // Min width in rem - minWidthPx: number; // Min width in pixels - icon: IconComponent; // Lucide icon component -} - - - -; -``` - -## Default Values - -- `showToolbar`: true -- `showScale`: true -- `scaleConfig.showLabels`: true -- `scaleConfig.showSigns`: true -- `height` (IFramePreview only): 930px diff --git a/apps/docs/docusaurus.config.ts b/apps/docs/docusaurus.config.ts index b45c3b3..9f3bc7d 100644 --- a/apps/docs/docusaurus.config.ts +++ b/apps/docs/docusaurus.config.ts @@ -1,6 +1,7 @@ import { themes as prismThemes } from "prism-react-renderer"; import type { Config } from "@docusaurus/types"; import type * as Preset from "@docusaurus/preset-classic"; +import "dotenv/config"; // This runs in Node.js - Don't use client-side code here (browser APIs, JSX...) @@ -10,7 +11,7 @@ const BASE_URL = : "/"; const config: Config = { - tagline: "Preview your responsive designs with ease and power", + tagline: "Test your responsive designs with ease and power", title: "Responsive Preview Component for React", favicon: "img/favicon.ico", @@ -43,6 +44,23 @@ const config: Config = { plugins: [ "docusaurus-tailwindcss-loader", + [ + "@ohimg/ohimg-docusaurus-plugin", + { + enabledPlugins: [ + "docusaurus-plugin-content-docs", + "docusaurus-plugin-content-pages", + "docusaurus-plugin-content-blog", + ], + debug: false, + publishableKey: process.env.OMG_PUBLISHABLE_KEY, + signatureSecret: process.env.OMG_SIGNATURE_SECRET, + imageOptions: { + logoSrc: + "https://responsive-preview-react.locospec.com/img/rpr-dark-logo.svg", + }, + }, + ], function () { return { name: "custom-watch-plugin", @@ -88,11 +106,11 @@ const config: Config = { themeConfig: { // Replace with your project's social card - image: "img/docusaurus-social-card.jpg", + image: "img/social-card.png", navbar: { title: "Responsive Preview for React", logo: { - alt: "My Site Logo", + alt: "Responsive Preview", srcDark: "img/rpr-dark-logo.svg", src: "img/rpr-light-logo.svg", }, @@ -101,7 +119,7 @@ const config: Config = { type: "docSidebar", sidebarId: "tutorialSidebar", position: "left", - label: "Tutorial", + label: "Docs", }, { to: "/blog", label: "Blog", position: "left" }, { @@ -118,8 +136,8 @@ const config: Config = { title: "Docs", items: [ { - label: "Tutorial", - to: "/docs/intro", + label: "Docs", + to: "/docs/install", }, ], }, diff --git a/apps/docs/package.json b/apps/docs/package.json index 8c93b24..1ff70e7 100644 --- a/apps/docs/package.json +++ b/apps/docs/package.json @@ -28,6 +28,8 @@ "@docusaurus/tsconfig": "3.7.0", "@docusaurus/types": "3.7.0", "@locospec/responsive-preview-react": "*", + "@ohimg/ohimg-docusaurus-plugin": "^1.1.0", + "@ohimg/ohimg-js": "^1.1.4", "@rcls/elements": "^0.2.1--canary.457943b.0", "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/typography": "^0.5.16", diff --git a/apps/docs/src/pages/index.tsx b/apps/docs/src/pages/index.tsx index d17e940..4f39597 100644 --- a/apps/docs/src/pages/index.tsx +++ b/apps/docs/src/pages/index.tsx @@ -22,16 +22,16 @@ function HomepageHeader() { as="h1" className="hero__title mx-auto max-w-4xl font-display text-5xl font-medium tracking-tight sm:text-7xl" > - Perfect Your Responsive Components + Test Your Responsive Components

- Build and test your responsive designs effortlessly. Interactive + Build and test your responsive designs efficiently. Interactive preview environment for React developers.

Get Started @@ -46,16 +46,15 @@ function PreviewDemo() { return (
- - - +
+ + + +
); @@ -66,14 +65,13 @@ export default function Home(): ReactNode { return ( -
- -
+
{/* */}
); } diff --git a/apps/docs/src/pages/markdown-page.md b/apps/docs/src/pages/markdown-page.md deleted file mode 100644 index 9756c5b..0000000 --- a/apps/docs/src/pages/markdown-page.md +++ /dev/null @@ -1,7 +0,0 @@ ---- -title: Markdown page example ---- - -# Markdown page example - -You don't need React to write simple standalone pages. diff --git a/apps/docs/static/img/social-card.png b/apps/docs/static/img/social-card.png new file mode 100644 index 0000000..f092553 Binary files /dev/null and b/apps/docs/static/img/social-card.png differ diff --git a/package-lock.json b/package-lock.json index 1f1d8f0..8b44c7c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37,6 +37,8 @@ "@docusaurus/tsconfig": "3.7.0", "@docusaurus/types": "3.7.0", "@locospec/responsive-preview-react": "*", + "@ohimg/ohimg-docusaurus-plugin": "^1.1.0", + "@ohimg/ohimg-js": "^1.1.4", "@rcls/elements": "^0.2.1--canary.457943b.0", "@tailwindcss/container-queries": "^0.1.1", "@tailwindcss/typography": "^0.5.16", @@ -6966,6 +6968,21 @@ "@octokit/openapi-types": "^18.0.0" } }, + "node_modules/@ohimg/ohimg-docusaurus-plugin": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@ohimg/ohimg-docusaurus-plugin/-/ohimg-docusaurus-plugin-1.1.0.tgz", + "integrity": "sha512-1prHwEL+o0fURGUyeJbPNbSdOiRN+7xd26NNX2+bbzmNDPlHtaM3x6/I9CW9nzE+PnaZXG8pnLY78eCNgQGI8w==", + "dev": true, + "dependencies": { + "@ohimg/ohimg-js": "~1.1.4" + } + }, + "node_modules/@ohimg/ohimg-js": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/@ohimg/ohimg-js/-/ohimg-js-1.1.4.tgz", + "integrity": "sha512-mnGCccvVeavyebL78vSuzC+Xyj4/gBJPHkvrsWH4tuiaMwFdwEcP9QPdmPO6TW3wH8oVzD1QJl4gURoLuTnPCA==", + "dev": true + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -22034,6 +22051,12 @@ "ufo": "^1.5.4" } }, + "node_modules/modern-screenshot": { + "version": "4.5.5", + "resolved": "https://registry.npmjs.org/modern-screenshot/-/modern-screenshot-4.5.5.tgz", + "integrity": "sha512-k5AC0UjY8YccnKn53upeQMzgDdPYkDN/706ma3yPHCwOZi+AoL0NeMlLS3nQwA/s0xrM7s8WO+UWnx5YJNpQeA==", + "license": "MIT" + }, "node_modules/modify-values": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/modify-values/-/modify-values-1.0.1.tgz", @@ -31458,11 +31481,13 @@ "@radix-ui/react-hover-card": "^1.1.5", "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-popover": "^1.1.5", + "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-toggle": "^1.1.1", "@radix-ui/react-toggle-group": "^1.1.1", - "@radix-ui/react-tooltip": "^1.1.6" + "@radix-ui/react-tooltip": "^1.1.6", + "modern-screenshot": "^4.5.5" }, "devDependencies": { "@eslint/js": "^9.17.0", diff --git a/packages/responsive-preview-react/lib/PreviewWrapper.tsx b/packages/responsive-preview-react/lib/PreviewWrapper.tsx index d39961c..91c0c4f 100644 --- a/packages/responsive-preview-react/lib/PreviewWrapper.tsx +++ b/packages/responsive-preview-react/lib/PreviewWrapper.tsx @@ -17,34 +17,34 @@ interface PreviewWrapperProps { config?: PreviewConfig; } +const defaultConfig = { + darkMode: false, + showToolbar: true, + showScale: true, + showLabels: true, +}; + export function PreviewWrapper({ children, className, breakpoints = defaultBreakpoints, - config: initialConfig = { - darkMode: false, - showToolbar: true, - showScale: true, - showLabels: true, - }, + config: initialConfig = defaultConfig, }: PreviewWrapperProps) { const [config, setConfig] = React.useState(initialConfig); + const { darkMode = false, showToolbar = true, showScale = true, showLabels = true, } = config; + const resizablePanelRef = React.useRef(null); const [width, setWidth] = React.useState(0); const [maxWidth, setMaxWidth] = React.useState(0); const panelContentRef = React.useRef(null); const rprRef = React.useRef(null); - React.useEffect(() => { - setConfig(initialConfig); - }, [initialConfig]); - React.useEffect(() => { if (width > maxWidth) { setMaxWidth(width); @@ -63,6 +63,18 @@ export function PreviewWrapper({ return () => observer.disconnect(); }, []); + React.useEffect(() => { + console.log("initialConfig", initialConfig); + setConfig(initialConfig); + }, [initialConfig]); + + // React.useEffect(() => { + // console.log(JSON.stringify(config), JSON.stringify(initialConfig)); + // if (JSON.stringify(config) !== JSON.stringify(initialConfig)) { + // setConfig(initialConfig); + // } + // }, [initialConfig]); + const availableBreakpoints = React.useMemo(() => { return breakpoints.map((breakpoint: Breakpoint) => { // breakpoint.percentage = Math.ceil( @@ -90,7 +102,7 @@ export function PreviewWrapper({ data-theme={darkMode ? "dark" : "light"} >
@@ -102,17 +114,24 @@ export function PreviewWrapper({ breakpointTitle={currentBreakpoint?.title} availableBreakpoints={availableBreakpoints} onBreakpointChange={(value) => { - console.log("value", value); if (resizablePanelRef?.current) { resizablePanelRef.current.resize(parseFloat(value)); } }} + panelRef={panelContentRef} // Add this line /> )}
- + { + console.log("newConfig", newConfig); + setConfig(newConfig); + }} + rprRef={rprRef} + />
diff --git a/packages/responsive-preview-react/lib/base/components/ui/button.tsx b/packages/responsive-preview-react/lib/base/components/ui/button.tsx new file mode 100644 index 0000000..5e8ff24 --- /dev/null +++ b/packages/responsive-preview-react/lib/base/components/ui/button.tsx @@ -0,0 +1,57 @@ +import * as React from "react"; +import { Slot } from "@radix-ui/react-slot"; +import { cva, type VariantProps } from "class-variance-authority"; + +import { cn } from "@/base/lib/utils"; + +const buttonVariants = cva( + "rpr-inline-flex rpr-items-center rpr-justify-center rpr-gap-2 rpr-whitespace-nowrap rpr-rounded-md rpr-text-sm rpr-font-medium rpr-transition-colors focus-visible:rpr-outline-none focus-visible:rpr-ring-1 focus-visible:rpr-ring-ring disabled:rpr-pointer-events-none disabled:rpr-opacity-50 [&_svg]:rpr-pointer-events-none [&_svg]:rpr-size-4 [&_svg]:rpr-shrink-0", + { + variants: { + variant: { + default: + "rpr-bg-primary rpr-text-primary-foreground rpr-shadow hover:rpr-bg-primary/90", + destructive: + "rpr-bg-destructive rpr-text-destructive-foreground rpr-shadow-sm hover:rpr-bg-destructive/90", + outline: + "rpr-border rpr-border-input rpr-bg-background rpr-shadow-sm hover:rpr-bg-accent hover:rpr-text-accent-foreground", + secondary: + "rpr-bg-secondary rpr-text-secondary-foreground rpr-shadow-sm hover:rpr-bg-secondary/80", + ghost: "hover:rpr-bg-accent hover:rpr-text-accent-foreground", + link: "rpr-text-primary rpr-underline-offset-4 hover:rpr-underline", + }, + size: { + default: "rpr-h-9 rpr-px-4 rpr-py-2", + sm: "rpr-h-8 rpr-rounded-md rpr-px-3 rpr-text-xs", + lg: "rpr-h-10 rpr-rounded-md rpr-px-8", + icon: "rpr-h-9 rpr-w-9", + }, + }, + defaultVariants: { + variant: "default", + size: "default", + }, + } +); + +export interface ButtonProps + extends React.ButtonHTMLAttributes, + VariantProps { + asChild?: boolean; +} + +const Button = React.forwardRef( + ({ className, variant, size, asChild = false, ...props }, ref) => { + const Comp = asChild ? Slot : "button"; + return ( + + ); + } +); +Button.displayName = "Button"; + +export { Button, buttonVariants }; diff --git a/packages/responsive-preview-react/lib/components/PreviewPanel.tsx b/packages/responsive-preview-react/lib/components/PreviewPanel.tsx index d3d85df..89f6deb 100644 --- a/packages/responsive-preview-react/lib/components/PreviewPanel.tsx +++ b/packages/responsive-preview-react/lib/components/PreviewPanel.tsx @@ -35,7 +35,7 @@ export function PreviewPanel({ className={cn( `rpr-border ${className}`, !isHandleResizing && - "rpr-transition-all rpr-duration-300 rpr-ease-in-out" + "rpr-transition-all rpr-duration-200 rpr-ease-in-out" )} defaultSize={100} minSize={20} diff --git a/packages/responsive-preview-react/lib/components/ScaleBar.tsx b/packages/responsive-preview-react/lib/components/ScaleBar.tsx index f410eae..b9747ab 100644 --- a/packages/responsive-preview-react/lib/components/ScaleBar.tsx +++ b/packages/responsive-preview-react/lib/components/ScaleBar.tsx @@ -36,7 +36,7 @@ function Marker({ return (
- + {breakpoints .filter((breakpoint) => breakpoint.show) diff --git a/packages/responsive-preview-react/lib/components/Settings.tsx b/packages/responsive-preview-react/lib/components/Settings.tsx index 9bb5331..abcce4e 100644 --- a/packages/responsive-preview-react/lib/components/Settings.tsx +++ b/packages/responsive-preview-react/lib/components/Settings.tsx @@ -3,7 +3,6 @@ import { RulerIcon, TextIcon, SettingsIcon, - MoonIcon, } from "lucide-react"; import { Popover, @@ -24,12 +23,14 @@ interface SettingsProps { export function Settings({ config, onChange, rprRef }: SettingsProps) { const { - darkMode = false, + // darkMode = false, showToolbar = true, showLabels = true, showScale = true, } = config; + // console.log("showToolbar", showToolbar); + const handleConfigChange = (key: string, value: boolean) => { onChange({ ...config, [key]: value }); }; @@ -47,14 +48,14 @@ export function Settings({ config, onChange, rprRef }: SettingsProps) { variant="outline" className="rpr-flex rpr-gap-1" > - handleConfigChange("darkMode", !darkMode)} className="rpr-p-2" > - + */} void; + panelRef: React.RefObject; } export function Toolbar({ @@ -32,6 +35,7 @@ export function Toolbar({ breakpointTitle, availableBreakpoints, onBreakpointChange, + panelRef, }: ToolbarProps) { const [isPlaying, setIsPlaying] = useState(false); const intervalRef = useRef(); @@ -39,6 +43,20 @@ export function Toolbar({ (bp) => bp.title === breakpointTitle ); + const handleScreenshot = async () => { + if (panelRef.current) { + try { + const dataUrl = await domToPng(panelRef.current); + const link = document.createElement("a"); + link.download = `preview-${width}px.png`; + link.href = dataUrl; + link.click(); + } catch (err) { + console.error("Screenshot failed:", err); + } + } + }; + const handlePrevBreakpoint = () => { if (currentIndex > 0) { const prevBp = availableBreakpoints[currentIndex - 1]; @@ -158,6 +176,22 @@ export function Toolbar({ + + + + + + + +

Take Screenshot

+
+
+
diff --git a/packages/responsive-preview-react/package.json b/packages/responsive-preview-react/package.json index b4b07df..e9c26c1 100644 --- a/packages/responsive-preview-react/package.json +++ b/packages/responsive-preview-react/package.json @@ -73,10 +73,12 @@ "@radix-ui/react-hover-card": "^1.1.5", "@radix-ui/react-label": "^2.1.1", "@radix-ui/react-popover": "^1.1.5", + "@radix-ui/react-slot": "^1.1.1", "@radix-ui/react-switch": "^1.1.2", "@radix-ui/react-tabs": "^1.1.2", "@radix-ui/react-toggle": "^1.1.1", "@radix-ui/react-toggle-group": "^1.1.1", - "@radix-ui/react-tooltip": "^1.1.6" + "@radix-ui/react-tooltip": "^1.1.6", + "modern-screenshot": "^4.5.5" } } diff --git a/packages/responsive-preview-react/tailwind.config.ts b/packages/responsive-preview-react/tailwind.config.ts index 0eb6df1..a31dc88 100644 --- a/packages/responsive-preview-react/tailwind.config.ts +++ b/packages/responsive-preview-react/tailwind.config.ts @@ -7,7 +7,7 @@ import animate from "tailwindcss-animate"; export default { prefix: "rpr-", - darkMode: ["class"], + darkMode: ["class", '[data-theme="dark"]'], content: [ "./index.html", "./src/**/*.{js,ts,jsx,tsx}",