Skip to content

Commit ded29bb

Browse files
committed
Initial commit
0 parents  commit ded29bb

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

43 files changed

+1902
-0
lines changed

.gitattributes

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
* text eol=lf

.gitignore

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
/coverage/
2+
3+
dist/
4+
node_modules/
5+
.DS_Store
6+
package-lock.json
7+
yarn.lock
8+
*.log

.husky/pre-commit

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
#!/usr/bin/env sh
2+
. "$(dirname -- "$0")/_/husky.sh"
3+
4+
npm test \
5+
&& npx lint-staged \
6+
&& npm run lint

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
# Changelog
2+
3+
## 1.0.0 (DEV)
4+
5+
- Initial release.

LICENSE

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
MIT License
2+
3+
Copyright (c) 2020-2022 Vovan-VE
4+
5+
Permission is hereby granted, free of charge, to any person obtaining a copy
6+
of this software and associated documentation files (the "Software"), to deal
7+
in the Software without restriction, including without limitation the rights
8+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9+
copies of the Software, and to permit persons to whom the Software is
10+
furnished to do so, subject to the following conditions:
11+
12+
The above copyright notice and this permission notice shall be included in all
13+
copies or substantial portions of the Software.
14+
15+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21+
SOFTWARE.

README.md

Lines changed: 280 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,280 @@
1+
# `@cubux/react-utils`
2+
3+
[![NPM latest](https://img.shields.io/npm/v/@cubux/react-utils.svg)](https://www.npmjs.com/package/@cubux/react-utils)
4+
5+
Utility functions related to React.
6+
7+
## Install
8+
9+
```sh
10+
npm i @cubux/react-utils
11+
```
12+
13+
## API
14+
15+
### `deprecated()` function
16+
17+
Wrap a component to emit deprecation warning.
18+
19+
```ts
20+
function deprecated<T extends React.ComponentType<any>>(
21+
Origin: T,
22+
options?: Options,
23+
): React.ComponentType<React.ComponentProps<T>>
24+
```
25+
26+
The `options` object can have the following properties:
27+
28+
| property | type | default | description |
29+
|-----------|-----------|---------|-------------------------------------------------------------------------------------------------------------------------------------|
30+
| `comment` | `string` || Extra comment to emit in console warning. Can also be obtained later with `getDevInfo()`. |
31+
| `tag` | `string` || A `tag` property to bypass to `setDevInfo()`. Can be obtained later with `getDevInfo()`. |
32+
| `withRef` | `boolean` | `false` | Whether to use `React.forwardRef()`, so `ref` React attribute can be used to refer to underlying element of the `Origin` component. |
33+
34+
A `getDevInfo()` can be used in development env to get details about deprecation.
35+
This can be used on "dev only" internal pages.
36+
37+
**Notice:** Component's (static) properties like `propTypes`, `defaultProps`,
38+
etc. are not touched because it was not necessary yet.
39+
40+
Does nothing in production env and returns origin component as is.
41+
42+
See also: `DevInfo`, `getDevInfo()`.
43+
44+
```tsx
45+
import { deprecated, getDevInfo } from '@cubux/react-utils';
46+
47+
function OldComponentOrigin() {
48+
return <div>...</div>;
49+
}
50+
51+
const OldComponent = deprecated(OldComponentOrigin, {
52+
comment: 'Use `NewComponent` instead.',
53+
});
54+
55+
if (process.env.NODE_ENV === 'development') {
56+
console.log(getDevInfo(OldComponent));
57+
}
58+
```
59+
60+
### `DevInfo` type
61+
62+
```ts
63+
interface DevInfo {
64+
type: string;
65+
tag?: string;
66+
Orig?: React.ComponentType<any>;
67+
comment?: string;
68+
}
69+
```
70+
71+
See also: `getDevInfo()`, `setDevInfo()`.
72+
73+
### `getDevInfo()` function
74+
75+
Get `DevInfo` for the given generated component.
76+
77+
```ts
78+
function getDevInfo<T extends DevInfo = DevInfo>(
79+
subject: ComponentType<any>,
80+
): T | undefined
81+
```
82+
83+
Does nothing in production env and always returns `undefined`.
84+
85+
An example can be found in `deprecated()`.
86+
87+
See also: `setDevInfo()`.
88+
89+
### `setDevInfo()` function
90+
91+
Set given `DevInfo` for the given component.
92+
93+
```ts
94+
function setDevInfo<T extends DevInfo>(
95+
subject: ComponentType<any>,
96+
info: T,
97+
): void
98+
```
99+
100+
Does nothing in production env.
101+
102+
See also: `getDevInfo()`.
103+
104+
### `isElementOf()` function
105+
106+
Check whether the given element is an element of the given component.
107+
108+
```ts
109+
function isElementOf<T extends React.ElementType>(
110+
element: any,
111+
component: T,
112+
): element is React.ReactElement<React.ComponentPropsWithoutRef<T>, T>
113+
```
114+
115+
**NOTICE:** The `react-is` peer dependency must be installed to use this
116+
function.
117+
118+
Enhanced version of `isElement()` from `react-is` package to use as Type Guard
119+
function.
120+
121+
```tsx
122+
import { FC, PropsWithChildren } from 'react';
123+
124+
interface FooProps {
125+
x: number;
126+
y?: string;
127+
}
128+
const Foo: FC<FooProps> = () => <div />;
129+
130+
const element = <Foo x={42} y="foo bar">baz</Foo>;
131+
if (isElementOf(element, Foo)) {
132+
const { props } = element;
133+
// `props` type is `PropsWithChildren<FooProps>`
134+
console.log(props);
135+
// { x: 42, y: "foo bar", children: "baz" }
136+
}
137+
138+
console.log(isElementOf(element, 'div'));
139+
// => `false`
140+
console.log(isElementOf(<div/>, Foo));
141+
// => `false`
142+
console.log(isElementOf(<div/>, 'a'));
143+
// => `false`
144+
console.log(isElementOf(<div/>, 'div'));
145+
// => `true`
146+
```
147+
148+
### `SvgFC` type
149+
150+
A `React.FunctionComponent` component receiving properties for `<svg/>` element.
151+
152+
```ts
153+
type SvgFC = React.FC<React.SVGProps<SVGSVGElement>>
154+
```
155+
156+
A component of this signature can be given for example with `svgo` package,
157+
which is used internally in CRA `react-scripts` for SVG files imports like
158+
following:
159+
160+
```ts
161+
// You are using CRA `react-scripts`, so
162+
import { ReactComponent } from './file.svg';
163+
```
164+
165+
Or writing such component manually:
166+
167+
```tsx
168+
const MySvg: SvgFC = (props) => (
169+
<svg
170+
{...props}
171+
width={16}
172+
height={16}
173+
viewBox="0 0 16 16"
174+
fill="currentColor"
175+
>
176+
<path d="M2,5 h12 v6 z" />
177+
</svg>
178+
);
179+
```
180+
181+
### `svgTransform` function
182+
183+
Creates new `SvgFC` component by reusing origin `SvgFC` applying a CSS
184+
`transform`.
185+
186+
```ts
187+
type CssTransform = string;
188+
type CalcTransform = (prev: CssTransform | undefined) => CssTransform;
189+
190+
function svgTransform(
191+
Orig: SvgFC,
192+
transform: CssTransform | CalcTransform,
193+
): SvgFC
194+
```
195+
196+
A `getDevInfo()` can be used in development env to get details about underlying
197+
transform. This can be used on "dev only" internal pages.
198+
199+
```tsx
200+
const MySvg: SvgFC = (props) => (
201+
<svg
202+
{...props}
203+
width={16}
204+
height={16}
205+
viewBox="0 0 16 16"
206+
fill="currentColor"
207+
>
208+
<path d="M2,5 h12 v6 z" />
209+
</svg>
210+
);
211+
212+
const MySvg1 = svgTransform(MySvg, 'scaleX(0.75), rotate(45deg)');
213+
```
214+
215+
See also: `getDevInfo()`.
216+
217+
### `svgFlipH()` function
218+
219+
Creates new `SvgFC` applying horizontal flip to origin `SvgFC` with CSS
220+
transform.
221+
222+
```ts
223+
function svgFlipH(Orig: SvgFC): SvgFC
224+
```
225+
226+
Uses `svgTransform()` internally with `'scaleX(-1)'` transform value.
227+
228+
See also: `transform()`, `svgFlipV()`, `svgRot180()`.
229+
230+
### `svgFlipV()` function
231+
232+
Creates new `SvgFC` applying vertical flip to origin `SvgFC` with CSS
233+
transform.
234+
235+
```ts
236+
function svgFlipV(Orig: SvgFC): SvgFC
237+
```
238+
239+
Uses `svgTransform()` internally with `'scaleY(-1)'` transform value.
240+
241+
See also: `transform()`, `svgFlipH()`, `svgRot180()`.
242+
243+
### `svgRot180()` function
244+
245+
Creates new `SvgFC` applying rotation 180 deg to origin `SvgFC` with CSS
246+
transform.
247+
248+
```ts
249+
function svgRot180(Orig: SvgFC): SvgFC
250+
```
251+
252+
Uses `svgTransform()` internally with `'rotate(180deg)'` transform value.
253+
254+
See also: `transform()`, `svgFlipH()`, `svgFlipV()`.
255+
256+
### `svgRot90L()` function
257+
258+
Creates new `SvgFC` applying rotation 90 deg anti-clockwise to origin `SvgFC`
259+
with CSS transform.
260+
261+
```ts
262+
function svgRot90L(Orig: SvgFC): SvgFC
263+
```
264+
265+
Uses `svgTransform()` internally with `'rotate(-90deg)'` transform value.
266+
267+
See also: `transform()`, `svgRot180()`.
268+
269+
### `svgRot90R()` function
270+
271+
Creates new `SvgFC` applying rotation 90 deg clockwise to origin `SvgFC` with
272+
CSS transform.
273+
274+
```ts
275+
function svgRot90R(Orig: SvgFC): SvgFC
276+
```
277+
278+
Uses `svgTransform()` internally with `'rotate(90deg)'` transform value.
279+
280+
See also: `transform()`, `svgRot180()`.

examples/react-18/index.html

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<head>
4+
<meta charset="UTF-8" />
5+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
6+
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
7+
<title>Playground</title>
8+
</head>
9+
10+
<body>
11+
<div id="root"></div>
12+
<script type="module" src="./index.tsx"></script>
13+
</body>
14+
</html>

examples/react-18/index.tsx

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
// import 'react-app-polyfill/stable';
2+
import React, { FC } from 'react';
3+
import { createRoot } from 'react-dom/client';
4+
import { svgRot90L } from '@cubux/react-utils';
5+
import { SvgFC, svgRot90R } from '@cubux/react-utils/svg';
6+
7+
const SvgIcon: SvgFC = (props) => (
8+
<svg
9+
{...props}
10+
width={16}
11+
height={16}
12+
viewBox="0 0 16 16"
13+
fill="currentColor"
14+
>
15+
<path d="M2,5 h12 v6 z" />
16+
</svg>
17+
);
18+
19+
const IconR1: SvgFC = svgRot90R(SvgIcon);
20+
const IconL1: SvgFC = svgRot90L(SvgIcon);
21+
22+
const App: FC = () => {
23+
return (
24+
<div>
25+
<h1>Foo bar</h1>
26+
<div>
27+
<IconL1 />
28+
<SvgIcon />
29+
<IconR1 />
30+
</div>
31+
</div>
32+
);
33+
};
34+
35+
createRoot(document.getElementById('root')!).render(<App />);

0 commit comments

Comments
 (0)