@wwog/react 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -141,32 +141,24 @@ function Example({ isActive }) {
141
141
  - `<True condition={...}>`:当 condition 为 true 时渲染子内容。
142
142
  - `<False condition={...}>`:当 condition 为 false 时渲染子内容。
143
143
 
144
- #### `<Toggle>`
144
+ #### `<Toggle>` (v1.2.0+)
145
145
 
146
146
  声明式切换组件,在预定义选项中切换值,并通过指定属性传递给子组件,支持自定义切换逻辑。
147
147
 
148
+ > v1.2.1 Indexing is now used to fix bugs with arbitrary values
149
+
148
150
  ```tsx
149
151
  import { Toggle } from "@wwog/react";
150
152
 
151
- interface ThemeChildProps {
152
- theme: string;
153
- toggleTheme: () => void;
154
- }
155
- const ThemeChild: React.FC<ThemeChildProps> = ({ theme, toggleTheme }) => (
156
- <div onClick={toggleTheme}>当前主题: {theme}</div>
157
- );
158
-
159
- <Toggle source="light" options={["light", "dark"]} target="theme" toggleTarget="toggleTheme">
160
- <ThemeChild />
161
- </Toggle>
153
+ <Toggle options={["light", "dark"]} render={(value,toggle)=>{/* xxx */}}/>
162
154
  ```
163
155
 
164
- - `source`:初始切换值。
165
156
  - `options`:可切换的值数组。
157
+ - `index`:默认:0。
166
158
  - `target`:传递切换值给子节点的属性名,默认 value。
167
159
  - `toggleTarget`:传递切换函数给子节点的属性名,默认 toggle。
168
160
  - `next`:自定义切换逻辑函数。
169
- - `children`:渲染内容。
161
+ - `render`:渲染函数。
170
162
 
171
163
  ### 通用组件
172
164
 
@@ -292,12 +284,20 @@ function Layout() {
292
284
 
293
285
  - Loop:灵活的迭代渲染,支持数组、对象和范围。
294
286
  - Try:封装异步逻辑,处理 Promise 状态。
295
- - Toggle:基于布尔状态切换 on 和 off 内容。
296
287
  - Pick:轻量版值选择渲染,类似枚举匹配。
297
288
  - Render:动态渲染函数,简化复杂渲染逻辑。
298
289
  - Once:确保内容仅渲染一次,适合初始化。
299
290
  - Each:增强列表渲染,支持过滤和排序。
300
291
 
292
+ ### hooks
293
+
294
+ - 一些常用的hooks的封装
295
+
296
+ ### useControlled (v1.2.0+)
297
+
298
+ - 受控组件和非受控组件的切换,方便组件开发
299
+
300
+
301
301
  ## License
302
302
 
303
303
  MIT
package/dist/index.d.mts CHANGED
@@ -194,70 +194,41 @@ interface ToggleProps<T = boolean> {
194
194
  /**
195
195
  * @description_en The initial value to toggle.
196
196
  * @description_zh 初始切换值。
197
+ * @default 0
197
198
  */
198
- source: T;
199
+ index?: number;
199
200
  /**
200
201
  * @description_en Array of values to toggle between.
201
202
  * @description_zh 可切换的值数组。
202
203
  */
203
204
  options: T[];
204
205
  /**
205
- * @description_en The prop name to pass the toggled value to children.
206
- * @description_zh 将切换后的值传递给子节点的属性名。
207
- * @default 'value'
208
- */
209
- target?: string;
210
- /**
211
- * @description_en The prop name to pass the toggle function to children.
212
- * @description_zh 将切换函数传递给子节点的属性名。
213
- * @default 'toggle'
214
- */
215
- toggleTarget?: string;
216
- /**
217
- * @description_en Function to determine the next value in the toggle sequence.
218
- * @description_zh 确定切换序列中下一个值的函数。
206
+ * @description_en Function to determine the next value index in the toggle sequence.
207
+ * @description_zh 确定切换序列中下一个值索引的函数。
219
208
  * @optional
220
209
  */
221
- next?: (current: T, options: T[]) => T;
210
+ next?: (curIndex: number, options: T[]) => number;
222
211
  /**
223
- * @description_en Content to render, receiving the toggled value and toggle function via the target and toggleTarget props.
224
- * @description_zh 渲染的内容,通过 target 和 toggleTarget 属性接收切换后的值和切换函数。
212
+ * @description_en Render function, receiving the toggled value and toggle function.
213
+ * @description_zh 渲染函数,接收切换后的值和切换函数。
225
214
  */
226
- children: ReactNode;
215
+ render: (value: T, toggle: () => void) => ReactNode;
227
216
  }
228
217
  /**
229
- * @description_zh 一个声明式组件,用于在预定义选项中切换值并通过指定属性传递给子组件,支持自定义切换逻辑。
230
- * @description_en A declarative component for toggling between predefined values and passing them to children via specified props, supporting custom toggle logic.
218
+ * @description_zh 一个声明式组件,用于在预定义选项中切换值并通过 render 函数传递给子组件,支持自定义切换逻辑。
219
+ * @description_en A declarative component for toggling between predefined values and passing them to children via a render function, supporting custom toggle logic.
231
220
  * @component
232
221
  * @example
233
222
  * ```tsx
234
- * interface ThemeChildProps {
235
- * theme: string;
236
- * toggleTheme: () => void;
237
- * }
238
- * const ThemeChild: FC<ThemeChildProps> = ({ theme, toggleTheme }) => (
239
- * <div onClick={toggleTheme}>当前主题: {theme}</div>
240
- * );
241
- *
242
- * <Toggle source="light" options={["light", "dark"]} target="theme" toggleTarget="toggleTheme">
243
- * <ThemeChild />
244
- * </Toggle>
245
- * ```
246
- *
247
- * @example
248
- * ```tsx
249
223
  * <Toggle
250
- * source={1}
251
- * options={[1, 2, 3]}
252
- * target="value"
253
- * toggleTarget="toggleValue"
254
- * next={(current, options) => options[(options.indexOf(current) + 1) % options.length]}
255
- * >
256
- * <ValueChild />
257
- * </Toggle>
224
+ * options={["light", "dark"]}
225
+ * render={(theme, toggleTheme) => (
226
+ * <div onClick={toggleTheme}>当前主题: {theme}</div>
227
+ * )}
228
+ * />
258
229
  * ```
259
230
  */
260
- declare const Toggle: <T>(props: ToggleProps<T>) => (string | number | bigint | React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>> | Iterable<React$1.ReactNode> | Promise<string | number | bigint | boolean | React$1.ReactPortal | React$1.ReactElement<unknown, string | React$1.JSXElementConstructor<any>> | Iterable<React$1.ReactNode> | null | undefined>)[] | null | undefined;
231
+ declare const Toggle: <T>(props: ToggleProps<T>) => React$1.ReactNode;
261
232
 
262
233
  interface SizeBoxProps {
263
234
  size?: number | string;
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import n,{useMemo as y,useState as S,Fragment as I,useCallback as T}from"react";function C(e,t){if(e===void 0)return;let l=0;if(Array.isArray(e)){for(const a of e)if(t(a,l++)===!1)break}else t(e,l)}const x=(e,t)=>e===t,g=e=>n.createElement(n.Fragment,null,e.children);g.displayName="Switch_Case";const E=e=>n.createElement(n.Fragment,null,e.children);E.displayName="Switch_Default";const p=e=>{const{value:t,compare:l=x,children:a,strict:o=!1}=e,r=new Set;let c=null,m=null,f=!1;return C(a,(s,d)=>{if(!n.isValidElement(s))throw new Error(`Switch Children only accepts valid React elements at index ${d}`);const i=s.type;if(i.displayName===g.displayName){const u=s.props;if(r.has(u.value))throw new Error(`Switch found duplicate Case value at index ${d}: ${JSON.stringify(u.value)}${o?" (detected in strict mode)":""}`);if(r.add(u.value),!c&&l(t,u.value)&&(c=u.children,o===!1))return!1}else if(i.displayName===E.displayName){if(f)throw new Error(`Switch can only have one Default child at index ${d}`);if(f=!0,m=s.props.children,!o&&c)return!1}else throw new Error(`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(i.displayName||i.name||i)} at index ${d}`)}),n.createElement(n.Fragment,null,c??m)};p.displayName="Switch",p.Case=g,p.Default=E,p.createTyped=function(){return{Switch:p,Case:g,Default:E}};const w=e=>n.createElement(n.Fragment,null,e.children),N=({children:e})=>n.createElement(n.Fragment,null,e),v=e=>n.createElement(n.Fragment,null,e.children);w.displayName="If_Then",N.displayName="If_Else",v.displayName="If_ElseIf";const h=({condition:e,children:t})=>{let l=null,a=null;const o=[];if(n.Children.forEach(t,r=>{if(!n.isValidElement(r))throw new Error("If component only accepts valid React elements");const c=r.type;if(c.displayName===w.displayName){if(l)throw new Error("If component can only have one Then child");l=r}else if(c.displayName===v.displayName)o.push(r);else if(c.displayName===N.displayName){if(a)throw new Error("If component can only have one Else child");a=r}else throw new Error(`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(c.displayName||c.name||c)}`)}),e)return l?n.createElement(n.Fragment,null,l.props.children):null;for(const r of o)if(r.props.condition)return n.createElement(n.Fragment,null,r.props.children);return a?n.createElement(n.Fragment,null,a.props.children):null};h.displayName="If",h.Then=w,h.ElseIf=v,h.Else=N,h.createTyped=function(){return{If:h,Then:w,ElseIf:v,Else:N}};const k=({condition:e,children:t})=>e?n.createElement(n.Fragment,null,t):null,b=({condition:e,children:t})=>e===!1?n.createElement(n.Fragment,null,t):null,$=({all:e,any:t,none:l,children:a,fallback:o})=>y(()=>(e&&(t||l)&&console.warn('When: Multiple condition types (all, any, none) provided; "all" takes precedence.'),!!(e&&e.length>0&&e.every(Boolean)||t&&t.length>0&&t.some(Boolean)||l&&l.length>0&&l.every(r=>!r))),[e,t,l])?n.createElement(n.Fragment,null,a):n.createElement(n.Fragment,null,o||null),D=({data:e,transform:t,render:l,fallback:a})=>{const o=y(()=>t.reduce((r,c)=>c(r),e),[e,t]);return o==null?n.createElement(n.Fragment,null,a||null):n.createElement(n.Fragment,null,l(o))},O=e=>{const{source:t,options:l,target:a="value",toggleTarget:o="toggle",next:r,children:c}=e,m=y(()=>l.includes(t)?t:l[0],[t,l]),[f,s]=S(m),d=()=>{s(i=>l.length?r?r(i,l):l[(l.indexOf(i)+1)%l.length]||l[0]:i)};return n.Children.map(c,i=>n.isValidElement(i)?n.cloneElement(i,{[a]:f,[o]:d}):i)},_=e=>{const{children:t,h:l,w:a,size:o,height:r,width:c,className:m}=e;return n.createElement("div",{style:{width:o||a||c,height:o||l||r,flexShrink:0},className:m},t)};function A(e){const{items:t,renderItem:l,filter:a}=e;return t?n.createElement(I,null,t.map((o,r)=>a&&!a(o)?null:l(o,r))):(console.error("ArrayRender: items is null"),null)}const B=({let:e,props:t,children:l,fallback:a})=>{const o=y(()=>typeof e=="function"?e(t):e,[e,t]);return!l||!Object.keys(o).length?n.createElement(n.Fragment,null,a||null):n.createElement(n.Fragment,null,l(o))},R="onChange",V="value";function P(e){const{defaultValue:t,onBeforeChange:l,trigger:a=R,valuePropName:o=V,props:r}=e,c=Object.prototype.hasOwnProperty.call(r,o),[m,f]=S(t),s=c?r[o]:m,d=y(()=>r[a],[r,a]),i=T(u=>{const F=typeof u=="function"?u(s):u;l&&l(F,s)===!1||(c||f(F),d&&d(F))},[c,l,a,s,d]);return[s,i]}export{A as ArrayRender,b as False,h as If,D as Pipe,B as Scope,_ as SizeBox,p as Switch,O as Toggle,k as True,$ as When,C as childrenLoop,P as useControlled};
1
+ import n,{useMemo as y,useEffect as C,useState as S,Fragment as x,useCallback as T}from"react";function I(e,l){if(e===void 0)return;let t=0;if(Array.isArray(e)){for(const r of e)if(l(r,t++)===!1)break}else l(e,t)}const b=(e,l)=>e===l,E=e=>n.createElement(n.Fragment,null,e.children);E.displayName="Switch_Case";const g=e=>n.createElement(n.Fragment,null,e.children);g.displayName="Switch_Default";const p=e=>{const{value:l,compare:t=b,children:r,strict:o=!1}=e,a=new Set;let i=null,u=null,d=!1;return I(r,(s,m)=>{if(!n.isValidElement(s))throw new Error(`Switch Children only accepts valid React elements at index ${m}`);const f=s.type;if(f.displayName===E.displayName){const c=s.props;if(a.has(c.value))throw new Error(`Switch found duplicate Case value at index ${m}: ${JSON.stringify(c.value)}${o?" (detected in strict mode)":""}`);if(a.add(c.value),!i&&t(l,c.value)&&(i=c.children,o===!1))return!1}else if(f.displayName===g.displayName){if(d)throw new Error(`Switch can only have one Default child at index ${m}`);if(d=!0,u=s.props.children,!o&&i)return!1}else throw new Error(`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(f.displayName||f.name||f)} at index ${m}`)}),n.createElement(n.Fragment,null,i??u)};p.displayName="Switch",p.Case=E,p.Default=g,p.createTyped=function(){return{Switch:p,Case:E,Default:g}};const w=e=>n.createElement(n.Fragment,null,e.children),N=({children:e})=>n.createElement(n.Fragment,null,e),v=e=>n.createElement(n.Fragment,null,e.children);w.displayName="If_Then",N.displayName="If_Else",v.displayName="If_ElseIf";const h=({condition:e,children:l})=>{let t=null,r=null;const o=[];if(n.Children.forEach(l,a=>{if(!n.isValidElement(a))throw new Error("If component only accepts valid React elements");const i=a.type;if(i.displayName===w.displayName){if(t)throw new Error("If component can only have one Then child");t=a}else if(i.displayName===v.displayName)o.push(a);else if(i.displayName===N.displayName){if(r)throw new Error("If component can only have one Else child");r=a}else throw new Error(`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(i.displayName||i.name||i)}`)}),e)return t?n.createElement(n.Fragment,null,t.props.children):null;for(const a of o)if(a.props.condition)return n.createElement(n.Fragment,null,a.props.children);return r?n.createElement(n.Fragment,null,r.props.children):null};h.displayName="If",h.Then=w,h.ElseIf=v,h.Else=N,h.createTyped=function(){return{If:h,Then:w,ElseIf:v,Else:N}};const k=({condition:e,children:l})=>e?n.createElement(n.Fragment,null,l):null,$=({condition:e,children:l})=>e===!1?n.createElement(n.Fragment,null,l):null,D=({all:e,any:l,none:t,children:r,fallback:o})=>y(()=>(e&&(l||t)&&console.warn('When: Multiple condition types (all, any, none) provided; "all" takes precedence.'),!!(e&&e.length>0&&e.every(Boolean)||l&&l.length>0&&l.some(Boolean)||t&&t.length>0&&t.every(a=>!a))),[e,l,t])?n.createElement(n.Fragment,null,r):n.createElement(n.Fragment,null,o||null),_=({data:e,transform:l,render:t,fallback:r})=>{const o=y(()=>l.reduce((a,i)=>i(a),e),[e,l]);return o==null?n.createElement(n.Fragment,null,r||null):n.createElement(n.Fragment,null,t(o))},A=e=>{const{index:l=0,options:t,next:r,render:o}=e;C(()=>{if(t.length<l+1)throw new Error(`Index ${l} is out of bounds for options array of length ${t.length}. Defaulting to first option.`)},[l,t]);const[a,i]=S(l),u=()=>{i(d=>t.length?r?r(d,t):(d+1)%t.length:d)};return o(t[a],u)},B=e=>{const{children:l,h:t,w:r,size:o,height:a,width:i,className:u}=e;return n.createElement("div",{style:{width:o||r||i,height:o||t||a,flexShrink:0},className:u},l)};function O(e){const{items:l,renderItem:t,filter:r}=e;return l?n.createElement(x,null,l.map((o,a)=>r&&!r(o)?null:t(o,a))):(console.error("ArrayRender: items is null"),null)}const R=({let:e,props:l,children:t,fallback:r})=>{const o=y(()=>typeof e=="function"?e(l):e,[e,l]);return!t||!Object.keys(o).length?n.createElement(n.Fragment,null,r||null):n.createElement(n.Fragment,null,t(o))},P="onChange",V="value";function j(e){const{defaultValue:l,onBeforeChange:t,trigger:r=P,valuePropName:o=V,props:a}=e,i=Object.prototype.hasOwnProperty.call(a,o),[u,d]=S(l),s=i?a[o]:u,m=y(()=>a[r],[a,r]),f=T(c=>{const F=typeof c=="function"?c(s):c;t&&t(F,s)===!1||(i||d(F),m&&m(F))},[i,t,r,s,m]);return[s,f]}export{O as ArrayRender,$ as False,h as If,_ as Pipe,R as Scope,B as SizeBox,p as Switch,A as Toggle,k as True,D as When,I as childrenLoop,j as useControlled};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wwog/react",
3
- "version": "1.2.0",
3
+ "version": "1.2.2",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "author": "wwog",
@@ -1,105 +1,67 @@
1
- import React, { ReactNode, useState, useMemo } from "react";
1
+ import React, { useState, useEffect, ReactNode } from "react";
2
2
 
3
3
  export interface ToggleProps<T = boolean> {
4
4
  /**
5
5
  * @description_en The initial value to toggle.
6
6
  * @description_zh 初始切换值。
7
+ * @default 0
7
8
  */
8
- source: T;
9
+ index?: number;
9
10
  /**
10
11
  * @description_en Array of values to toggle between.
11
12
  * @description_zh 可切换的值数组。
12
13
  */
13
14
  options: T[];
14
15
  /**
15
- * @description_en The prop name to pass the toggled value to children.
16
- * @description_zh 将切换后的值传递给子节点的属性名。
17
- * @default 'value'
18
- */
19
- target?: string;
20
- /**
21
- * @description_en The prop name to pass the toggle function to children.
22
- * @description_zh 将切换函数传递给子节点的属性名。
23
- * @default 'toggle'
24
- */
25
- toggleTarget?: string;
26
- /**
27
- * @description_en Function to determine the next value in the toggle sequence.
28
- * @description_zh 确定切换序列中下一个值的函数。
16
+ * @description_en Function to determine the next value index in the toggle sequence.
17
+ * @description_zh 确定切换序列中下一个值索引的函数。
29
18
  * @optional
30
19
  */
31
- next?: (current: T, options: T[]) => T;
20
+ next?: (curIndex: number, options: T[]) => number;
32
21
  /**
33
- * @description_en Content to render, receiving the toggled value and toggle function via the target and toggleTarget props.
34
- * @description_zh 渲染的内容,通过 target 和 toggleTarget 属性接收切换后的值和切换函数。
22
+ * @description_en Render function, receiving the toggled value and toggle function.
23
+ * @description_zh 渲染函数,接收切换后的值和切换函数。
35
24
  */
36
- children: ReactNode;
25
+ render: (value: T, toggle: () => void) => ReactNode;
37
26
  }
38
27
 
39
28
  /**
40
- * @description_zh 一个声明式组件,用于在预定义选项中切换值并通过指定属性传递给子组件,支持自定义切换逻辑。
41
- * @description_en A declarative component for toggling between predefined values and passing them to children via specified props, supporting custom toggle logic.
29
+ * @description_zh 一个声明式组件,用于在预定义选项中切换值并通过 render 函数传递给子组件,支持自定义切换逻辑。
30
+ * @description_en A declarative component for toggling between predefined values and passing them to children via a render function, supporting custom toggle logic.
42
31
  * @component
43
32
  * @example
44
33
  * ```tsx
45
- * interface ThemeChildProps {
46
- * theme: string;
47
- * toggleTheme: () => void;
48
- * }
49
- * const ThemeChild: FC<ThemeChildProps> = ({ theme, toggleTheme }) => (
50
- * <div onClick={toggleTheme}>当前主题: {theme}</div>
51
- * );
52
- *
53
- * <Toggle source="light" options={["light", "dark"]} target="theme" toggleTarget="toggleTheme">
54
- * <ThemeChild />
55
- * </Toggle>
56
- * ```
57
- *
58
- * @example
59
- * ```tsx
60
34
  * <Toggle
61
- * source={1}
62
- * options={[1, 2, 3]}
63
- * target="value"
64
- * toggleTarget="toggleValue"
65
- * next={(current, options) => options[(options.indexOf(current) + 1) % options.length]}
66
- * >
67
- * <ValueChild />
68
- * </Toggle>
35
+ * options={["light", "dark"]}
36
+ * render={(theme, toggleTheme) => (
37
+ * <div onClick={toggleTheme}>当前主题: {theme}</div>
38
+ * )}
39
+ * />
69
40
  * ```
70
41
  */
71
42
  export const Toggle = <T,>(props: ToggleProps<T>) => {
72
43
  const {
73
- source,
44
+ index = 0,
74
45
  options,
75
- target = "value",
76
- toggleTarget = "toggle",
77
46
  next,
78
- children,
47
+ render,
79
48
  } = props;
80
- const defaultValue = useMemo(() => {
81
- return options.includes(source) ? source : options[0];
82
- }, [source, options]);
83
49
 
84
- const [currentValue, setCurrentValue] = useState<T>(defaultValue);
50
+ useEffect(() => {
51
+ if (options.length < index + 1) {
52
+ throw new Error(
53
+ `Index ${index} is out of bounds for options array of length ${options.length}. Defaulting to first option.`
54
+ );
55
+ }
56
+ }, [index, options]);
85
57
 
58
+ const [curIndex, setCurIndex] = useState<number>(index);
86
59
  const toggle = () => {
87
- setCurrentValue((prev) => {
60
+ setCurIndex((prev) => {
88
61
  if (!options.length) return prev;
89
- const nextValue = next
90
- ? next(prev, options)
91
- : options[(options.indexOf(prev) + 1) % options.length] || options[0];
92
- return nextValue;
62
+ return next ? next(prev, options) : (prev + 1) % options.length;
93
63
  });
94
64
  };
95
65
 
96
- return React.Children.map(children, (child) => {
97
- if (React.isValidElement(child)) {
98
- return React.cloneElement(child, {
99
- [target]: currentValue,
100
- [toggleTarget]: toggle,
101
- } as { [key: string]: any });
102
- }
103
- return child;
104
- });
66
+ return render(options[curIndex], toggle);
105
67
  };