feat(root): RSC components added, packages modified, filter dom props function adapted (#1289)

* feat(root): rsc components added, packages modified, filter dom props function adapted

* fix(root): eslint/prettier issues
This commit is contained in:
Junior Garcia 2023-08-05 18:53:34 -03:00 committed by GitHub
parent 2e4ce7bcee
commit eefda8d6e2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
72 changed files with 451 additions and 159 deletions

View File

@ -0,0 +1,22 @@
---
"@nextui-org/react-utils": patch
"@nextui-org/accordion": patch
"@nextui-org/dropdown": patch
"@nextui-org/skeleton": patch
"@nextui-org/divider": patch
"@nextui-org/spinner": patch
"@nextui-org/button": patch
"@nextui-org/spacer": patch
"@nextui-org/badge": patch
"@nextui-org/input": patch
"@nextui-org/table": patch
"@nextui-org/card": patch
"@nextui-org/code": patch
"@nextui-org/tabs": patch
"@nextui-org/kbd": patch
"@nextui-org/system": patch
---
- "use client" directive removed from components that didn't need it
- packages adapted to support RSC imports
- filterDomProps function was modified to enable/disabled it

View File

@ -1,4 +1,4 @@
import {Spacer} from "@nextui-org/react";
import {Spacer} from "@nextui-org/spacer";
import {Hero} from "@/components/marketing/hero";
import {FeaturesGrid} from "@/components/marketing/features-grid";

View File

@ -1,7 +1,7 @@
"use client";
import {BlogPost} from "contentlayer/generated";
import {Card, CardFooter, CardBody, CardHeader, Link, Avatar} from "@nextui-org/react";
import {Card, CardFooter, CardBody, CardHeader, Link, Avatar, Image} from "@nextui-org/react";
import Balancer from "react-wrap-balancer";
import {format, parseISO} from "date-fns";
import NextLink from "next/link";
@ -39,10 +39,9 @@ const BlogPostCard = (post: BlogPost) => {
<Balancer>{post.title}</Balancer>
</Link>
</CardHeader>
<CardBody className="px-3 pt-0 pb-1">
<p className="font-normal text-default-600">
<Balancer>{post.description}</Balancer>
</p>
<CardBody className="pt-0 px-2 pb-1">
<Image className="mb-3" src={post.image} />
<p className="font-normal px-1 text-default-600">{post.description}</p>
</CardBody>
<CardFooter className="flex justify-between items-center">
<time className="block text-small text-default-500" dateTime={post.date}>

View File

@ -1,12 +1,15 @@
import {Button, ButtonProps, Link} from "@nextui-org/react";
import {Button, ButtonProps, Code, Link, Tooltip} from "@nextui-org/react";
import {ReactNode} from "react";
import Balancer from "react-wrap-balancer";
import {GithubIcon, NpmIcon, AdobeIcon, StorybookIcon} from "@/components/icons";
import {GithubIcon, NpmIcon, AdobeIcon, StorybookIcon, NextJsIcon} from "@/components/icons";
import {COMPONENT_PATH, COMPONENT_THEME_PATH} from "@/libs/github/constants";
export interface ComponentLinksProps {
component: string;
styles?: string;
storybook?: string;
rscCompatible?: boolean;
reactAriaHook?: string;
}
@ -14,27 +17,40 @@ const ButtonLink = ({
children,
href,
startContent,
tooltip,
...props
}: ButtonProps & {
href: string;
}) => (
<Button
isExternal
as={Link}
className="!text-small py-4 bg-default-100 dark:bg-default-50 text-default-700"
href={href}
size="sm"
startContent={startContent}
{...props}
>
{children}
</Button>
);
tooltip?: string | ReactNode;
}) => {
const button = (
<Button
isExternal
as={Link}
className="!text-small py-4 bg-default-100 dark:bg-default-50 text-default-700"
href={href}
size="sm"
startContent={startContent}
{...props}
>
{children}
</Button>
);
return tooltip ? (
<Tooltip className="max-w-[230px]" content={tooltip}>
{button}
</Tooltip>
) : (
button
);
};
export const ComponentLinks = ({
component,
storybook,
styles,
rscCompatible,
reactAriaHook,
}: ComponentLinksProps) => {
if (!component) {
@ -63,6 +79,26 @@ export const ComponentLinks = ({
React Aria
</ButtonLink>
)}
{rscCompatible && (
<ButtonLink
href="https://nextjs.org/docs/getting-started/react-essentials#server-components"
startContent={<NextJsIcon size={18} />}
tooltip={
<p>
<Balancer>
This component doesn&apos;t use the
<Code className="font-normal bg-transparent px-0 py-0 text-code-mdx">
`use client;`
</Code>
directive making it compatible with RSC.
</Balancer>
</p>
}
>
Server component
</ButtonLink>
)}
<ButtonLink href={`${COMPONENT_PATH}/${component}`} startContent={<GithubIcon size={20} />}>
Source
</ButtonLink>

View File

@ -30,8 +30,7 @@ export const nextuiTheme: SandpackTheme = {
},
font: {
body: "Inter var",
mono:
'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
mono: 'ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace',
size: "14px",
lineHeight: "1.5rem",
},

View File

@ -9,7 +9,7 @@ import {badgeContent} from "@/content/components/badge";
Badges are used as a small numerical value or status descriptor for UI elements.
<ComponentLinks component="badge" />
<ComponentLinks component="badge" rscCompatible />
---

View File

@ -9,7 +9,7 @@ import {codeContent} from "@/content/components/code";
Code is a component used to display inline code.
<ComponentLinks component="code" />
<ComponentLinks component="code" rscCompatible />
---

View File

@ -9,7 +9,7 @@ import {dividerContent} from "@/content/components/divider";
Divider is a component that separates content in a page.
<ComponentLinks component="divider" reactAriaHook="useSeparator" />
<ComponentLinks component="divider" reactAriaHook="useSeparator" rscCompatible />
---

View File

@ -9,7 +9,7 @@ import {kbdContent} from "@/content/components/kbd";
Keyboard key is a component to display which key or combination of keys performs a given action.
<ComponentLinks component="kbd" />
<ComponentLinks component="kbd" rscCompatible />
---

View File

@ -9,7 +9,7 @@ import {skeletonContent} from "@/content/components/skeleton";
Skeleton is a placeholder to show a loading state and the expected shape of a component.
<ComponentLinks component="skeleton" />
<ComponentLinks component="skeleton" rscCompatible />
---

View File

@ -9,7 +9,7 @@ import {spacerContent} from "@/content/components/spacer";
Spacer is a component used to add space between components.
<ComponentLinks component="spacer" />
<ComponentLinks component="spacer" rscCompatible />
---

View File

@ -9,7 +9,7 @@ import {spinnerContent} from "@/content/components/spinner";
Spinner express an unspecified wait time or display the length of a process.
<ComponentLinks component="spinner" />
<ComponentLinks component="spinner" rscCompatible />
---

View File

@ -22,6 +22,13 @@
"@nextui-org/shared-icons": "workspace:*",
"@nextui-org/shared-utils": "workspace:*",
"@nextui-org/theme": "workspace:*",
"@nextui-org/spacer": "workspace:*",
"@nextui-org/kbd": "workspace:*",
"@nextui-org/code": "workspace:*",
"@nextui-org/badge": "workspace:*",
"@nextui-org/skeleton": "workspace:*",
"@nextui-org/spinner": "workspace:*",
"@nextui-org/divider": "workspace:*",
"@nextui-org/use-clipboard": "workspace:*",
"@nextui-org/use-infinite-scroll": "workspace:*",
"@nextui-org/use-is-mobile": "workspace:*",

View File

@ -67,6 +67,7 @@ export function useAccordionItem<T extends object = {}>(props: UseAccordionItemP
} = props;
const Component = as || "div";
const shouldFilterDOMProps = typeof Component === "string";
const domRef = useDOMRef<HTMLButtonElement>(ref);
@ -132,10 +133,15 @@ export function useAccordionItem<T extends object = {}>(props: UseAccordionItemP
"data-open": dataAttr(isOpen),
"data-disabled": dataAttr(isDisabled),
className: slots.base({class: baseStyles}),
...mergeProps(filterDOMProps(otherProps), props),
...mergeProps(
filterDOMProps(otherProps, {
enabled: shouldFilterDOMProps,
}),
props,
),
};
},
[baseStyles, otherProps, slots, item.props, isOpen, isDisabled],
[baseStyles, shouldFilterDOMProps, otherProps, slots, item.props, isOpen, isDisabled],
);
const getButtonProps: PropGetter = (props = {}) => {

View File

@ -1,6 +1,6 @@
import type {ReactNode} from "react";
import {forwardRef} from "@nextui-org/system";
import {forwardRef} from "@nextui-org/system/utils";
import {UseBadgeProps, useBadge} from "./use-badge";
@ -11,13 +11,14 @@ export interface BadgeProps extends UseBadgeProps {
const Badge = forwardRef<"span", BadgeProps>((props, ref) => {
const {Component, children, content, slots, classNames, getBadgeProps} = useBadge({
...props,
ref,
});
return (
<div className={slots.base({class: classNames?.base})}>
{children}
<Component {...getBadgeProps()}>{content}</Component>
<Component ref={ref} {...getBadgeProps()}>
{content}
</Component>
</div>
);
});

View File

@ -2,8 +2,8 @@ import type {BadgeSlots, BadgeVariantProps, SlotsToClasses} from "@nextui-org/th
import type {ReactNode} from "react";
import {badge} from "@nextui-org/theme";
import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system";
import {useDOMRef} from "@nextui-org/react-utils";
import {HTMLNextUIProps, PropGetter} from "@nextui-org/system/types";
import {mapPropsVariants} from "@nextui-org/system/utils";
import {clsx} from "@nextui-org/shared-utils";
import {ReactRef} from "@nextui-org/react-utils";
import {useMemo} from "react";
@ -41,12 +41,10 @@ export type UseBadgeProps = Props & BadgeVariantProps;
export function useBadge(originalProps: UseBadgeProps) {
const [props, variantProps] = mapPropsVariants(originalProps, badge.variantKeys);
const {as, ref, children, className, content, classNames, ...otherProps} = props;
const {as, children, className, content, classNames, ...otherProps} = props;
const Component = as || "span";
const domRef = useDOMRef(ref);
const isOneChar = useMemo(
() => String(content)?.length === 1 || originalProps?.isOneChar,
[content, originalProps?.isOneChar],
@ -68,7 +66,6 @@ export function useBadge(originalProps: UseBadgeProps) {
const getBadgeProps: PropGetter = () => {
return {
ref: domRef,
className: slots.badge({class: baseStyles}),
"data-invisible": originalProps.isInvisible,
...otherProps,

View File

@ -4,5 +4,4 @@ export default defineConfig({
clean: true,
target: "es2019",
format: ["cjs", "esm"],
banner: {js: '"use client";'},
});

View File

@ -92,6 +92,7 @@ export function useButton(props: UseButtonProps) {
} = props;
const Component = as || "button";
const shouldFilterDOMProps = typeof Component === "string";
const domRef = useDOMRef(ref);
@ -164,7 +165,9 @@ export function useButton(props: UseButtonProps) {
ariaButtonProps,
focusProps,
hoverProps,
filterDOMProps(otherProps),
filterDOMProps(otherProps, {
enabled: shouldFilterDOMProps,
}),
filterDOMProps(props),
),
}),
@ -173,6 +176,7 @@ export function useButton(props: UseButtonProps) {
isDisabled,
isFocused,
isPressed,
shouldFilterDOMProps,
isFocusVisible,
isHovered,
ariaButtonProps,

View File

@ -81,6 +81,7 @@ export function useCard(originalProps: UseCardProps) {
const domRef = useDOMRef<HTMLDivElement>(ref);
const Component = as || (originalProps.isPressable ? "button" : "div");
const shouldFilterDOMProps = typeof Component === "string";
const baseStyles = clsx(classNames?.base, className);
@ -154,7 +155,9 @@ export function useCard(originalProps: UseCardProps) {
...mergeProps(
originalProps.isPressable ? {...buttonProps, ...focusProps, role: "button"} : {},
originalProps.isHoverable ? hoverProps : {},
filterDOMProps(otherProps),
filterDOMProps(otherProps, {
enabled: shouldFilterDOMProps,
}),
filterDOMProps(props),
),
};
@ -163,6 +166,7 @@ export function useCard(originalProps: UseCardProps) {
domRef,
slots,
baseStyles,
shouldFilterDOMProps,
originalProps.isPressable,
originalProps.isHoverable,
originalProps.isDisabled,

View File

@ -1,13 +1,17 @@
import {forwardRef} from "@nextui-org/system";
import {forwardRef} from "@nextui-org/system/utils";
import {useCode, UseCodeProps} from "./use-code";
export interface CodeProps extends UseCodeProps {}
const Code = forwardRef<"div", CodeProps>((props, ref) => {
const {Component, children, getCodeProps} = useCode({...props, ref});
const {Component, children, getCodeProps} = useCode({...props});
return <Component {...getCodeProps()}>{children}</Component>;
return (
<Component ref={ref} {...getCodeProps()}>
{children}
</Component>
);
});
Code.displayName = "NextUI.Code";

View File

@ -1,8 +1,8 @@
import type {CodeVariantProps} from "@nextui-org/theme";
import {code} from "@nextui-org/theme";
import {HTMLNextUIProps, PropGetter, mapPropsVariants} from "@nextui-org/system";
import {useDOMRef} from "@nextui-org/react-utils";
import {HTMLNextUIProps, PropGetter} from "@nextui-org/system/types";
import {mapPropsVariants} from "@nextui-org/system/utils";
import {ReactRef} from "@nextui-org/react-utils";
import {useMemo} from "react";
@ -16,12 +16,10 @@ export interface UseCodeProps extends HTMLNextUIProps<"code">, CodeVariantProps
export function useCode(originalProps: UseCodeProps) {
const [props, variantProps] = mapPropsVariants(originalProps, code.variantKeys);
const {ref, as, children, className, ...otherProps} = props;
const {as, children, className, ...otherProps} = props;
const Component = as || "code";
const domRef = useDOMRef(ref);
const classNames = useMemo(
() =>
code({
@ -33,7 +31,6 @@ export function useCode(originalProps: UseCodeProps) {
const getCodeProps: PropGetter = () => {
return {
ref: domRef,
className: classNames,
...otherProps,
};

View File

@ -4,5 +4,4 @@ export default defineConfig({
clean: true,
target: "es2019",
format: ["cjs", "esm"],
banner: {js: '"use client";'},
});

View File

@ -38,11 +38,10 @@
},
"dependencies": {
"@nextui-org/shared-utils": "workspace:*",
"@nextui-org/react-utils": "workspace:*",
"@nextui-org/react-rsc-utils": "workspace:*",
"@nextui-org/system": "workspace:*",
"@nextui-org/theme": "workspace:*",
"@react-aria/separator": "^3.3.3",
"@react-aria/utils": "^3.18.0"
"@react-types/shared": "^3.18.1"
},
"devDependencies": {
"clean-package": "2.2.0",

View File

@ -1,13 +1,13 @@
import {forwardRef} from "@nextui-org/system";
import {forwardRef} from "@nextui-org/system/utils";
import {UseDividerProps, useDivider} from "./use-divider";
export interface DividerProps extends Omit<UseDividerProps, "children"> {}
const Divider = forwardRef<"div", DividerProps>((props, ref) => {
const {Component, getDividerProps} = useDivider({ref, ...props});
const {Component, getDividerProps} = useDivider({...props});
return <Component {...getDividerProps()} />;
return <Component ref={ref} {...getDividerProps()} />;
});
Divider.displayName = "NextUI.Divider";

View File

@ -1,11 +1,10 @@
import type {DividerVariantProps} from "@nextui-org/theme";
import {SeparatorProps as AriaSeparatorProps, useSeparator} from "@react-aria/separator";
import {HTMLNextUIProps, PropGetter} from "@nextui-org/system";
import {HTMLNextUIProps, PropGetter} from "@nextui-org/system/types";
import {divider} from "@nextui-org/theme";
import {useDOMRef} from "@nextui-org/react-utils";
import {Ref, useCallback, useMemo} from "react";
import {mergeProps} from "@react-aria/utils";
import {SeparatorProps as AriaSeparatorProps, useSeparator} from "./use-separator";
interface Props extends HTMLNextUIProps<"hr"> {
/**
@ -17,12 +16,10 @@ interface Props extends HTMLNextUIProps<"hr"> {
export type UseDividerProps = Props & DividerVariantProps & Omit<AriaSeparatorProps, "elementType">;
export function useDivider(props: UseDividerProps) {
const {ref, as, className, orientation, ...otherProps} = props;
const {as, className, orientation, ...otherProps} = props;
let Component = as || "hr";
const domRef = useDOMRef(ref);
if (Component === "hr" && orientation === "vertical") {
Component = "div";
}
@ -43,13 +40,14 @@ export function useDivider(props: UseDividerProps) {
const getDividerProps: PropGetter = useCallback(
(props = {}) => ({
ref: domRef,
className: styles,
role: "separator",
"data-orientation": orientation,
...mergeProps(separatorProps, otherProps, props),
...separatorProps,
...otherProps,
...props,
}),
[domRef, styles, orientation, separatorProps, otherProps],
[styles, orientation, separatorProps, otherProps],
);
return {Component, getDividerProps};

View File

@ -0,0 +1,53 @@
/*
* Based on @react-aria/separator but with some changes to support RSC.
*/
import type {AriaLabelingProps, DOMAttributes, DOMProps, Orientation} from "@react-types/shared";
import {filterDOMProps} from "@nextui-org/react-rsc-utils";
export interface SeparatorProps extends DOMProps, AriaLabelingProps {
/**
* The orientation of the separator.
* @default 'horizontal'
*/
orientation?: Orientation;
/** The HTML element type that will be used to render the separator. */
elementType?: string;
}
export interface SeparatorAria {
/** Props for the separator element. */
separatorProps: DOMAttributes;
}
/**
* Provides the accessibility implementation for a separator.
* A separator is a visual divider between two groups of content,
* e.g. groups of menu items or sections of a page.
*/
export function useSeparator(props: SeparatorProps): SeparatorAria {
let domProps = filterDOMProps(props, {
enabled: typeof props.elementType === "string",
});
let ariaOrientation: Orientation | undefined;
// if orientation is horizontal, aria-orientation default is horizontal, so we leave it undefined
// if it's vertical, we need to specify it
if (props.orientation === "vertical") {
ariaOrientation = "vertical";
}
// hr elements implicitly have role = separator and a horizontal orientation
if (props.elementType !== "hr") {
return {
separatorProps: {
...domProps,
role: "separator",
"aria-orientation": ariaOrientation,
},
};
}
return {separatorProps: domProps};
}

View File

@ -4,5 +4,4 @@ export default defineConfig({
clean: true,
target: "es2019",
format: ["cjs", "esm"],
banner: {js: '"use client";'},
});

View File

@ -59,6 +59,7 @@ export function useDropdownItem<T extends object>(originalProps: UseDropdownItem
const domRef = useRef<HTMLLIElement>(null);
const Component = as || "li";
const shouldFilterDOMProps = typeof Component === "string";
const {rendered, key} = item;
@ -127,7 +128,9 @@ export function useDropdownItem<T extends object>(originalProps: UseDropdownItem
itemProps,
isReadOnly ? {} : mergeProps(focusProps, pressProps),
hoverProps,
filterDOMProps(otherProps),
filterDOMProps(otherProps, {
enabled: shouldFilterDOMProps,
}),
props,
),
"data-focus": dataAttr(isFocused),

View File

@ -93,6 +93,7 @@ export function useInput<T extends HTMLInputElement | HTMLTextAreaElement = HTML
);
const Component = as || "div";
const baseStyles = clsx(classNames?.base, className, !!inputValue ? "is-filled" : "");
const isMultiline = originalProps.isMultiline;

View File

@ -1,5 +1,5 @@
import {useMemo} from "react";
import {forwardRef} from "@nextui-org/system";
import {forwardRef} from "@nextui-org/system/utils";
import {UseKbdProps, useKbd} from "./use-kbd";
import {kbdKeysLabelMap, kbdKeysMap} from "./utils";
@ -9,7 +9,6 @@ export interface KbdProps extends UseKbdProps {}
const Kbd = forwardRef<"kbd", KbdProps>((props, ref) => {
const {Component, children, slots, classNames, keysToRender, getKbdProps} = useKbd({
...props,
ref,
});
const keysContent = useMemo(() => {
@ -25,7 +24,7 @@ const Kbd = forwardRef<"kbd", KbdProps>((props, ref) => {
}, [keysToRender]);
return (
<Component {...getKbdProps()}>
<Component ref={ref} {...getKbdProps()}>
{keysContent}
{children && <span className={slots.content({class: classNames?.content})}>{children}</span>}
</Component>

View File

@ -1,12 +1,11 @@
import type {KbdVariantProps, KbdSlots, SlotsToClasses} from "@nextui-org/theme";
import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system";
import {HTMLNextUIProps, PropGetter} from "@nextui-org/system/types";
import {mapPropsVariants} from "@nextui-org/system/utils";
import {kbd} from "@nextui-org/theme";
import {useDOMRef} from "@nextui-org/react-utils";
import {clsx} from "@nextui-org/shared-utils";
import {ReactRef} from "@nextui-org/react-utils";
import {useMemo} from "react";
import {mergeProps} from "@react-aria/utils";
import {KbdKey} from "./utils";
@ -40,12 +39,10 @@ export type UseKbdProps = Props & KbdVariantProps;
export function useKbd(originalProps: UseKbdProps) {
const [props, variantProps] = mapPropsVariants(originalProps, kbd.variantKeys);
const {ref, as, children, className, keys, title, classNames, ...otherProps} = props;
const {as, children, className, keys, title, classNames, ...otherProps} = props;
const Component = as || "kbd";
const domRef = useDOMRef(ref);
const slots = useMemo(
() =>
kbd({
@ -59,8 +56,8 @@ export function useKbd(originalProps: UseKbdProps) {
const keysToRender = typeof keys === "string" ? [keys] : Array.isArray(keys) ? keys : [];
const getKbdProps: PropGetter = (props = {}) => ({
ref: domRef,
...mergeProps(otherProps, props),
...otherProps,
...props,
className: clsx(slots.base({class: clsx(baseStyles, props.className)})),
});

View File

@ -4,5 +4,4 @@ export default defineConfig({
clean: true,
target: "es2019",
format: ["cjs", "esm"],
banner: {js: '"use client";'},
});

View File

@ -1,14 +1,14 @@
import {forwardRef} from "@nextui-org/system";
import {forwardRef} from "@nextui-org/system/utils";
import {UseSkeletonProps, useSkeleton} from "./use-skeleton";
export interface SkeletonProps extends UseSkeletonProps {}
const Skeleton = forwardRef<"div", SkeletonProps>((props, ref) => {
const {Component, children, getSkeletonProps, getContentProps} = useSkeleton({...props, ref});
const {Component, children, getSkeletonProps, getContentProps} = useSkeleton({...props});
return (
<Component {...getSkeletonProps()}>
<Component ref={ref} {...getSkeletonProps()}>
<div {...getContentProps()}>{children}</div>
</Component>
);

View File

@ -1,17 +1,16 @@
import type {SkeletonVariantProps, SkeletonSlots, SlotsToClasses} from "@nextui-org/theme";
import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system/types";
import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system";
import {mapPropsVariants} from "@nextui-org/system/utils";
import {skeleton} from "@nextui-org/theme";
import {useDOMRef} from "@nextui-org/react-utils";
import {clsx, dataAttr} from "@nextui-org/shared-utils";
import {ReactRef} from "@nextui-org/react-utils";
import {useMemo} from "react";
import {useMemo, Ref} from "react";
interface Props extends HTMLNextUIProps<"div"> {
/**
* Ref to the DOM node.
*/
ref?: ReactRef<HTMLElement | null>;
ref?: Ref<HTMLElement | null>;
/**
* The skeleton will be visible while isLoading is `false`.
* @default false
@ -37,12 +36,10 @@ export type UseSkeletonProps = Props & SkeletonVariantProps;
export function useSkeleton(originalProps: UseSkeletonProps) {
const [props, variantProps] = mapPropsVariants(originalProps, skeleton.variantKeys);
const {ref, as, children, isLoaded = false, className, classNames, ...otherProps} = props;
const {as, children, isLoaded = false, className, classNames, ...otherProps} = props;
const Component = as || "div";
const domRef = useDOMRef(ref);
const slots = useMemo(
() =>
skeleton({
@ -55,7 +52,6 @@ export function useSkeleton(originalProps: UseSkeletonProps) {
const getSkeletonProps: PropGetter = (props = {}) => {
return {
ref: domRef,
"data-loaded": dataAttr(isLoaded),
className: slots.base({class: clsx(baseStyles, props?.className)}),
...otherProps,
@ -68,7 +64,7 @@ export function useSkeleton(originalProps: UseSkeletonProps) {
};
};
return {Component, domRef, children, slots, classNames, getSkeletonProps, getContentProps};
return {Component, children, slots, classNames, getSkeletonProps, getContentProps};
}
export type UseSkeletonReturn = ReturnType<typeof useSkeleton>;

View File

@ -4,5 +4,4 @@ export default defineConfig({
clean: true,
target: "es2019",
format: ["cjs", "esm"],
banner: {js: '"use client";'},
});

View File

@ -1,13 +1,13 @@
import {forwardRef} from "@nextui-org/system";
import {forwardRef} from "@nextui-org/system/utils";
import {UseSpacerProps, useSpacer} from "./use-spacer";
export interface SpacerProps extends UseSpacerProps {}
const Spacer = forwardRef<"span", SpacerProps>((props, ref) => {
const {Component, getSpacerProps} = useSpacer({...props, ref});
const {Component, getSpacerProps} = useSpacer({...props});
return <Component {...getSpacerProps()} />;
return <Component ref={ref} {...getSpacerProps()} />;
});
Spacer.displayName = "NextUI.Spacer";

View File

@ -1,8 +1,8 @@
import type {SpacerVariantProps} from "@nextui-org/theme";
import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system";
import {HTMLNextUIProps, PropGetter} from "@nextui-org/system/types";
import {mapPropsVariants} from "@nextui-org/system/utils";
import {spacer} from "@nextui-org/theme";
import {useDOMRef} from "@nextui-org/react-utils";
import {clsx, dataAttr} from "@nextui-org/shared-utils";
import {ReactRef} from "@nextui-org/react-utils";
import {useMemo} from "react";
@ -39,12 +39,10 @@ export const getMargin = (value: Space) => {
export function useSpacer(originalProps: UseSpacerProps) {
const [props, variantProps] = mapPropsVariants(originalProps, spacer.variantKeys);
const {ref, as, className, x = 1, y = 1, ...otherProps} = props;
const {as, className, x = 1, y = 1, ...otherProps} = props;
const Component = as || "span";
const domRef = useDOMRef(ref);
const styles = useMemo(
() =>
spacer({
@ -58,7 +56,6 @@ export function useSpacer(originalProps: UseSpacerProps) {
const marginTop = getMargin(y);
const getSpacerProps: PropGetter = (props = {}) => ({
ref: domRef,
...props,
...otherProps,
"aria-hidden": dataAttr(true),

View File

@ -4,5 +4,4 @@ export default defineConfig({
clean: true,
target: "es2019",
format: ["cjs", "esm"],
banner: {js: '"use client";'},
});

View File

@ -1,14 +1,14 @@
import {forwardRef} from "@nextui-org/system";
import {forwardRef} from "@nextui-org/system/utils";
import {UseSpinnerProps, useSpinner} from "./use-spinner";
export interface SpinnerProps extends UseSpinnerProps {}
const Spinner = forwardRef<"div", SpinnerProps>((props, ref) => {
const {domRef, slots, classNames, label, getSpinnerProps} = useSpinner({ref, ...props});
const {slots, classNames, label, getSpinnerProps} = useSpinner({...props});
return (
<div ref={domRef} {...getSpinnerProps()}>
<div ref={ref} {...getSpinnerProps()}>
<div className={slots.wrapper({class: classNames?.wrapper})}>
<i className={slots.circle1({class: classNames?.circle1})} />
<i className={slots.circle2({class: classNames?.circle2})} />

View File

@ -1,17 +1,16 @@
import type {SpinnerVariantProps, SpinnerSlots, SlotsToClasses} from "@nextui-org/theme";
import type {HTMLNextUIProps, PropGetter} from "@nextui-org/system/types";
import {HTMLNextUIProps, mapPropsVariants, PropGetter} from "@nextui-org/system";
import {mapPropsVariants} from "@nextui-org/system/utils";
import {spinner} from "@nextui-org/theme";
import {clsx} from "@nextui-org/shared-utils";
import {ReactRef} from "@nextui-org/react-utils";
import {useDOMRef} from "@nextui-org/react-utils";
import {useMemo, useCallback} from "react";
import {useMemo, useCallback, Ref} from "react";
interface Props extends HTMLNextUIProps<"div"> {
/**
* Ref to the DOM node.
*/
ref?: ReactRef<HTMLElement | null>;
ref?: Ref<HTMLElement | null>;
/**
* Spinner label, in case you passed it will be used as `aria-label`.
*/
@ -39,9 +38,7 @@ export type UseSpinnerProps = Props & SpinnerVariantProps;
export function useSpinner(originalProps: UseSpinnerProps) {
const [props, variantProps] = mapPropsVariants(originalProps, spinner.variantKeys);
const {ref, children, className, classNames, label: labelProp, ...otherProps} = props;
const domRef = useDOMRef(ref);
const {children, className, classNames, label: labelProp, ...otherProps} = props;
const slots = useMemo(() => spinner({...variantProps}), [...Object.values(variantProps)]);
@ -68,7 +65,7 @@ export function useSpinner(originalProps: UseSpinnerProps) {
[ariaLabel, slots, baseStyles, otherProps],
);
return {domRef, label, slots, classNames, getSpinnerProps};
return {label, slots, classNames, getSpinnerProps};
}
export type UseSpinnerReturn = ReturnType<typeof useSpinner>;

View File

@ -4,5 +4,4 @@ export default defineConfig({
clean: true,
target: "es2019",
format: ["cjs", "esm"],
banner: {js: '"use client";'},
});

View File

@ -41,6 +41,8 @@ const TableBody = forwardRef<"tbody", TableBodyProps>((props, ref) => {
} = props;
const Component = as || "tbody";
const shouldFilterDOMProps = typeof Component === "string";
const domRef = useDOMRef(ref);
const {rowGroupProps} = useTableRowGroup();
@ -126,7 +128,13 @@ const TableBody = forwardRef<"tbody", TableBodyProps>((props, ref) => {
return (
<Component
ref={domRef}
{...mergeProps(rowGroupProps, filterDOMProps(bodyProps), otherProps)}
{...mergeProps(
rowGroupProps,
filterDOMProps(bodyProps, {
enabled: shouldFilterDOMProps,
}),
otherProps,
)}
className={slots.tbody?.({class: tbodyStyles})}
data-empty={dataAttr(collection.size === 0)}
data-loading={dataAttr(isLoading)}

View File

@ -29,6 +29,8 @@ const TableCell = forwardRef<"td", TableCellProps>((props, ref) => {
const {as, className, node, rowKey, slots, state, classNames, ...otherProps} = props;
const Component = as || "td";
const shouldFilterDOMProps = typeof Component === "string";
const domRef = useDOMRef(ref);
const {gridCellProps} = useTableCell({node}, state, domRef);
@ -52,7 +54,14 @@ const TableCell = forwardRef<"td", TableCellProps>((props, ref) => {
ref={domRef}
data-focus-visible={dataAttr(isFocusVisible)}
data-selected={dataAttr(isRowSelected)}
{...mergeProps(gridCellProps, focusProps, filterDOMProps(node.props), otherProps)}
{...mergeProps(
gridCellProps,
focusProps,
filterDOMProps(node.props, {
enabled: shouldFilterDOMProps,
}),
otherProps,
)}
className={slots.td?.({class: tdStyles})}
>
{cell}

View File

@ -48,6 +48,8 @@ const TableCheckboxCell = forwardRef<"td", TableCheckboxCellProps>((props, ref)
} = props;
const Component = as || "td";
const shouldFilterDOMProps = typeof Component === "string";
const domRef = useDOMRef(ref);
const {gridCellProps} = useTableCell({node}, state, domRef);
@ -67,7 +69,14 @@ const TableCheckboxCell = forwardRef<"td", TableCheckboxCellProps>((props, ref)
ref={domRef}
data-focus-visible={dataAttr(isFocusVisible)}
data-selected={dataAttr(isRowSelected)}
{...mergeProps(gridCellProps, focusProps, filterDOMProps(node.props), otherProps)}
{...mergeProps(
gridCellProps,
focusProps,
filterDOMProps(node.props, {
enabled: shouldFilterDOMProps,
}),
otherProps,
)}
className={slots.td?.({class: tdStyles})}
>
{isSingleSelectionMode ? (

View File

@ -27,6 +27,8 @@ const TableColumnHeader = forwardRef<"th", TableColumnHeaderProps>((props, ref)
const {as, className, state, node, slots, classNames, ...otherProps} = props;
const Component = as || "th";
const shouldFilterDOMProps = typeof Component === "string";
const domRef = useDOMRef(ref);
const {columnHeaderProps} = useTableColumnHeader({node}, state, domRef);
@ -49,7 +51,9 @@ const TableColumnHeader = forwardRef<"th", TableColumnHeaderProps>((props, ref)
{...mergeProps(
columnHeaderProps,
focusProps,
filterDOMProps(columnProps),
filterDOMProps(columnProps, {
enabled: shouldFilterDOMProps,
}),
allowsSorting ? hoverProps : {},
otherProps,
)}

View File

@ -23,6 +23,7 @@ const TableHeaderRow = forwardRef<"tr", TableHeaderRowProps>((props, ref) => {
const {as, className, children, node, slots, classNames, state, ...otherProps} = props;
const Component = as || "tr";
const shouldFilterDOMProps = typeof Component === "string";
const domRef = useDOMRef(ref);
const {rowProps} = useTableHeaderRow({node}, state, domRef);
@ -32,7 +33,13 @@ const TableHeaderRow = forwardRef<"tr", TableHeaderRowProps>((props, ref) => {
return (
<Component
ref={domRef}
{...mergeProps(rowProps, filterDOMProps(node.props), otherProps)}
{...mergeProps(
rowProps,
filterDOMProps(node.props, {
enabled: shouldFilterDOMProps,
}),
otherProps,
)}
className={slots.tr?.({class: trStyles})}
>
{children}

View File

@ -28,6 +28,7 @@ const TableRow = forwardRef<"tr", TableRowProps>((props, ref) => {
props;
const Component = as || "tr";
const shouldFilterDOMProps = typeof Component === "string";
const domRef = useDOMRef(ref);
@ -70,7 +71,9 @@ const TableRow = forwardRef<"tr", TableRowProps>((props, ref) => {
{...mergeProps(
rowProps,
isSelectable ? {...hoverProps, ...focusProps} : {},
filterDOMProps(node.props),
filterDOMProps(node.props, {
enabled: shouldFilterDOMProps,
}),
otherProps,
)}
className={slots.tr?.({class: trStyles})}

View File

@ -41,6 +41,8 @@ const TableSelectAllCheckbox = forwardRef<"th", TableSelectAllCheckboxProps>((pr
} = props;
const Component = as || "th";
const shouldFilterDOMProps = typeof Component === "string";
const domRef = useDOMRef(ref);
const {columnHeaderProps} = useTableColumnHeader({node}, state, domRef);
@ -61,8 +63,12 @@ const TableSelectAllCheckbox = forwardRef<"th", TableSelectAllCheckboxProps>((pr
{...mergeProps(
columnHeaderProps,
focusProps,
filterDOMProps(node.props),
filterDOMProps(otherProps),
filterDOMProps(node.props, {
enabled: shouldFilterDOMProps,
}),
filterDOMProps(otherProps, {
enabled: shouldFilterDOMProps,
}),
)}
className={slots.th?.({class: thStyles})}
>

View File

@ -162,6 +162,7 @@ export function useTable<T extends object>(originalProps: UseTableProps<T>) {
} = props;
const Component = as || "table";
const shouldFilterDOMProps = typeof Component === "string";
const domRef = useDOMRef(ref);
const domBaseRef = useDOMRef(baseRef);
@ -248,11 +249,17 @@ export function useTable<T extends object>(originalProps: UseTableProps<T>) {
const getTableProps: PropGetter = useCallback(
(props) => ({
...mergeProps(gridProps, filterDOMProps(otherProps), props),
...mergeProps(
gridProps,
filterDOMProps(otherProps, {
enabled: shouldFilterDOMProps,
}),
props,
),
ref: domRef,
className: slots.table({class: clsx(classNames?.table, props?.className)}),
}),
[classNames?.table, slots, gridProps, otherProps],
[classNames?.table, shouldFilterDOMProps, slots, gridProps, otherProps],
);
return {

View File

@ -49,6 +49,7 @@ const Tab = forwardRef<"button", TabItemProps>((props, ref) => {
const domRef = useDOMRef(ref);
const Component = as || "button";
const shouldFilterDOMProps = typeof Component === "string";
const {
tabProps,
@ -103,7 +104,9 @@ const Tab = forwardRef<"button", TabItemProps>((props, ref) => {
...hoverProps,
}
: {},
filterDOMProps(otherProps),
filterDOMProps(otherProps, {
enabled: shouldFilterDOMProps,
}),
)}
className={slots.tab?.({class: tabStyles})}
type={Component === "button" ? "button" : undefined}

View File

@ -76,6 +76,7 @@ export function useTabs<T extends object>(originalProps: UseTabsProps<T>) {
} = props;
const Component = as || "div";
const shouldFilterDOMProps = typeof Component === "string";
const domRef = useDOMRef(ref);
@ -123,7 +124,12 @@ export function useTabs<T extends object>(originalProps: UseTabsProps<T>) {
(props) => ({
"data-slot": "base",
className: slots.base({class: clsx(baseStyles, props?.className)}),
...mergeProps(filterDOMProps(otherProps), props),
...mergeProps(
filterDOMProps(otherProps, {
enabled: shouldFilterDOMProps,
}),
props,
),
}),
[baseStyles, otherProps, slots],
);

View File

@ -0,0 +1 @@
export * from "./dist/extend-variants";

View File

@ -0,0 +1 @@
module.exports = require("./dist/extend-variants");

View File

@ -11,7 +11,13 @@
"main": "src/index.ts",
"sideEffects": false,
"files": [
"dist"
"dist",
"utils.js",
"utils.d.ts",
"types.js",
"types.d.ts",
"extend-variants.js",
"extend-variants.d.ts"
],
"publishConfig": {
"access": "public"

1
packages/core/system/types.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export * from "./dist/types";

View File

@ -0,0 +1 @@
module.exports = require("./dist/types");

1
packages/core/system/utils.d.ts vendored Normal file
View File

@ -0,0 +1 @@
export * from "./dist/utils";

View File

@ -0,0 +1 @@
module.exports = require("./dist/utils");

View File

@ -0,0 +1,24 @@
# @nextui-org/react-rsc-utils
A Quick description of the component
> This is an internal utility, not intended for public usage.
## Installation
```sh
yarn add @nextui-org/react-rsc-utils
# or
npm i @nextui-org/react-rsc-utils
```
## Contribution
Yes please! See the
[contributing guidelines](https://github.com/nextui-org/nextui/blob/master/CONTRIBUTING.md)
for details.
## Licence
This project is licensed under the terms of the
[MIT license](https://github.com/nextui-org/nextui/blob/master/LICENSE).

View File

@ -0,0 +1 @@
export * from "./dist/children";

View File

@ -0,0 +1 @@
module.exports = require("./dist/children");

View File

@ -0,0 +1 @@
export * from "./dist/filter-dom-props";

View File

@ -0,0 +1 @@
module.exports = require("./dist/filter-dom-props");

View File

@ -0,0 +1,52 @@
{
"name": "@nextui-org/react-rsc-utils",
"version": "2.0.1",
"description": "A set of utilities for react compatible with RSC",
"keywords": [
"react-rsc-utils"
],
"author": "Junior Garcia <jrgarciadev@gmail.com>",
"homepage": "https://nextui.org",
"license": "MIT",
"main": "src/index.ts",
"sideEffects": false,
"files": [
"dist",
"children.d.ts",
"children.js",
"filter-dom-props.d.ts",
"filter-dom-props.js"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/nextui-org/nextui.git",
"directory": "packages/utilities/react-rsc-utils"
},
"bugs": {
"url": "https://github.com/nextui-org/nextui/issues"
},
"scripts": {
"build": "tsup src --dts",
"build:fast": "tsup src",
"dev": "yarn build:fast -- --watch",
"clean": "rimraf dist .turbo",
"typecheck": "tsc --noEmit",
"prepack": "clean-package",
"postpack": "clean-package restore"
},
"devDependencies": {
"clean-package": "2.2.0"
},
"clean-package": "../../../clean-package.config.json",
"tsup": {
"clean": true,
"target": "es2019",
"format": [
"cjs",
"esm"
]
}
}

View File

@ -14,6 +14,10 @@ const DOMPropNames = new Set([
]);
interface Options {
/**
* If the filter should be enabled.
*/
enabled?: boolean;
/**
* If labelling associated aria properties should be included in the filter.
*/
@ -37,11 +41,16 @@ export function filterDOMProps(
props: DOMProps & AriaLabelingProps,
opts: Options = {
labelable: true,
enabled: true,
},
): DOMProps & AriaLabelingProps {
let {labelable, propNames} = opts;
let filteredProps = {};
if (!opts.enabled) {
return props;
}
for (const prop in props) {
if (
(Object.prototype.hasOwnProperty.call(props, prop) &&

View File

@ -0,0 +1,2 @@
export * from "./children";
export * from "./filter-dom-props";

View File

@ -0,0 +1,4 @@
{
"extends": "../../../tsconfig.json",
"include": ["src", "index.ts"]
}

View File

@ -38,7 +38,7 @@
},
"dependencies": {
"@nextui-org/shared-utils": "workspace:*",
"@react-aria/utils": "^3.18.0"
"@nextui-org/react-rsc-utils": "workspace:*"
},
"devDependencies": {
"clean-package": "2.2.0",

View File

@ -1,6 +1,6 @@
export * from "./context";
export * from "./children";
export * from "./refs";
export * from "./dom";
export * from "./dimensions";
export * from "./filter-dom-props";
export * from "@nextui-org/react-rsc-utils";

View File

@ -8,6 +8,7 @@ export function mockImage() {
//@ts-expect-error
window.Image = class Image {
onload: VoidFunction = () => {
// eslint-disable-next-line no-console
console.log("called");
};
onerror: VoidFunction = () => {};

57
pnpm-lock.yaml generated
View File

@ -273,6 +273,18 @@ importers:
'@nextui-org/aria-utils':
specifier: workspace:*
version: link:../../packages/utilities/aria-utils
'@nextui-org/badge':
specifier: workspace:*
version: link:../../packages/components/badge
'@nextui-org/code':
specifier: workspace:*
version: link:../../packages/components/code
'@nextui-org/divider':
specifier: workspace:*
version: link:../../packages/components/divider
'@nextui-org/kbd':
specifier: workspace:*
version: link:../../packages/components/kbd
'@nextui-org/react':
specifier: workspace:*
version: link:../../packages/core/react
@ -282,6 +294,15 @@ importers:
'@nextui-org/shared-utils':
specifier: workspace:*
version: link:../../packages/utilities/shared-utils
'@nextui-org/skeleton':
specifier: workspace:*
version: link:../../packages/components/skeleton
'@nextui-org/spacer':
specifier: workspace:*
version: link:../../packages/components/spacer
'@nextui-org/spinner':
specifier: workspace:*
version: link:../../packages/components/spinner
'@nextui-org/theme':
specifier: workspace:*
version: link:../../packages/core/theme
@ -930,9 +951,9 @@ importers:
packages/components/divider:
dependencies:
'@nextui-org/react-utils':
'@nextui-org/react-rsc-utils':
specifier: workspace:*
version: link:../../utilities/react-utils
version: link:../../utilities/react-rsc-utils
'@nextui-org/shared-utils':
specifier: workspace:*
version: link:../../utilities/shared-utils
@ -942,12 +963,9 @@ importers:
'@nextui-org/theme':
specifier: workspace:*
version: link:../../core/theme
'@react-aria/separator':
specifier: ^3.3.3
version: 3.3.3(react@18.2.0)
'@react-aria/utils':
specifier: ^3.18.0
version: 3.18.0(react@18.2.0)
'@react-types/shared':
specifier: ^3.18.1
version: 3.18.1(react@18.2.0)
devDependencies:
clean-package:
specifier: 2.2.0
@ -2539,14 +2557,20 @@ importers:
specifier: ^18.2.0
version: 18.2.0
packages/utilities/react-rsc-utils:
devDependencies:
clean-package:
specifier: 2.2.0
version: 2.2.0
packages/utilities/react-utils:
dependencies:
'@nextui-org/react-rsc-utils':
specifier: workspace:*
version: link:../react-rsc-utils
'@nextui-org/shared-utils':
specifier: workspace:*
version: link:../shared-utils
'@react-aria/utils':
specifier: ^3.18.0
version: 3.18.0(react@18.2.0)
devDependencies:
clean-package:
specifier: 2.2.0
@ -7782,17 +7806,6 @@ packages:
react: 18.2.0
dev: false
/@react-aria/separator@3.3.3(react@18.2.0):
resolution: {integrity: sha512-kBGEXSSUiJLPS9foS5/7jgzpdp3/Yb1aMvVuvRGuNxDUsPAmvaYUT3qZ44Zf3hoxKfRFb4452KcoZ03w3Jfcvg==}
peerDependencies:
react: ^16.8.0 || ^17.0.0-rc.1 || ^18.0.0
dependencies:
'@react-aria/utils': 3.18.0(react@18.2.0)
'@react-types/shared': 3.18.1(react@18.2.0)
'@swc/helpers': 0.5.1
react: 18.2.0
dev: false
/@react-aria/ssr@3.6.0(react@18.2.0):
resolution: {integrity: sha512-OFiYQdv+Yk7AO7IsQu/fAEPijbeTwrrEYvdNoJ3sblBBedD5j5fBTNWrUPNVlwC4XWWnWTCMaRIVsJujsFiWXg==}
peerDependencies: