Skip to content

Commit 77637a8

Browse files
author
k.golikov
committed
Add rooks demo page
1 parent 6b230e4 commit 77637a8

File tree

8 files changed

+314
-7
lines changed

8 files changed

+314
-7
lines changed

src/components/NpmLink.tsx

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,16 @@ import getNpmPackageLink from '../utils/getNpmPackageLink';
44

55
interface Props {
66
packageName: string;
7+
plain?: boolean;
78
children?: ReactNode;
89
}
910

10-
const NpmLink: FunctionComponent<Props> = ({ packageName, children }) => {
11+
const NpmLink: FunctionComponent<Props> = ({ packageName, plain, children }) => {
1112
const link = getNpmPackageLink(packageName);
1213

13-
return (
14-
<ExternalLink href={link}>
15-
<code>{children || packageName}</code>
16-
</ExternalLink>
17-
);
14+
const text = children || packageName;
15+
16+
return <ExternalLink href={link}>{plain ? text : <code>{text}</code>}</ExternalLink>;
1817
};
1918

2019
export default React.memo(NpmLink);

src/constants/router/menuItems.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import {
1111
StarFilled,
1212
ToolFilled
1313
} from '@ant-design/icons';
14+
import ElectricBoltIcon from '@mui/icons-material/ElectricBolt';
1415
import { routes } from './routes';
1516

1617
const menuItems: MenuItem[] = [
@@ -122,6 +123,10 @@ const menuItems: MenuItem[] = [
122123
{
123124
route: routes.notFound,
124125
icon: QuestionCircleFilled
126+
},
127+
{
128+
route: routes.rooksDemo,
129+
icon: ElectricBoltIcon
125130
}
126131
]
127132
},

src/constants/router/routes.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import DataUrlViewPage from '../../pages/dataUrlViewPage/DataUrlViewPage';
3131
import RouteContextInitializer from '../../layouts/RouteContextInitializer';
3232
import JsonToYamlPage from '../../pages/jsonToYamlPage/JsonToYamlPage';
3333
import CounterPage from '../../pages/counterPage/CounterPage';
34+
import RooksDemoPage from '../../pages/rooksDemoPage/RooksDemoPage';
3435

3536
export interface AppRoute extends Omit<RouteProps, 'element'> {
3637
path: string;
@@ -66,6 +67,7 @@ type AppRoutesMap = Readonly<{
6667
dataUrl: AppRoute;
6768
dataUrlView: AppRoute;
6869
counter: AppRoute;
70+
rooksDemo: AppRoute;
6971
settings: AppRoute;
7072
about: AppRoute;
7173
}>;
@@ -202,6 +204,11 @@ export const routes: AppRoutesMap = {
202204
component: CounterPage,
203205
title: 'Counter'
204206
},
207+
rooksDemo: {
208+
path: '/other/rooks-demo',
209+
component: RooksDemoPage,
210+
title: 'Rooks Demo'
211+
},
205212
settings: {
206213
path: '/settings',
207214
component: SettingsPage,

src/pages/counterPage/components/counterItem/CounterItem.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ const CounterItem: FunctionComponent<Props> = ({ counter, onChange, onRemove })
4343
<Flex row>
4444
<Input value={counter.name} onChange={handleNameChange} className={styles.input} />
4545
<ButtonGroup>
46-
<Popover
46+
<Popover //TODO use Dropdown (https://ant.design/components/dropdown/) instead
4747
placement="bottomRight"
4848
content={
4949
<Flex column gap={8}>

src/pages/rooksDemoPage/RooksDemoPage.module.scss

Whitespace-only changes.
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
import React, { ChangeEvent, EventHandler, FunctionComponent, useCallback, useEffect, useState } from 'react';
2+
import PageContainer from '../../layouts/pages/pageContainer/PageContainer';
3+
import NpmLink from '../../components/NpmLink';
4+
import { Button, Col, Input, Space } from 'antd';
5+
import RooksHookHeading from './components/RooksHookHeading';
6+
import {
7+
useCounter,
8+
useDebounce,
9+
useDidMount,
10+
useInput,
11+
useIntervalWhen,
12+
useLocalstorageState,
13+
useSessionstorageState,
14+
useToggle,
15+
useWillUnmount
16+
} from 'rooks';
17+
import delay from '../../utils/delay';
18+
import Flex from '../../components/flex/Flex';
19+
import { MinusOutlined, PlusOutlined, ReloadOutlined } from '@ant-design/icons';
20+
import Text from 'antd/lib/typography/Text';
21+
import getLocalStorageKey from '../../utils/getLocalStorageKey';
22+
import TextArea from 'antd/lib/input/TextArea';
23+
import searchWords from './data/words.json';
24+
import { isEmpty } from 'lodash';
25+
import pluralize from 'pluralize';
26+
27+
interface StorageState {
28+
text: string;
29+
}
30+
31+
const initialStorageState: StorageState = {
32+
text: 'Initial text'
33+
};
34+
35+
const titleExtra = (
36+
<NpmLink packageName="rooks" plain>
37+
npm package
38+
</NpmLink>
39+
);
40+
41+
const RooksDemoPage: FunctionComponent = () => {
42+
const [didMountValue, setDidMountValue] = useState<string>('not mounted');
43+
44+
//! useInput
45+
const debounceInput = useInput('');
46+
const [debounceResults, setDebounceResults] = useState<string[]>(searchWords);
47+
48+
//! useCounter
49+
const {
50+
value: timeoutCounter,
51+
increment: incrementTimeoutCounter,
52+
decrement: decrementTimeoutCounter,
53+
reset: resetTimeoutCounter
54+
} = useCounter(0);
55+
56+
//! useToggle
57+
const [isIntervalEnabled, toggleIntervalEnabled] = useToggle(true);
58+
59+
//! useLocalstorageState
60+
const [localStorageState, setLocalStorageState] = useLocalstorageState<StorageState>(
61+
getLocalStorageKey('rooks-demo', 'localStorageState'),
62+
initialStorageState
63+
);
64+
//! useSessionstorageState
65+
const [sessionStorageState, setSessionStorageState] = useSessionstorageState<StorageState>(
66+
getLocalStorageKey('rooks-demo', 'sessionStorageState'),
67+
initialStorageState
68+
);
69+
70+
//! useDidMount
71+
useDidMount(async () => {
72+
await delay(1000);
73+
setDidMountValue('mounted 1s ago');
74+
});
75+
76+
//! useWillUnmount
77+
useWillUnmount(async () => {
78+
await delay(10);
79+
setDidMountValue('unmounted 0.01s ago');
80+
});
81+
82+
const timeoutCallback = useCallback(() => {
83+
incrementTimeoutCounter();
84+
}, []);
85+
86+
//! useIntervalWhen
87+
useIntervalWhen(timeoutCallback, 500, isIntervalEnabled);
88+
89+
const handleLocalStorageTextChange = useCallback<EventHandler<ChangeEvent<HTMLTextAreaElement>>>((event) => {
90+
setLocalStorageState({
91+
...localStorageState,
92+
text: event.target.value
93+
});
94+
}, []);
95+
96+
const handleSessionStorageTextChange = useCallback<EventHandler<ChangeEvent<HTMLTextAreaElement>>>((event) => {
97+
setSessionStorageState({
98+
...sessionStorageState,
99+
text: event.target.value
100+
});
101+
}, []);
102+
103+
const handleEveryDebounceInputChange = useCallback((searchQuery: string) => {
104+
setDebounceResults(
105+
searchWords.filter((word) => {
106+
if (isEmpty(searchQuery)) {
107+
return true;
108+
}
109+
110+
return word.trim().toLocaleLowerCase().includes(searchQuery.trim().toLocaleLowerCase());
111+
})
112+
);
113+
}, []);
114+
115+
const handleDebounceInputChange = useDebounce(handleEveryDebounceInputChange, 500);
116+
117+
useEffect(() => handleDebounceInputChange(debounceInput.value), [debounceInput.value]);
118+
119+
return (
120+
<PageContainer title="Rooks demo" titleExtra={titleExtra}>
121+
<Space direction="vertical" size="middle">
122+
<Col>
123+
<RooksHookHeading hooks={['useDidMount', 'useWillUnmount']} />
124+
{didMountValue}
125+
</Col>
126+
<Col>
127+
<RooksHookHeading hooks={['useIntervalWhen', 'useCounter', 'useToggle']} />
128+
<Flex column gap={6}>
129+
<Flex row gap={6}>
130+
<Button onClick={toggleIntervalEnabled}>
131+
<Text type={isIntervalEnabled ? 'success' : 'danger'}>
132+
{isIntervalEnabled ? 'Interval enabled' : 'Interval disabled'}
133+
</Text>
134+
</Button>
135+
</Flex>
136+
<Flex row gap={6} align="center">
137+
<Button icon={<PlusOutlined />} size="small" onClick={incrementTimeoutCounter} />
138+
<Button icon={<MinusOutlined />} size="small" onClick={decrementTimeoutCounter} />
139+
<Button icon={<ReloadOutlined />} size="small" onClick={resetTimeoutCounter} />
140+
<h3 className="m-0">{timeoutCounter}</h3>
141+
</Flex>
142+
</Flex>
143+
</Col>
144+
<Col>
145+
<RooksHookHeading hooks={['useLocalstorageState']} />
146+
<TextArea value={localStorageState.text} onChange={handleLocalStorageTextChange} />
147+
</Col>
148+
<Col>
149+
<RooksHookHeading hooks={['useSessionstorageState']} />
150+
<TextArea value={sessionStorageState.text} onChange={handleSessionStorageTextChange} />
151+
</Col>
152+
<Col>
153+
<RooksHookHeading hooks={['useDebounce', 'useInput']} />
154+
<Flex column gap={6}>
155+
<Input {...debounceInput} placeholder="Search..." />
156+
<TextArea
157+
readOnly
158+
placeholder="Results"
159+
rows={4}
160+
value={debounceResults.join('\n')}
161+
showCount={{ formatter: () => pluralize('result', debounceResults.length, true) }}
162+
/>
163+
</Flex>
164+
</Col>
165+
</Space>
166+
</PageContainer>
167+
);
168+
};
169+
170+
export default RooksDemoPage;
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React, { FunctionComponent } from 'react';
2+
import Flex from '../../../components/flex/Flex';
3+
import ExternalLink from '../../../components/ExternalLink';
4+
import { isEqual, sortBy } from 'lodash';
5+
6+
interface Props {
7+
hooks: string[];
8+
}
9+
10+
const getLink = (hook: string) => `https://react-hooks.org/docs/${hook}`;
11+
12+
const RooksHookHeading: FunctionComponent<Props> = ({ hooks }) => {
13+
return (
14+
<h3>
15+
<Flex row gap={8}>
16+
{hooks.map((hook) => (
17+
<ExternalLink href={getLink(hook)}>{hook}</ExternalLink>
18+
))}
19+
</Flex>
20+
</h3>
21+
);
22+
};
23+
24+
export default React.memo(RooksHookHeading, (prev, next) => isEqual(sortBy(prev), sortBy(next)));
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
[
2+
"galaxy",
3+
"flow",
4+
"pastel",
5+
"level",
6+
"deal",
7+
"casualty",
8+
"gallery",
9+
"chimpanzee",
10+
"mourning",
11+
"fur",
12+
"illness",
13+
"exchange",
14+
"stand",
15+
"forbid",
16+
"rhetoric",
17+
"story",
18+
"detective",
19+
"epicalyx",
20+
"beach",
21+
"gradient",
22+
"pledge",
23+
"monopoly",
24+
"bin",
25+
"island",
26+
"cycle",
27+
"liver",
28+
"watch",
29+
"uncle",
30+
"tip",
31+
"tendency",
32+
"trance",
33+
"seize",
34+
"reactor",
35+
"charm",
36+
"absorption",
37+
"opinion",
38+
"prison",
39+
"tropical",
40+
"heart",
41+
"bike",
42+
"pass",
43+
"radiation",
44+
"trust",
45+
"salvation",
46+
"period",
47+
"salad",
48+
"imposter",
49+
"cutting",
50+
"systematic",
51+
"last",
52+
"pioneer",
53+
"cheap",
54+
"volume",
55+
"robot",
56+
"continuation",
57+
"archive",
58+
"world",
59+
"bold",
60+
"sector",
61+
"chair",
62+
"tough",
63+
"script",
64+
"border",
65+
"detail",
66+
"pillow",
67+
"talented",
68+
"action",
69+
"rhythm",
70+
"writer",
71+
"trace",
72+
"psychology",
73+
"formulate",
74+
"genuine",
75+
"credibility",
76+
"contract",
77+
"blade",
78+
"desert",
79+
"sensitive",
80+
"fisherman",
81+
"hesitate",
82+
"art",
83+
"slide",
84+
"patrol",
85+
"cottage",
86+
"dead",
87+
"yearn",
88+
"waste",
89+
"coat",
90+
"information",
91+
"button",
92+
"depend",
93+
"owner",
94+
"interface",
95+
"hard",
96+
"line",
97+
"slip",
98+
"dance",
99+
"nerve",
100+
"nail",
101+
"gate"
102+
]

0 commit comments

Comments
 (0)