Skip to content

Commit 61b146e

Browse files
committed
feat(react-login-page): Add index/tagName props to Block.
1 parent 9f03252 commit 61b146e

File tree

6 files changed

+247
-44
lines changed

6 files changed

+247
-44
lines changed

core/README.md

Lines changed: 156 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -17,11 +17,11 @@
1717
Getting Started
1818
===
1919

20-
Some `react` login pages, which can be used quickly after installation.
20+
Encapsulated login page components based on `react-login-page` basic components are provided for quick installation and use. These components help streamline the process of creating login pages and offer flexible APIs for modifying and packaging these components.
2121

2222
## React Login Page
2323

24-
Current components are used to quickly develop more `login` pages
24+
This component is designed to facilitate the development of additional login pages and offers a highly flexible API for modifying packaged login page components.
2525

2626
### Install
2727

@@ -53,8 +53,8 @@ const Demo = () => {
5353
);
5454
}}
5555
</Render>
56-
<Login.Block name="logo">⚛️</Login.Block>
57-
<Login.Block name="title">Login</Login.Block>
56+
<Login.Block name="logo" tagName="span">⚛️</Login.Block>
57+
<Login.Block name="title" tagName="span">Login</Login.Block>
5858
<Login.Input name="username" placeholder="Please input Username" />
5959
<Login.Input name="password" placeholder="please enter password" />
6060
<Login.Button name="submit" type="submit">Submit</Login.Button>
@@ -65,49 +65,105 @@ const Demo = () => {
6565
export default Demo;
6666
```
6767

68+
Change the control order by using `index`, Provide more flexible API encapsulation.
69+
70+
```jsx mdx:preview
71+
import React from 'react';
72+
import Login, { Render } from 'react-login-page';
73+
74+
const Demo = () => {
75+
return (
76+
<Login>
77+
<Render>
78+
{({ blocks }, { fields, buttons }) => {
79+
return (
80+
<div>
81+
<header>{blocks.logo} {blocks.title}</header>
82+
{fields.sort((a, b) => a.index - b.index).map((item, idx) => {
83+
return <label key={item.name + idx}>{item.children}</label>
84+
})}
85+
<div>
86+
{buttons.sort((a, b) => a.index - b.index).map((item, idx) => {
87+
return React.cloneElement(item.children, {
88+
...item.props,
89+
key: item.name + idx,
90+
})
91+
})}
92+
</div>
93+
</div>
94+
);
95+
}}
96+
</Render>
97+
<Login.Block name="logo" tagName="span">⚛️</Login.Block>
98+
<Login.Block name="title" tagName="span">Login</Login.Block>
99+
<Login.Input name="username" index={1} placeholder="Please input Username" />
100+
<Login.Input name="password" index={0} placeholder="please enter password" />
101+
<Login.Button name="submit" index={1} type="submit">Submit</Login.Button>
102+
<Login.Button name="reset" index={0} type="reset">Reset</Login.Button>
103+
</Login>
104+
);
105+
}
106+
export default Demo;
107+
```
108+
68109
### `Login.Block`
69110

70111
```jsx
71112
<Login.Block name="title">Login</Login.Block>
72113
```
73114

74115
```jsx
75-
import { FC, PropsWithChildren } from "react";
76-
export interface BlockProps extends React.HTMLAttributes<HTMLDivElement> {
116+
import { PropsWithChildren } from 'react';
117+
import { BlockTagType } from 'react-login-page';
118+
export interface BlockProps<Tag extends BlockTagType = 'div'> extends React.ReactElement<Tag> {
77119
name?: string;
120+
/** Can be shown or hidden with controls */
78121
visible?: boolean;
122+
/** "index" refers to the use of indexes to control the order of controls, which can provide more flexible API encapsulation. */
123+
index?: number;
124+
/** custom created element */
125+
tagName?: Tag;
79126
}
80-
export declare const Block: FC<PropsWithChildren<BlockProps>>;
127+
export declare const Block: {
128+
<Tag extends keyof JSX.IntrinsicElements = "div">(props: PropsWithChildren<Partial<BlockProps<Tag>>>): null;
129+
displayName: string;
130+
};
81131
```
82132

83133
### `Login.Input`
84134

85135
```jsx
86-
<Login.Block name="title">Login</Login.Block>
136+
<Login.Input type="password" placeholder="Password" />
87137
```
88138

89139
```tsx
90-
import React, { FC, PropsWithChildren } from "react";
140+
import React, { FC, PropsWithChildren } from 'react';
91141
export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> {
92142
name?: string;
93143
/** Used to define the name of form controls */
94144
rename?: string;
145+
/** Can be shown or hidden with controls */
95146
visible?: boolean;
147+
/** "index" refers to the use of indexes to control the order of controls, which can provide more flexible API encapsulation. */
148+
index?: number;
96149
}
97150
export declare const Input: FC<PropsWithChildren<InputProps>>;
98151
```
99152

100153
### `Login.Button`
101154

102155
```jsx
103-
<Login.Block name="title">Login</Login.Block>
156+
<Login.Button name="submit" type="submit" />
104157
```
105158

106159
```jsx
107-
import { FC, PropsWithChildren } from "react";
160+
import { FC, PropsWithChildren } from 'react';
108161
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
109162
name?: string;
163+
/** Can be shown or hidden with controls */
110164
visible?: boolean;
165+
/** "index" refers to the use of indexes to control the order of controls, which can provide more flexible API encapsulation. */
166+
index?: number;
111167
}
112168
export declare const Button: FC<PropsWithChildren<ButtonProps>>;
113169
```
@@ -134,10 +190,48 @@ import { Render } from 'react-login-page';
134190
</Render>
135191
```
136192

193+
```tsx
194+
import { FC } from 'react';
195+
import { RenderStateProps, InitialState } from 'react-login-page';
196+
export type RenderChildren = {
197+
children?: (props: Required<RenderStateProps>, data: InitialState['data']) => React.ReactNode;
198+
} | {
199+
children?: React.ReactNode;
200+
};
201+
export declare const Render: FC<RenderChildren>;
202+
```
203+
204+
`index` refers to the use of indexes to control the order of controls
205+
206+
```tsx
207+
<Render>
208+
{({ blocks }, { fields, buttons }) => {
209+
return (
210+
<div>
211+
<header>{blocks.logo} {blocks.title}</header>
212+
{fields.sort((a, b) => a.index - b.index).map((item, idx) => {
213+
return <label key={item.name + idx}>{item.children}</label>
214+
})}
215+
<div>
216+
{buttons.sort((a, b) => a.index - b.index).map((item, idx) => {
217+
const child = item.children;
218+
if (!isValidElement(child)) return null;
219+
return cloneElement(child, {
220+
...child.props,
221+
key: item.name + idx,
222+
})
223+
})}
224+
</div>
225+
</div>
226+
);
227+
}}
228+
</Render>
229+
```
230+
137231
### `useStore`
138232

139233
```jsx mdx:preview
140-
import React, { Fragment } from 'react';
234+
import React from 'react';
141235
import Login, { Render, Provider, Container, useStore } from 'react-login-page';
142236

143237
const RenderLoginPage = () => {
@@ -161,8 +255,8 @@ const Demo = () => {
161255
<Container>
162256
<RenderLoginPage />
163257
</Container>
164-
<Login.Block name="logo">⚛️</Login.Block>
165-
<Login.Block name="title">Login</Login.Block>
258+
<Login.Block name="logo" tagName="span">⚛️</Login.Block>
259+
<Login.Block name="title" tagName="span">Login</Login.Block>
166260
<Login.Input name="username" placeholder="Please input Username" />
167261
<Login.Input name="password" placeholder="please enter password" />
168262
<Login.Button name="submit" type="submit">Submit</Login.Button>
@@ -174,6 +268,54 @@ const Demo = () => {
174268
export default Demo;
175269
```
176270

271+
`index` refers to the use of indexes to control the order of controls
272+
273+
```jsx mdx:preview
274+
import React, { isValidElement, cloneElement } from 'react';
275+
import Login, { Render, Provider, Container, useStore } from 'react-login-page';
276+
277+
const RenderLoginPage = () => {
278+
const { blocks, data } = useStore();
279+
const { fields, buttons } = data;
280+
return (
281+
<Render>
282+
<header>{blocks.logo} {blocks.title}</header>
283+
{fields.sort((a, b) => a.index - b.index).map((item, idx) => {
284+
return <label key={item.name + idx}>{item.children}</label>
285+
})}
286+
<div>
287+
{buttons.sort((a, b) => a.index - b.index).map((item, idx) => {
288+
const child = item.children;
289+
if (!isValidElement(child)) return null;
290+
return cloneElement(child, {
291+
...child.props,
292+
key: item.name + idx,
293+
})
294+
})}
295+
</div>
296+
</Render>
297+
);
298+
}
299+
300+
const Demo = () => {
301+
return (
302+
<Provider>
303+
<Container>
304+
<RenderLoginPage />
305+
</Container>
306+
<Login.Block name="logo" tagName="span">⚛️</Login.Block>
307+
<Login.Block name="title" tagName="span">Login</Login.Block>
308+
<Login.Input name="username" index={1} placeholder="Please input Username" />
309+
<Login.Input name="password" placeholder="please enter password" />
310+
<Login.Button name="submit" index={1} type="submit">Submit</Login.Button>
311+
<Login.Button name="reset" type="reset">Reset</Login.Button>
312+
</Provider>
313+
);
314+
}
315+
316+
export default Demo;
317+
```
318+
177319
## Contributors
178320

179321
As always, thanks to our amazing contributors!

core/src/Block.tsx

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,36 @@
1-
import { FC, PropsWithChildren, useRef, useEffect } from 'react';
2-
import { useStore } from './store';
1+
import { PropsWithChildren, useRef, useEffect, createElement } from 'react';
2+
import { useStore, BlockTagType } from './store';
33

4-
export interface BlockProps extends React.HTMLAttributes<HTMLDivElement> {
4+
// export type BlockTagType = React.ComponentType | keyof JSX.IntrinsicElements;
5+
6+
export interface BlockProps<Tag extends BlockTagType = 'div'> extends React.ReactElement<Tag> {
57
name?: string;
8+
/** Can be shown or hidden with controls */
69
visible?: boolean;
10+
/** "index" refers to the use of indexes to control the order of controls, which can provide more flexible API encapsulation. */
11+
index?: number;
12+
/** custom created element */
13+
tagName?: Tag;
714
}
815

9-
export const Block: FC<PropsWithChildren<BlockProps>> = (props) => {
10-
const ref = useRef<BlockProps>();
16+
export const Block = <Tag extends BlockTagType = 'div'>(props: PropsWithChildren<Partial<BlockProps<Tag>>>) => {
17+
const ref = useRef<Partial<BlockProps<Tag>>>();
1118
const { blocks = {}, dispatch } = useStore();
1219
useEffect(() => {
13-
const { name, visible = true, ...elmProps } = props;
20+
const { name, visible = true, tagName = 'div', ...elmProps } = props;
1421
if (ref.current !== elmProps && name) {
1522
ref.current = { ...elmProps };
23+
24+
const div = (visible ? createElement(tagName, { ...elmProps }, elmProps.children) : null);
1625
dispatch({
17-
blocks: { ...blocks, [name]: visible ? <div {...elmProps} /> : null },
26+
// blocks: { ...blocks, [name]: div as unknown as React.ReactElement<Tag> },
27+
// @ts-ignore
28+
blocks: { ...blocks, [name]: div },
1829
});
1930
}
2031
}, [props, ref]);
2132

2233
return null;
23-
};
34+
}
2435

2536
Block.displayName = 'Login.Block';

core/src/Button.tsx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,10 @@ import { useStore } from './store';
33

44
export interface ButtonProps extends React.ButtonHTMLAttributes<HTMLButtonElement> {
55
name?: string;
6+
/** Can be shown or hidden with controls */
67
visible?: boolean;
8+
/** "index" refers to the use of indexes to control the order of controls, which can provide more flexible API encapsulation. */
9+
index?: number;
710
}
811

912
export const Button: FC<PropsWithChildren<ButtonProps>> = (props) => {

core/src/Input.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,10 @@ export interface InputProps extends React.InputHTMLAttributes<HTMLInputElement>
55
name?: string;
66
/** Used to define the name of form controls */
77
rename?: string;
8+
/** Can be shown or hidden with controls */
89
visible?: boolean;
10+
/** "index" refers to the use of indexes to control the order of controls, which can provide more flexible API encapsulation. */
11+
index?: number;
912
}
1013

1114
export const Input: FC<PropsWithChildren<InputProps>> = (props) => {
@@ -15,8 +18,12 @@ export const Input: FC<PropsWithChildren<InputProps>> = (props) => {
1518
useEffect(() => {
1619
if (ref.current !== props && name) {
1720
ref.current = { ...props };
21+
const oldProps = fields[name]?.props;
22+
if (rename) {
23+
console.log('oldProps:', oldProps, name, rename, fields[name])
24+
}
1825
dispatch({
19-
fields: { ...fields, [name]: visible ? <input {...elmProps} name={rename || name} /> : null },
26+
fields: { ...fields, [name]: visible ? <input {...elmProps} name={rename || name} /> : null },
2027
});
2128
}
2229
}, [props, name, ref]);

core/src/Render.tsx

Lines changed: 6 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,18 @@
11
import { FC, Fragment, Children, cloneElement, isValidElement } from 'react';
2-
import { useStore, RenderStateProps } from './store';
2+
import { useStore, RenderStateProps , InitialState} from './store';
33

44
export type RenderChildren =
5-
| { children?: (props: Required<RenderStateProps>) => React.ReactNode }
5+
| { children?: (props: Required<RenderStateProps>, data: InitialState['data']) => React.ReactNode }
66
| { children?: React.ReactNode };
77

88
export const Render: FC<RenderChildren> = ({ children }) => {
9-
const { fields = {}, buttons = {}, blocks = {} } = useStore();
10-
const childs = typeof children === 'function' ? [children] : Children.toArray(children);
9+
const { fields = {}, buttons = {}, blocks = {}, data } = useStore();
10+
const childs = typeof children === 'function' ? [] : Children.toArray(children);
1111
return (
1212
<Fragment>
13-
{typeof children === 'function' && !isValidElement(children) && children({ fields, buttons, blocks })}
14-
{childs.map((child, key) => {
13+
{typeof children === 'function' && !isValidElement(children) && children({ fields, buttons, blocks }, { ...data })}
14+
{typeof children !== 'function' && childs.map((child, key) => {
1515
if (!isValidElement(child)) return null;
16-
if (child.type && typeof child.type === 'string') {
17-
return cloneElement(child, { key });
18-
}
1916
return cloneElement(child, {
2017
...child.props,
2118
key,

0 commit comments

Comments
 (0)