diff --git a/README.md b/README.md index a2b78cc..dfa87c4 100644 --- a/README.md +++ b/README.md @@ -23,12 +23,19 @@ npm install artifak Packages can also be installed independently. Simply do `yarn add ` or `npm install ` to add them to your list of dependencies. Below is a list of available packages. +- @artifak/**container** - @artifak/**grid** - @artifak/**typography** - @artifak/**flex** +- @artifak/**text-input** - @artifak/**component-generator** - @artifak/**imagery** - @artifak/**media** - @artifak/**fluidsizing** - @artifak/**usematchmedia** - @artifak/**usewindowsize** +- @artifak/**usedebouncedfn** +- @artifak/**hextorgb** +- @artifak/**hextorgba** +- @artifak/**pxtoem** +- @artifak/**pxtorem** diff --git a/bundlesize.config.json b/bundlesize.config.json index 55dcfba..3cc9df1 100644 --- a/bundlesize.config.json +++ b/bundlesize.config.json @@ -6,6 +6,9 @@ { "path": "./packages/component-generator/dist/*.js" }, + { + "path": "./packages/container/dist/*.js" + }, { "path": "./packages/flex/dist/*.js" }, @@ -15,6 +18,9 @@ { "path": "./packages/imagery/dist/*.js" }, + { + "path": "./packages/text-input/dist/*.js" + }, { "path": "./packages/typography/dist/*.js" }, @@ -41,6 +47,12 @@ }, { "path": "./packages/usewindowsize/dist/*.js" + }, + { + "path": "./packages/usedebouncedfn/dist/*.js" + }, + { + "path": "./packages/useclickaway/dist/*.js" } ] } diff --git a/docs/components/Global/HTMLhead.tsx b/docs/components/Global/HTMLhead.tsx index b41293c..f00ce62 100644 --- a/docs/components/Global/HTMLhead.tsx +++ b/docs/components/Global/HTMLhead.tsx @@ -73,6 +73,10 @@ export const HTMLhead: React.FC = ({ href="/favicon-16x16.png" /> + ` or `npm install ` to add them to your list of dependencies. Below is a list of available packages. -- @artifak/**block** +- @artifak/**container** - @artifak/**grid** - @artifak/**typography** - @artifak/**flex** +- @artifak/**text-input** - @artifak/**component-generator** - @artifak/**imagery** - @artifak/**media** - @artifak/**fluidsizing** - @artifak/**usematchmedia** - @artifak/**usewindowsize** +- @artifak/**usedebouncedfn** +- @artifak/**hextorgb** +- @artifak/**hextorgba** +- @artifak/**pxtoem** +- @artifak/**pxtorem** diff --git a/packages/artifak/package.json b/packages/artifak/package.json index 045f8c4..f1bab89 100644 --- a/packages/artifak/package.json +++ b/packages/artifak/package.json @@ -44,6 +44,7 @@ }, "dependencies": { "@artifak/component-generator": "^2.0.1", + "@artifak/container": "^1.0.0", "@artifak/flex": "^1.1.7", "@artifak/fluidsizing": "^1.0.1", "@artifak/grid": "^1.1.5", @@ -53,7 +54,10 @@ "@artifak/media": "^1.0.2", "@artifak/pxtoem": "^1.0.1", "@artifak/pxtorem": "^1.0.1", + "@artifak/text-input": "^1.0.2", "@artifak/typography": "^2.0.0", + "@artifak/useclickaway": "^1.0.0", + "@artifak/usedebouncedfn": "^1.0.0", "@artifak/usematchmedia": "^1.0.2", "@artifak/usewindowsize": "^1.0.2" }, diff --git a/packages/artifak/src/index.tsx b/packages/artifak/src/index.tsx index 02f773c..ce1a5ed 100644 --- a/packages/artifak/src/index.tsx +++ b/packages/artifak/src/index.tsx @@ -8,8 +8,7 @@ export { ComponentsRecord } from '@artifak/component-generator'; -export { BlockBase, createBlocks } from '@artifak/block'; -export { BlockBaseProps } from '@artifak/block'; +export { Container } from '@artifak/container'; export { FlexRow, FlexRowBase, FlexCol, FlexColBase } from '@artifak/flex'; export { FlexRowBaseProps, FlexColBaseProps } from '@artifak/flex'; @@ -17,6 +16,8 @@ export { FlexRowBaseProps, FlexColBaseProps } from '@artifak/flex'; export { Grid, GridBase, GridItem, GridItemBase } from '@artifak/grid'; export { GridBaseProps, GridItemBaseProps } from '@artifak/grid'; +export { createTextInputs } from '@artifak/text-input'; + export { createTypography, fontWeight, @@ -59,6 +60,8 @@ export { export { fluidSizing } from '@artifak/fluidsizing'; export { useMatchMedia } from '@artifak/usematchmedia'; export { useWindowSize } from '@artifak/usewindowsize'; +export { useClickAway } from '@artifak/useclickaway'; +export { useDebouncedFn } from '@artifak/usedebouncedfn'; export { hexToRGB, getHexColorRGBvalues } from '@artifak/hextorgb'; export { hexToRGBA } from '@artifak/hextorgba'; export { pxToEm } from '@artifak/pxtoem'; diff --git a/packages/bundler/index.js b/packages/bundler/index.js index 8f25cc1..c5b6c97 100755 --- a/packages/bundler/index.js +++ b/packages/bundler/index.js @@ -46,7 +46,6 @@ const baseOutputOptions = { 'styled-components': 'StyledComponents', 'styled-system': 'StyledSystem' }, - sourcemap: true, exports: 'named' }; @@ -64,6 +63,11 @@ const formatOptions = [ format: 'esm', esModule: true }, + { + file: `dist/${fileName}.esm.min.js`, + format: 'esm', + esModule: true + }, { file: `dist/${fileName}.umd.js`, format: 'umd', diff --git a/packages/bundler/package.json b/packages/bundler/package.json index 0299dc2..a2c6bd1 100644 --- a/packages/bundler/package.json +++ b/packages/bundler/package.json @@ -31,7 +31,7 @@ "@rollup/plugin-node-resolve": "^11.2.0", "rollup": "^2.39.0", "rollup-plugin-terser": "^7.0.2", - "rollup-plugin-typescript2": "^0.29.0", + "rollup-plugin-typescript2": "^0.30.0", "typescript": "^4.1.5", "typescript-plugin-styled-components": "^1.4.4" } diff --git a/packages/component-generator/README.md b/packages/component-generator/README.md index 493fbd4..f567f09 100644 --- a/packages/component-generator/README.md +++ b/packages/component-generator/README.md @@ -1,7 +1,7 @@ # `@artifak/component-generator` Component Generator library to generate similar components based off of styles and or attributes you provide. You can also view the docs -at [Artifak Typography](https://www.artifak.dev/?content=Generator). +at [Artifak Generator](https://www.artifak.dev/?content=Generator). ## Installation @@ -19,82 +19,122 @@ npm install @artifak/component-generator ## Usage -### createStyledComponent - -To generate your components, simply define a config object consisting of styles/attributes with the key as your component name. Note that you **must** define the `as` property in order to have it render as the HTML tag that you desire. In the example below, we've defined a component called `H1` to render as an `h1` HTML tag. We've also defined the fontSize in an array that matches the media query widths. +### createComponents -| Arguments | Type | -| --------- | ------ | -| config | object | +This generates components based on the config provided. Below a usage example with full configuration applied. ```ts -import { createStyledComponent } from '@artifak/component-generator'; -import { system } from 'styled-system'; - -const styles = { - fontSize: [16, 36, 96], - margin: 0, - as: 'h1' -}; - -const variants = { - primary: { - color: 'red' - }, - secondary: { - color: 'blue' - } -}; - -const styleProps = [ - system({ - textTransform: true - }) -]; - -const element = 'h1'; - -const H1 = createStyledComponent({ - styles, - variants, - styleProps, - element -}); -``` +import { position, PositionProps } from 'styled-system'; + import { createComponents } from 'artifak'; + import { theme } from './theme'; -### createBaseComponents + // # EXAMPLE 1 - USING A CONFIGURATION OBJECT AS BASE + // Note that this is just an example, please use the + // "button" HTML element whenever possible. In the + // examples, we will see full usage of the + // createComponents function. -createBaseComponents will require a base Styled Component to generate your desired components. This is used to conveniently generate similar components. For creating the base component, you can use createStyledComponent. - -| Arguments | Type | -| --------- | ------ | -| styles | object | - -```ts -import { - createStyledComponent, - createBaseComponents -} from '@artifak/component-generator'; - -const baseComponentConfig = { - styles: { - display: 'block', - margin: '0 auto' - } -}; - -const BaseComponent = createStyledComponent(); - -const typeSettings = { - H1: { + const base = { styles: { - fontSize: [16, 17, 19, 21], - lineHeight: 1.5, - marginTop: 0, - as: 'h1' + display: 'block', + padding: ['1em', '1.5em 1em'], + width: '100%', + fontSize: ['1rem', '1.2rem'] + }, + styleProps: [position], + attrs: { role: 'button' }, + element: 'div' + }; + + // Define your component styles + const config = { + TextButton: { + background: transparent, + border: 'none', + '&:hover': { + color: theme.colors.primary + }, + attrs: { name: 'text-button' }, + as: 'span' + }, + + FilledButton: { + margin: '0 auto', + padding: ['1.2em 1em', '1.5em 1.2em'], + background: theme.colors.primary + '&:hover': { + background: theme.colors.secondary + }, + attrs: { name: 'filled-button' } + }, + + OutlinedButton: { + background: transparent, + border: 1px solid black, + padding: 0, + '&:hover': { + borderColor: theme.colors.primary + }, + attrs: { name: 'outlined-button' } } - } -}; + }; + + // Invoke and destructure for usage. Since a configuration object + // is provided, you will also get the base as a component. + // You can rename it as use as a base for something else if desired. + export const { + Base: ButtonBase, + FilledButton, + OutlinedButton, + TextButton + } = createComponents(BaseComponent, config); + + + // # EXAMPLE 2 - USING A COMPONENT AS BASE + // You can also pass in a component to use as base. + // For example, let us create a BasicButton component. + + const BasicButton = styled('div')( + { + display: 'block', + padding: ['1em', '1.5em 1em'], + width: '100%', + fontSize: ['1rem', '1.2rem'] + } + ); + + // Invoke and call like Example 1. Here we will reuse the + // config constant we previously defined in Example 1. + // Note that passing a component as base will not yield + // a Base component in the results. + export const { + FilledButton, + OutlinedButton, + TextButton + } = createComponents(BasicButton, config); +``` + +### createStyledComponent -const { H1 } = createBaseComponents(typeSettings); +Creates a single styled component based on the configuration that you provide. + +```ts +import { HTMLAttributes } from 'react'; +import { position, PositionProps } from 'styled-system'; +import { theme } from './theme'; + +type CardProps = { + isHidden: boolean; +} & PositionProps; + +const StyledArticle = createStyledComponent< + CardProps, + typeof theme, + HTMLAttributes +>({ + styles: { position: 'relative' }, + attrs: { title: 'Hello World' }, + styledProps: [position], + element: 'article' +}); ``` diff --git a/packages/component-generator/src/typings.ts b/packages/component-generator/src/typings.ts index f29ddc8..6620a11 100644 --- a/packages/component-generator/src/typings.ts +++ b/packages/component-generator/src/typings.ts @@ -44,7 +44,7 @@ export type BaseConfig = { styleProps?: styleFn[]; element?: keyof JSX.IntrinsicElements; component?: ThemedStyledFunction< - keyof JSX.IntrinsicElements | React.ComponentType, + keyof JSX.IntrinsicElements, ThemeType | any, React.ComponentType, keyof any diff --git a/packages/container/README.md b/packages/container/README.md new file mode 100644 index 0000000..3af1b69 --- /dev/null +++ b/packages/container/README.md @@ -0,0 +1,33 @@ +# `@artifak/container` + +A basic container component. By default has full 100% width and margin 0 auto. All you need to do is specify your max widths. + +## Usage + +Apart from widths, the component also accepts the following style properties: + +- background +- border +- color +- display +- layout +- position +- shadow +- space +- typography + +Below is a usage example. + +```ts +import { Container } from 'artifak'; + +export function Example() { + return ( + + <> +

Hello World

+ +
+ ); +} +``` diff --git a/packages/container/__tests__/__snapshots__/container.test.tsx.snap b/packages/container/__tests__/__snapshots__/container.test.tsx.snap new file mode 100644 index 0000000..28d9001 --- /dev/null +++ b/packages/container/__tests__/__snapshots__/container.test.tsx.snap @@ -0,0 +1,27 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`@artifak/container renders 1`] = ` + +
+

+ Hello World +

+
+
+`; + +exports[`@artifak/container renders 2`] = ` + +
+

+ Hello World +

+
+
+`; diff --git a/packages/container/__tests__/container.test.tsx b/packages/container/__tests__/container.test.tsx new file mode 100644 index 0000000..bbda174 --- /dev/null +++ b/packages/container/__tests__/container.test.tsx @@ -0,0 +1,29 @@ +import { Container } from '../src'; +import React from 'react'; +import { render } from '@testing-library/react'; + +describe('@artifak/container', () => { + it('renders', () => { + const { asFragment } = render( + + <> +

Hello World

+ +
+ ); + + expect(asFragment()).toMatchSnapshot(); + }); + + it('renders', () => { + const { asFragment } = render( + + <> +

Hello World

+ +
+ ); + + expect(asFragment()).toMatchSnapshot(); + }); +}); diff --git a/packages/container/index.stories.tsx b/packages/container/index.stories.tsx new file mode 100644 index 0000000..84b28ed --- /dev/null +++ b/packages/container/index.stories.tsx @@ -0,0 +1,22 @@ +import React from 'react'; +import { Container } from './src'; + +export const ContainerTest = () => { + return ( + + <> +

Hello World

+ +
+ ); +}; + +export default { + title: 'Container', + component: [ContainerTest] +}; diff --git a/packages/container/package.json b/packages/container/package.json new file mode 100644 index 0000000..b57c332 --- /dev/null +++ b/packages/container/package.json @@ -0,0 +1,55 @@ +{ + "name": "@artifak/container", + "version": "1.0.0", + "description": "A basic React container component", + "keywords": [ + "react", + "react-component", + "component", + "ui", + "container", + "grid", + "block", + "layout" + ], + "author": "Julian Low", + "license": "MIT", + "sideEffects": false, + "main": "dist/container.cjs.js", + "module": "dist/container.esm.js", + "src": "src/index.tsx", + "types": "dist/src/index.d.ts", + "directories": { + "test": "__tests__", + "src": "src" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/heyjul3s/artifak.git" + }, + "scripts": { + "build": "rimraf dist && builder", + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "bugs": { + "url": "https://github.com/heyjul3s/artifak/issues" + }, + "homepage": "https://github.com/heyjul3s/artifak#readme", + "devDependencies": { + "@artifak/bundler": "^1.1.4" + }, + "peerDependencies": { + "react": ">=17.0.1", + "react-dom": ">=17.0.1", + "styled-components": ">=5.2.1" + }, + "dependencies": { + "@artifak/component-generator": "^2.0.3" + } +} diff --git a/packages/container/src/index.tsx b/packages/container/src/index.tsx new file mode 100644 index 0000000..97cc862 --- /dev/null +++ b/packages/container/src/index.tsx @@ -0,0 +1,13 @@ +import React from 'react'; +import { createStyledComponent, BaseProps } from '@artifak/component-generator'; + +export function Container({ children, ...props }) { + return {children}; +} + +const ContainerBase = createStyledComponent>({ + styles: { + margin: '0 auto', + width: '100%' + } +}); diff --git a/packages/container/tsconfig.json b/packages/container/tsconfig.json new file mode 100644 index 0000000..0a8ae60 --- /dev/null +++ b/packages/container/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "allowSyntheticDefaultImports": true, + "compilerOptions": { + "outDir": "dist" + }, + "references": [], + "include": ["src"] +} diff --git a/packages/fluidsizing/README.md b/packages/fluidsizing/README.md index ba051e6..3a1dfbe 100644 --- a/packages/fluidsizing/README.md +++ b/packages/fluidsizing/README.md @@ -1,6 +1,6 @@ # `@artifak/fluidsizing` -A utility function that computes a fluid sizing value based on the min and max values provided. +A utility function that computes a fluid sizing value based on the min and max values provided, eliminating the need for specifying width based media queries. Alternatively, docs can be found at [Artifak Fluid Sizing](https://www.artifak.dev/?content=Typography). ## Installation diff --git a/packages/text-input/README.md b/packages/text-input/README.md index 91ccf55..11d73ca 100644 --- a/packages/text-input/README.md +++ b/packages/text-input/README.md @@ -1,11 +1,70 @@ # `@artifak/text-input` -> TODO: description +A text based input element generator. -## Usage +## Installation + +### Yarn +```sh +yarn add @artifak/text-input ``` -const textInput = require('@artifak/text-input'); -// TODO: DEMONSTRATE API +### NPM + +```sh +npm install @artifak/text-input +``` + +## Usage + +```ts +import { createTextInputs } from '@artifak/text-input'; + +const inputs = { + base: { + styles: { + width: '100%', + border: '1px solid black', + padding: ['1em'] + } + }, + + components: { + InputSearch: { + maxWidth: '300px', + attrs: { type: 'search' } + }, + + InputUrl: { + display: 'block', + attrs: { type: 'url' } + } + } +}; + +type BasicUsageProps = { + disabled?: boolean; + placeholder?: string; + onBlur?: () => void; +}; + +const { Base: InputText, InputSearch, InputUrl } = createTextInputs< + typeof inputs.components, + BasicUsageProps +>(inputs.base, inputs.components); + +export function BasicUsage() { + const onBlur = (text: string) => () => { + console.log('Blur', text); + }; + + return ( + <> + + + + + ); +} ``` diff --git a/packages/typography/README.md b/packages/typography/README.md index a5465a8..5391544 100644 --- a/packages/typography/README.md +++ b/packages/typography/README.md @@ -21,24 +21,62 @@ npm install @artifak/typography ### createTypography -To generate your typography components, simply define a styles object with the key as your component name. Note that you **must** define the `as` property in order to have it render as the HTML tag that you desire. In the example below, we've defined a component called `H1` to render as an `h1` HTML tag. We've also defined the fontSize in an array that matches the media query widths. - -| Arguments | Type | -| --------- | ------ | -| styles | object | +Generates your typography components. ```ts -import { createTypography } from '@artifak/typography'; +import { createTypographyComponents, fontWeight } from 'artifak'; +import { theme } from '../theme'; + +const base = { + styles: { + color: theme.colors.text, + fontFamily: theme.fontFamily.arial + } +}; -const typeStyles = { +const components = { H1: { - fontSize: [16, 36, 96], - margin: 0, + fontSize: [48, 96], + margin: '0 0 0.25em', + lineHeight: 1.15, as: 'h1' + }, + + H2: { + fontSize: [37, 39, 41, 43], + lineHeight: 1.45, + as: 'h2' + }, + + H3: { + fontSize: [27, 28, 30, 32], + lineHeight: 1.45, + as: 'h3' + }, + + H4: { + fontSize: [18, 20, 22, 24], + lineHeight: 1.45, + as: 'h4' + }, + + H5: { + fontSize: [16, 17, 19, 21], + lineHeight: 1.45, + as: 'h5' + }, + + H6: { + fontSize: [16, 17, 19, 21], + lineHeight: 1.45, + as: 'h6' } }; -const { H1 } = createTypography(typeStyles); +export const { H1, H2, H3, H4, H5, H6 } = createTypography< + typeof components, + typeof theme +>(base, components); ``` Other than helping you generate new typography components, it also contains other utility functions as well to help you in styling. diff --git a/packages/useclickaway/README.md b/packages/useclickaway/README.md new file mode 100644 index 0000000..ef56a49 --- /dev/null +++ b/packages/useclickaway/README.md @@ -0,0 +1,29 @@ +# `@artifak/useclickaway` + +A React hook to trigger a callback when a click event occurs on a target that is not the specified ref HTML element. + +## Usage + +```ts +import React from 'react'; +import { useClickAway } from '@artifak/useclickaway; + +const Dummy = () => { + const ref: React.RefObject = React.createRef(); + + const onClickAway = () => { + console.log('Clicked'); + }; + + useClickAway(ref, onClickAway); + + return ( +
+ Wrapper +
+ Click Me +
+
+ ); + }; +``` diff --git a/packages/useclickaway/__tests__/useclickaway.test.tsx b/packages/useclickaway/__tests__/useclickaway.test.tsx new file mode 100644 index 0000000..6199f36 --- /dev/null +++ b/packages/useclickaway/__tests__/useclickaway.test.tsx @@ -0,0 +1,37 @@ +import React from 'react'; +import { render, screen } from '@testing-library/react'; +import { useClickAway } from '../src'; + +describe('@artifak/useclickaway', () => { + const onClickAway = jest.fn(); + + const Dummy = () => { + const ref: React.RefObject = React.createRef(); + + useClickAway(ref, onClickAway); + + const onClick = () => { + console.log('Clicked'); + }; + + return ( +
+ Wrapper +
+ Click Me +
+
+ ); + }; + + it('calls the clickAway callback when click event occurs on non-target ref', () => { + render(); + + const Wrapper = screen.getByText('Wrapper'); + + Wrapper.focus(); + Wrapper.click(); + + expect(onClickAway).toHaveBeenCalled(); + }); +}); diff --git a/packages/useclickaway/package.json b/packages/useclickaway/package.json new file mode 100644 index 0000000..23936a6 --- /dev/null +++ b/packages/useclickaway/package.json @@ -0,0 +1,42 @@ +{ + "name": "@artifak/useclickaway", + "version": "1.0.0", + "description": "A hook to handle click events when not occurring on targeted element.", + "keywords": [], + "author": "Julian Low", + "license": "MIT", + "sideEffects": false, + "main": "dist/useclickaway.cjs.js", + "module": "dist/useclickaway.esm.js", + "src": "src/index.ts", + "types": "dist/src/index.d.ts", + "directories": { + "src": "src", + "test": "__tests__" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/heyjul3s/artifak.git" + }, + "scripts": { + "build": "rimraf dist && builder", + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "bugs": { + "url": "https://github.com/heyjul3s/artifak/issues" + }, + "homepage": "https://github.com/heyjul3s/artifak#readme", + "devDependencies": { + "@artifak/bundler": "^1.1.4" + }, + "peerDependencies": { + "react": ">=17.0.1", + "react-dom": ">=17.0.1" + } +} diff --git a/packages/useclickaway/src/index.ts b/packages/useclickaway/src/index.ts new file mode 100644 index 0000000..f8a8768 --- /dev/null +++ b/packages/useclickaway/src/index.ts @@ -0,0 +1,18 @@ +import { useEffect, RefObject } from 'react'; + +export function useClickAway( + ref: RefObject, + onClickAway: (e: Event) => void +) { + const handleClick = (e: Event) => { + ref.current && !ref.current.contains(e.target as Node) && onClickAway(e); + }; + + useEffect(() => { + document.addEventListener('click', handleClick); + + return function unmountUseClickAway() { + document.removeEventListener('click', handleClick); + }; + }, [ref]); +} diff --git a/packages/useclickaway/tsconfig.json b/packages/useclickaway/tsconfig.json new file mode 100644 index 0000000..cd9bd18 --- /dev/null +++ b/packages/useclickaway/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "." + }, + "references": [], + "include": ["src"] +} diff --git a/packages/usedebouncedfn/README.md b/packages/usedebouncedfn/README.md new file mode 100644 index 0000000..df96593 --- /dev/null +++ b/packages/usedebouncedfn/README.md @@ -0,0 +1,80 @@ +# `@artifak/usedebouncefn` + +A react hook to debounce functions/callbacks. + +## Installation + +### Yarn + +```sh +yarn add @artifak/usedebouncefn +``` + +### NPM + +```sh +npm install @artifak/usedebouncefn +``` + +## Usage + +```ts +import React from 'react'; +import { useDebouncedFn } from 'artifak'; + +export function Debounce() { + const [searchString, setSearchString] = React.useState(''); + const [dropDownOptions, setDropdownOptions] = React.useState([]); + + const getDropDown = async val => { + await fetch('http://jsonplaceholder.typicode.com/posts') + .then(res => { + if (res.ok) { + return res.json(); + } + + throw res; + }) + .then(res => { + const data = res.filter(datum => { + return datum.title.includes(val); + }); + + setDropdownOptions(data); + }) + .catch(res => { + console.error(res); + }); + }; + + const debounceDropDown = useDebouncedFn( + nextValue => getDropDown(nextValue), + 1000 + ); + + const onInputChangeHandler = ev => { + const nextValue = ev.target.value; + setSearchString(nextValue); + debounceDropDown(nextValue); + }; + + return ( +
+ + + {dropDownOptions.length && ( +
+ {dropDownOptions.map(dropDownOption => ( +
{dropDownOption.title}
+ ))} +
+ )} +
+ ); +} +``` diff --git a/packages/usedebouncedfn/__tests__/useDebounceFn.test.ts b/packages/usedebouncedfn/__tests__/useDebounceFn.test.ts new file mode 100644 index 0000000..8034aa8 --- /dev/null +++ b/packages/usedebouncedfn/__tests__/useDebounceFn.test.ts @@ -0,0 +1,10 @@ +import { renderHook } from '@testing-library/react-hooks'; +import { useDebouncedFn } from '../src'; + +describe('@artifak/usedebouncefn', () => { + test('should match media query', () => { + const testFn = () => console.log('Hello World'); + const { result } = renderHook(() => useDebouncedFn(testFn, 300)); + expect(result.current).toBeDefined(); + }); +}); diff --git a/packages/usedebouncedfn/package.json b/packages/usedebouncedfn/package.json new file mode 100644 index 0000000..ba986bb --- /dev/null +++ b/packages/usedebouncedfn/package.json @@ -0,0 +1,48 @@ +{ + "name": "@artifak/usedebouncedfn", + "version": "1.0.0", + "description": "A React hook to debounce functions or callbacks.", + "keywords": [ + "react", + "debounce", + "hooks" + ], + "author": "Julian Low", + "license": "MIT", + "sideEffects": false, + "main": "dist/usedebouncedfn.cjs.js", + "module": "dist/usedebouncedfn.esm.js", + "src": "src/index.ts", + "types": "dist/src/index.d.ts", + "directories": { + "src": "src", + "test": "__tests__" + }, + "files": [ + "dist" + ], + "publishConfig": { + "access": "public" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/heyjul3s/artifak.git" + }, + "scripts": { + "build": "rimraf dist && builder", + "test": "echo \"Error: run tests from root\" && exit 1" + }, + "bugs": { + "url": "https://github.com/heyjul3s/artifak/issues" + }, + "homepage": "https://github.com/heyjul3s/artifak#readme", + "devDependencies": { + "@artifak/bundler": "^1.1.4", + "@types/lodash.debounce": "^4.0.6" + }, + "dependencies": { + "lodash.debounce": "^4.0.8", + "react-async-hook": "^3.6.2", + "use-constant": "^1.1.0" + } +} diff --git a/packages/usedebouncedfn/src/index.ts b/packages/usedebouncedfn/src/index.ts new file mode 100644 index 0000000..eb53cad --- /dev/null +++ b/packages/usedebouncedfn/src/index.ts @@ -0,0 +1,20 @@ +import { useRef, useCallback, useEffect } from 'react'; +import debounce from 'lodash.debounce'; + +export function useDebouncedFn< + Fn extends (...args: any[]) => any, + Delay extends number +>(callback: Fn, delay: Delay) { + const memoizedCallback = useCallback(callback, []); + const debouncedFn = useRef(debounce(memoizedCallback, delay)); + + useEffect(() => { + debouncedFn.current = debounce(memoizedCallback, delay); + + return function unmountUseDebounce() { + debouncedFn.current.cancel(); + }; + }, [debouncedFn, delay]); + + return debouncedFn.current; +} diff --git a/packages/usedebouncedfn/tsconfig.json b/packages/usedebouncedfn/tsconfig.json new file mode 100644 index 0000000..cd9bd18 --- /dev/null +++ b/packages/usedebouncedfn/tsconfig.json @@ -0,0 +1,9 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "dist", + "rootDir": "." + }, + "references": [], + "include": ["src"] +} diff --git a/packages/usematchmedia/package.json b/packages/usematchmedia/package.json index b0926d7..4cfaea5 100644 --- a/packages/usematchmedia/package.json +++ b/packages/usematchmedia/package.json @@ -4,7 +4,7 @@ "description": "A React hook for matching media queries.", "keywords": [], "author": "Julian Low", - "license": "ISC", + "license": "MIT", "sideEffects": false, "main": "dist/usematchmedia.cjs.js", "module": "dist/usematchmedia.esm.js", diff --git a/packages/usewindowsize/package.json b/packages/usewindowsize/package.json index d5a6755..d480712 100644 --- a/packages/usewindowsize/package.json +++ b/packages/usewindowsize/package.json @@ -4,7 +4,7 @@ "description": "A React hook that returns the window size on resize.", "keywords": [], "author": "Julian Low", - "license": "ISC", + "license": "MIT", "sideEffects": false, "main": "dist/usewindowsize.cjs.js", "module": "dist/usewindowsize.esm.js", diff --git a/yarn.lock b/yarn.lock index 3f08927..754c6b8 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2601,6 +2601,14 @@ estree-walker "^1.0.1" picomatch "^2.2.2" +"@rollup/pluginutils@^4.1.0": + version "4.1.0" + resolved "https://registry.yarnpkg.com/@rollup/pluginutils/-/pluginutils-4.1.0.tgz#0dcc61c780e39257554feb7f77207dceca13c838" + integrity sha512-TrBhfJkFxA+ER+ew2U2/fHbebhLT/l/2pRk0hfj9KusXUuRXd2v0R58AfaZK9VXDQ4TogOSEmICVrQAA3zFnHQ== + dependencies: + estree-walker "^2.0.1" + picomatch "^2.2.2" + "@scarf/scarf@^1.1.0": version "1.1.0" resolved "https://registry.yarnpkg.com/@scarf/scarf/-/scarf-1.1.0.tgz#b84b4a91cd938a688d36245b7a7db6fbc476a499" @@ -3481,6 +3489,18 @@ resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.7.tgz#98a993516c859eb0d5c4c8f098317a9ea68db9ad" integrity sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA== +"@types/lodash.debounce@^4.0.6": + version "4.0.6" + resolved "https://registry.yarnpkg.com/@types/lodash.debounce/-/lodash.debounce-4.0.6.tgz#c5a2326cd3efc46566c47e4c0aa248dc0ee57d60" + integrity sha512-4WTmnnhCfDvvuLMaF3KV4Qfki93KebocUF45msxhYyjMttZDQYzHkO639ohhk8+oco2cluAFL3t5+Jn4mleylQ== + dependencies: + "@types/lodash" "*" + +"@types/lodash@*": + version "4.14.168" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.168.tgz#fe24632e79b7ade3f132891afff86caa5e5ce008" + integrity sha512-oVfRvqHV/V6D1yifJbVRU3TMp8OT6o6BG+U9MkwuJ3U8/CsDHvalRpsxBqivn71ztOFZBTfJMvETbqHiaNSj7Q== + "@types/markdown-to-jsx@^6.11.0": version "6.11.3" resolved "https://registry.yarnpkg.com/@types/markdown-to-jsx/-/markdown-to-jsx-6.11.3.tgz#cdd1619308fecbc8be7e6a26f3751260249b020e" @@ -12154,6 +12174,11 @@ raw-loader@^4.0.1: loader-utils "^2.0.0" schema-utils "^3.0.0" +react-async-hook@^3.6.2: + version "3.6.2" + resolved "https://registry.yarnpkg.com/react-async-hook/-/react-async-hook-3.6.2.tgz#360018e5cd6ecc8841152a79875841b9e49dbdba" + integrity sha512-RkwHCJ8V7I6umKZLHneapuTRWf+eO4LOj0qUwUDsSn27jrAOcW6ClbV3x22Z4hVxH9bA0zb7y+ozDJDJ8PnZoA== + react-color@^2.17.0: version "2.19.3" resolved "https://registry.yarnpkg.com/react-color/-/react-color-2.19.3.tgz#ec6c6b4568312a3c6a18420ab0472e146aa5683d" @@ -12853,14 +12878,7 @@ resolve-url@^0.2.1: resolved "https://registry.yarnpkg.com/resolve-url/-/resolve-url-0.2.1.tgz#2c637fe77c893afd2a663fe21aa9080068e2052a" integrity sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= -resolve@1.17.0: - version "1.17.0" - resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" - integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== - dependencies: - path-parse "^1.0.6" - -resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.19.0, resolve@^1.3.2: +resolve@1.20.0, resolve@^1.1.6, resolve@^1.10.0, resolve@^1.12.0, resolve@^1.17.0, resolve@^1.18.1, resolve@^1.19.0, resolve@^1.3.2: version "1.20.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.20.0.tgz#629a013fb3f70755d6f0b7935cc1c2c5378b1975" integrity sha512-wENBPt4ySzg4ybFQW2TT1zMQucPK95HSh/nq2CFTZVOGut2+pQvSsgtda4d26YrYcr067wjbmzOG8byDPBX63A== @@ -12931,16 +12949,16 @@ rollup-plugin-terser@^7.0.2: serialize-javascript "^4.0.0" terser "^5.0.0" -rollup-plugin-typescript2@^0.29.0: - version "0.29.0" - resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.29.0.tgz#b7ad83f5241dbc5bdf1e98d9c3fca005ffe39e1a" - integrity sha512-YytahBSZCIjn/elFugEGQR5qTsVhxhUwGZIsA9TmrSsC88qroGo65O5HZP/TTArH2dm0vUmYWhKchhwi2wL9bw== +rollup-plugin-typescript2@^0.30.0: + version "0.30.0" + resolved "https://registry.yarnpkg.com/rollup-plugin-typescript2/-/rollup-plugin-typescript2-0.30.0.tgz#1cc99ac2309bf4b9d0a3ebdbc2002aecd56083d3" + integrity sha512-NUFszIQyhgDdhRS9ya/VEmsnpTe+GERDMmFo0Y+kf8ds51Xy57nPNGglJY+W6x1vcouA7Au7nsTgsLFj2I0PxQ== dependencies: - "@rollup/pluginutils" "^3.1.0" + "@rollup/pluginutils" "^4.1.0" find-cache-dir "^3.3.1" fs-extra "8.1.0" - resolve "1.17.0" - tslib "2.0.1" + resolve "1.20.0" + tslib "2.1.0" rollup@^2.39.0: version "2.39.0" @@ -14275,21 +14293,16 @@ ts-pnp@^1.1.6: resolved "https://registry.yarnpkg.com/ts-pnp/-/ts-pnp-1.2.0.tgz#a500ad084b0798f1c3071af391e65912c86bca92" integrity sha512-csd+vJOb/gkzvcCHgTGSChYpy5f1/XKNsmvBGO4JXS+z1v2HobugDz4s1IeFXM3wZB44uczs+eazB5Q/ccdhQw== -tslib@2.0.1: - version "2.0.1" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.0.1.tgz#410eb0d113e5b6356490eec749603725b021b43e" - integrity sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ== +tslib@2.1.0, tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3: + version "2.1.0" + resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" + integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== tslib@^1.8.1, tslib@^1.9.0: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== -tslib@^2.0.0, tslib@^2.0.1, tslib@^2.0.3: - version "2.1.0" - resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a" - integrity sha512-hcVC3wYEziELGGmEEXue7D75zbwIIVUMWAVbHItGPx0ziyXxrOMQx4rQEVEV45Ut/1IotuEvwqPopzIOkDMf0A== - tsutils@^3.17.1: version "3.20.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.20.0.tgz#ea03ea45462e146b53d70ce0893de453ff24f698" @@ -14636,6 +14649,11 @@ use-composed-ref@^1.0.0: dependencies: ts-essentials "^2.0.3" +use-constant@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/use-constant/-/use-constant-1.1.0.tgz#76d36a0edf16d4cc8565361f522b55da5f8f3f22" + integrity sha512-yrflEfv7Xv/W8WlYV6nwRH01K+2BpR4cWxuzY03yPRjYZuHixhGlvnJN5O2bRYrXGpJ4zy8QjFABGIQ2QXeBOA== + use-isomorphic-layout-effect@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz#7bb6589170cd2987a152042f9084f9effb75c225"