Skip to content

Commit

Permalink
feat: All components have been upgraded for use in React 19
Browse files Browse the repository at this point in the history
  • Loading branch information
eric-crowell committed Jun 27, 2024
1 parent 3d68570 commit cf9d6b8
Show file tree
Hide file tree
Showing 45 changed files with 297 additions and 4,037 deletions.
2 changes: 0 additions & 2 deletions .storybook/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@ import { mergeConfig } from 'vite';

const config: StorybookConfig = {
stories: [
'../stories/**/*.mdx',
'../stories/**/*.stories.@(js|jsx|mjs|ts|tsx)',
'../packages/ui/src/**/*.stories.@(js|jsx|mjs|ts|tsx)'
],
addons: [
Expand Down
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@
"@do-ob/vite-lib-config": "^3.0.1",
"@eslint/compat": "^1.1.0",
"@heroicons/react": "^2.1.3",
"@nextui-org/react": "^2.4.2",
"@storybook/addon-a11y": "8.2.0-alpha.9",
"@storybook/addon-essentials": "8.2.0-alpha.9",
"@storybook/addon-interactions": "8.2.0-alpha.9",
Expand Down
5 changes: 5 additions & 0 deletions packages/ui/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,11 @@
"import": "./dist/*.js",
"types": "./dist/*.d.ts"
},
"./utility": {
"require": "./dist/utility.js",
"import": "./dist/utility.js",
"types": "./dist/utility.d.ts"
},
"./index": null,
"./package.json": "./package.json"
},
Expand Down
6 changes: 3 additions & 3 deletions packages/ui/src/components/Button/Button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import React from 'react';
import { Button as AriaButton } from 'react-aria-components';
import { fillStyles, emptyStyles, twMerge, interactiveStyles } from '@do-ob/ui/utility';
import { fillStyles, emptyStyles, cn, interactiveStyles } from '@do-ob/ui/utility';

export interface ButtonProps<
Element extends React.ElementType = typeof AriaButton
Expand Down Expand Up @@ -75,13 +75,13 @@ export function Button<
case 'light':
return emptyStyles[color];
case 'faded':
return twMerge(fillStyles[color], emptyStyles[color]);
return cn(fillStyles[color], emptyStyles[color]);
}
})();

return (
<Tag
className={twMerge(
className={cn(
'rounded inline-flex justify-center items-center no-underline',
interactiveStyles.focus,
interactiveStyles.mouse,
Expand Down
20 changes: 20 additions & 0 deletions packages/ui/src/components/Image/Image.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import type { Meta, StoryObj } from '@storybook/react';

import { Image } from './Image';

const meta = {
component: Image,
} satisfies Meta<typeof Image>;

export default meta;

type Story = StoryObj<typeof meta>;

export const Default: Story = {
args: {
src: 'https://github.com/do-ob-io/shared/blob/main/do-ob-logo-readme.png?raw=true',
alt: 'do-ob',
width: 200,
height: 130,
}
};
86 changes: 56 additions & 30 deletions packages/ui/src/components/Image/Image.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
'use client';

import { use, useState } from 'react';
import { twMerge } from '@do-ob/ui/utility';
import { DoobUiContext } from '@do-ob/ui/context';
import { cn } from '@do-ob/ui/utility';
import { useState, CSSProperties, use } from 'react';

export interface ImageProps<
Element extends React.ElementType = 'img'
> extends React.ImgHTMLAttributes<HTMLImageElement> {
as?: Element;
src: string;
alt: string;
width?: number;
height?: number;
quality?: number;
width?: number | string;
height?: number | string;
layout?: 'fixed' | 'intrinsic' | 'responsive' | 'fill';
objectFit?: CSSProperties['objectFit'];
objectPosition?: CSSProperties['objectPosition'];
priority?: boolean;
quality?: number;
placeholder?: 'blur' | 'empty';
blurDataURL?: string;
className?: string;
style?: React.CSSProperties;
objectFit?: 'fill' | 'contain' | 'cover' | 'none' | 'scale-down';
objectPosition?: string;
onLoadingComplete?: (result: { naturalWidth: number; naturalHeight: number }) => void;
}

export function Image<
Expand All @@ -28,41 +31,64 @@ export function Image<
alt,
width,
height,
priority = false,
layout = 'intrinsic',
objectFit,
objectPosition,
priority,
// quality,
// placeholder,
// blurDataURL,
className,
style,
objectFit = 'contain',
objectPosition = 'center',
onLoadingComplete,
...props
}: ImageProps<Element> & React.ComponentPropsWithoutRef<Element>) {

const { image } = use(DoobUiContext);
const [ loaded, loadedSet ] = useState(false);
const [ isLoading, setIsLoading ] = useState(!priority);

const Tag = as ?? image ?? 'img';

const style: CSSProperties = {
width: layout === 'fill' ? '100%' : width,
height: layout === 'fill' ? '100%' : height,
objectFit,
objectPosition,
};

const handleLoad = (event: React.SyntheticEvent<HTMLImageElement>) => {
setIsLoading(false);
const target = event.target as HTMLImageElement;
if (onLoadingComplete) {
onLoadingComplete({ naturalWidth: target.naturalWidth, naturalHeight: target.naturalHeight });
}
};

return (
<picture className="relative flex items-center justify-center">
<div className={twMerge('absolute inset-0 size-full animate-pulse bg-background-fg/50 dark:bg-background-dark-fg/50 rounded-md', loaded && 'hidden')} />
<div
className={cn(
'relative overflow-hidden rounded',
layout === 'fill' && 'size-full',
className
)}
style={{
width: layout !== 'fill' && layout !== 'responsive' ? width : undefined,
height: layout !== 'fill' && layout !== 'responsive' ? height : undefined
}}
>
{isLoading ? (
<div className="absolute inset-0 size-full animate-pulse bg-gray-500 opacity-50" />
) : null}
<Tag
className={twMerge(
'relative',
className
)}
src={src}
alt={alt}
width={width}
className={`absolute inset-0 transition-opacity duration-500 ${isLoading ? 'opacity-0' : 'opacity-100'}`}
style={style}
onLoad={handleLoad}
height={height}
priority={priority}
width={width}
decoding={priority ? 'sync' : 'async'}
loading={priority ? 'eager' : 'lazy'}
onLoad={() => loadedSet(true)}
style={{
objectFit,
objectPosition,
...style,
}}
{...props}
/>
</picture>
</div>
);
}
};
6 changes: 4 additions & 2 deletions packages/ui/src/components/Link/Link.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { twMerge, interactiveStyles } from '@do-ob/ui/utility';
'use client';

import { cn, interactiveStyles } from '@do-ob/ui/utility';
import { Link as AriaLink } from 'react-aria-components';
import { ArrowTopRightOnSquareIcon } from '@do-ob/ui/icons-hero-solid';
// import { DoobUiContext } from '@do-ob/ui/context';
Expand Down Expand Up @@ -30,7 +32,7 @@ export function Link<

return (
<Tag
className={twMerge(
className={cn(
'underline text-sky-800 dark:text-sky-300 hover:decoration-wavy inline-flex items-center',
interactiveStyles.focus,
interactiveStyles.mouse,
Expand Down
8 changes: 5 additions & 3 deletions packages/ui/src/components/Popover/Popover.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
'use client';

import React from 'react';
import {
Popover as AriaPopover,
Dialog as AriaDialog,
DialogTrigger as AriaDialogTrigger,
// OverlayArrow as AriaOverlayArrow
} from 'react-aria-components';
import { fillStyles, twMerge } from '@do-ob/ui/utility';
import { fillStyles, cn } from '@do-ob/ui/utility';
// import { ArrowUpIcon } from '@do-ob/ui/icons-hero-solid';

export interface PopoverProps {
Expand Down Expand Up @@ -39,13 +41,13 @@ export function Popover({
<AriaPopover
placement={placement}
offset={offset}
className="min-w-56 origin-top-left overflow-auto rounded-md bg-background p-1 shadow-lg ring-1 ring-background-fg/30 fill-mode-forwards entering:animate-in entering:fade-in entering:zoom-in-95 exiting:animate-out exiting:fade-out exiting:zoom-out-95 dark:bg-background-dark dark:ring-background-dark-fg/30"
className="min-w-56 origin-top-left overflow-auto rounded bg-background p-1 shadow-lg ring-1 ring-background-fg/30 fill-mode-forwards entering:animate-in entering:fade-in entering:zoom-in-95 exiting:animate-out exiting:fade-out exiting:zoom-out-95 dark:bg-background-dark dark:ring-background-dark-fg/30"
>
{/* <AriaOverlayArrow>
<ArrowUpIcon className="block size-4 bg-red-500 fill-black" />
</AriaOverlayArrow> */}
<AriaDialog
className={twMerge(
className={cn(
'focus-visible:outline-none p-2',
fillStyles.background,
)}
Expand Down
8 changes: 3 additions & 5 deletions packages/ui/src/tailwind.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import type { Config } from 'tailwindcss';
import tailwindPlugin from 'tailwindcss/plugin';
import tailwindColors from 'tailwindcss/colors';
import { join } from 'node:path';
import { nextui, NextUIPluginConfig } from '@nextui-org/react';
import tailwindReactAria from 'tailwindcss-react-aria-components';
import tailwindContainerQueries from '@tailwindcss/container-queries';
import tailwindAnimate from 'tailwindcss-animate';
Expand Down Expand Up @@ -246,13 +245,13 @@ export function applyForegroundColors(initialColors: Record<string, { DEFAULT: s

export interface DoobTailwindPreset {
root?: string;
nextConfig?: NextUIPluginConfig;
colors?: Record<string, Record<string, string>>;
typographyConfig?: Parameters<typeof tailwindTypography>[0];
}

export function doobTailwindPreset({
root = process.cwd(),
nextConfig = {},
colors = {},
typographyConfig = {},
}: DoobTailwindPreset): Config & { content: string[] } {

Expand All @@ -263,7 +262,6 @@ export function doobTailwindPreset({
join(root, 'node_modules/@nextui-org/theme/dist/**/*.{js,ts,jsx,tsx}'),
],
plugins: [
nextui(nextConfig),
tailwindTypography(typographyConfig),
tailwindContainerQueries,
tailwindReactAria,
Expand All @@ -275,7 +273,7 @@ export function doobTailwindPreset({
aspectRatio: {
'photo': '3 / 2',
},
colors: applyForegroundColors(extendedColors),
colors: applyForegroundColors({ ...extendedColors, ...colors }),
}
}
};
Expand Down
12 changes: 5 additions & 7 deletions packages/ui/src/utility.ts
Original file line number Diff line number Diff line change
@@ -1,21 +1,19 @@
'use client';

import { ThemeColor } from '@do-ob/ui/types';
import { twMerge as twMergeLib } from 'tailwind-merge';
// import { twMerge } from 'tailwind-merge';
import { clsx } from '@do-ob/core';

/**
* Merges tailwind classes.
*/
export function twMerge(...classes: unknown[]): string {
return twMergeLib(clsx(...classes));
export function cn(...classes: unknown[]): string {
return clsx(...classes);
}

export type TwColors = 'background' | 'primary' | 'secondary' | 'success' | 'warning' | 'danger';

export const interactiveStyles = {
focus: 'focus:outline-blue-500 outline-offset-1 focus:outline-2 focus:outline-offset-4 transition-all',
mouse: 'brightness-100 dark:hover:brightness-125 dark:active:brightness-150 hover:brightness-75 active:brightness-50 transition-all duration-200 cursor-pointer',
focus: 'focus-visible:outline-blue-500 outline-offset-1 focus-visible:outline-4 focus-visible:outline-offset-2 focus-visible:transition-all focus-visible:duration-200 focus:outline-none',
mouse: 'brightness-100 dark:hover:brightness-125 dark:active:brightness-150 hover:brightness-75 active:brightness-50 hover:transition-all hover:duration-200 cursor-pointer',
};

export const fillStyles: Record<TwColors, string> = {
Expand Down
9 changes: 2 additions & 7 deletions packages/ui/src/widgets.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,10 @@
export { Form } from './widgets/Form/Form';
export { Navigation, type NavigationProps } from './widgets/Navigation/Navigation';
export { SearchButton } from './widgets/SearchButton/SearchButton';
export { SearchForm } from './widgets/SearchForm/SearchForm';
export { SearchInput } from './widgets/SearchInput/SearchInput';
export { ThemeSwitch } from './widgets/ThemeSwitch/ThemeSwitch';
export { ThemeButton } from './widgets/ThemeButton/ThemeButton';
export { Typewriter } from './widgets/Typewriter/Typewriter';
export { TypewriterInput } from './widgets/TypewriterInput/TypewriterInput';
export { Hero } from './widgets/Hero/Hero';
export { HeroArticles } from './widgets/HeroArticles/HeroArticles';
export { HeroStandard } from './widgets/Hero/HeroStandard';
export { HeroPrompt } from './widgets/Hero/HeroPrompt';
// export { HeroPrompt } from './widgets/Hero/HeroPrompt';
export { Brand, type BrandProps } from './widgets/Brand/Brand';

export { Header, type HeaderProps } from './widgets/Header/Header';
17 changes: 11 additions & 6 deletions packages/ui/src/widgets/Brand/Brand.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Image } from '@do-ob/ui/components';
import { twMerge } from '@do-ob/ui/utility';
import { cn } from '@do-ob/ui/utility';

/**
* Brand properties.
Expand Down Expand Up @@ -60,32 +60,37 @@ export function Brand({
nameShort,
image = null,
size = 'md',
className,
classNames = {},
...props
}: BrandProps & React.HTMLAttributes<HTMLDivElement>) {

return (
<div className="flex size-full items-center gap-4" {...props}>
<div className={cn(
'flex flex-row flex-nowrap items-center gap-4 whitespace-nowrap',
className
)} {...props}>
{image && <Image
src={image}
alt="Brand"
width={imageSizes[size]}
height={imageSizes[size]}
loading="eager"
className={twMerge(
objectFit="contain"
priority
className={cn(
imageSizeStyles[size],
classNames.image
)}
/>}
{(name && name?.length) ? (<h1 className={twMerge(
{(name && name?.length) ? (<h1 className={cn(
'tracking-tight hidden lg:inline',
textSizes[size],
'leading-none whitespace-nowrap',
classNames.name,
)}>
{name}
</h1>) : null}
{(name && name?.length) ? (<h1 className={twMerge(
{(name && name?.length) ? (<h1 className={cn(
'tracking-tight leading-tight inline lg:hidden whitespace-nowrap',
textSizes[size],
classNames.name,
Expand Down
3 changes: 1 addition & 2 deletions packages/ui/src/widgets/Header/Header.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,8 +87,7 @@ const links: Link[] = [
export const Default: Story = {
args: {
brand: {
name: 'Brand',
image: 'https://via.placeholder.com/64',
image: 'https://github.com/do-ob-io/shared/blob/main/do-ob-logo-readme.png?raw=true',
},
navigation: {
links,
Expand Down
Loading

0 comments on commit cf9d6b8

Please sign in to comment.