Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs(themes): add theme generator #3707

Open
wants to merge 15 commits into
base: canary
Choose a base branch
from

Conversation

xylish7
Copy link

@xylish7 xylish7 commented Sep 3, 2024

Closes #3066

📝 Description

This PR introduces a theme generator to the NextUI documentation. The theme generator is accessible via a new navigation item, Themes, making it easier for users to create and customize themes.

image

⛳️ Current Behavior

Currently, the NextUI documentation does not include a theme generator, limiting users' ability to easily create and preview custom themes.

🚀 New Behavior

With this PR, a theme generator will be added to the NextUI documentation, allowing users to generate and customize themes directly within the platform.

💣 Breaking Change

No, this PR does not introduce any breaking changes.

📝 Additional Information

There is an issue with the default color in the white theme; it does not perfectly match the tints and shades of the zinc color used by NextUI's default palette. Although a suitable solution hasn't been found yet, this should not be a blocker, as users can manually adjust the colors. Efforts to resolve this issue will continue.

This version is more concise and polished, improving clarity and flow while retaining all important details.

Summary by CodeRabbit

  • New Features

    • Added a NavbarItem for direct navigation to the themes section.
    • Introduced a FontSizes component for configuring font size settings.
    • Added FontSizeInput components for various font size categories.
    • Launched a FontSizeShowcase component to display text elements with different font sizes.
    • Introduced a CircularProgress component to showcase variations of circular progress indicators.
    • Added a ThemeBuilder component to manage theme configurations and loading states.
  • Bug Fixes

    • Resolved issues related to state management and color updates in theme components.
  • Documentation

    • Updated documentation to reflect new components and usage instructions.
  • Chores

    • Added new dependencies for color management and UI enhancements.

@xylish7 xylish7 requested a review from jrgarciadev as a code owner September 3, 2024 15:25
Copy link

changeset-bot bot commented Sep 3, 2024

⚠️ No Changeset found

Latest commit: a461954

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

Copy link

vercel bot commented Sep 3, 2024

@xylish7 is attempting to deploy a commit to the NextUI Inc Team on Vercel.

A member of the Team first needs to authorize it.

Copy link
Contributor

coderabbitai bot commented Sep 3, 2024

Walkthrough

The changes introduce a new navigation item in the Navbar component for accessing the "/themes" route, enhancing user navigation. Additionally, two new dependencies, react-colorful and values.js, are added to the project's package.json file. These updates primarily focus on improving the navigation experience without altering the existing logic or structure of the application significantly.

Changes

File Path Change Summary
apps/docs/components/navbar.tsx Added NavbarItem for navigation to the "/themes" route.
apps/docs/package.json Added dependencies: react-colorful and values.js.

Assessment against linked issues

Objective Addressed Explanation
Theme generator implementation (3066)
Documentation mention for theme builder No documentation updates were included in the changes.
User interface for theme customization It is unclear if the new link will be documented or integrated into the theme generator.

Possibly related PRs

Suggested labels

📋 Scope : Docs

Suggested reviewers

  • wingkwong
  • jrgarciadev

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@xylish7 xylish7 changed the title Docs/themes docs(themes): add theme generator Sep 3, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

Outside diff range, codebase verification and nitpick comments (11)
apps/docs/components/themes/components/showcase/font-size.tsx (1)

1-14: LGTM! Consider adding more font sizes and line heights to showcase.

The FontSize component is correctly implemented and showcases different font sizes using the ShowcaseComponent. The class names used for the p elements are not defined in this file, but they are likely defined in a global CSS file or a CSS-in-JS library.

Consider adding more font sizes and line heights to showcase, such as:

 <div className="flex flex-col gap-2">
   <p className="text-tiny">Tiny</p>
   <p className="text-small">Small</p>
   <p className="text-medium">Medium</p>
   <p className="text-large">Large</p>
+  <p className="text-xlarge">Extra Large</p>
+  <p className="text-2xlarge">2X Large</p>
 </div>
apps/docs/components/themes/components/number-input.tsx (1)

11-21: Consider adding support for additional props and custom validation patterns.

The component is correctly implemented and follows best practices by validating the input value. However, it can be improved by adding support for additional props and custom validation patterns.

Consider adding support for the following props:

  • min: The minimum value allowed.
  • max: The maximum value allowed.
  • step: The step value for incrementing/decrementing the value.
  • precision: The number of decimal places to allow.
  • pattern: A custom validation pattern to replace the default floatNumberPattern.

For example, the component props can be updated as follows:

interface NumberInputProps {
  label: string;
  value: string;
  onChange: (value: string) => void;
  min?: number;
  max?: number;
  step?: number;
  precision?: number;
  pattern?: RegExp;
}

Then, the handleChange function can be updated to use the new props:

function handleChange(event: React.ChangeEvent<HTMLInputElement>) {
  const value = event.target.value;
  const {min, max, step, precision, pattern} = props;

  if ((pattern || floatNumberPattern).test(value) || !value) {
    const newValue = Math.min(Math.max(Number(value), min ?? -Infinity), max ?? Infinity);
    const roundedValue = Number(newValue.toFixed(precision ?? 2));
    onChange(roundedValue.toString());
  }
}

Finally, the Input component can be updated to use the new props:

return (
  <Input
    label={label}
    size="sm"
    value={value.toString()}
    onChange={handleChange}
    min={min}
    max={max}
    step={step}
  />
);
apps/docs/components/themes/components/showcase/tabs.tsx (1)

15-21: Consider adding more tabs to each NextUITabs instance.

To provide a more comprehensive showcase of the NextUITabs component, consider adding more tabs to each NextUITabs instance. This will help users better understand how the component behaves with different numbers of tabs.

For example, you can add more tabs like this:

<NextUITabs key={color} aria-label="Tabs colors" color={color} radius="full">
  <Tab key="photos" title="Photos" />
  <Tab key="music" title="Music" />
  <Tab key="videos" title="Videos" />
+ <Tab key="documents" title="Documents" />
+ <Tab key="downloads" title="Downloads" />
</NextUITabs>
apps/docs/components/themes/components/showcase/content.tsx (1)

6-37: Consider extracting the repeated card structure into a separate sub-component.

The Content component contains a repeated structure for rendering the content color cards. To improve readability and maintainability, consider extracting this repeated structure into a separate sub-component.

Here's an example of how you can extract the card structure:

const ContentCard: React.FC<{ className?: string; title: string; description: string }> = ({
  className,
  title,
  description,
}) => (
  <Card className={className}>
    <CardBody>
      <p>{title}</p>
      <p>{description}</p>
    </CardBody>
  </Card>
);

export function Content() {
  return (
    <ShowcaseComponent id={contentShowcaseId} name="Content colors">
      <div className="grid grid-cols-2 lg:grid-cols-4 gap-4">
        <ContentCard
          title="Content 1"
          description="bg-content1 text-content-foreground1"
        />
        <ContentCard
          className="bg-content2 text-content2-foreground"
          title="Content 2"
          description="bg-content2 text-content-foreground2"
        />
        <ContentCard
          className="bg-content3 text-content3-foreground"
          title="Content 3"
          description="bg-content3 text-content-foreground3"
        />
        <ContentCard
          className="bg-content4 text-content4-foreground"
          title="Content 4"
          description="bg-content4 text-content-foreground4"
        />
      </div>
    </ShowcaseComponent>
  );
}

This refactoring enhances the code by:

  • Reducing duplication and improving maintainability.
  • Making the Content component more focused and readable.
  • Providing a reusable ContentCard component for future use.
apps/docs/components/themes/components/showcase/index.tsx (1)

23-47: LGTM!

The code changes are approved.

Nitpick: Consider adding a comment to explain the purpose of the Showcase component.

To improve the maintainability and readability of the code, consider adding a brief comment above the Showcase component to explain its purpose and the role of the rendered UI components.

Here's an example:

+/**
+ * Showcase component that renders various UI components for theme preview.
+ */
 export function Showcase() {
   return (
     <div className="grid grid-cols-1 gap-4 w-full " id={showcaseId}>
       {/* Rendered UI components */}
     </div>
   );
 }
apps/docs/components/themes/components/showcase/badge.tsx (1)

6-29: LGTM!

The Badge component provides a clear and concise way to showcase the NextUIBadge component with different colors. The use of the ShowcaseComponent helps to keep the code DRY and maintainable.

Consider using a placeholder image or a more reliable source for avatar images in a production environment. The hardcoded src prop of the NextUIAvatar component is acceptable for a showcase component, but it may not be suitable for a production environment.

apps/docs/components/themes/components/select-template.tsx (2)

11-40: Simplify the handleChange function and improve the Select and SelectItem components.

Consider the following improvements:

  1. Simplify the handleChange function by using the find method directly on the templates array:
-function handleChange(e: React.ChangeEvent<HTMLSelectElement>) {
-  const value = e.target.value as TemplateType;
-  const template = templates.find((template) => template.name === value);
-
-  if (template) {
-    onChange(template);
-  }
-}
+function handleChange(value: string) {
+  const template = templates.find((template) => template.name === value);
+  onChange(template!);
+}
  1. Simplify the selectedKeys prop of the Select component by using a ternary operator:
-selectedKeys={name === null ? [] : [name]}
+selectedKeys={name ? [name] : []}
  1. Set the value prop of the SelectItem component to the name of the template instead of the index:
-<SelectItem key={template.name} value={index}>
+<SelectItem key={template.name} value={template.name}>

46-54: Simplify the component and improve accessibility.

Consider the following improvements:

  1. Simplify the component by using the Object.values method instead of Object.entries:
-{Object.entries(colors).map(([key, value]) => (
-  <div key={key} className="w-2 h-full" style={{background: value}} />
-))}
+{Object.values(colors).map((value, index) => (
+  <div key={index} className="w-2 h-full" style={{background: value}} />
+))}
  1. Add an aria-label to the div elements to improve accessibility:
-<div key={index} className="w-2 h-full" style={{background: value}} />
+<div key={index} className="w-2 h-full" style={{background: value}} aria-label={`Color ${index + 1}`} />
apps/docs/components/themes/components/configuration/actions.tsx (2)

30-30: Consider using a constant for the timeout duration.

To improve readability and maintainability, consider defining a constant for the timeout duration used in the setTimeout function.

Apply this diff to define a constant for the timeout duration:

+const COPIED_TIMEOUT_MS = 1500;

function handleCopyConfig() {
  navigator.clipboard.writeText(JSON.stringify(onCopy(), null, 2));

  setCopied(true);
-  setTimeout(() => setCopied(false), 1500);
+  setTimeout(() => setCopied(false), COPIED_TIMEOUT_MS);
}

36-36: Consider adding aria-labels to the buttons for accessibility.

To improve accessibility, consider adding aria-label attributes to the buttons.

Apply this diff to add aria-label attributes to the buttons:

<Tooltip content={isLight ? "Dark" : "Light"}>
-  <Button isIconOnly color="secondary" size="sm" variant="flat" onClick={onToggleTheme}>
+  <Button isIconOnly color="secondary" size="sm" variant="flat" onClick={onToggleTheme} aria-label="Toggle theme">
    {isLight ? (
      <Icon className="text-lg" icon={MoonIcon} />
    ) : (
      <Icon className="text-lg" icon={SunIcon} />
    )}
  </Button>
</Tooltip>
<Tooltip content="Reset theme">
-  <Button isIconOnly color="secondary" size="sm" variant="flat" onClick={onResetTheme}>
+  <Button isIconOnly color="secondary" size="sm" variant="flat" onClick={onResetTheme} aria-label="Reset theme">
    <Icon className="text-lg" icon={UndoLeftIcon} />
  </Button>
</Tooltip>
<Tooltip content="Copy configuration">
-  <Button isIconOnly color="secondary" size="sm" variant="flat" onClick={handleCopyConfig}>
+  <Button isIconOnly color="secondary" size="sm" variant="flat" onClick={handleCopyConfig} aria-label="Copy configuration">
    {copied ? (
      <Icon className="text-lg" icon={CheckCircleIcon} />
    ) : (
      <Icon className="text-lg" icon={CopyIcon} />
    )}
  </Button>
</Tooltip>

Also applies to: 45-45, 50-50

apps/docs/components/themes/components/configuration/brand-colors.tsx (1)

22-68: Consider extracting the repeated color picker configurations into a separate component or a loop.

The component contains repeated configurations for each color picker, which can be extracted into a separate component or rendered using a loop to reduce code duplication and improve maintainability.

Here's an example of how you can extract the color picker configurations into a separate component:

interface ColorPickerConfigProps {
  hexColor: string;
  icon?: React.ReactNode;
  label: string;
  type: string;
  onChange: (hexColor: string) => void;
  onClose: (hexColor: string) => void;
}

function ColorPickerConfig({hexColor, icon, label, type, onChange, onClose}: ColorPickerConfigProps) {
  return (
    <ColorPicker
      hexColor={hexColor}
      icon={icon}
      label={label}
      type={type}
      onChange={onChange}
      onClose={onClose}
    />
  );
}

Then, you can render the color pickers using a loop or by calling the ColorPickerConfig component for each brand color type:

<ConfigSection id={colorsId} title="Brand colors">
  {["default", "primary", "secondary", "success", "warning", "danger"].map((type) => (
    <ColorPickerConfig
      key={type}
      hexColor={config[theme].brandColor[type]}
      icon={type !== "default" ? syncIcon : undefined}
      label={capitalize(type)}
      type={type}
      onChange={(hexColor) => setCssColor(type, hexColor, theme)}
      onClose={(hexColor) => setBrandColor({[type]: hexColor}, theme, syncThemes)}
    />
  ))}
</ConfigSection>

This approach reduces code duplication and makes the component more maintainable.

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 87336c7 and b57a5d3.

Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
Files selected for processing (52)
  • apps/docs/app/themes/page.tsx (1 hunks)
  • apps/docs/components/navbar.tsx (1 hunks)
  • apps/docs/components/themes/components/color-picker.tsx (1 hunks)
  • apps/docs/components/themes/components/config-section.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/actions.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/base-colors.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/border-widths.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/brand-colors.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/font-sizes.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/index.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/line-heights.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/other-colors.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/other.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/radiuses.tsx (1 hunks)
  • apps/docs/components/themes/components/copy-button.tsx (1 hunks)
  • apps/docs/components/themes/components/number-input.tsx (1 hunks)
  • apps/docs/components/themes/components/select-template.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase-component.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/avatar.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/badge.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/breadcrumbs.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/button.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/checkbox.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/chip.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/circular-progress.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/code.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/content.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/divider.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/dropdown.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/font-size.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/index.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/input.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/line-height.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/link.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/pagination.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/select.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/slider.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/switch.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase/tabs.tsx (1 hunks)
  • apps/docs/components/themes/constants.ts (1 hunks)
  • apps/docs/components/themes/css-vars.ts (1 hunks)
  • apps/docs/components/themes/index.tsx (1 hunks)
  • apps/docs/components/themes/provider.tsx (1 hunks)
  • apps/docs/components/themes/templates/coffee.ts (1 hunks)
  • apps/docs/components/themes/templates/emerald.ts (1 hunks)
  • apps/docs/components/themes/templates/index.ts (1 hunks)
  • apps/docs/components/themes/templates/nextui.ts (1 hunks)
  • apps/docs/components/themes/types.ts (1 hunks)
  • apps/docs/components/themes/utils/colors.ts (1 hunks)
  • apps/docs/components/themes/utils/config.ts (1 hunks)
  • apps/docs/hooks/use-previous.ts (1 hunks)
  • apps/docs/package.json (2 hunks)
Files skipped from review due to trivial changes (1)
  • apps/docs/components/themes/components/showcase/line-height.tsx
Additional comments not posted (89)
apps/docs/app/themes/page.tsx (1)

1-9: LGTM!

The code changes are approved for the following reasons:

  • The component is correctly implemented.
  • The component name and the file name match, following the best practices.
  • The component is using a default export, which is a common practice in Next.js.
  • The styling classes are using Tailwind CSS utility classes, which is a popular CSS framework.
apps/docs/components/themes/templates/nextui.ts (1)

1-8: LGTM!

The code changes are approved for the following reasons:

  • The file is a template for the NextUI theme configuration.
  • The configuration object is correctly structured and initialized with the appropriate values from the constants module.
  • The file is small and focused on a single responsibility, which is good for maintainability.
apps/docs/components/themes/index.tsx (1)

1-14: LGTM!

The ThemeBuilder component is well-structured and follows the provider pattern. It properly wraps the Configuration and Showcase components with the ThemeBuilderProvider component, allowing them to access the common context.

The use of the "use client" directive is correct for a client component in a Next.js app directory.

The code changes are approved.

apps/docs/components/themes/templates/index.ts (4)

1-1: LGTM!

The import statement is correctly used to import the required Template type.


3-5: LGTM!

The import statements are correctly used to import the required constants.


7-11: LGTM!

The templates array is correctly defined and exported. The use of object literals to define each template is a clean and readable approach. The value property is correctly assigned the imported constants.


Line range hint 12-12: LGTM!

The empty line at the end of the file is a good practice for improving readability.

apps/docs/hooks/use-previous.ts (1)

1-18: LGTM!

The usePrevious hook is a well-implemented custom hook that follows the best practices for React hooks. It provides a useful functionality to store and retrieve the previous value of a given parameter.

Some key observations:

  • The hook is correctly typed with a generic type T, allowing it to be used with any type of value.
  • The JSDoc comments provide a clear description of the hook's purpose and parameters.
  • The useRef hook is used to store the previous value, and the useEffect hook is used to update the ref whenever the value changes.
  • The useEffect hook has the correct dependency array, ensuring that the ref is updated only when the value changes.

Overall, the code is clean, concise, and follows the best practices for custom hooks in React.

apps/docs/components/themes/components/config-section.tsx (1)

1-24: LGTM!

The ConfigSection component is well-implemented and follows best practices. It provides a reusable and customizable solution for rendering configuration sections.

Observations:

  • The component is properly typed with an interface for its props.
  • It uses the clsx utility for managing conditional class names, which is a good approach.
  • The component is structured in a clean and readable manner.
  • It accepts props for customization, such as the number of columns and an optional ID.

The code changes are approved.

apps/docs/components/themes/components/copy-button.tsx (1)

1-26: LGTM!

The CopyButton component is well-structured, follows best practices, and is reusable. The code changes are approved.

apps/docs/components/themes/components/showcase/chip.tsx (1)

1-20: LGTM!

The code is well-structured and follows best practices. The Chip component effectively showcases the different variants and sizes of the NextUI Chip component. The sizes and variants arrays are defined as constants, which is a good practice.

The code changes are approved.

apps/docs/components/themes/components/showcase/divider.tsx (1)

1-26: LGTM!

The Divider component is well-implemented and effectively showcases the usage of the NextUIDivider component. The code is properly structured, uses the imports correctly, and has valid JSX. The Tailwind CSS classes are used appropriately for styling. I don't see any issues or areas for improvement.

Great job! The code changes are approved.

apps/docs/components/themes/components/showcase/circular-progress.tsx (1)

1-19: LGTM!

The code is well-structured and follows best practices. The CircularProgress component effectively showcases the different color and size variants of the NextUICircularProgress component. The use of the ShowcaseComponent helps to maintain consistency with other showcase components. The sizes constant is appropriately typed using the NextUISize type.

apps/docs/components/themes/components/showcase/link.tsx (1)

1-31: LGTM!

The Link component is well-structured and effectively demonstrates the usage of the NextUILink component with different colors and sizes. The code is clean, readable, and follows best practices.

apps/docs/components/themes/components/showcase/input.tsx (1)

6-22: LGTM!

The code changes are approved.

apps/docs/components/themes/components/showcase/code.tsx (1)

6-17: LGTM!

The Code component is implemented correctly and showcases the NextUICode component effectively. The use of the ShowcaseComponent provides a consistent layout and styling, and the different colors demonstrate the available color options. The radiuses and sizes arrays are passed as props to control the available radius and size options.

apps/docs/components/themes/components/showcase/slider.tsx (1)

6-23: LGTM!

The Slider component is implemented correctly and showcases the NextUISlider component with different variations. The code follows good practices such as using the aria-label attribute for accessibility and the key prop for unique identification of rendered components. The handling of the default color and the values for defaultValue, maxValue, minValue, and step props are appropriate.

The code changes are approved.

apps/docs/components/themes/components/showcase/switch.tsx (1)

1-31: LGTM!

The code looks good and follows best practices:

  • The imports are properly named and organized.
  • The Switch component showcases the different color variants of the NextUISwitch component.
  • The sizes constant is used to define the sizes for the ShowcaseComponent.
  • The code is well-structured and readable.

Great job!

apps/docs/components/themes/components/showcase/button.tsx (1)

1-35: LGTM!

The Button component is a good example of how to use the ShowcaseComponent component to display components with different properties. The component is correctly implemented and follows the best practices.

The component uses the NextUIButton component from the @nextui-org/react library to display the buttons and the NextUIRadius, NextUISize, and NextUIVariant types from the ../../types module to define the radiuses, sizes, and variants of the buttons.

The code changes are approved.

apps/docs/components/themes/components/showcase/checkbox.tsx (1)

1-32: LGTM!

The code for the Checkbox showcase component looks good:

  • It properly imports the necessary components and types.
  • The component structure is clear and easy to understand.
  • It showcases the different variants of the NextUI Checkbox component with various colors, radiuses, and sizes.
  • The use of the ShowcaseComponent helps in organizing and presenting the checkbox variants effectively.
  • The radiuses and sizes arrays provide a clean separation of the available customization options.

Overall, the code follows best practices and contributes to the theme builder's functionality.

apps/docs/components/themes/templates/emerald.ts (1)

1-44: LGTM!

The code changes are approved for the following reasons:

  • The file is well-structured and follows a consistent pattern for defining the theme configuration.
  • The color palette values are hardcoded, which is acceptable for a predefined theme template.
  • The theme configuration leverages the initial theme constants to ensure consistency with the default themes.
  • The file does not have any obvious issues or code smells.
apps/docs/components/themes/components/showcase/breadcrumbs.tsx (3)

1-4: LGTM!

The code changes are approved.


6-28: LGTM!

The code changes are approved.


30-32: LGTM!

The code changes are approved.

apps/docs/components/themes/components/configuration/radiuses.tsx (1)

1-44: LGTM!

The Radiuses component is well-structured and follows best practices. It effectively uses the ThemeBuilderContext to manage the theme configuration and updates the radius values correctly. The component is modular and can be easily integrated into the theme builder.

The code changes are approved.

apps/docs/components/themes/components/showcase/index.tsx (1)

1-22: LGTM!

The code changes are approved.

apps/docs/components/themes/templates/coffee.ts (1)

1-54: LGTM!

The code changes are approved.

apps/docs/components/themes/components/showcase/pagination.tsx (3)

1-5: LGTM!

The code changes are approved.


6-35: LGTM!

The code changes are approved.


37-39: LGTM!

The code changes are approved.

apps/docs/components/themes/components/showcase/badge.tsx (1)

31-32: LGTM!

The sizes and variants arrays provide a clear separation of concerns and make it easy to modify the available options for the NextUIBadge component. Defining the arrays as constants outside the Badge component helps to keep the component focused on rendering the NextUIBadge components and makes it easy to reuse the arrays in other parts of the codebase if needed.

apps/docs/components/themes/components/configuration/font-sizes.tsx (1)

13-52: LGTM!

The FontSizes component is well-implemented:

  • It correctly uses the ThemeBuilderContext to manage the font sizes.
  • It renders a ConfigSection with NumberInput components for each font size.
  • The NumberInput components are properly bound to the config prop.
  • The onChange handlers correctly update the context and CSS variables.

The code changes are approved.

apps/docs/components/themes/components/configuration/line-heights.tsx (1)

1-52: LGTM!

The LineHeights component is well-structured and follows best practices:

  • It is typed with an interface for props.
  • It uses the useContext hook to access the theme context.
  • It renders a section with input components for each line height size.
  • The onChange handlers update the context and CSS variables.

The code changes are approved.

apps/docs/components/themes/components/showcase/avatar.tsx (1)

1-52: LGTM!

The Avatar component is well-structured and serves its purpose of showcasing different color variants of the NextUIAvatar component. The use of the ShowcaseComponent and the radiuses and sizes arrays helps to provide a consistent and customizable showcase. The code is readable and easy to understand.

The code changes are approved.

apps/docs/components/themes/components/showcase/select.tsx (1)

1-60: LGTM!

The Select component is well-structured and follows best practices for React components. It showcases the select component with different configurations using the ShowcaseComponent. The component uses a good approach to render the options for the select component and to limit its width. It also sets a default selected value, a label, and a placeholder for the select component. The component passes the configurations as props to the ShowcaseComponent and imports the required types and components from other files.

The code changes are approved.

apps/docs/components/themes/components/showcase/dropdown.tsx (1)

1-48: LGTM!

The code changes in this new file are well-structured, modular, and follow best practices. The Dropdown and DropdownContent components are separated, allowing for reusability and customization. The use of the ShowcaseComponent and the NextUIDropdown component from the @nextui-org/react library ensures consistency with the library's design system.

The code imports necessary components and types, and the color and variant props are used appropriately to customize the appearance of the dropdown.

Overall, the code is clean, readable, and maintainable.

apps/docs/components/themes/components/configuration/other-colors.tsx (2)

17-46: LGTM!

The OtherColors component is well-structured and correctly implemented:

  • It uses the useContext hook to access the ThemeBuilderContext.
  • It renders a ConfigSection component to group the color pickers.
  • The ColorPicker components are correctly configured with the current color values, labels, and change handlers.
  • The onChange and onClose handlers are correctly updating the CSS variables and the theme configuration.

10-15: LGTM!

The OtherColorsProps interface correctly specifies the props and their types for the OtherColors component.

apps/docs/components/themes/components/configuration/actions.tsx (1)

1-60: LGTM!

The code changes are approved. The component is well-structured and follows best practices for React components.

apps/docs/components/themes/components/configuration/base-colors.tsx (1)

15-64: LGTM!

The BaseColors component is well-structured and follows best practices:

  • It uses the ThemeBuilderContext appropriately for accessing and updating the theme configuration.
  • The ColorPicker components are used consistently for each base color.
  • The onChange and onClose callbacks update the CSS variables and the theme configuration correctly.
  • The component is modular and reusable.

The code changes are approved.

apps/docs/components/themes/types.ts (4)

2-13: LGTM!

The ColorShades interface is well-defined and follows a common convention for defining color shades.


15-30: LGTM!

The ColorPickerType type is well-defined and covers a comprehensive set of color picker types.


32-45: LGTM!

The NextUI component prop types are well-defined and cover the necessary options for NextUI colors, sizes, variants, and radii.


47-121: LGTM!

The theme-related types and interfaces are well-structured and cover the necessary aspects of theme configuration. The separation of light and dark theme colors, layout configurations, and the ability to define predefined theme templates provide a solid foundation for theme management.

apps/docs/components/themes/components/configuration/brand-colors.tsx (1)

1-71: LGTM!

The BrandColors component is well-structured and correctly implements the functionality for configuring brand colors in the theme builder. The usage of context and props is appropriate, and the color picker configurations are consistent and mapped correctly to the corresponding brand color types.

apps/docs/components/themes/constants.ts (6)

1-3: LGTM!

The code changes are approved.


6-8: LGTM!

The code changes are approved.


11-11: LGTM!

The code changes are approved.


14-18: LGTM!

The code changes are approved.


21-22: LGTM!

The code changes are approved.


25-107: LGTM!

The code changes are approved.

apps/docs/components/themes/utils/colors.ts (6)

11-13: LGTM!

The code changes are approved.


18-32: LGTM!

The code changes are approved.


37-82: LGTM!

The code changes are approved.


87-93: LGTM!

The code changes are approved.


107-109: LGTM!

The code changes are approved.


114-133: LGTM!

The code changes are approved.

apps/docs/package.json (2)

75-75: LGTM!

The addition of the react-colorful dependency is approved. It provides a color picker component for React applications, which aligns with the project's requirements for color selection functionality. The version constraint ^5.6.1 is also reasonable.


99-99: LGTM!

The addition of the values.js dependency is approved. It provides utilities for managing color values and conversions, which aligns with the project's requirements for advanced color handling capabilities. The version constraint ^2.1.1 is also reasonable.

apps/docs/components/themes/components/showcase-component.tsx (4)

16-96: LGTM!

The ShowcaseComponent is well-implemented and follows best practices. The use of useState for state management, cloneElement for applying props to children, and conditional rendering of customization options based on the presence of corresponding props is appreciated. The component also maintains consistency with the rest of the codebase by using a combination of Tailwind CSS classes and NextUI components for styling.


98-104: LGTM!

The defaultRadiuses constant is correctly typed and initialized. The radius options are comprehensive, covering common use cases.


106-116: LGTM!

The defaultVariants constant is correctly typed and initialized. The variant options are comprehensive, covering common use cases.


118-122: LGTM!

The defaultSizes constant is correctly typed and initialized. The size options are comprehensive, covering common use cases.

apps/docs/components/themes/components/color-picker.tsx (2)

22-92: LGTM!

The ColorPicker component is well-implemented and uses appropriate libraries for color picking and manipulation. The code is structured, readable, and follows best practices. The component correctly handles user interactions and updates the selected color when necessary.


94-129: LGTM!

The getColor function is a well-implemented utility function that maps the ColorPickerType to the corresponding CSS class name. The function uses a switch statement to handle the different cases and returns the appropriate CSS class name based on the input. The function handles all possible cases and returns undefined for the default case.

apps/docs/components/themes/components/configuration/index.tsx (1)

1-127: Excellent work on the Configuration component!

The component is well-structured, modular, and effectively manages the theme configuration using the ThemeBuilderContext. It handles theme-related actions cleanly and provides a comprehensive set of theme configuration sections for a user-friendly customization experience.

Some key highlights:

  • Clear separation of concerns with well-defined sub-components for each configuration section.
  • Effective utilization of the ThemeBuilderContext for accessing and modifying the theme configuration.
  • Clean and readable handling of theme syncing, toggling, and resetting actions.
  • Comprehensive set of theme configuration sections rendered in a user-friendly card layout.

Overall, the code changes are of high quality and align well with best practices for React component development.

apps/docs/components/themes/utils/config.ts (1)

12-116: LGTM!

The generatePluginConfig function is well-structured, modular, and follows best practices:

  • It uses destructuring to extract values from the Config object, which improves readability.
  • It uses template literals to generate CSS values, which is a good practice.
  • It uses the readableColor function to generate foreground colors for content colors, which improves accessibility.
  • It uses the generateThemeColor function to generate color values based on the provided brand and base colors, which is a good abstraction.
  • It returns an object with the generated layout and themes objects, which is a good way to structure the plugin configuration.

The code changes are approved.

apps/docs/components/themes/css-vars.ts (8)

31-38: LGTM!

The code changes are approved.


40-44: LGTM!

The code changes are approved.


46-50: LGTM!

The code changes are approved.


52-56: LGTM!

The code changes are approved.


58-62: LGTM!

The code changes are approved.


64-79: LGTM!

The code changes are approved.


81-88: LGTM!

The code changes are approved.


90-106: LGTM!

The code changes are approved.

apps/docs/components/themes/provider.tsx (13)

48-48: There is only one local storage related import.

The file is only using the useLocalStorage hook from the usehooks-ts library. There are no other local storage related imports in the file. Therefore, you should continue using the useLocalStorage hook.


52-61: LGTM!

The code changes are approved.


63-79: LGTM!

The code changes are approved.


81-115: LGTM!

The code changes are approved.


117-131: LGTM!

The code changes are approved.


133-167: LGTM!

The code changes are approved.


169-182: LGTM!

The code changes are approved.


184-197: LGTM!

The code changes are approved.


199-212: LGTM!

The code changes are approved.


214-227: LGTM!

The code changes are approved.


229-242: LGTM!

The code changes are approved.


244-271: LGTM!

The code changes are approved.


273-275: LGTM!

The code changes are approved.

apps/docs/components/navbar.tsx (1)

239-249: LGTM!

The new NavbarItem component for the "Themes" section is implemented correctly:

  • The NextLink component links to the correct "/themes" route.
  • The link is styled appropriately using the navLinkClasses and is conditionally marked as active based on the current pathname.
  • The onClick handler tracks user interactions with this new item, providing valuable analytics data.

These changes enhance the navigation functionality by providing users with direct access to the themes section, thereby improving the overall user experience.

Comment on lines 13 to 44
export function BorderWidths({config}: BorderWidthsProps) {
const {setBorderWidth} = useContext(ThemeBuilderContext);

return (
<ConfigSection cols={3} title="Border width (px)">
<NumberInput
label="Small"
value={config.layout.borderWidth.small}
onChange={(value) => {
setBorderWidth({small: value});
setCssBorderWidth("small", value);
}}
/>
<NumberInput
label="Medium"
value={config.layout.borderWidth.medium}
onChange={(value) => {
setBorderWidth({medium: value});
setCssBorderWidth("medium", value);
}}
/>
<NumberInput
label="Large"
value={config.layout.borderWidth.large}
onChange={(value) => {
setBorderWidth({large: value});
setCssBorderWidth("large", value);
}}
/>
</ConfigSection>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting the onChange handler to a separate function.

The onChange handlers for the NumberInput components are duplicated. Consider extracting the handler to a separate function to avoid duplication and improve maintainability.

Apply this diff to extract the onChange handler:

+const handleBorderWidthChange = (key: keyof Config["layout"]["borderWidth"]) => (value: number) => {
+  setBorderWidth({[key]: value});
+  setCssBorderWidth(key, value);
+};
+
 return (
   <ConfigSection cols={3} title="Border width (px)">
     <NumberInput
       label="Small"
       value={config.layout.borderWidth.small}
-      onChange={(value) => {
-        setBorderWidth({small: value});
-        setCssBorderWidth("small", value);
-      }}
+      onChange={handleBorderWidthChange("small")}
     />
     <NumberInput
       label="Medium" 
       value={config.layout.borderWidth.medium}
-      onChange={(value) => {
-        setBorderWidth({medium: value});
-        setCssBorderWidth("medium", value);
-      }}
+      onChange={handleBorderWidthChange("medium")}
     />
     <NumberInput
       label="Large"
       value={config.layout.borderWidth.large}
-      onChange={(value) => {
-        setBorderWidth({large: value});
-        setCssBorderWidth("large", value);
-      }}
+      onChange={handleBorderWidthChange("large")}
     />
   </ConfigSection>
 );
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function BorderWidths({config}: BorderWidthsProps) {
const {setBorderWidth} = useContext(ThemeBuilderContext);
return (
<ConfigSection cols={3} title="Border width (px)">
<NumberInput
label="Small"
value={config.layout.borderWidth.small}
onChange={(value) => {
setBorderWidth({small: value});
setCssBorderWidth("small", value);
}}
/>
<NumberInput
label="Medium"
value={config.layout.borderWidth.medium}
onChange={(value) => {
setBorderWidth({medium: value});
setCssBorderWidth("medium", value);
}}
/>
<NumberInput
label="Large"
value={config.layout.borderWidth.large}
onChange={(value) => {
setBorderWidth({large: value});
setCssBorderWidth("large", value);
}}
/>
</ConfigSection>
);
}
export function BorderWidths({config}: BorderWidthsProps) {
const {setBorderWidth} = useContext(ThemeBuilderContext);
const handleBorderWidthChange = (key: keyof Config["layout"]["borderWidth"]) => (value: number) => {
setBorderWidth({[key]: value});
setCssBorderWidth(key, value);
};
return (
<ConfigSection cols={3} title="Border width (px)">
<NumberInput
label="Small"
value={config.layout.borderWidth.small}
onChange={handleBorderWidthChange("small")}
/>
<NumberInput
label="Medium"
value={config.layout.borderWidth.medium}
onChange={handleBorderWidthChange("medium")}
/>
<NumberInput
label="Large"
value={config.layout.borderWidth.large}
onChange={handleBorderWidthChange("large")}
/>
</ConfigSection>
);
}

Comment on lines 13 to 44
export function Other({config}: OtherProps) {
const {setOtherParams} = useContext(ThemeBuilderContext);

return (
<ConfigSection cols={1} title="Other">
<NumberInput
label="Disabled opacity (0-1)"
value={config.layout.otherParams.disabledOpacity}
onChange={(value) => {
setOtherParams({disabledOpacity: value});
setOtherCssParams("disabledOpacity", value);
}}
/>
<NumberInput
label="Divider weight (px)"
value={config.layout.otherParams.dividerWeight}
onChange={(value) => {
setOtherParams({dividerWeight: value});
setOtherCssParams("dividerWeight", value);
}}
/>
<NumberInput
label="Hover opacity (0-1)"
value={config.layout.otherParams.hoverOpacity}
onChange={(value) => {
setOtherParams({hoverOpacity: value});
setOtherCssParams("hoverOpacity", value);
}}
/>
</ConfigSection>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider extracting the configuration options to a separate object.

To make the component more reusable and maintainable, you can extract the configuration options to a separate object. This will allow you to easily add or remove options without modifying the component code.

Here's an example of how you can refactor the code:

+const configOptions = [
+  {
+    label: 'Disabled opacity (0-1)',
+    key: 'disabledOpacity',
+  },
+  {
+    label: 'Divider weight (px)',
+    key: 'dividerWeight',
+  },
+  {
+    label: 'Hover opacity (0-1)',
+    key: 'hoverOpacity',
+  },
+];
+
 export function Other({config}: OtherProps) {
   const {setOtherParams} = useContext(ThemeBuilderContext);

+  const handleChange = (key: keyof Config['layout']['otherParams'], value: number) => {
+    setOtherParams({ [key]: value });
+    setOtherCssParams(key, value);
+  };
+
   return (
     <ConfigSection cols={1} title="Other">
-      <NumberInput
-        label="Disabled opacity (0-1)"
-        value={config.layout.otherParams.disabledOpacity}
-        onChange={(value) => {
-          setOtherParams({disabledOpacity: value});
-          setOtherCssParams("disabledOpacity", value);
-        }}
-      />
-      <NumberInput
-        label="Divider weight (px)"
-        value={config.layout.otherParams.dividerWeight}
-        onChange={(value) => {
-          setOtherParams({dividerWeight: value});
-          setOtherCssParams("dividerWeight", value);
-        }}
-      />
-      <NumberInput
-        label="Hover opacity (0-1)"
-        value={config.layout.otherParams.hoverOpacity}
-        onChange={(value) => {
-          setOtherParams({hoverOpacity: value});
-          setOtherCssParams("hoverOpacity", value);
-        }}
-      />
+      {configOptions.map(({label, key}) => (
+        <NumberInput
+          key={key}
+          label={label}
+          value={config.layout.otherParams[key]}
+          onChange={(value) => handleChange(key, value)}
+        />
+      ))}
     </ConfigSection>
   );
 }
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function Other({config}: OtherProps) {
const {setOtherParams} = useContext(ThemeBuilderContext);
return (
<ConfigSection cols={1} title="Other">
<NumberInput
label="Disabled opacity (0-1)"
value={config.layout.otherParams.disabledOpacity}
onChange={(value) => {
setOtherParams({disabledOpacity: value});
setOtherCssParams("disabledOpacity", value);
}}
/>
<NumberInput
label="Divider weight (px)"
value={config.layout.otherParams.dividerWeight}
onChange={(value) => {
setOtherParams({dividerWeight: value});
setOtherCssParams("dividerWeight", value);
}}
/>
<NumberInput
label="Hover opacity (0-1)"
value={config.layout.otherParams.hoverOpacity}
onChange={(value) => {
setOtherParams({hoverOpacity: value});
setOtherCssParams("hoverOpacity", value);
}}
/>
</ConfigSection>
);
}
const configOptions = [
{
label: 'Disabled opacity (0-1)',
key: 'disabledOpacity',
},
{
label: 'Divider weight (px)',
key: 'dividerWeight',
},
{
label: 'Hover opacity (0-1)',
key: 'hoverOpacity',
},
];
export function Other({config}: OtherProps) {
const {setOtherParams} = useContext(ThemeBuilderContext);
const handleChange = (key: keyof Config['layout']['otherParams'], value: number) => {
setOtherParams({ [key]: value });
setOtherCssParams(key, value);
};
return (
<ConfigSection cols={1} title="Other">
{configOptions.map(({label, key}) => (
<NumberInput
key={key}
label={label}
value={config.layout.otherParams[key]}
onChange={(value) => handleChange(key, value)}
/>
))}
</ConfigSection>
);
}

Comment on lines 18 to 41
<NumberInput
label="Disabled opacity (0-1)"
value={config.layout.otherParams.disabledOpacity}
onChange={(value) => {
setOtherParams({disabledOpacity: value});
setOtherCssParams("disabledOpacity", value);
}}
/>
<NumberInput
label="Divider weight (px)"
value={config.layout.otherParams.dividerWeight}
onChange={(value) => {
setOtherParams({dividerWeight: value});
setOtherCssParams("dividerWeight", value);
}}
/>
<NumberInput
label="Hover opacity (0-1)"
value={config.layout.otherParams.hoverOpacity}
onChange={(value) => {
setOtherParams({hoverOpacity: value});
setOtherCssParams("hoverOpacity", value);
}}
/>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider refactoring the onChange handlers to reduce duplication.

The onChange handlers for the NumberInput components have similar logic. You can extract this logic to a separate function to reduce duplication and improve maintainability.

Here's an example of how you can refactor the code:

+const handleChange = (key: keyof Config['layout']['otherParams'], value: number) => {
+  setOtherParams({ [key]: value });
+  setOtherCssParams(key, value);
+};
+
 <NumberInput
   label="Disabled opacity (0-1)"
   value={config.layout.otherParams.disabledOpacity}
-  onChange={(value) => {
-    setOtherParams({disabledOpacity: value});
-    setOtherCssParams("disabledOpacity", value);
-  }}
+  onChange={(value) => handleChange('disabledOpacity', value)}
 />
 <NumberInput
   label="Divider weight (px)"
   value={config.layout.otherParams.dividerWeight}
-  onChange={(value) => {
-    setOtherParams({dividerWeight: value});
-    setOtherCssParams("dividerWeight", value);
-  }}
+  onChange={(value) => handleChange('dividerWeight', value)}
 />
 <NumberInput
   label="Hover opacity (0-1)"
   value={config.layout.otherParams.hoverOpacity}
-  onChange={(value) => {
-    setOtherParams({hoverOpacity: value});
-    setOtherCssParams("hoverOpacity", value);
-  }}
+  onChange={(value) => handleChange('hoverOpacity', value)}
 />
Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<NumberInput
label="Disabled opacity (0-1)"
value={config.layout.otherParams.disabledOpacity}
onChange={(value) => {
setOtherParams({disabledOpacity: value});
setOtherCssParams("disabledOpacity", value);
}}
/>
<NumberInput
label="Divider weight (px)"
value={config.layout.otherParams.dividerWeight}
onChange={(value) => {
setOtherParams({dividerWeight: value});
setOtherCssParams("dividerWeight", value);
}}
/>
<NumberInput
label="Hover opacity (0-1)"
value={config.layout.otherParams.hoverOpacity}
onChange={(value) => {
setOtherParams({hoverOpacity: value});
setOtherCssParams("hoverOpacity", value);
}}
/>
const handleChange = (key: keyof Config['layout']['otherParams'], value: number) => {
setOtherParams({ [key]: value });
setOtherCssParams(key, value);
};
<NumberInput
label="Disabled opacity (0-1)"
value={config.layout.otherParams.disabledOpacity}
onChange={(value) => handleChange('disabledOpacity', value)}
/>
<NumberInput
label="Divider weight (px)"
value={config.layout.otherParams.dividerWeight}
onChange={(value) => handleChange('dividerWeight', value)}
/>
<NumberInput
label="Hover opacity (0-1)"
value={config.layout.otherParams.hoverOpacity}
onChange={(value) => handleChange('hoverOpacity', value)}
/>

Comment on lines 7 to 29
export function setCssColor(colorType: ColorPickerType, value: string, theme: ThemeType) {
const brandColorsEl = document.getElementById(colorsId);
const commonColorsEl = document.getElementById(baseColorsId);
const showcaseEl = document.getElementById(showcaseId);
const colorWeight = getColorWeight(colorType, theme);
const themeColor = generateThemeColor(value, theme, colorWeight);

if (!brandColorsEl || !commonColorsEl || !showcaseEl) return;

Object.keys(themeColor).forEach((key) => {
const value = hexToHsl(themeColor[key as keyof ThemeColor]);

if (key === "DEFAULT") {
brandColorsEl.style.setProperty(`--nextui-${colorType}`, value);
commonColorsEl.style.setProperty(`--nextui-${colorType}`, value);
showcaseEl.style.setProperty(`--nextui-${colorType}`, value);
} else {
brandColorsEl.style.setProperty(`--nextui-${colorType}-${key}`, value);
commonColorsEl.style.setProperty(`--nextui-${colorType}-${key}`, value);
showcaseEl.style.setProperty(`--nextui-${colorType}-${key}`, value);
}
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add error handling for missing elements.

The function assumes that the elements with IDs colorsId, baseColorsId, and showcaseId exist in the DOM. If any of these elements are not found, the function will return without any error handling or logging.

Consider adding error handling or logging for cases where the elements are not found in the DOM. For example:

if (!brandColorsEl || !commonColorsEl || !showcaseEl) {
  console.error("One or more required elements are missing from the DOM.");
  return;
}

Comment on lines 108 to 141
export function setAllCssVars(config: Config, theme: ThemeType) {
setCssColor("default", config[theme].brandColor.default, theme);
setCssColor("primary", config[theme].brandColor.primary, theme);
setCssColor("secondary", config[theme].brandColor.secondary, theme);
setCssColor("success", config[theme].brandColor.success, theme);
setCssColor("warning", config[theme].brandColor.warning, theme);
setCssColor("danger", config[theme].brandColor.danger, theme);
setCssColor("foreground", config[theme].baseColor.foreground, theme);
setCssContentColor(1, config[theme].baseColor.content1);
setCssContentColor(2, config[theme].baseColor.content2);
setCssContentColor(3, config[theme].baseColor.content3);
setCssContentColor(4, config[theme].baseColor.content4);
setCssBackground(config[theme].baseColor.background);
setCssFontSize("tiny", config.layout.fontSize.tiny);
setCssFontSize("small", config.layout.fontSize.small);
setCssFontSize("medium", config.layout.fontSize.medium);
setCssFontSize("large", config.layout.fontSize.large);
setCssLineHeight("tiny", config.layout.lineHeight.tiny);
setCssLineHeight("small", config.layout.lineHeight.small);
setCssLineHeight("medium", config.layout.lineHeight.medium);
setCssLineHeight("large", config.layout.lineHeight.large);
setCssRadius("small", config.layout.radius.small);
setCssRadius("medium", config.layout.radius.medium);
setCssRadius("large", config.layout.radius.large);
setCssBorderWidth("small", config.layout.borderWidth.small);
setCssBorderWidth("medium", config.layout.borderWidth.medium);
setCssBorderWidth("large", config.layout.borderWidth.large);
setCssOtherColor("divider", config[theme].otherColor.divider);
setCssOtherColor("focus", config[theme].otherColor.focus);
setCssOtherColor("overlay", config[theme].otherColor.overlay);
setOtherCssParams("disabledOpacity", config.layout.otherParams.disabledOpacity);
setOtherCssParams("dividerWeight", config.layout.otherParams.dividerWeight);
setOtherCssParams("hoverOpacity", config.layout.otherParams.hoverOpacity);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add error handling for invalid configuration or theme.

The function assumes that the provided config and theme objects have all the required properties and that their values are valid. If the configuration or theme is invalid or missing required properties, the function will set the CSS variables with undefined or invalid values without any error handling or logging.

Consider adding error handling or logging for cases where the configuration or theme might be invalid or missing required properties. For example:

if (!config[theme] || !config[theme].brandColor || !config[theme].baseColor || !config.layout) {
  console.error("Invalid configuration or theme provided.");
  return;
}

@wingkwong wingkwong added this to the v2.5.0 milestone Sep 4, 2024
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between b57a5d3 and 876816d.

Files selected for processing (6)
  • apps/docs/components/themes/components/configuration/border-widths.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/other.tsx (1 hunks)
  • apps/docs/components/themes/components/showcase-component.tsx (1 hunks)
  • apps/docs/components/themes/css-vars.ts (1 hunks)
  • apps/docs/components/themes/utils/colors.ts (1 hunks)
  • apps/docs/components/themes/utils/config.ts (1 hunks)
Files skipped from review as they are similar to previous changes (6)
  • apps/docs/components/themes/components/configuration/border-widths.tsx
  • apps/docs/components/themes/components/configuration/other.tsx
  • apps/docs/components/themes/components/showcase-component.tsx
  • apps/docs/components/themes/css-vars.ts
  • apps/docs/components/themes/utils/colors.ts
  • apps/docs/components/themes/utils/config.ts

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Outside diff range and nitpick comments (6)
apps/docs/components/themes/utils/shared.ts (2)

1-7: Add error handling for the Clipboard API call.

The copyData function is correctly implemented and uses the appropriate Clipboard API method. However, it does not handle errors that may occur when writing to the clipboard.

Consider adding error handling using a try-catch block:

export function copyData(data: string) {
+  try {
    navigator.clipboard.writeText(data);
+  } catch (err) {
+    console.error('Failed to copy data: ', err);
+  }
}

9-17: Add a comment about the limitation of not handling circular references.

The stringifyData function is correctly implemented and uses the appropriate JSON.stringify method. However, it does not handle circular references in the data, which could lead to an error.

Consider adding a comment to document this limitation:

/**
 * Stringify data
 *
 * @param data
 * @returns
+ * @throws {TypeError} Will throw an error if the data contains circular references.
 */
export function stringifyData(data: unknown) {
  return JSON.stringify(data, null, 2);
}
apps/docs/components/themes/components/configuration/base-colors.tsx (1)

19-26: Consider refactoring to reduce code duplication.

While the current implementation is functional and readable, there is an opportunity to reduce code duplication in the ColorPicker configurations. The onChange, onClose, and onCopy handlers for each ColorPicker follow a similar pattern, with only minor variations based on the specific base color.

Consider extracting the common logic into separate functions or creating a higher-order component that encapsulates the shared behavior. This refactoring would make the code more DRY (Don't Repeat Yourself) and easier to maintain.

For example, you could create a BaseColorPicker component that accepts the base color type as a prop and internally handles the specific logic for each color. This would allow you to simplify the BaseColors component and reduce the repetition of similar code blocks.

Also applies to: 27-34, 35-42, 43-50, 51-58, 59-66

apps/docs/components/themes/components/configuration/brand-colors.tsx (1)

1-76: Excellent work on the BrandColors component!

The component is well-structured, follows a consistent pattern, and effectively encapsulates the logic for configuring brand colors. It provides a user-friendly interface for modifying the colors and seamlessly integrates with the theme configuration using the useThemeBuilder hook.

The use of the ColorPicker component for each brand color type, along with the appropriate callbacks for handling color changes, updates, and copying of configurations, demonstrates a clear separation of concerns and promotes code reusability.

Overall, the component is implemented in a clean and maintainable manner, making it easy to understand and extend.

To further improve code reusability and maintainability, consider extracting the repeated ColorPicker configuration into a separate component or function. This would reduce code duplication and make it easier to update the configuration if needed.

For example, you could create a BrandColorPicker component that encapsulates the common props and callbacks:

interface BrandColorPickerProps {
  hexColor: string;
  icon?: React.ReactNode;
  label: string;
  type: string;
  onChange: (hexColor: string) => void;
  onClose: (hexColor: string) => void;
  onCopy: (theme: ThemeType) => void;
}

function BrandColorPicker({hexColor, icon, label, type, onChange, onClose, onCopy}: BrandColorPickerProps) {
  return (
    <ColorPicker
      hexColor={hexColor}
      icon={icon}
      label={label}
      type={type}
      onChange={(hexColor) => onChange(hexColor)}
      onClose={(hexColor) => onClose(hexColor)}
      onCopy={(theme) => onCopy(theme)}
    />
  );
}

Then, you can use the BrandColorPicker component within the BrandColors component:

<BrandColorPicker
  hexColor={config[theme].brandColor.primary}
  icon={syncIcon}
  label="Primary"
  type="primary"
  onChange={(hexColor) => setCssColor("primary", hexColor, theme)}
  onClose={(hexColor) => setBrandColor({primary: hexColor}, theme, syncThemes)}
  onCopy={(theme) => copyBrandColorConfig(config, "primary", theme)}
/>

This refactoring would make the code more concise and easier to maintain.

apps/docs/components/themes/components/color-picker.tsx (2)

1-105: Excellent work on the ColorPicker component!

The component is well-structured, modular, and follows best practices. It effectively utilizes various dependencies to create a comprehensive color picker with a user-friendly interface. The use of hooks for state management and side effects is appropriate and well-implemented.

Just a few minor suggestions:

  1. Consider adding a default value for the icon prop to improve clarity and avoid potential issues when the prop is not provided.

  2. Add comments to explain the purpose of the useEffect hook. While the code is readable, a brief comment can help other developers quickly understand the intention behind updating the selected color when the popover is opened.

Overall, the ColorPicker component is a solid piece of code that enhances the theme customization experience. Great job!


107-142: The getColor function is a clean and straightforward utility function.

It effectively maps the color picker types to their corresponding CSS classes using a switch statement. The cases cover all the possible color picker types, and the default case handles any unmatched types by returning undefined.

One minor suggestion:

  1. Consider adding a return type annotation to the function signature for clarity. While TypeScript can infer the return type, explicitly specifying it can improve readability and maintainability.
-function getColor(type: ColorPickerType) {
+function getColor(type: ColorPickerType): string | undefined {

Overall, the getColor function is a well-written utility function that complements the ColorPicker component nicely.

Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 876816d and 81869d0.

Files selected for processing (15)
  • apps/docs/components/themes/components/color-picker.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/base-colors.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/border-widths.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/brand-colors.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/font-sizes.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/index.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/line-heights.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/other-colors.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/other.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/radiuses.tsx (1 hunks)
  • apps/docs/components/themes/components/copy-button.tsx (1 hunks)
  • apps/docs/components/themes/constants.ts (1 hunks)
  • apps/docs/components/themes/provider.tsx (1 hunks)
  • apps/docs/components/themes/utils/config.ts (1 hunks)
  • apps/docs/components/themes/utils/shared.ts (1 hunks)
Additional comments not posted (32)
apps/docs/components/themes/components/configuration/radiuses.tsx (1)

11-42: LGTM!

The Radiuses component is well-structured and follows best practices:

  • It correctly uses the useThemeBuilder hook to access the setRadius function from the theme context.
  • The ConfigSection component is used appropriately to group the radius inputs.
  • The NumberInput components are correctly bound to the corresponding radius values in the theme config.
  • When a radius value is changed, it updates the theme config and sets the corresponding CSS variable.
  • The component is correctly typed with the RadiusesProps interface.
  • It is a functional component, which aligns with React best practices.
  • The component is small and focused, which promotes reusability and maintainability.

Great job!

apps/docs/components/themes/components/configuration/border-widths.tsx (1)

1-38: LGTM! The component is well-structured and addresses the past review comments.

The BorderWidths component is implemented correctly and follows a consistent pattern for handling the border width configuration. The extraction of the handleChange function eliminates duplication and improves maintainability, addressing the concerns raised in the past review comments.

The component correctly updates both the theme configuration and the CSS variables when the border width values are changed, ensuring that the changes are reflected in the theme.

Great job on the improvements!

apps/docs/components/themes/components/configuration/other.tsx (1)

1-38: LGTM!

The Other component is well-structured, properly typed, and follows best practices for rendering configuration inputs. The use of the useThemeBuilder hook and the handleChange function ensures a clean and maintainable implementation.

The component is reusable and can be easily extended to include additional configuration options if needed.

Great job!

apps/docs/components/themes/components/configuration/line-heights.tsx (1)

1-50: LGTM!

The LineHeights component is well-structured and follows best practices for managing the theme state and updating the CSS variables. The code is clean, readable, and maintainable.

apps/docs/components/themes/components/copy-button.tsx (1)

22-61: LGTM!

The CopyButton component is well-structured and follows best practices. The use of the Dropdown component and icons enhances the user experience. The useState hook is used correctly to manage the copied state. The component is properly typed using the CopyButtonProps interface. The handleCopy function is correctly implemented and sets the copied state for 1.5 seconds. The component correctly passes the rest props to the Button component.

apps/docs/components/themes/components/configuration/other-colors.tsx (1)

16-48: LGTM!

The OtherColors component is well-structured and follows a clear pattern for rendering the color pickers. The use of the useThemeBuilder hook and the setOtherColor function ensures that the theme colors are updated correctly. The onChange, onClose, and onCopy event handlers provide a good user experience for modifying and synchronizing the colors. The component is properly typed with the OtherColorsProps interface.

Overall, the component implementation looks good and does not have any apparent issues or areas for improvement.

apps/docs/components/themes/constants.ts (5)

5-8: LGTM!

The color weight constants are defined appropriately.


10-11: LGTM!

The regex pattern for floating-point numbers is defined correctly.


13-18: LGTM!

The element ID constants are defined appropriately and follow a consistent naming convention.


20-22: LGTM!

The local storage key constants are defined appropriately.


24-107: LGTM!

The initial light and dark themes, as well as the layout settings, are defined appropriately. The color values are imported from the "@nextui-org/theme" package, ensuring consistency with the NextUI theme. The layout settings cover various aspects of the theme, providing a good starting point for customization.

apps/docs/components/themes/components/configuration/base-colors.tsx (1)

1-69: Well-structured and modular component for configuring base colors!

The BaseColors component is well-organized and effectively utilizes the useThemeBuilder hook to manage theme updates. The modular design, with the use of ColorPicker and ConfigSection components, promotes code reusability and maintainability.

The component provides a user-friendly interface for configuring base colors, with real-time updates to CSS variables and the ability to persist changes to the theme configuration. The onCopy handlers add a nice touch, allowing users to easily copy base color configurations across themes.

Overall, the component is implemented in a clean and efficient manner, adhering to best practices and promoting a positive user experience.

apps/docs/components/themes/components/configuration/index.tsx (8)

1-26: Imports look good!

The imports are well-organized, follow a logical order, and cover all the necessary dependencies for the component. There are no unused imports or missing dependencies.


27-37: Component declaration and state management are properly set up.

The Configuration component is correctly declared as a default export. The state variables and hooks are appropriately named, initialized, and used to manage the selected template, theme configuration, and theme syncing.


39-53: useEffect hook correctly handles theme changes.

The useEffect hook is properly implemented to update the CSS variables and the configuration when the theme changes. It correctly checks for theme changes by comparing the current theme with the previous theme and updates the necessary variables and local storage.


54-60: handleThemeSyncing function correctly handles theme syncing.

The handleThemeSyncing function is a simple and effective implementation to handle the syncing of the themes. It correctly updates the syncThemes state variable based on the provided boolean parameter.


61-67: handleToggleTheme function correctly toggles the theme.

The handleToggleTheme function is properly implemented to toggle the theme between light and dark based on the current theme state. It uses the setTheme function from the useTheme hook to update the theme correctly.


68-81: handleResetTheme function correctly resets the theme.

The handleResetTheme function is properly implemented to reset the theme based on the selected template or default configuration. It correctly updates the configuration and CSS variables using the setConfiguration, setAllCssVars, and resetConfig functions as needed.


83-126: JSX structure and organization are well-designed.

The JSX returned by the component is well-structured and organized. It properly renders the necessary sections for theme configuration, including actions, a switch for theme syncing, a template selector, and several theme configuration sections. The use of separate components for each configuration section promotes modularity and readability.


127-127: Component export is correct.

The component is properly exported as the default export, allowing it to be easily imported and used in other parts of the application.

apps/docs/components/themes/utils/config.ts (6)

8-36: LGTM!

The generateLayoutConfig function correctly maps the layout properties from the input Config object to the output NextUIPluginConfig["layout"] object. It uses template literals to convert the layout values to the expected units and directly assigns the disabledOpacity, dividerWeight, and hoverOpacity values from the input Config object to the output object.


38-68: LGTM!

The generateThemeColorsConfig function correctly generates the theme colors configuration based on the input Config object and the specified ThemeType. It uses the generateThemeColor function to generate the color configurations for various colors and directly assigns other colors from the input Config object to the output object. For the content colors, it assigns the color value from the input Config object as the DEFAULT value and uses the readableColor function to determine the foreground color based on the contrast.


73-85: LGTM!

The generatePluginConfig function correctly generates the complete plugin configuration for the NextUI theme plugin. It combines the theme colors configuration for both light and dark themes using the generateThemeColorsConfig function and includes the layout configuration generated by the generateLayoutConfig function.


87-97: LGTM!

The copyBrandColorConfig function correctly copies the brand color configuration for a specific color type and theme to the clipboard. It generates the theme color configuration using the generateThemeColor function, stringifies the generated color configuration using the stringifyData function, and copies it to the clipboard using the copyData function.


99-127: LGTM!

The copyBaseColorConfig function correctly copies the base color configuration for a specific color type and theme to the clipboard. It uses a switch statement to handle different cases based on the colorType. For the background color type, it directly copies the color value from the input Config object. For the foreground color type, it generates the theme color configuration using the generateThemeColor function and copies it to the clipboard. For the content color types, it copies an object containing the DEFAULT color value from the input Config object and the foreground color determined by the readableColor function to the clipboard.


129-135: LGTM!

The copyOtherColorConfig function correctly copies the other color configuration for a specific color type and theme to the clipboard. It directly copies the color value for the specified colorType from the input Config object to the clipboard using the copyData function.

apps/docs/components/themes/provider.tsx (6)

1-6: LGTM!

The imports are correctly used and there are no issues.


7-46: LGTM!

The ThemeBuilderContextProps interface and ThemeBuilderContext creation are correctly implemented. The default values for the context are properly defined.


47-128: LGTM!

The ThemeBuilderProvider component correctly manages the theme configuration state using the useState and useLocalStorage hooks. The update methods (setConfiguration, resetConfig, setBrandColor, and setBaseColor) properly update the theme configuration based on the provided arguments.


130-165: LGTM!

The setOtherColor method correctly updates the otherColor property of the theme configuration based on the provided arguments. The code is properly structured and follows the same pattern as the other update methods.


167-225: LGTM!

The methods to update the layout properties (setBorderWidth, setLineHeight, setFontSize, setRadius, and setOtherParams) correctly update the corresponding properties in the layout object of the theme configuration. The code is properly structured and follows a consistent pattern.


227-257: LGTM!

The ThemeBuilderContext.Provider is correctly rendered with the theme configuration and update methods as the context value. The useThemeBuilder custom hook is properly defined and provides access to the theme builder context. The error handling in the custom hook ensures that it is used within the ThemeBuilderProvider component.

Comment on lines 11 to 50
export function FontSizes({config}: FontSizesProps) {
const {setFontSize} = useThemeBuilder();

return (
<ConfigSection title="Font size (rem)">
<NumberInput
label="Tiny"
value={config.layout.fontSize.tiny}
onChange={(value) => {
setFontSize({tiny: value});
setCssFontSize("tiny", value);
}}
/>
<NumberInput
label="Small"
value={config.layout.fontSize.small}
onChange={(value) => {
setFontSize({small: value});
setCssFontSize("small", value);
}}
/>
<NumberInput
label="Medium"
value={config.layout.fontSize.medium}
onChange={(value) => {
setFontSize({medium: value});
setCssFontSize("medium", value);
}}
/>
<NumberInput
label="Large"
value={config.layout.fontSize.large}
onChange={(value) => {
setFontSize({large: value});
setCssFontSize("large", value);
}}
/>
</ConfigSection>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM! Consider extracting repeated logic for better maintainability.

The FontSizes component is well-structured and modular, with clear separation of concerns. The use of the useThemeBuilder hook and setCssFontSize function ensures that the theme and CSS variables are updated consistently. The NumberInput components provide a user-friendly way to input font size values, and the component is type-safe with the FontSizesProps interface.

To further improve the component, consider extracting the repeated NumberInput and onChange logic into a separate component or function. This will reduce duplication and make the component more maintainable.

Here's an example of how you could extract the repeated logic:

interface FontSizeInputProps {
  label: string;
  value: number;
  onChange: (value: number) => void;
}

function FontSizeInput({label, value, onChange}: FontSizeInputProps) {
  return (
    <NumberInput
      label={label}
      value={value}
      onChange={(value) => {
        onChange(value);
        setCssFontSize(label.toLowerCase(), value);
      }}
    />
  );
}

export function FontSizes({config}: FontSizesProps) {
  const {setFontSize} = useThemeBuilder();

  return (
    <ConfigSection title="Font size (rem)">
      <FontSizeInput
        label="Tiny"
        value={config.layout.fontSize.tiny}
        onChange={(value) => setFontSize({tiny: value})}
      />
      <FontSizeInput
        label="Small"
        value={config.layout.fontSize.small}
        onChange={(value) => setFontSize({small: value})}
      />
      <FontSizeInput
        label="Medium"
        value={config.layout.fontSize.medium}
        onChange={(value) => setFontSize({medium: value})}
      />
      <FontSizeInput
        label="Large"
        value={config.layout.fontSize.large}
        onChange={(value) => setFontSize({large: value})}
      />
    </ConfigSection>
  );
}

This extracts the NumberInput and onChange logic into a separate FontSizeInput component, which takes a label, value, and onChange prop. The FontSizes component then renders a FontSizeInput for each font size option, passing the appropriate props.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Outside diff range and nitpick comments (1)
apps/docs/components/navbar.tsx (1)

239-249: Consider placement and feature flag for the Themes NavbarItem.

The addition of the Themes NavbarItem effectively supports the introduction of the theme generator feature. However, consider the following points:

  1. Placement: While the current position seems appropriate, discuss with the team if this is the optimal placement for the best user experience.

  2. Feature Flag: Consider implementing a feature flag for this navigation item. This would allow for easier management of the feature across different environments or user groups, especially if the theme generator is not yet ready for all users.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 81869d0 and dfc38ea.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (1)
  • apps/docs/components/navbar.tsx (1 hunks)

Comment on lines 239 to 249
</NavbarItem>{" "}
<NavbarItem>
<NextLink
className={navLinkClasses}
color="foreground"
data-active={includes(pathname, "themes")}
href="/themes"
onClick={() => handlePressNavbarItem("Figma", "/themes")}
>
Themes
</NextLink>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Correct the onClick handler argument for the Themes NavbarItem.

The onClick handler for the Themes NavbarItem is using an incorrect first argument. It's currently set to "Figma" instead of "Themes".

Please update the onClick handler as follows:

-              onClick={() => handlePressNavbarItem("Figma", "/themes")}
+              onClick={() => handlePressNavbarItem("Themes", "/themes")}

This change will ensure that the correct analytics event is tracked when users click on the Themes navigation item.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
</NavbarItem>{" "}
<NavbarItem>
<NextLink
className={navLinkClasses}
color="foreground"
data-active={includes(pathname, "themes")}
href="/themes"
onClick={() => handlePressNavbarItem("Figma", "/themes")}
>
Themes
</NextLink>
</NavbarItem>{" "}
<NavbarItem>
<NextLink
className={navLinkClasses}
color="foreground"
data-active={includes(pathname, "themes")}
href="/themes"
onClick={() => handlePressNavbarItem("Themes", "/themes")}
>
Themes
</NextLink>

Copy link
Member

@wingkwong wingkwong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. please resolve coderabbitai comments.
  2. typecheck is failed. please take a look

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between dfc38ea and 275403b.

📒 Files selected for processing (2)
  • apps/docs/components/navbar.tsx (1 hunks)
  • apps/docs/components/themes/components/configuration/font-sizes.tsx (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/docs/components/navbar.tsx
🔇 Additional comments (2)
apps/docs/components/themes/components/configuration/font-sizes.tsx (2)

1-5: LGTM! Imports are well-organized and relevant.

The imports are logically structured and include all necessary dependencies for the components in this file. There are no unused imports, which is good for maintaining clean code.


7-9: LGTM! FontSizesProps interface is well-defined.

The FontSizesProps interface is concise and correctly defines the expected props for the FontSizes component. It uses the Config type, which seems to be imported from a types file, ensuring type safety.

Comment on lines +11 to +20
export function FontSizes({config}: FontSizesProps) {
return (
<ConfigSection title="Font size (rem)">
<FontSizeInput label="Tiny" type="tiny" value={config.layout.fontSize.tiny} />
<FontSizeInput label="Small" type="small" value={config.layout.fontSize.small} />
<FontSizeInput label="Medium" type="medium" value={config.layout.fontSize.medium} />
<FontSizeInput label="Large" type="large" value={config.layout.fontSize.large} />
</ConfigSection>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using an enum or union type for font size keys.

The FontSizes component looks good overall, but we can improve type safety for the type prop passed to FontSizeInput components. Instead of using string literals, consider defining an enum or union type for font size keys.

Here's a suggested improvement:

type FontSizeKey = 'tiny' | 'small' | 'medium' | 'large';

export function FontSizes({config}: FontSizesProps) {
  return (
    <ConfigSection title="Font size (rem)">
      {(['tiny', 'small', 'medium', 'large'] as const).map((size) => (
        <FontSizeInput
          key={size}
          label={size.charAt(0).toUpperCase() + size.slice(1)}
          type={size}
          value={config.layout.fontSize[size]}
        />
      ))}
    </ConfigSection>
  );
}

This approach ensures type safety and reduces repetition in the component.

Comment on lines +22 to +26
interface FontSizeInputProps {
label: string;
type: keyof ConfigLayout["fontSize"];
value: string;
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using a more specific type for the value prop.

The FontSizeInputProps interface is well-defined, but the value prop type could be more specific. Font sizes are typically numbers, so using string might be too broad.

Consider changing the value prop type to number:

interface FontSizeInputProps {
  label: string;
  type: keyof ConfigLayout["fontSize"];
  value: number;
}

This change would make the interface more precise and prevent potential type-related issues when working with font sizes.

Comment on lines +28 to +41
function FontSizeInput({label, type, value}: FontSizeInputProps) {
const {setFontSize} = useThemeBuilder();

return (
<NumberInput
label={label}
value={value}
onChange={(value) => {
setFontSize({[type]: value});
setCssFontSize(type, value);
}}
/>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider adding input validation for font sizes.

The FontSizeInput component looks good overall, but it lacks input validation. Adding validation would prevent users from entering invalid font sizes.

Consider adding a validation function to the onChange handler:

function FontSizeInput({label, type, value}: FontSizeInputProps) {
  const {setFontSize} = useThemeBuilder();

  const handleChange = (newValue: number) => {
    if (newValue > 0 && newValue <= 10) {  // Example validation
      setFontSize({[type]: newValue});
      setCssFontSize(type, newValue);
    } else {
      // Handle invalid input (e.g., show an error message)
    }
  };

  return (
    <NumberInput
      label={label}
      value={value}
      onChange={handleChange}
      min={0.1}  // Add min prop
      max={10}   // Add max prop
      step={0.1} // Add step prop for finer control
    />
  );
}

This change adds basic validation and constrains the input range, improving the user experience and preventing potential issues with extreme font sizes.

@xylish7
Copy link
Author

xylish7 commented Sep 30, 2024

@wingkwong I fixed the ts error and also implemented the feedback from coderabbitai, but the new reviews from coderabbit ai feels a little bit overkill and don't think they improve the quality of code. I don't find that level of abstraction needed for this simple case.

Let me know if there is anything else I need to handle.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Outside diff range and nitpick comments (1)
apps/docs/components/themes/components/showcase/line-height.tsx (1)

3-14: Consider improving component naming and content for clarity.

While the implementation is good, there are a couple of points to consider:

  1. The component name FontSizeShowcase doesn't align perfectly with the showcase name "Line height (rem)". Consider renaming it to LineHeightShowcase for consistency.

  2. The text content of each paragraph doesn't provide information about the actual line height values. Consider adding the line height values to each paragraph for better clarity. For example:

<p className="text-tiny">Tiny (line-height: X rem)</p>
<p className="text-small">Small (line-height: Y rem)</p>
<p className="text-medium">Medium (line-height: Z rem)</p>
<p className="text-large">Large (line-height: W rem)</p>

Replace X, Y, Z, and W with the actual line height values in rem.

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between 275403b and cb1a2f3.

📒 Files selected for processing (1)
  • apps/docs/components/themes/components/showcase/line-height.tsx (1 hunks)
🔇 Additional comments (2)
apps/docs/components/themes/components/showcase/line-height.tsx (2)

1-1: LGTM: Import statement is correct.

The import of ShowcaseComponent is appropriate and necessary for the component's implementation.


3-14: LGTM: Component structure and implementation are solid.

The FontSizeShowcase component is well-structured and follows React best practices. The use of flex layout for spacing elements is appropriate.

@wingkwong
Copy link
Member

Thanks. We'll review gradually.

Copy link

vercel bot commented Oct 13, 2024

The latest updates on your projects. Learn more about Vercel for Git ↗︎

Name Status Preview Comments Updated (UTC)
nextui-docs-v2 ❌ Failed (Inspect) Oct 13, 2024 7:31am
nextui-storybook-v2 ✅ Ready (Inspect) Visit Preview 💬 Add feedback Oct 13, 2024 7:31am

Copy link
Member

@wingkwong wingkwong left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

docs s failed to build. please double check.

@xylish7
Copy link
Author

xylish7 commented Oct 13, 2024

Hmm...it's weird why it's complaining about brand color not being defined since...since the types seems to be correct. Will look into it.

@xylish7
Copy link
Author

xylish7 commented Oct 15, 2024

I'm unable to see why there is a issue with that.

The Config interface looks like this

export interface Config {
  light: ConfigColors;
  dark: ConfigColors;
  layout: ConfigLayout;
}

The ThemeType type loooks like this:

export type ThemeType = "light" | "dark";

And the issue is mentioned in brand-colors.tsx, where it says that config[theme] is undefined, which, based on the types definition of Config and ThemeType it can't be true.

image

Can it be a Typescript compiler issue or I'm missing something?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Outside diff range and nitpick comments (2)
apps/docs/components/themes/index.tsx (2)

10-11: LGTM: Component declaration and state initialization look good.

The ThemeBuilder component is correctly exported and uses the useState hook appropriately for client-side rendering.

Consider using the useClientSideEffect hook from @react-hookz/web for a more declarative approach to handling client-side effects. This can simplify your code and make it more readable:

import { useClientSideEffect } from '@react-hookz/web';

export function ThemeBuilder() {
  const isClient = useClientSideEffect();

  // Rest of the component...
}

17-27: LGTM: Conditional rendering is well-implemented.

The component's return statement effectively uses conditional rendering based on the isClient state. The structure with ThemeBuilderProvider wrapping the main components is a good practice for state management.

Consider adding an aria-label to the loading container div for better accessibility:

- <div className="flex justify-center items-center w-full h-min md:h-[calc(100vh-15rem)]">
+ <div className="flex justify-center items-center w-full h-min md:h-[calc(100vh-15rem)]" aria-label="Theme generator loading state">
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between cb1a2f3 and c9c165c.

📒 Files selected for processing (2)
  • apps/docs/components/themes/components/showcase/circular-progress.tsx (1 hunks)
  • apps/docs/components/themes/index.tsx (1 hunks)
🧰 Additional context used
🔇 Additional comments (3)
apps/docs/components/themes/index.tsx (2)

1-8: LGTM: Imports and "use client" directive are correctly implemented.

The "use client" directive and import statements are properly structured. The use of NextUI's Progress component aligns with the project's integration of NextUI.


13-15: LGTM: useEffect hook is correctly implemented.

The useEffect hook is properly used to update the isClient state after the component mounts. The empty dependency array ensures this effect runs only once, which is the intended behavior for distinguishing between server-side and client-side rendering.

apps/docs/components/themes/components/showcase/circular-progress.tsx (1)

1-4: Imports look good!

The necessary components and types are imported correctly. The use of relative paths for local imports and the specific import of CircularProgress from NextUI demonstrate good import practices.

);
}

const sizes: NextUISize[] = ["sm", "md", "lg"];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider moving sizes constant to a shared file

The sizes constant is well-defined and typed correctly. However, for better maintainability and reusability, consider moving this constant to a shared file (e.g., constants.ts or theme-constants.ts) if it's used across multiple components in the theme showcase.

// In a new file, e.g., theme-constants.ts
import { NextUISize } from "./types";

export const SHOWCASE_SIZES: NextUISize[] = ["sm", "md", "lg"];

// Then in this file:
import { SHOWCASE_SIZES } from "../../theme-constants";

// ...

<ShowcaseComponent defaultVariant="solid" name="CircularProgress" sizes={SHOWCASE_SIZES}>
  {/* ... */}
</ShowcaseComponent>

This approach would centralize size definitions and make it easier to maintain consistent sizes across different showcase components.

Comment on lines +6 to +29
export function CircularProgress() {
return (
<ShowcaseComponent defaultVariant="solid" name="CircularProgress" sizes={sizes}>
<NextUICircularProgress aria-label="default" color="default">
Default
</NextUICircularProgress>
<NextUICircularProgress aria-label="primary" color="primary">
Primary
</NextUICircularProgress>
<NextUICircularProgress aria-label="secondary" color="secondary">
Secondary
</NextUICircularProgress>
<NextUICircularProgress aria-label="success" color="success">
Success
</NextUICircularProgress>
<NextUICircularProgress aria-label="warning" color="warning">
Warning
</NextUICircularProgress>
<NextUICircularProgress aria-label="danger" color="danger">
Danger
</NextUICircularProgress>
</ShowcaseComponent>
);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Well-structured component with room for enhancement

The CircularProgress component is well-organized and demonstrates good practices:

  • Proper use of functional components
  • Consistent formatting
  • Good accessibility with aria-labels
  • Comprehensive color variations

To improve reusability, consider accepting props for customization:

- export function CircularProgress() {
+ export function CircularProgress({ customSizes = sizes, customColors = ['default', 'primary', 'secondary', 'success', 'warning', 'danger'] }) {
   return (
-    <ShowcaseComponent defaultVariant="solid" name="CircularProgress" sizes={sizes}>
+    <ShowcaseComponent defaultVariant="solid" name="CircularProgress" sizes={customSizes}>
-      <NextUICircularProgress aria-label="default" color="default">
-        Default
-      </NextUICircularProgress>
-      <NextUICircularProgress aria-label="primary" color="primary">
-        Primary
-      </NextUICircularProgress>
-      <NextUICircularProgress aria-label="secondary" color="secondary">
-        Secondary
-      </NextUICircularProgress>
-      <NextUICircularProgress aria-label="success" color="success">
-        Success
-      </NextUICircularProgress>
-      <NextUICircularProgress aria-label="warning" color="warning">
-        Warning
-      </NextUICircularProgress>
-      <NextUICircularProgress aria-label="danger" color="danger">
-        Danger
-      </NextUICircularProgress>
+      {customColors.map((color) => (
+        <NextUICircularProgress key={color} aria-label={color} color={color}>
+          {color.charAt(0).toUpperCase() + color.slice(1)}
+        </NextUICircularProgress>
+      ))}
     </ShowcaseComponent>
   );
 }

This change would allow users of the component to customize the sizes and colors as needed, making it more flexible for different use cases within the theme showcase.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function CircularProgress() {
return (
<ShowcaseComponent defaultVariant="solid" name="CircularProgress" sizes={sizes}>
<NextUICircularProgress aria-label="default" color="default">
Default
</NextUICircularProgress>
<NextUICircularProgress aria-label="primary" color="primary">
Primary
</NextUICircularProgress>
<NextUICircularProgress aria-label="secondary" color="secondary">
Secondary
</NextUICircularProgress>
<NextUICircularProgress aria-label="success" color="success">
Success
</NextUICircularProgress>
<NextUICircularProgress aria-label="warning" color="warning">
Warning
</NextUICircularProgress>
<NextUICircularProgress aria-label="danger" color="danger">
Danger
</NextUICircularProgress>
</ShowcaseComponent>
);
}
export function CircularProgress({ customSizes = sizes, customColors = ['default', 'primary', 'secondary', 'success', 'warning', 'danger'] }) {
return (
<ShowcaseComponent defaultVariant="solid" name="CircularProgress" sizes={customSizes}>
{customColors.map((color) => (
<NextUICircularProgress key={color} aria-label={color} color={color}>
{color.charAt(0).toUpperCase() + color.slice(1)}
</NextUICircularProgress>
))}
</ShowcaseComponent>
);
}

@xylish7
Copy link
Author

xylish7 commented Oct 18, 2024

So the problem was related to SSR. The theme builder was rendered on server side and because of this some values were initialized with false. I've added a check to render the theme builder only on client side and while the js is loading, a loader is displayed.

image

The build succeeded. Let me know if there is anything else I need to fix. Thank you!

@jrgarciadev
Copy link
Member

Hey @xylish7 thanks for this great contribution 🙏🏻 could you please fix the conflicts?

@xylish7
Copy link
Author

xylish7 commented Nov 6, 2024

Sure! Will look into it today 👍🏻

@xylish7
Copy link
Author

xylish7 commented Nov 6, 2024

@jrgarciadev I've fixed the merge conflicts. Only pnpm-lock.yaml was conflicting.

I also run the build and test command which successfully ran, but there was an error related to the usage of new Image which I haven't touched, so not sure if that was already present or it was because I regenerated the pnpm-lock.yaml file

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between c9c165c and a461954.

📒 Files selected for processing (2)
  • apps/docs/components/navbar.tsx (1 hunks)
  • apps/docs/package.json (2 hunks)
🔇 Additional comments (2)
apps/docs/package.json (1)

74-74: LGTM! Good choice for the color picker.

The addition of react-colorful is appropriate for implementing the theme generator's color picking functionality. It's a lightweight and well-maintained library.

apps/docs/components/navbar.tsx (1)

240-248: LGTM! The Themes navigation item is well implemented.

The implementation follows the established patterns and correctly integrates with the existing navigation structure. The active state handling, event tracking, and styling are all properly configured.

@@ -92,6 +93,7 @@
"unified": "^11.0.5",
"unist-util-visit": "^5.0.0",
"usehooks-ts": "^2.9.1",
"values.js": "^2.1.1",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Consider using color2k instead of values.js.

The project already includes color2k as a dependency, which provides similar color manipulation functionality and is actively maintained. Consider leveraging color2k instead of adding values.js to:

  1. Reduce bundle size by reusing existing dependencies
  2. Maintain consistency in color manipulation across the codebase
  3. Benefit from active maintenance and updates

@wingkwong wingkwong modified the milestones: v2.6.0, v2.7.0 Nov 30, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[Feature Request] Theme generator
3 participants