diff --git a/packages/ui/README.md b/packages/ui/README.md index 7219ffb..b5dec2b 100644 --- a/packages/ui/README.md +++ b/packages/ui/README.md @@ -12,27 +12,37 @@ A TailwindCSS and NextUI-(Open Source) components library for React projects. Th Indended for use with modern ESM TypeScript in other do-ob projects. Documentation is limited at this time. -## Installation - -To use the components from this library in your project, follow these steps: - -1. Install TailwindCSS and NextUI: - -Make sure you have TailwindCSS and NextUI set up in your project. If not, follow their respective installation guides: +# Libraries Leveraged * [TailwindCSS Installation Guide](https://tailwindcss.com/docs/installation) * [NextUI Installation Guide](https://nextui.org/docs/guide/installation) -2. Install the component library: +## Installation -```bash -npm install @do-ob/ui -``` +1. Install the required dependencies using `npm`, `pnpm`, or `yarn`: -```bash -pnpm add @do-ob/ui +``` +'@do-ob/ui' 'react' 'react-dom' '@nextui-org/react' 'framer-motion' 'tailwindcss' 'tailwindcss' '@tailwindcss/typography' '@heroicons/react' ``` -```bash -yarn add @do-ob/ui +2. Add the following to your `tailwind.config.ts` file: + +```typescript +import type { Config } from 'tailwindcss'; +import { nextui } from '@nextui-org/react'; +import tailwindTypography from '@tailwindcss/typography'; + +const config: Config = { + darkMode: 'class', + content: [ + './node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}', + './node_modules/@do-ob/ui/dist/**/*.{js,ts,jsx,tsx}', + './app/**/*.{js,ts,jsx,tsx,mdx}', + ], + plugins: [ + nextui(), + tailwindTypography, + ], +}; +export default config; ``` \ No newline at end of file diff --git a/packages/ui/package.json b/packages/ui/package.json index 83de6a5..ea90613 100644 --- a/packages/ui/package.json +++ b/packages/ui/package.json @@ -37,7 +37,9 @@ "peerDependencies": { "@heroicons/react": "^2.1.3", "@nextui-org/react": "^2.4.1", + "@tailwindcss/typography": "^0.5.13", "framer-motion": "^11.2.10", - "react": "latest" + "react": "latest", + "tailwindcss": "^3.4.3" } } \ No newline at end of file diff --git a/packages/ui/src/components/Article/Article.stories.tsx b/packages/ui/src/components/Article/Article.stories.tsx new file mode 100644 index 0000000..ca77a56 --- /dev/null +++ b/packages/ui/src/components/Article/Article.stories.tsx @@ -0,0 +1,94 @@ +import type { Meta, StoryObj } from '@storybook/react'; + +import { Article } from './Article'; + +const meta = { + component: Article, +} satisfies Meta; + +export default meta; + +type Story = StoryObj; + +export const Default: Story = { + render: (args) => ( +
+

Lorem Ipsum Dolor Sit Amet

+

Consectetur Adipiscing Elit

+

Sed Do Eiusmod Tempor

+ +

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer nec odio. Praesent libero. Sed cursus ante dapibus diam.

+ +

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam nec ante. Sed lacinia, urna non tincidunt mattis, tortor neque adipiscing diam, a cursus ipsum ante quis turpis. Nulla facilisi. Ut fringilla. Suspendisse potenti. Nunc feugiat mi a tellus consequat imperdiet. Vestibulum sapien. Proin quam. Etiam ultrices.

+ +

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus lacinia odio vitae vestibulum vestibulum. Cras venenatis euismod malesuada. Nulla tincidunt ligula libero, a fermentum risus ultricies ac. Maecenas non ligula nec leo hendrerit vehicula ac id libero. Integer ac risus in sem lacinia rhoncus. Donec in erat nec quam tristique tristique. Duis in orci non elit dapibus commodo ac sit amet mauris. Fusce vel dui augue. Proin et metus ac nisi laoreet scelerisque. Integer a massa dolor. Suspendisse potenti. Nullam interdum metus nisi, et scelerisque nulla dignissim ac.

+ + +
    +
  • Lorem ipsum dolor sit amet
  • +
  • Consectetur adipiscing elit
  • +
  • Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
  • +
+ +
    +
  1. Lorem ipsum dolor sit amet
  2. +
  3. Consectetur adipiscing elit
  4. +
  5. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
  6. +
+ +
Lorem ipsum dolor sit amet, consectetur adipiscing elit.
+ +
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. +
+ +

<Lorem ipsum>

+ +

+        {`function loremIpsum() {
+    return "Lorem ipsum dolor sit amet, consectetur adipiscing elit.";
+}`}
+      
+ + + + + + + + + + + + + + + + + + + + + + + + + +
Heading 1Heading 2Heading 3
Row 1 Col 1Row 1 Col 2Row 1 Col 3
Row 2 Col 1Row 2 Col 2Row 2 Col 3
Row 3 Col 1Row 3 Col 2Row 3 Col 3
+ +

Lorem Ipsum Image

+ +

Lorem Ipsum

+ +

Lorem Ipsum

+ +
+ +
    +
  • Lorem ipsum dolor sit amet
  • +
  • Consectetur adipiscing elit
  • +
  • Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua
  • +
+
+ ), +}; diff --git a/packages/ui/src/components/Article/Article.tsx b/packages/ui/src/components/Article/Article.tsx new file mode 100644 index 0000000..6de9d2b --- /dev/null +++ b/packages/ui/src/components/Article/Article.tsx @@ -0,0 +1,10 @@ +import { HTMLAttributes, PropsWithChildren } from 'react'; +import { clsx, clmg } from '@do-ob/core'; + +export function Article({ children, className, ...props }: PropsWithChildren>) { + return ( +
+ {children} +
+ ); +} diff --git a/packages/ui/src/components/Navigation/Navigation.stories.tsx b/packages/ui/src/components/Navigation/Navigation.stories.tsx index 57d1121..9feca94 100644 --- a/packages/ui/src/components/Navigation/Navigation.stories.tsx +++ b/packages/ui/src/components/Navigation/Navigation.stories.tsx @@ -14,6 +14,8 @@ export default meta; type Story = StoryObj; +const logo = 'https://via.placeholder.com/100'; + const links: Link[] = [ { title: 'Home', @@ -99,6 +101,7 @@ const socials: SocialLinks = [ export const Standard: Story = { args: { title: 'Navigation', + logo, links, search: '#search', modeToggle: true, @@ -109,6 +112,7 @@ export const Standard: Story = { export const Island: Story = { args: { title: 'Navigation', + logo, variant: 'island', links, search: '#search', @@ -120,6 +124,7 @@ export const Island: Story = { export const Extended: Story = { args: { title: 'Navigation', + logo, variant: 'extended', links, search: '#search', diff --git a/packages/ui/src/components/Navigation/NavigationExtended.tsx b/packages/ui/src/components/Navigation/NavigationExtended.tsx index f49c485..78f2e70 100644 --- a/packages/ui/src/components/Navigation/NavigationExtended.tsx +++ b/packages/ui/src/components/Navigation/NavigationExtended.tsx @@ -1,4 +1,7 @@ -import { Navbar, NavbarContent } from '@nextui-org/react'; +'use client'; + +import React from 'react'; +import { Navbar, NavbarContent, NavbarMenuToggle } from '@nextui-org/react'; import { clsx, clmg } from '@do-ob/core'; import type { NavigationProps } from './data/NavigationContext'; import { NavigationPart_Brand } from './parts/NavigationPart_Brand'; @@ -6,31 +9,45 @@ import { NavigationPart_Links } from './parts/NavigationPart_Links'; import { NavigationPart_Actions } from './parts/NavigationPart_Actions'; import { NavigationProvider } from './data/NavigationProvider'; import { twColors } from '@do-ob/ui/utility'; +import { NavigationPart_Menu } from './parts/NavigationPart_Menu'; export function NavigationExtended(props: NavigationProps) { + const [ isMenuOpen, setIsMenuOpen ] = React.useState(false); + const colors = twColors(props.color); return ( - - + +
+ +
- + - -
+ +
+
+ + ); diff --git a/packages/ui/src/components/Navigation/NavigationIsland.tsx b/packages/ui/src/components/Navigation/NavigationIsland.tsx index cf3816f..f8d22b4 100644 --- a/packages/ui/src/components/Navigation/NavigationIsland.tsx +++ b/packages/ui/src/components/Navigation/NavigationIsland.tsx @@ -1,4 +1,7 @@ -import { Navbar, NavbarContent } from '@nextui-org/react'; +'use client'; + +import React from 'react'; +import { Navbar, NavbarContent, NavbarMenuToggle } from '@nextui-org/react'; import { clsx, clmg } from '@do-ob/core'; import type { NavigationProps } from './data/NavigationContext'; @@ -7,25 +10,47 @@ import { NavigationPart_Brand } from './parts/NavigationPart_Brand'; import { NavigationPart_Links } from './parts/NavigationPart_Links'; import { NavigationPart_Actions } from './parts/NavigationPart_Actions'; import { twColors } from '@do-ob/ui/utility'; +import { NavigationPart_Menu } from './parts/NavigationPart_Menu'; export function NavigationIsland(props: NavigationProps) { + const [ isMenuOpen, setIsMenuOpen ] = React.useState(false); + const colors = twColors(props.color); return ( - + - -
- -
-
+ + {props.links && props.links.length > 0 && ( + +
+ +
+
+ )} + - +
+ +
+
+ + +
); diff --git a/packages/ui/src/components/Navigation/NavigationStandard.tsx b/packages/ui/src/components/Navigation/NavigationStandard.tsx index cc4c7f5..5a43bcd 100644 --- a/packages/ui/src/components/Navigation/NavigationStandard.tsx +++ b/packages/ui/src/components/Navigation/NavigationStandard.tsx @@ -1,4 +1,7 @@ -import { Navbar, NavbarContent } from '@nextui-org/react'; +'use client'; + +import React from 'react'; +import { Navbar, NavbarContent, NavbarMenuToggle } from '@nextui-org/react'; import { clsx, clmg } from '@do-ob/core'; import type { NavigationProps } from './data/NavigationContext'; import { NavigationProvider } from './data/NavigationProvider'; @@ -6,31 +9,42 @@ import { NavigationPart_Brand } from './parts/NavigationPart_Brand'; import { NavigationPart_Links } from './parts/NavigationPart_Links'; import { NavigationPart_Actions } from './parts/NavigationPart_Actions'; import { twColors } from '@do-ob/ui/utility'; +import { NavigationPart_Menu } from './parts/NavigationPart_Menu'; export function NavigationStandard(props: NavigationProps) { + const [ isMenuOpen, setIsMenuOpen ] = React.useState(false); + const colors = twColors(props.color); return ( - + -
+
+ + + ); diff --git a/packages/ui/src/components/Navigation/data/NavigationContext.ts b/packages/ui/src/components/Navigation/data/NavigationContext.ts index da183aa..8db469f 100644 --- a/packages/ui/src/components/Navigation/data/NavigationContext.ts +++ b/packages/ui/src/components/Navigation/data/NavigationContext.ts @@ -12,6 +12,11 @@ export interface NavigationProps { */ title?: string; + /** + * A shortened version of the brand title + */ + titleShort?: string; + /** * The brand image to display */ @@ -47,6 +52,11 @@ export interface NavigationProps { */ socials?: SocialLinks; + /** + * The navigation position attribute + */ + position?: 'static' | 'sticky'; + /** * Class names to modify. */ diff --git a/packages/ui/src/components/Navigation/parts/NavigationPart_Brand.tsx b/packages/ui/src/components/Navigation/parts/NavigationPart_Brand.tsx index e0706e2..2914597 100644 --- a/packages/ui/src/components/Navigation/parts/NavigationPart_Brand.tsx +++ b/packages/ui/src/components/Navigation/parts/NavigationPart_Brand.tsx @@ -11,15 +11,26 @@ import { NavigationContext } from '../data/NavigationContext'; export function NavigationPart_Brand() { const { image: imageNode } = React.useContext(DoobUiContext); - const { title, logo, classNames } = React.useContext(NavigationContext); + const { title, titleShort, logo, classNames } = React.useContext(NavigationContext); return ( {logo ? ( - {title} + {title} ) : null} -

{title}

+

{title}

+

{titleShort ?? title}

); diff --git a/packages/ui/src/components/Navigation/parts/NavigationPart_Menu.tsx b/packages/ui/src/components/Navigation/parts/NavigationPart_Menu.tsx new file mode 100644 index 0000000..c2de11c --- /dev/null +++ b/packages/ui/src/components/Navigation/parts/NavigationPart_Menu.tsx @@ -0,0 +1,55 @@ +'use client'; + +import React from 'react'; +import { NavbarMenu, NavbarMenuItem, Link, NavbarItem } from '@nextui-org/react'; +import { NavigationContext, NavigationProps } from '../data/NavigationContext'; +import { NavigationPart_Actions } from './NavigationPart_Actions'; + +/** + * Navigation Menu component + */ +export function NavigationPart_Menu() { + + const { links = [] } = React.useContext(NavigationContext); + + return ( + + + + + {links.map((link) => ( + + + {link.title} + + {link.links && ( + + )} + + ))} + + ); +} + +/** + * Navigation Menu Sublinks component + */ +export function NavigationPart_MenuSublinks({ links = [], level = 1 }: NavigationProps & { level?: number }) { + return links.map((link) => ( +
+ +

{link.title}

+ + {link.links && ( + + )} +
+ )); +} diff --git a/packages/ui/src/hooks/useMode.ts b/packages/ui/src/hooks/useMode.ts index ecd98f4..ecfb52f 100644 --- a/packages/ui/src/hooks/useMode.ts +++ b/packages/ui/src/hooks/useMode.ts @@ -7,27 +7,6 @@ import type { ThemeMode } from '@do-ob/ui/types'; export function useMode(prefer: ThemeMode = 'light') { const [ mode, modeSet ] = React.useState(prefer); - React.useLayoutEffect(() => { - // Observe the theme mode class name of the html element - const observer = new MutationObserver(() => { - const next = document.documentElement.classList.contains('dark') ? 'dark' : 'light'; - if (next !== mode) { - modeSet(next); - } - }); - // Start observing the theme mode class name of the html element - observer.observe(document.documentElement, { attributes: true, attributeFilter: [ 'class' ] }); - - // Check if the html element already has a mode class name. - if (document.documentElement.classList.contains('light') || document.documentElement.classList.contains('dark')) { - const documentTheme = document.documentElement.classList.contains('dark') ? 'dark' : 'light'; - modeSet(documentTheme); - } - - // Clean up the observer - return () => observer.disconnect(); - }, [ mode ]); - const modeToggle = () => { const next = mode === 'light' ? 'dark' : 'light'; modeSet(next); diff --git a/packages/ui/src/tailwind.ts b/packages/ui/src/tailwind.ts new file mode 100644 index 0000000..65c2338 --- /dev/null +++ b/packages/ui/src/tailwind.ts @@ -0,0 +1,36 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +import type { Config } from 'tailwindcss'; +import { join } from 'node:path'; +import { nextui, NextUIPluginConfig } from '@nextui-org/react'; +import tailwindTypography from '@tailwindcss/typography'; + +export interface DoobTailwindConfig { + root?: string; + config?: Config; + nextConfig?: NextUIPluginConfig; + typographyConfig?: Parameters[0]; +} + +export function tailwindConfig({ + root = process.cwd(), + config = { content: [] }, + nextConfig = {}, + typographyConfig = {}, +}: DoobTailwindConfig): Config { + + return { + ...config, + darkMode: 'class', + content: [ + join(root, 'node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}'), + join(root, 'node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}'), + ...config.content as string[], + ], + plugins: [ + nextui(nextConfig), + tailwindTypography(typographyConfig), + ...config.plugins as any[], + ], + }; + +};