feat(core): root function added to use NextUI.<element> as a component, snippet structure started, plop templated improved

This commit is contained in:
Junior Garcia 2022-10-05 21:27:27 -03:00
parent 655de55c53
commit e80eb0d938
42 changed files with 970 additions and 10 deletions

View File

@ -0,0 +1,24 @@
# @nextui-org/code
A Quick description of the component
> This is an internal utility, not intended for public usage.
## Installation
```sh
yarn add @nextui-org/code
# or
npm i @nextui-org/code
```
## 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,25 @@
import * as React from "react";
import {render} from "@testing-library/react";
import {Code} from "../src";
describe("Code", () => {
it("should render correctly", () => {
const wrapper = render(<Code />);
expect(() => wrapper.unmount()).not.toThrow();
});
it("ref should be forwarded", () => {
const ref = React.createRef<HTMLDivElement>();
render(<Code ref={ref} />);
expect(ref.current).not.toBeNull();
});
it("should support block mode", () => {
const {container} = render(<Code block />);
expect(container.querySelector("pre")).not.toBeNull();
});
});

View File

@ -0,0 +1,3 @@
{ "replace": { "main": "dist/index.cjs.js", "module": "dist/index.esm.js",
"types": "dist/index.d.ts", "exports": { ".": { "import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js" }, "./package.json": "./package.json" } } }

View File

@ -0,0 +1,48 @@
{
"name": "@nextui-org/code",
"version": "1.0.0-beta.11",
"description": "Code is a component used to display inline code.",
"keywords": [
"code"
],
"author": "Junior Garcia <jrgarciadev@gmail.com>",
"homepage": "https://nextui.org",
"license": "MIT",
"main": "src/index.ts",
"sideEffects": false,
"files": [
"dist"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/nextui-org/nextui.git",
"directory": "packages/components/code"
},
"bugs": {
"url": "https://github.com/nextui-org/nextui/issues"
},
"scripts": {
"build": "tsup src/index.ts --format=esm,cjs --dts",
"dev": "yarn build:fast -- --watch",
"clean": "rimraf dist .turbo",
"typecheck": "tsc --noEmit",
"build:fast": "tsup src/index.ts --format=esm,cjs",
"prepack": "clean-package",
"postpack": "clean-package restore"
},
"peerDependencies": {
"react": ">=16.8.0"
},
"dependencies": {
"@nextui-org/system": "workspace:*",
"@nextui-org/shared-utils": "workspace:*",
"@nextui-org/dom-utils": "workspace:*"
},
"devDependencies": {
"clean-package": "2.1.1",
"react": "^17.0.2"
}
}

View File

@ -0,0 +1,8 @@
import {styled} from "@nextui-org/system";
export const StyledCode = styled("code", {});
export const StyledPre = styled("pre", {
width: "initial",
mw: "100%",
});

View File

@ -0,0 +1,41 @@
import {HTMLNextUIProps, forwardRef} from "@nextui-org/system";
import {useDOMRef} from "@nextui-org/dom-utils";
import {clsx, __DEV__} from "@nextui-org/shared-utils";
import {StyledCode, StyledPre} from "./code.styles";
export interface CodeProps extends HTMLNextUIProps<"code"> {
/**
* Whether to show a <pre/> component as a wrapper.
* @default false
*/
block?: boolean;
}
const Code = forwardRef<CodeProps, "code">((props, ref) => {
const {children, block = false, className, ...otherProps} = props;
const domRef = useDOMRef(ref);
if (!block) {
return (
<StyledCode ref={domRef} className={clsx("nextui-code", className)} {...otherProps}>
{children}
</StyledCode>
);
}
return (
<StyledPre ref={domRef} className={clsx("nextui-code", className)} {...otherProps}>
<StyledCode>{children}</StyledCode>
</StyledPre>
);
});
if (__DEV__) {
Code.displayName = "NextUI.Code";
}
Code.toString = () => ".nextui-code";
export default Code;

View File

@ -0,0 +1,5 @@
// export types
export type {CodeProps} from "./code";
// export component
export {default as Code} from "./code";

View File

@ -0,0 +1,13 @@
import React from "react";
import {Meta} from "@storybook/react";
import {Code} from "../src";
export default {
title: "Display/Code",
component: Code,
} as Meta;
export const Default = () => <Code>npm install @nextui-org/react</Code>;
export const Block = () => <Code block>npm install @nextui-org/react</Code>;

View File

@ -0,0 +1,9 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"paths": {
"@stitches/react": ["../../../node_modules/@stitches/react"]
}
},
"include": ["src", "index.ts"]
}

View File

@ -0,0 +1,13 @@
import {defineConfig} from "tsup";
import {findUpSync} from "find-up";
export default defineConfig({
clean: true,
minify: false,
treeshake: true,
format: ["cjs", "esm"],
outExtension(ctx) {
return {js: `.${ctx.format}.js`};
},
inject: process.env.JSX ? [findUpSync("react-shim.js")!] : undefined,
});

View File

@ -1,12 +1,11 @@
import React from "react";
import {Meta} from "@storybook/react";
import { Grid } from "../src";
import {Grid} from "../src";
export default {
title: "Grid",
title: "Layout/Grid",
component: Grid,
} as Meta;
export const Default = () => <Grid />;

View File

@ -0,0 +1,24 @@
# @nextui-org/snippet
A Quick description of the component
> This is an internal utility, not intended for public usage.
## Installation
```sh
yarn add @nextui-org/snippet
# or
npm i @nextui-org/snippet
```
## 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,19 @@
import * as React from "react";
import {render} from "@testing-library/react";
import { Snippet } from "../src";
describe("Snippet", () => {
it("should render correctly", () => {
const wrapper = render(<Snippet />);
expect(() => wrapper.unmount()).not.toThrow();
});
it("ref should be forwarded", () => {
const ref = React.createRef<HTMLDivElement>();
render(<Snippet ref={ref} />);
expect(ref.current).not.toBeNull();
});
});

View File

@ -0,0 +1,3 @@
{ "replace": { "main": "dist/index.cjs.js", "module": "dist/index.esm.js",
"types": "dist/index.d.ts", "exports": { ".": { "import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js" }, "./package.json": "./package.json" } } }

View File

@ -0,0 +1,49 @@
{
"name": "@nextui-org/snippet",
"version": "1.0.0-beta.11",
"description": "Display a snippet of copyable code for the command line.",
"keywords": [
"snippet"
],
"author": "Junior Garcia <jrgarciadev@gmail.com>",
"homepage": "https://nextui.org",
"license": "MIT",
"main": "src/index.ts",
"sideEffects": false,
"files": [
"dist"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/nextui-org/nextui.git",
"directory": "packages/components/snippet"
},
"bugs": {
"url": "https://github.com/nextui-org/nextui/issues"
},
"scripts": {
"build": "tsup src/index.ts --format=esm,cjs --dts",
"dev": "yarn build:fast -- --watch",
"clean": "rimraf dist .turbo",
"typecheck": "tsc --noEmit",
"build:fast": "tsup src/index.ts --format=esm,cjs",
"prepack": "clean-package",
"postpack": "clean-package restore"
},
"peerDependencies": {
"react": ">=16.8.0"
},
"dependencies": {
"@nextui-org/system": "workspace:*",
"@nextui-org/shared-utils": "workspace:*",
"@nextui-org/shared-css": "workspace:*",
"@nextui-org/dom-utils": "workspace:*"
},
"devDependencies": {
"clean-package": "2.1.1",
"react": "^17.0.2"
}
}

View File

@ -0,0 +1,5 @@
// export types
export type {SnippetProps} from "./snippet";
// export component
export {default as Snippet} from "./snippet";

View File

@ -0,0 +1,23 @@
import {nextui, forwardRef, HTMLNextUIProps} from "@nextui-org/system";
import {useDOMRef} from "@nextui-org/dom-utils";
import {clsx, __DEV__} from "@nextui-org/shared-utils";
export interface SnippetIconProps extends HTMLNextUIProps<"svg"> {}
const SnippetIcon = forwardRef<SnippetIconProps, "div">((props, ref) => {
const {className, ...otherProps} = props;
const domRef = useDOMRef(ref);
return (
<nextui.svg ref={domRef} className={clsx("nextui-snippet-icon", className)} {...otherProps} />
);
});
if (__DEV__) {
SnippetIcon.displayName = "NextUI.SnippetIcon";
}
SnippetIcon.toString = () => ".nextui-SnippetIcon";
export default SnippetIcon;

View File

@ -0,0 +1,137 @@
import {styled} from "@nextui-org/system";
import {cssFocusVisible} from "@nextui-org/shared-css";
export const StyledSnippet = styled("div", {
position: "relative",
width: "initial",
maxWidth: "100%",
padding: "calc($space$lg * 0.75) $space$lg",
br: "$lg",
bg: "$background",
variants: {
color: {
default: {
$$snippetBorderColor: "$border",
$$snippetBgColor: "$background",
color: "$text",
},
primary: {
$$snippetBorderColor: "$border",
$$snippetBgColor: "$primary",
color: "$text",
},
success: {
$$snippetBorderColor: "$success",
$$snippetBgColor: "$background",
color: "$success",
},
warning: {
$$snippetBorderColor: "$warning",
$$snippetBgColor: "$background",
color: "$warning",
},
error: {
$$snippetBorderColor: "$error",
$$snippetBgColor: "$background",
color: "$error",
},
secondary: {
$$snippetBorderColor: "$secondary",
$$snippetBgColor: "$background",
color: "$secondary",
},
invert: {
$$snippetBorderColor: "$foreground",
$$snippetBgColor: "$foreground",
color: "$background",
},
},
borderWeight: {
light: {
$$borderWeight: "$light",
},
normal: {
$$borderWeight: "$normal",
},
bold: {
$$borderWeight: "$bold",
},
extrabold: {
$$borderWeight: "$extrabold",
},
black: {
$$borderWeight: "$black",
},
},
bordered: {
true: {
border: "$$borderWeight solid $$snippetBorderColor",
},
},
filled: {
true: {
backgroundColor: "$$snippetBgColor",
},
},
},
defaultVariants: {
color: "default",
borderWeight: "normal",
filled: false,
},
});
export const StyledSnippetPre = styled("pre", {
margin: 0,
padding: 0,
border: "none",
br: 0,
bgColor: "transparent",
color: "inherit",
fontSize: "$sm",
"*": {
margin: 0,
padding: 0,
fontSize: "inherit",
color: "inherit",
},
variants: {
withCopyButton: {
true: {
width: "calc(100% - 2 * $lg)",
},
false: {
width: "100%",
},
},
},
});
export const StyledSnippetCopyButton = styled(
"button",
{
display: "inline-flex",
jc: "center",
border: "none",
ai: "flex-start",
bg: "transparent",
width: "calc(2 * $space$lg)",
br: "$xs",
color: "inherit",
transition: "opacity 0.2s ease 0s",
cursor: "pointer",
us: "none",
"@motion": {
transition: "none",
},
"&:hover": {
opacity: "0.7",
},
svg: {
path: {
fill: "$accents6",
},
},
},
cssFocusVisible,
);

View File

@ -0,0 +1,26 @@
import {forwardRef} from "@nextui-org/system";
import {useDOMRef} from "@nextui-org/dom-utils";
import {clsx, __DEV__} from "@nextui-org/shared-utils";
import {StyledSnippet} from "./snippet.styles";
import {UseSnippetProps, useSnippet} from "./use-snippet";
export interface SnippetProps extends UseSnippetProps {}
const Snippet = forwardRef<SnippetProps, "div">((props, ref) => {
const {className, ...otherProps} = useSnippet(props);
const domRef = useDOMRef(ref);
return (
<StyledSnippet ref={domRef} className={clsx("nextui-snippet", className)} {...otherProps} />
);
});
if (__DEV__) {
Snippet.displayName = "NextUI.Snippet";
}
Snippet.toString = () => ".nextui-snippet";
export default Snippet;

View File

@ -0,0 +1,11 @@
import {HTMLNextUIProps} from "@nextui-org/system";
export interface UseSnippetProps extends HTMLNextUIProps<"div"> {}
export function useSnippet(props: UseSnippetProps) {
const {...otherProps} = props;
return {...otherProps};
}
export type UseSnippetReturn = ReturnType<typeof useSnippet>;

View File

@ -0,0 +1,12 @@
import React from "react";
import {Meta} from "@storybook/react";
import { Snippet } from "../src";
export default {
title: "Snippet",
component: Snippet,
} as Meta;
export const Default = () => <Snippet />;

View File

@ -0,0 +1,9 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"paths": {
"@stitches/react": ["../../../node_modules/@stitches/react"]
}
},
"include": ["src", "index.ts"]
}

View File

@ -0,0 +1,13 @@
import {defineConfig} from "tsup";
import {findUpSync} from "find-up";
export default defineConfig({
clean: true,
minify: false,
treeshake: true,
format: ["cjs", "esm"],
outExtension(ctx) {
return {js: `.${ctx.format}.js`};
},
inject: process.env.JSX ? [findUpSync("react-shim.js")!] : undefined,
});

View File

@ -48,7 +48,8 @@
"@nextui-org/badge": "workspace:*",
"@nextui-org/grid": "workspace:*",
"@nextui-org/text": "workspace:*",
"@nextui-org/link": "workspace:*"
"@nextui-org/link": "workspace:*",
"@nextui-org/code": "workspace:*"
},
"peerDependencies": {
"react": ">=16.8.0",

View File

@ -8,3 +8,4 @@ export * from "@nextui-org/badge";
export * from "@nextui-org/grid";
export * from "@nextui-org/text";
export * from "@nextui-org/link";
export * from "@nextui-org/code";

View File

@ -6,3 +6,4 @@ export * from "./theme-provider";
export * from "./use-theme";
export * from "./css-baseline";
export * from "./system";
export * from "./root";

View File

@ -0,0 +1,42 @@
/**
* Part of this code is taken from @chakra-ui/system 🙏🏻
*/
import {styled} from "./stitches.config";
import {As, DOMElements, NextUIComponent, HTMLNextUIComponents} from "./system";
type NextUIRoot = {
<T extends As, P = {}>(component: T): NextUIComponent<T, P>;
};
function root() {
const cache = new Map<DOMElements, NextUIComponent<DOMElements>>();
return new Proxy(styled, {
/**
* @example
* const Div = NextUI("div")
* const StyledComponent = NextUI(AnotherComponent)
*/
apply(target, thisArg, argArray: [DOMElements]) {
return styled(...argArray);
},
/**
* @example
* <nextui.div />
*/
get(_, element: DOMElements) {
if (!cache.has(element)) {
cache.set(element, styled(element));
}
return cache.get(element);
},
}) as NextUIRoot & HTMLNextUIComponents;
}
/**
* The NextUI root serves as an object of stitches styled components enabled JSX elements,
* and also a function that can be used to enable custom component receive nextui's props.
*
*/
export const nextui = root();

View File

@ -1,5 +1,5 @@
/**
* Part of this code is taken from @chakra-ui/system
* Part of this code is taken from @chakra-ui/system
*/
import {forwardRef as baseForwardRef} from "react";
@ -7,6 +7,18 @@ import {forwardRef as baseForwardRef} from "react";
import {CSS} from "./stitches.config";
export type As<Props = any> = React.ElementType<Props>;
export type DOMElements = keyof JSX.IntrinsicElements;
export interface NextUIProps {
/**
* The HTML element to render.
*/
as?: As;
/**
* The stiches's css style object
*/
css?: CSS;
}
export type OmitCommonProps<Target, OmitAdditionalProps extends keyof any = never> = Omit<
Target,
@ -53,10 +65,7 @@ export type PropsOf<T extends As> = React.ComponentPropsWithoutRef<T> & {
};
export type HTMLNextUIProps<T extends As, K extends object = {}> = Omit<
Omit<PropsOf<T>, "ref" | "color"> & {
as?: As;
css?: CSS;
},
Omit<PropsOf<T>, "ref" | "color"> & NextUIProps,
keyof K
> &
K;
@ -76,3 +85,10 @@ export function forwardRef<
return baseForwardRef(component) as unknown as ComponentWithAs<Component, Props> &
CompoundComponents;
}
export interface NextUIComponent<C extends As, P = {}>
extends ComponentWithAs<C, NextUIProps & P> {}
export type HTMLNextUIComponents = {
[Tag in DOMElements]: NextUIComponent<Tag, {}>;
};

View File

@ -0,0 +1,24 @@
# @nextui-org/use-clipboard
A Quick description of the component
> This is an internal utility, not intended for public usage.
## Installation
```sh
yarn add @nextui-org/use-clipboard
# or
npm i @nextui-org/use-clipboard
```
## 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,38 @@
import {renderHook, act} from "@testing-library/react-hooks";
import {useClipboard} from "../src";
describe("UseClipboard", () => {
beforeAll(() => {
jest.useFakeTimers();
// navigator.clipboard.writeText mock
Object.assign(navigator, {
clipboard: {
writeText: (data: string) =>
new Promise((res, rej) => {
try {
res(data);
} catch (error) {
rej(error);
}
}),
},
});
});
afterAll(() => {
jest.useRealTimers();
});
it("should copy text to clipboard", () => {
jest.spyOn(navigator.clipboard, "writeText");
const {result} = renderHook(() => useClipboard({timeout: 0}));
act(() => {
result.current.copy("test");
});
expect(navigator.clipboard.writeText).toHaveBeenCalledWith("test");
});
});

View File

@ -0,0 +1,3 @@
{ "replace": { "main": "dist/index.cjs.js", "module": "dist/index.esm.js",
"types": "dist/index.d.ts", "exports": { ".": { "import": "./dist/index.esm.js",
"require": "./dist/index.cjs.js" }, "./package.json": "./package.json" } } }

View File

@ -0,0 +1,43 @@
{
"name": "@nextui-org/use-clipboard",
"version": "1.0.0-beta.11",
"description": "Wrapper around navigator.clipboard with feedback timeout",
"keywords": [
"use-clipboard"
],
"author": "Junior Garcia <jrgarciadev@gmail.com>",
"homepage": "https://nextui.org",
"license": "MIT",
"main": "src/index.ts",
"sideEffects": false,
"files": [
"dist"
],
"publishConfig": {
"access": "public"
},
"repository": {
"type": "git",
"url": "git+https://github.com/nextui-org/nextui.git",
"directory": "packages/hooks/use-clipboard"
},
"bugs": {
"url": "https://github.com/nextui-org/nextui/issues"
},
"scripts": {
"build": "tsup src/index.ts --format=esm,cjs --dts",
"dev": "yarn build:fast -- --watch",
"clean": "rimraf dist .turbo",
"typecheck": "tsc --noEmit",
"build:fast": "tsup src/index.ts --format=esm,cjs",
"prepack": "clean-package",
"postpack": "clean-package restore"
},
"peerDependencies": {
"react": ">=16.8.0"
},
"devDependencies": {
"clean-package": "2.1.1",
"react": "^17.0.2"
}
}

View File

@ -0,0 +1,55 @@
import {useState} from "react";
export interface UseClipboardProps {
/**
* The time in milliseconds to wait before resetting the clipboard.
* @default 2000
*/
timeout?: number;
}
/**
* Copies the given text to the clipboard.
* @param {number} timeout - timeout in ms, default 2000
* @returns {copy, copied, error, reset} - copy function, copied state, error state, reset function
*/
export function useClipboard({timeout = 2000}: UseClipboardProps = {}) {
const [error, setError] = useState<Error | null>(null);
const [copied, setCopied] = useState(false);
const [copyTimeout, setCopyTimeout] = useState<ReturnType<typeof setTimeout> | null>(null);
const onClearTimeout = () => {
if (copyTimeout) {
clearTimeout(copyTimeout);
}
};
const handleCopyResult = (value: boolean) => {
onClearTimeout();
if (timeout > 0) {
setCopyTimeout(setTimeout(() => setCopied(false), timeout));
setCopied(value);
}
};
const copy = (valueToCopy: any) => {
if ("clipboard" in navigator) {
navigator.clipboard
.writeText(valueToCopy)
.then(() => handleCopyResult(true))
.catch((err) => setError(err));
} else {
setError(new Error("useClipboard: navigator.clipboard is not supported"));
}
};
const reset = () => {
setCopied(false);
setError(null);
onClearTimeout();
};
return {copy, reset, error, copied};
}
export type UseClipboardReturn = ReturnType<typeof useClipboard>;

View File

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

View File

@ -0,0 +1,13 @@
import {defineConfig} from "tsup";
import {findUpSync} from "find-up";
export default defineConfig({
clean: true,
minify: false,
treeshake: true,
format: ["cjs", "esm"],
outExtension(ctx) {
return {js: `.${ctx.format}.js`};
},
inject: process.env.JSX ? [findUpSync("react-shim.js")!] : undefined,
});

View File

@ -0,0 +1,87 @@
import React, {useEffect, useState} from "react";
import {clsx} from "./clsx";
interface CSSTransitionProps {
visible?: boolean;
childrenRef?: React.RefObject<HTMLElement>;
enterTime?: number;
leaveTime?: number;
clearTime?: number;
className?: string;
name?: string;
onExited?: () => void;
onEntered?: () => void;
}
export const CSSTransition: React.FC<React.PropsWithChildren<CSSTransitionProps>> = ({
children,
childrenRef,
visible = false,
enterTime = 60,
leaveTime = 60,
clearTime = 60,
className = "",
name = "transition",
onExited,
onEntered,
...props
}) => {
const [classes, setClasses] = useState<string>("");
const [renderable, setRenderable] = useState<boolean>(visible);
useEffect(() => {
const statusClassName = visible ? "enter" : "leave";
const time = visible ? enterTime : leaveTime;
if (visible && !renderable) {
setRenderable(true);
}
setClasses(`${name}-${statusClassName}`);
// set class to active
const timer = setTimeout(() => {
setClasses(`${name}-${statusClassName} ${name}-${statusClassName}-active`);
if (statusClassName === "leave") {
onExited?.();
} else {
onEntered?.();
}
clearTimeout(timer);
}, time);
// remove classess when animation over
const clearClassesTimer = setTimeout(() => {
if (!visible) {
setClasses("");
setRenderable(false);
}
clearTimeout(clearClassesTimer);
}, time + clearTime);
return () => {
clearTimeout(timer);
clearTimeout(clearClassesTimer);
};
}, [visible, renderable]);
// update children ref classes
useEffect(() => {
if (!childrenRef?.current) {
return;
}
const classesArr = classes.split(" ");
const refClassesArr = childrenRef.current.className.split(" ");
const newRefClassesArr = refClassesArr.filter((item) => !item.includes(name));
childrenRef.current.className = clsx(newRefClassesArr, classesArr);
}, [childrenRef, classes]);
if (!React.isValidElement(children) || !renderable) return null;
return React.cloneElement(children, {
...props,
className: clsx(children.props.className, className, !childrenRef?.current ? classes : ""),
});
};

View File

@ -0,0 +1,8 @@
/**
* Capitalizes the first letter of a string
* @param {string} text
* @returns {string}
*/
export const capitalize = (text: string) => {
return text.charAt(0).toUpperCase() + text.slice(1);
};

View File

@ -8,3 +8,5 @@ export * from "./color";
export * from "./object";
export * from "./text";
export * from "./dimensions";
export * from "./css-transition";
export * from "./functions";

View File

@ -0,0 +1,11 @@
import {renderHook} from "@testing-library/react";
import { {{camelCase hookName}} } from "../src";
describe("{{camelCase hookName}}", () => {
it("should work correctly", () => {
const {result} = renderHook(() => {{camelCase hookName}}());
// Add your test here
});
});

View File

@ -0,0 +1,11 @@
export interface Use{{capitalize hookName}}Props {}
export function {{camelCase hookName}}(props: Use{{capitalize hookName}}Props = {}) {
const {...otherProps} = props;
return {...otherProps};
}
export type Use{{capitalize hookName}}Return = ReturnType<typeof {{camelCase hookName}}>;

View File

@ -13,6 +13,12 @@ const camelCase = (str) => {
const workspaces = ["components", "core", "hooks", "utilities"];
const generators = ["component", "package", "hook"];
const defaultOutDirs = {
component: "components",
hook: "hooks",
package: "utilities",
};
/**
* @param {import("plop").NodePlopAPI} plop
*/
@ -20,6 +26,9 @@ module.exports = function main(plop) {
plop.setHelper("capitalize", (text) => {
return capitalize(camelCase(text));
});
plop.setHelper("camelCase", (text) => {
return camelCase(text);
});
generators.forEach((gen) => {
plop.setGenerator(gen, {
@ -29,6 +38,29 @@ module.exports = function main(plop) {
type: "input",
name: `${gen}Name`,
message: `Enter ${gen} name:`,
validate: (value) => {
if (!value) {
return `${gen} name is required`;
}
// check is has a valid hook name "use-something"
if (gen === "hook" && !value.startsWith("use-")) {
return "Hook name must start with 'use-'";
}
// check is case is correct
if (value !== value.toLowerCase()) {
return `${gen} name must be in lowercase`;
}
// cannot have spaces
if (value.includes(" ")) {
return `${gen} name cannot have spaces`;
}
return true;
},
},
{
type: "input",
@ -39,8 +71,15 @@ module.exports = function main(plop) {
type: "list",
name: "outDir",
message: `where should this ${gen} live?`,
default: "packages",
default: defaultOutDirs[gen],
choices: workspaces,
validate: (value) => {
if (!value) {
return `outDir is required`;
}
return true;
},
},
],
actions(answers) {

42
pnpm-lock.yaml generated
View File

@ -340,6 +340,21 @@ importers:
clean-package: 2.1.1
react: 17.0.2
packages/components/code:
specifiers:
'@nextui-org/dom-utils': workspace:*
'@nextui-org/shared-utils': workspace:*
'@nextui-org/system': workspace:*
clean-package: 2.1.1
react: ^17.0.2
dependencies:
'@nextui-org/dom-utils': link:../../utilities/dom-utils
'@nextui-org/shared-utils': link:../../utilities/shared-utils
'@nextui-org/system': link:../../core/system
devDependencies:
clean-package: 2.1.1
react: 17.0.2
packages/components/col:
specifiers:
'@nextui-org/dom-utils': workspace:*
@ -443,6 +458,23 @@ importers:
clean-package: 2.1.1
react: 17.0.2
packages/components/snippet:
specifiers:
'@nextui-org/dom-utils': workspace:*
'@nextui-org/shared-css': workspace:*
'@nextui-org/shared-utils': workspace:*
'@nextui-org/system': workspace:*
clean-package: 2.1.1
react: ^17.0.2
dependencies:
'@nextui-org/dom-utils': link:../../utilities/dom-utils
'@nextui-org/shared-css': link:../../utilities/shared-css
'@nextui-org/shared-utils': link:../../utilities/shared-utils
'@nextui-org/system': link:../../core/system
devDependencies:
clean-package: 2.1.1
react: 17.0.2
packages/components/spacer:
specifiers:
'@nextui-org/dom-utils': workspace:*
@ -502,6 +534,7 @@ importers:
specifiers:
'@nextui-org/avatar': workspace:*
'@nextui-org/badge': workspace:*
'@nextui-org/code': workspace:*
'@nextui-org/col': workspace:*
'@nextui-org/container': workspace:*
'@nextui-org/grid': workspace:*
@ -518,6 +551,7 @@ importers:
dependencies:
'@nextui-org/avatar': link:../../components/avatar
'@nextui-org/badge': link:../../components/badge
'@nextui-org/code': link:../../components/code
'@nextui-org/col': link:../../components/col
'@nextui-org/container': link:../../components/container
'@nextui-org/grid': link:../../components/grid
@ -550,6 +584,14 @@ importers:
react: 17.0.2
react-aria: 3.18.0_react@17.0.2
packages/hooks/use-clipboard:
specifiers:
clean-package: 2.1.1
react: ^17.0.2
devDependencies:
clean-package: 2.1.1
react: 17.0.2
packages/hooks/use-ssr:
specifiers:
clean-package: 2.1.1