Comparing version 0.0.6 to 0.0.7
@@ -19,4 +19,13 @@ module.exports = (api, targets) => { | ||
], | ||
plugins: [[`@babel/plugin-transform-typescript`]], | ||
plugins: [ | ||
[`@babel/plugin-transform-typescript`], | ||
[ | ||
`babel-plugin-root-import`, | ||
{ | ||
rootPathSuffix: `./src`, | ||
rootPathPrefix: `~/`, | ||
}, | ||
], | ||
], | ||
} | ||
} |
module.exports = { | ||
rootDir: `.`, | ||
testEnvironment: `node`, | ||
modulePathIgnorePatterns: [`dist`], | ||
modulePathIgnorePatterns: [`dist`, `example`], | ||
moduleNameMapper: { | ||
"~/(.*)": `<rootDir>/src`, | ||
}, | ||
testRegex: `test.(ts|js)$`, | ||
@@ -6,0 +9,0 @@ coverageDirectory: `./coverage/`, |
{ | ||
"name": "corners", | ||
"version": "0.0.6", | ||
"version": "0.0.7", | ||
"private": false, | ||
@@ -8,14 +8,10 @@ "main": "dist/index.js", | ||
"scripts": { | ||
"serve": "nodemon ./example/dist/server.js", | ||
"build:server": "node ./example/esbuild.config.server.js", | ||
"build:client": "node ./example/esbuild.config.client.js", | ||
"prebuild:example": "rm -rf example/dist && mkdir example/dist && cp example/src/index.html example/dist", | ||
"build:example": "concurrently \"yarn build:server\" \"yarn build:client\"", | ||
"dev": "concurrently \"yarn build:example\" \"yarn serve\"", | ||
"dev": "esbuild example/client.tsx --outfile=example/web/client.js --servedir=example/web --serve=3001 --bundle --minify --sourcemap", | ||
"build:example": "esbuild example/client.tsx --outfile=example/web/client.js --bundle --minify --sourcemap", | ||
"predeploy": "yarn build:example", | ||
"deploy": "gh-pages -d example/web", | ||
"build": "node ./esbuild.config.js", | ||
"pretest": "tsc --noEmit", | ||
"test": "jest --watch --no-coverage", | ||
"test:cov": "jest --watch", | ||
"test:once": "jest", | ||
"prelint": "ls-lint", | ||
"lint": "eslint \"{src,test}/**/*.ts{,x}\"", | ||
@@ -28,6 +24,5 @@ "lint:fix": "yarn lint -- --fix" | ||
"@babel/preset-env": "^7.15.8", | ||
"@banka/eslint-config": "^1.0.0", | ||
"@banka/eslint-config-react": "^1.0.0", | ||
"@emotion/react": "^11.7.1", | ||
"@emotion/styled": "^11.6.0", | ||
"@ls-lint/ls-lint": "^1.10.0", | ||
"@types/express": "^4.17.13", | ||
@@ -41,2 +36,3 @@ "@types/faker": "^5.5.8", | ||
"@typescript-eslint/parser": "^5.0.0", | ||
"babel-plugin-root-import": "^6.6.0", | ||
"concurrently": "^6.4.0", | ||
@@ -48,5 +44,8 @@ "esbuild": "^0.14.5", | ||
"eslint-plugin-jest": "^25.2.1", | ||
"eslint-plugin-jsx-a11y": "^6.5.1", | ||
"eslint-plugin-prettier": "^4.0.0", | ||
"eslint-plugin-react": "^7.28.0", | ||
"express": "^4.17.1", | ||
"faker": "^5.5.3", | ||
"gh-pages": "^3.2.3", | ||
"husky": "^7.0.2", | ||
@@ -68,3 +67,3 @@ "jest": "^27.2.5", | ||
"extends": [ | ||
"@banka" | ||
"@banka/react" | ||
], | ||
@@ -81,3 +80,7 @@ "parserOptions": { | ||
"react-dom": ">=17.0.0" | ||
}, | ||
"homepage": "https://github.com/jeremybanka/corners#readme", | ||
"bugs": { | ||
"url": "https://github.com/jeremybanka/luum/issues" | ||
} | ||
} |
@@ -1,4 +0,73 @@ | ||
Let's face it: `border-radius` is terrible. | ||
## License | ||
<hr> | ||
<div align="center"> | ||
<img alt="corners logo" src="./corners.png"/> | ||
</div> | ||
<br> | ||
<p align="center"> | ||
<a href="https://bundlephobia.com/result?p=corners"> | ||
<img alt="Bundlephobia" src="https://img.shields.io/bundlephobia/minzip/corners?style=for-the-badge&labelColor=333"> | ||
</a> | ||
<a aria-label="Types" href="https://www.npmjs.com/package/corners"> | ||
<img alt="Types" src="https://img.shields.io/npm/types/corners?style=for-the-badge&labelColor=333"> | ||
</a> | ||
<a aria-label="Build status" href="https://github.com/jeremybanka/corners/actions/workflows/pipeline.yml"> | ||
<img alt="Build status" src="https://img.shields.io/github/workflow/status/jeremybanka/corners/CI/main?style=for-the-badge&labelColor=333"> | ||
</a> | ||
<a aria-label="NPM version" href="https://www.npmjs.com/package/corners"> | ||
<img alt="NPM Version" src="https://img.shields.io/npm/v/corners?style=for-the-badge&labelColor=333"> | ||
</a> | ||
<a aria-label="License" href="https://github.com/jeremybanka/corners/blob/main/LICENSE"> | ||
<img alt="MIT License" src="https://img.shields.io/github/license/jeremybanka/corners?style=for-the-badge&labelColor=333"> | ||
</a> | ||
</p> | ||
```shell | ||
npm i corners | ||
``` | ||
```shell | ||
yarn add corners | ||
``` | ||
<hr> | ||
Create react components with angled or smooth-rounded corners. | ||
## Features | ||
- [x] Use one of the preset component factories like `rounded`: `const MyButton = rounded.button` | ||
- [x] Make your own component factory with `corners`: `const dogEared = corners(chamfer, null, null, null).size(40)` | ||
- [x] CSS clip-path ensures corners are rendered as empty space | ||
- [x] Components may be dynamically sized: a resize observer is used to detect changes to component size and update the clip-path | ||
- [ ] Specify the corner size when calling a factory (e.g. `rounded.cornerSize(10).div`) | ||
- [ ] Support for drop shadows (e.g. `corners(round).options({shadow: {...}}).div`) | ||
- [ ] Support for positioning elements outside of the target element | ||
## API | ||
### corners(...cornerFns).size(cornerSize) | ||
```jsx harmony | ||
import type { FC } from "react" | ||
import corners, { chamfer } from "corners" | ||
const upperLeftDogeared = corners(null, null, null, chamfer).size(20) | ||
const DogearedDiv = upperLeftDogeared.div | ||
const MyComponent: FC = () => ( | ||
<DogEaredDiv style={{ background: "black" }}> | ||
Hello, World! | ||
</DogEaredDiv> | ||
) | ||
``` | ||
| Argument | Type | Required? | Description | | ||
| ---------- | ----------------------------------------------- | --------- | --------------------------------------------------------------------------------- | | ||
| cornerFns | <code>Maybe<[DrawCorner](#drawcorner)>[]</code> | Yes | 1, 2, or 4 functions that specify the corners for this factory in clockwise order | | ||
| cornerSize | number | Yes | Equivalent to the `N`px given in css `border-radius` | | ||
## LICENSE | ||
MIT |
@@ -1,2 +0,2 @@ | ||
export const HTML_ELEMENT_NAMES = [ | ||
export const HTML_ELEMENT_NAMES: ReadonlyArray<string> = [ | ||
`a`, | ||
@@ -116,4 +116,5 @@ `abbr`, | ||
`webview`, | ||
] as const | ||
export type ElementOf<T extends Readonly<string[]>> = T[number] | ||
] | ||
export type ElementOf<T extends ReadonlyArray<any>> = T[number] | ||
export type HTMLElementName = ElementOf<typeof HTML_ELEMENT_NAMES> |
113
src/index.ts
@@ -1,105 +0,16 @@ | ||
import type { FC, ForwardRefExoticComponent } from "react" | ||
import { useRef, createElement, useId } from "react" | ||
import { corners } from "./react/createComponentFactory" | ||
import { HTMLElementName, HTML_ELEMENT_NAMES } from "./constants/html" | ||
import { useSize } from "~/hooks/useSize" | ||
import { | ||
chamfer, | ||
createPathfinder, | ||
DrawCorner, | ||
Pathfinder, | ||
round, | ||
} from "~/utils/svg" | ||
export function withCorners<P>( | ||
pathfinder: Pathfinder, | ||
WrappedComponent: ForwardRefExoticComponent<P> | string, | ||
cornerSize?: number | ||
): FC<P> { | ||
const WithCorners: FC<P> = (props) => { | ||
const pathId = useId ? useId() : Math.random().toString() | ||
const nodeRef = useRef<HTMLElement>(null) | ||
const size = useSize(nodeRef) | ||
console.log(pathId, size) | ||
const d = pathfinder(size.height, size.width, cornerSize) | ||
return createElement( | ||
WrappedComponent, | ||
{ | ||
...props, | ||
ref: nodeRef, | ||
style: { | ||
clipPath: `url(#${pathId})`, | ||
}, | ||
}, | ||
[ | ||
createElement( | ||
`svg`, | ||
{ | ||
width: `10`, | ||
height: `10`, | ||
viewBox: `0 0 10 10`, | ||
style: { position: `absolute`, opacity: 0, pointerEvents: `none` }, | ||
}, | ||
[ | ||
createElement( | ||
`clipPath`, | ||
{ id: pathId, clipPathUnits: `objectBoundingBox` }, | ||
[createElement(`path`, { d })] | ||
), | ||
] | ||
), | ||
props.children, | ||
] | ||
) | ||
} | ||
return WithCorners | ||
export type Point2d = { | ||
x: number | ||
y: number | ||
} | ||
const enhanceComponentFactory = ( | ||
cornerFunction: <P>( | ||
WrappedComponent: ForwardRefExoticComponent<P> | string, | ||
cornerSize?: number | ||
) => FC<P> | ||
): typeof cornerFunction & { | ||
[K in HTMLElementName]: ReturnType<typeof cornerFunction> | ||
} => { | ||
const enhancedCornerFunction = cornerFunction as typeof cornerFunction & { | ||
[K in HTMLElementName]: ReturnType<typeof cornerFunction> | ||
} | ||
HTML_ELEMENT_NAMES.forEach((name) => { | ||
enhancedCornerFunction[name] = cornerFunction(name) | ||
}) | ||
return enhancedCornerFunction | ||
} | ||
export type DrawCorner = (p1: Point2d, p2: Point2d, idx: number) => string[] | ||
const createComponentFactory = ( | ||
cornerSize: number, | ||
...corners: (DrawCorner | null)[] | ||
): typeof componentFactory & { | ||
[K in HTMLElementName]: ReturnType<typeof componentFactory> | ||
} => { | ||
const pathfinder = createPathfinder(cornerSize, ...corners) | ||
const componentFactory = function <P>( | ||
WrappedComponent: ForwardRefExoticComponent<P> | string, | ||
cornerSize?: number | ||
): FC<P> { | ||
return withCorners(pathfinder, WrappedComponent, cornerSize) | ||
} | ||
return enhanceComponentFactory(componentFactory) | ||
} | ||
interface ICorners { | ||
(...cornerFns: (DrawCorner | null)[]): { | ||
size: (s: number) => ReturnType<typeof createComponentFactory> | ||
} | ||
} | ||
const corners: ICorners = (...cornerFns) => ({ | ||
size: (s) => createComponentFactory(s, ...cornerFns), | ||
}) | ||
export const rounded = corners(round).size(20) | ||
export const chamfered = corners(chamfer).size(20) | ||
export const semiChamfered = corners(null, chamfer).size(20) | ||
export const demiChamfered = corners(chamfer, null).size(20) | ||
export default corners | ||
export * from "./utils/svg/createCorner" | ||
export * from "./preset/factories" | ||
export * from "./preset/corners" | ||
export * from "./react/useSize" | ||
export * from "./utils/svg/createPathfinder" | ||
export * from "./utils/svg/writePathPoint" |
@@ -1,8 +0,19 @@ | ||
import { drawRoundedBox } from "~/utils/svg" | ||
import type { CornerSpec } from "~/index" | ||
import { round, createCorner } from "~/index" | ||
describe(`dumb tests`, () => { | ||
it(`dumb test`, () => { | ||
const result = drawRoundedBox(10, 10, 1) | ||
console.log(result) | ||
const RoundedSpec: CornerSpec = [ | ||
[`curve`, { x: 0.438, y: 0 }, { x: 0.68, y: 0 }, { x: 0.84, y: 0.16 }], | ||
[`symmetric`, { x: 1, y: 0.562 }], | ||
] | ||
describe(`createCorner`, () => { | ||
it(`creates an equivalent to round`, () => { | ||
const roundFromCreate = createCorner(RoundedSpec) | ||
const pointA = { x: 90, y: 0 } | ||
const pointB = { x: 100, y: 10 } | ||
// const findRounded10FromCreate = createPathfinder(10, roundFromCreate) | ||
// const findRounded10 = createPathfinder(10, round) | ||
expect(round(pointA, pointB, 0)).toEqual(roundFromCreate(pointA, pointB, 0)) | ||
// expect(findRounded10FromCreate(400, 300)).toEqual(findRounded10(400, 300)) | ||
}) | ||
}) |
@@ -30,3 +30,3 @@ { | ||
"target": "ES2019", | ||
"types": ["react/next", "react-dom/next"], | ||
"types": ["react/next", "react-dom/next", "jest"], | ||
"typeRoots": ["node_modules/@types", "types"] | ||
@@ -33,0 +33,0 @@ }, |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Misc. License Issues
License(Experimental) A package's licensing information has fine-grained problems.
Found 1 instance in 1 package
No bug tracker
MaintenancePackage does not have a linked bug tracker in package.json.
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found.
Found 1 instance in 1 package
No website
QualityPackage does not have a website.
Found 1 instance in 1 package
231052
29
0
0
1499
1
1
74
37