mirror of
https://github.com/nextui-org/nextui.git
synced 2025-12-08 19:26:11 +00:00
feat(core): root function added to use NextUI.<element> as a component, snippet structure started, plop templated improved
This commit is contained in:
parent
655de55c53
commit
e80eb0d938
24
packages/components/code/README.md
Normal file
24
packages/components/code/README.md
Normal 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).
|
||||
25
packages/components/code/__tests__/code.test.tsx
Normal file
25
packages/components/code/__tests__/code.test.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
3
packages/components/code/clean-package.config.json
Normal file
3
packages/components/code/clean-package.config.json
Normal 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" } } }
|
||||
48
packages/components/code/package.json
Normal file
48
packages/components/code/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
8
packages/components/code/src/code.styles.ts
Normal file
8
packages/components/code/src/code.styles.ts
Normal 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%",
|
||||
});
|
||||
41
packages/components/code/src/code.tsx
Normal file
41
packages/components/code/src/code.tsx
Normal 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;
|
||||
5
packages/components/code/src/index.ts
Normal file
5
packages/components/code/src/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
// export types
|
||||
export type {CodeProps} from "./code";
|
||||
|
||||
// export component
|
||||
export {default as Code} from "./code";
|
||||
13
packages/components/code/stories/code.stories.tsx
Normal file
13
packages/components/code/stories/code.stories.tsx
Normal 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>;
|
||||
9
packages/components/code/tsconfig.json
Normal file
9
packages/components/code/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@stitches/react": ["../../../node_modules/@stitches/react"]
|
||||
}
|
||||
},
|
||||
"include": ["src", "index.ts"]
|
||||
}
|
||||
13
packages/components/code/tsup.config.ts
Normal file
13
packages/components/code/tsup.config.ts
Normal 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,
|
||||
});
|
||||
@ -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 />;
|
||||
|
||||
24
packages/components/snippet/README.md
Normal file
24
packages/components/snippet/README.md
Normal 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).
|
||||
19
packages/components/snippet/__tests__/snippet.test.tsx
Normal file
19
packages/components/snippet/__tests__/snippet.test.tsx
Normal 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();
|
||||
});
|
||||
});
|
||||
3
packages/components/snippet/clean-package.config.json
Normal file
3
packages/components/snippet/clean-package.config.json
Normal 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" } } }
|
||||
49
packages/components/snippet/package.json
Normal file
49
packages/components/snippet/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
5
packages/components/snippet/src/index.ts
Normal file
5
packages/components/snippet/src/index.ts
Normal file
@ -0,0 +1,5 @@
|
||||
// export types
|
||||
export type {SnippetProps} from "./snippet";
|
||||
|
||||
// export component
|
||||
export {default as Snippet} from "./snippet";
|
||||
23
packages/components/snippet/src/snippet-icon.tsx
Normal file
23
packages/components/snippet/src/snippet-icon.tsx
Normal 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;
|
||||
137
packages/components/snippet/src/snippet.styles.ts
Normal file
137
packages/components/snippet/src/snippet.styles.ts
Normal 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,
|
||||
);
|
||||
26
packages/components/snippet/src/snippet.tsx
Normal file
26
packages/components/snippet/src/snippet.tsx
Normal 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;
|
||||
11
packages/components/snippet/src/use-snippet.ts
Normal file
11
packages/components/snippet/src/use-snippet.ts
Normal 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>;
|
||||
12
packages/components/snippet/stories/snippet.stories.tsx
Normal file
12
packages/components/snippet/stories/snippet.stories.tsx
Normal 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 />;
|
||||
9
packages/components/snippet/tsconfig.json
Normal file
9
packages/components/snippet/tsconfig.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"paths": {
|
||||
"@stitches/react": ["../../../node_modules/@stitches/react"]
|
||||
}
|
||||
},
|
||||
"include": ["src", "index.ts"]
|
||||
}
|
||||
13
packages/components/snippet/tsup.config.ts
Normal file
13
packages/components/snippet/tsup.config.ts
Normal 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,
|
||||
});
|
||||
@ -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",
|
||||
|
||||
@ -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";
|
||||
|
||||
@ -6,3 +6,4 @@ export * from "./theme-provider";
|
||||
export * from "./use-theme";
|
||||
export * from "./css-baseline";
|
||||
export * from "./system";
|
||||
export * from "./root";
|
||||
|
||||
42
packages/core/system/src/root.ts
Normal file
42
packages/core/system/src/root.ts
Normal 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();
|
||||
@ -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, {}>;
|
||||
};
|
||||
|
||||
24
packages/hooks/use-clipboard/README.md
Normal file
24
packages/hooks/use-clipboard/README.md
Normal 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).
|
||||
38
packages/hooks/use-clipboard/__tests__/clipboard.test.tsx
Normal file
38
packages/hooks/use-clipboard/__tests__/clipboard.test.tsx
Normal 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");
|
||||
});
|
||||
});
|
||||
3
packages/hooks/use-clipboard/clean-package.config.json
Normal file
3
packages/hooks/use-clipboard/clean-package.config.json
Normal 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" } } }
|
||||
43
packages/hooks/use-clipboard/package.json
Normal file
43
packages/hooks/use-clipboard/package.json
Normal 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"
|
||||
}
|
||||
}
|
||||
55
packages/hooks/use-clipboard/src/index.ts
Normal file
55
packages/hooks/use-clipboard/src/index.ts
Normal 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>;
|
||||
4
packages/hooks/use-clipboard/tsconfig.json
Normal file
4
packages/hooks/use-clipboard/tsconfig.json
Normal file
@ -0,0 +1,4 @@
|
||||
{
|
||||
"extends": "../../../tsconfig.json",
|
||||
"include": ["src", "index.ts"]
|
||||
}
|
||||
13
packages/hooks/use-clipboard/tsup.config.ts
Normal file
13
packages/hooks/use-clipboard/tsup.config.ts
Normal 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,
|
||||
});
|
||||
87
packages/utilities/shared-utils/src/css-transition.tsx
Normal file
87
packages/utilities/shared-utils/src/css-transition.tsx
Normal 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 : ""),
|
||||
});
|
||||
};
|
||||
8
packages/utilities/shared-utils/src/functions.ts
Normal file
8
packages/utilities/shared-utils/src/functions.ts
Normal 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);
|
||||
};
|
||||
@ -8,3 +8,5 @@ export * from "./color";
|
||||
export * from "./object";
|
||||
export * from "./text";
|
||||
export * from "./dimensions";
|
||||
export * from "./css-transition";
|
||||
export * from "./functions";
|
||||
|
||||
11
plop/hook/__tests__/{{hookName}}.test.tsx.hbs
Normal file
11
plop/hook/__tests__/{{hookName}}.test.tsx.hbs
Normal 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
|
||||
});
|
||||
});
|
||||
@ -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}}>;
|
||||
|
||||
41
plopfile.js
41
plopfile.js
@ -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
42
pnpm-lock.yaml
generated
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user