@wwog/react 1.2.3 → 1.2.5

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
@@ -312,6 +312,47 @@ function Layout() {
312
312
  }
313
313
  ```
314
314
 
315
+ #### `<ClassName>` (v1.2.5+)
316
+
317
+ 用于将 CSS 类名分类编写的组件,内置类似`clsx`的功能,并且可以去除重复的 className。支持基础类、悬停态、激活态等多种状态的类名管理。
318
+
319
+ ```tsx
320
+ import { ClassName } from "@wwog/react";
321
+
322
+ function Example() {
323
+ return (
324
+ <ClassName
325
+ className={{
326
+ base: "p-2 bg-white",
327
+ hover: "hover:bg-gray-100",
328
+ active: "active:bg-gray-200",
329
+ focus: "focus:ring-2",
330
+ }}
331
+ >
332
+ <button>点击我</button>
333
+ </ClassName>
334
+ );
335
+ }
336
+ ```
337
+
338
+ 还可以使用容器包装元素:
339
+
340
+ ```tsx
341
+ <ClassName
342
+ className={{
343
+ base: ["p-2", { "bg-red-500": isError }],
344
+ hover: { "hover:bg-blue-500": true },
345
+ }}
346
+ asWrapper="span"
347
+ >
348
+ 内容
349
+ </ClassName>
350
+ ```
351
+
352
+ - `className`:分类的类名对象,支持各种状态的类名(base, hover, active, focus, disabled 等)
353
+ - `asWrapper`:是否生成包含所有 className 的 wrapper,默认 false,传递标签名如'div'或'span'
354
+ - `children`:子元素,通常是一个 React 元素
355
+
315
356
  ### hooks
316
357
 
317
358
  - 一些常用的 hooks 的封装
@@ -320,12 +361,49 @@ function Layout() {
320
361
 
321
362
  - 受控组件和非受控组件的切换,方便组件开发
322
363
 
323
- ### method
364
+ ### utils
324
365
 
325
366
  - 用于部分组件的内部函数,如需要也可使用
326
367
 
327
- formatDate 比较标准的格式化时间函数
328
- childrenLoop 可以中断的子节点遍历,让一些分支流程拥有极致性能
368
+ #### `formatDate`
369
+
370
+ 比较标准的格式化时间函数
371
+
372
+ #### `childrenLoop`
373
+
374
+ 可以中断的子节点遍历,让一些分支流程拥有极致性能
375
+
376
+ #### `Counter`
377
+
378
+ 计数器
379
+
380
+ #### `cn` (v1.2.5+)
381
+
382
+ 一个高效的 CSS 类名合并工具函数,类似于`clsx`或`classnames`,但能自动去除重复的类名。
383
+
384
+ ```tsx
385
+ import { cn } from "@wwog/react";
386
+
387
+ function Example({ isActive, isDisabled }) {
388
+ return (
389
+ <div
390
+ className={cn("base-class", ["array-class-1", "array-class-2"], {
391
+ "active-class": isActive,
392
+ "disabled-class": isDisabled,
393
+ })}
394
+ >
395
+ 内容
396
+ </div>
397
+ );
398
+ }
399
+ ```
400
+
401
+ 支持多种参数类型:
402
+
403
+ - 字符串: `"class1 class2"`
404
+ - 字符串数组: `["class1", "class2"]`
405
+ - 对象: `{ "class1": true, "class2": false }`
406
+ - 以上类型的任意组合
329
407
 
330
408
  ## License
331
409
 
package/dist/index.d.mts CHANGED
@@ -1,4 +1,4 @@
1
- import React$1, { ReactNode, FC, Dispatch } from 'react';
1
+ import React$1, { ReactNode, FC, HTMLElementType, Dispatch } from 'react';
2
2
 
3
3
  interface SwitchProps<T> {
4
4
  value: T;
@@ -190,46 +190,6 @@ interface PipeProps {
190
190
  */
191
191
  declare const Pipe: FC<PipeProps>;
192
192
 
193
- interface ToggleProps<T = boolean> {
194
- /**
195
- * @description_en The initial value to toggle.
196
- * @description_zh 初始切换值。
197
- * @default 0
198
- */
199
- index?: number;
200
- /**
201
- * @description_en Array of values to toggle between.
202
- * @description_zh 可切换的值数组。
203
- */
204
- options: T[];
205
- /**
206
- * @description_en Function to determine the next value index in the toggle sequence.
207
- * @description_zh 确定切换序列中下一个值索引的函数。
208
- * @optional
209
- */
210
- next?: (curIndex: number, options: T[]) => number;
211
- /**
212
- * @description_en Render function, receiving the toggled value and toggle function.
213
- * @description_zh 渲染函数,接收切换后的值和切换函数。
214
- */
215
- render: (value: T, toggle: () => void) => ReactNode;
216
- }
217
- /**
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.
220
- * @component
221
- * @example
222
- * ```tsx
223
- * <Toggle
224
- * options={["light", "dark"]}
225
- * render={(theme, toggleTheme) => (
226
- * <div onClick={toggleTheme}>当前主题: {theme}</div>
227
- * )}
228
- * />
229
- * ```
230
- */
231
- declare const Toggle: <T>(props: ToggleProps<T>) => React$1.ReactNode;
232
-
233
193
  interface SizeBoxProps {
234
194
  size?: number | string;
235
195
  height?: number | string;
@@ -244,13 +204,6 @@ interface SizeBoxProps {
244
204
  */
245
205
  declare const SizeBox: FC<SizeBoxProps>;
246
206
 
247
- interface ArrayRenderProps<T> {
248
- items: T[];
249
- renderItem: (item: T, index: number) => React$1.ReactNode;
250
- filter?: (item: T) => boolean;
251
- }
252
- declare function ArrayRender<T>(props: ArrayRenderProps<T>): ReactNode;
253
-
254
207
  /**
255
208
  * Props for the `Scope` component.
256
209
  *
@@ -306,6 +259,141 @@ interface ScopeProps {
306
259
  */
307
260
  declare const Scope: FC<ScopeProps>;
308
261
 
262
+ /**
263
+ * @description 性能优化,替代 React.Children.forEach, 回调可以返回 false 来中断循环
264
+ * @description_en Replace React.Children.forEach, the callback can return false to interrupt the loop
265
+ */
266
+ declare function childrenLoop(children: React$1.ReactNode | undefined, callback: (child: React$1.ReactNode, index: number) => boolean | void): void;
267
+ /**
268
+ * @param schema
269
+ * @example
270
+ * YY | 18 | Two-digit year
271
+ * YYYY | 2018 | Four-digit year
272
+ * M | 1-12 | The month, beginning at 1
273
+ * MM | 01-12 | The month, 2-digits
274
+ * MMM | Jan-Dec | The abbreviated month name
275
+ * MMMM | January-December | The full month name
276
+ * D | 1-31 | The day of the month
277
+ * DD | 01-31 | The day of the month, 2-digits
278
+ * d | 0-6 | The day of the week, with Sunday as 0
279
+ * dd | Su-Sa | The min name of the day of the week
280
+ * ddd | Sun-Sat | The short name of the day of the week
281
+ * dddd | Sunday-Saturday | The name of the day of the week
282
+ * H | 0-23 | The hour
283
+ * HH | 00-23 | The hour, 2-digits
284
+ * h | 1-12 | The hour, 12-hour clock
285
+ * hh | 01-12 | The hour, 12-hour clock, 2-digits
286
+ * m | 0-59 | The minute
287
+ * mm | 00-59 | The minute, 2-digits
288
+ * s | 0-59 | The second
289
+ * ss | 00-59 | The second, 2-digits
290
+ * SSS | 000-999 | The millisecond, 3-digits
291
+ * Z | +05:00 | The offset from UTC, ±HH:mm
292
+ * ZZ | +0500 | The offset from UTC, ±HHmm
293
+ * A | AM | PM
294
+ * a | am | pm
295
+ */
296
+ declare function formatDate(schema: string, date?: Date): string;
297
+ declare class Counter {
298
+ count: number;
299
+ /**
300
+ * @description 获取下一个计数值,不考虑越界。
301
+ * @description_en Get the next count value, without considering overflow.
302
+ */
303
+ next(): number;
304
+ }
305
+ type CxInput = string | string[] | Record<string, boolean> | undefined | null | false;
306
+ declare function cx(...args: CxInput[]): string;
307
+
308
+ interface ClassNameProps {
309
+ className?: {
310
+ base?: CxInput;
311
+ hover?: CxInput;
312
+ active?: CxInput;
313
+ focus?: CxInput;
314
+ disabled?: CxInput;
315
+ [key: string]: CxInput;
316
+ };
317
+ /**
318
+ * @description 传入容器标签名.是否生成包含所有 `className` 的 `wrapper`, 默认 false, 传递 `true` 为 `div。`
319
+ * @description_en Whether to generate a `wrapper` containing all `className`, default is false, and pass the container tag name, if `true` will be `div`.
320
+ */
321
+ asWrapper?: boolean | HTMLElementType;
322
+ children?: React$1.ReactNode;
323
+ }
324
+ /**
325
+ * @description 用于将 `className` 分类编写的组件,内置了类似`clsx`的功能,并且去除重复的 className。
326
+ * @description_en A component for `className` classification, built-in similar to `clsx` functionality, and removes duplicate className.
327
+ * @component
328
+ * @example
329
+ * ```tsx
330
+ * <ClassName className={{ base: "p-2 bg-red", hover: ["hover:bg-blue", { "hover:text-white": true }] }}>
331
+ * <button>Click me</button>
332
+ * </ClassName>
333
+ * ```
334
+ *
335
+ * @example
336
+ * ```tsx
337
+ * <ClassName
338
+ * className={{
339
+ * base: ["p-2", { "bg-red": condition }],
340
+ * hover: { "hover:bg-blue": true },
341
+ * }}
342
+ * asWrapper="span"
343
+ * >
344
+ * <button>Click me</button>
345
+ * </ClassName>
346
+ * ```
347
+ */
348
+ declare const ClassName: FC<ClassNameProps>;
349
+
350
+ interface ToggleProps<T = boolean> {
351
+ /**
352
+ * @description_en The initial value to toggle.
353
+ * @description_zh 初始切换值。
354
+ * @default 0
355
+ */
356
+ index?: number;
357
+ /**
358
+ * @description_en Array of values to toggle between.
359
+ * @description_zh 可切换的值数组。
360
+ */
361
+ options: T[];
362
+ /**
363
+ * @description_en Function to determine the next value index in the toggle sequence.
364
+ * @description_zh 确定切换序列中下一个值索引的函数。
365
+ * @optional
366
+ */
367
+ next?: (curIndex: number, options: T[]) => number;
368
+ /**
369
+ * @description_en Render function, receiving the toggled value and toggle function.
370
+ * @description_zh 渲染函数,接收切换后的值和切换函数。
371
+ */
372
+ render: (value: T, toggle: () => void) => ReactNode;
373
+ }
374
+ /**
375
+ * @description_zh 一个声明式组件,用于在预定义选项中切换值并通过 render 函数传递给子组件,支持自定义切换逻辑。
376
+ * @description_en A declarative component for toggling between predefined values and passing them to children via a render function, supporting custom toggle logic.
377
+ * @component
378
+ * @example
379
+ * ```tsx
380
+ * <Toggle
381
+ * options={["light", "dark"]}
382
+ * render={(theme, toggleTheme) => (
383
+ * <div onClick={toggleTheme}>当前主题: {theme}</div>
384
+ * )}
385
+ * />
386
+ * ```
387
+ */
388
+ declare const Toggle: <T>(props: ToggleProps<T>) => React$1.ReactNode;
389
+
390
+ interface ArrayRenderProps<T> {
391
+ items: T[];
392
+ renderItem: (item: T, index: number) => React$1.ReactNode;
393
+ filter?: (item: T) => boolean;
394
+ }
395
+ declare function ArrayRender<T>(props: ArrayRenderProps<T>): ReactNode;
396
+
309
397
  interface DateRenderProps<T = string> {
310
398
  /**
311
399
  * @description_en The input date to render (Date object, ISO string, or timestamp).
@@ -349,42 +437,6 @@ interface DateRenderProps<T = string> {
349
437
  */
350
438
  declare function DateRender<T = string>({ source, format, children, }: DateRenderProps<T>): React$1.JSX.Element | null;
351
439
 
352
- /**
353
- * @description 性能优化,替代 React.Children.forEach, 回调可以返回 false 来中断循环
354
- * @description_en Replace React.Children.forEach, the callback can return false to interrupt the loop
355
- */
356
- declare function childrenLoop(children: React$1.ReactNode | undefined, callback: (child: React$1.ReactNode, index: number) => boolean | void): void;
357
- /**
358
- * @param schema
359
- * @example
360
- * YY | 18 | Two-digit year
361
- * YYYY | 2018 | Four-digit year
362
- * M | 1-12 | The month, beginning at 1
363
- * MM | 01-12 | The month, 2-digits
364
- * MMM | Jan-Dec | The abbreviated month name
365
- * MMMM | January-December | The full month name
366
- * D | 1-31 | The day of the month
367
- * DD | 01-31 | The day of the month, 2-digits
368
- * d | 0-6 | The day of the week, with Sunday as 0
369
- * dd | Su-Sa | The min name of the day of the week
370
- * ddd | Sun-Sat | The short name of the day of the week
371
- * dddd | Sunday-Saturday | The name of the day of the week
372
- * H | 0-23 | The hour
373
- * HH | 00-23 | The hour, 2-digits
374
- * h | 1-12 | The hour, 12-hour clock
375
- * hh | 01-12 | The hour, 12-hour clock, 2-digits
376
- * m | 0-59 | The minute
377
- * mm | 00-59 | The minute, 2-digits
378
- * s | 0-59 | The second
379
- * ss | 00-59 | The second, 2-digits
380
- * SSS | 000-999 | The millisecond, 3-digits
381
- * Z | +05:00 | The offset from UTC, ±HH:mm
382
- * ZZ | +0500 | The offset from UTC, ±HHmm
383
- * A | AM | PM
384
- * a | am | pm
385
- */
386
- declare function formatDate(schema: string, date?: Date): string;
387
-
388
440
  interface UseControlledOptions<T> {
389
441
  /**
390
442
  * @description - 非受控模式下的默认值,会被受控模式下的值覆盖
@@ -412,5 +464,5 @@ interface UseControlledOptions<T> {
412
464
  }
413
465
  declare function useControlled<T>(options: UseControlledOptions<T>): [T, Dispatch<React.SetStateAction<T>>];
414
466
 
415
- export { ArrayRender, DateRender, False, If, Pipe, Scope, SizeBox, Switch, Toggle, True, When, childrenLoop, formatDate, useControlled };
416
- export type { ArrayRenderProps, DateRenderProps, ElseIfProps, ElseProps, FalseProps, IfProps, PipeProps, ScopeProps, SwitchCaseProps, SwitchDefaultProps, SwitchProps, ThenProps, ToggleProps, TrueProps, UseControlledOptions, WhenProps };
467
+ export { ArrayRender, ClassName, Counter, DateRender, False, If, Pipe, Scope, SizeBox, Switch, Toggle, True, When, childrenLoop, cx, formatDate, useControlled };
468
+ export type { ArrayRenderProps, ClassNameProps, CxInput, DateRenderProps, ElseIfProps, ElseProps, FalseProps, IfProps, PipeProps, ScopeProps, SwitchCaseProps, SwitchDefaultProps, SwitchProps, ThenProps, ToggleProps, TrueProps, UseControlledOptions, WhenProps };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import r,{useMemo as h,useEffect as J,useState as b,Fragment as O,useCallback as H}from"react";function I(e,t){if(e===void 0)return;let n=0;if(Array.isArray(e)){for(const l of e)if(t(l,n++)===!1)break}else t(e,n)}function R(e,t){const n=t||new Date,l=n.getFullYear(),a=n.getMonth()+1,o=n.getDate(),i=n.getHours(),c=n.getMinutes(),u=n.getSeconds(),d=n.getMilliseconds(),s=n.getDay(),f=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],m=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],y=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],T=["January","February","March","April","May","June","July","August","September","October","November","December"],M=s===0?6:s-1,Y=m[M],v=f[M],D=a-1,C=T[D],A=y[D],x={YY:l.toString().slice(2),YYYY:l.toString(),M:a.toString(),MM:a.toString().padStart(2,"0"),MMM:A,MMMM:C,D:o.toString(),DD:o.toString().padStart(2,"0"),d:s.toString(),dd:v,ddd:v,dddd:Y,H:i.toString(),HH:i.toString().padStart(2,"0"),h:(i%12).toString(),hh:(i%12).toString().padStart(2,"0"),m:c.toString(),mm:c.toString().padStart(2,"0"),s:u.toString(),ss:u.toString().padStart(2,"0"),SSS:d.toString().padStart(3,"0"),Z:"+08:00",ZZ:"+0800",A:i<12?"AM":"PM",a:i<12?"am":"pm"};return e.replace(/YYYY|YY|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|SSS|Z{1,2}|A|a/g,k=>x[k])}const _=(e,t)=>e===t,S=e=>r.createElement(r.Fragment,null,e.children);S.displayName="Switch_Case";const E=e=>r.createElement(r.Fragment,null,e.children);E.displayName="Switch_Default";const g=e=>{const{value:t,compare:n=_,children:l,strict:a=!1}=e,o=new Set;let i=null,c=null,u=!1;return I(l,(d,s)=>{if(!r.isValidElement(d))throw new Error(`Switch Children only accepts valid React elements at index ${s}`);const f=d.type;if(f.displayName===S.displayName){const m=d.props;if(o.has(m.value))throw new Error(`Switch found duplicate Case value at index ${s}: ${JSON.stringify(m.value)}${a?" (detected in strict mode)":""}`);if(o.add(m.value),!i&&n(t,m.value)&&(i=m.children,a===!1))return!1}else if(f.displayName===E.displayName){if(u)throw new Error(`Switch can only have one Default child at index ${s}`);if(u=!0,c=d.props.children,!a&&i)return!1}else throw new Error(`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(f.displayName||f.name||f)} at index ${s}`)}),r.createElement(r.Fragment,null,i??c)};g.displayName="Switch",g.Case=S,g.Default=E,g.createTyped=function(){return{Switch:g,Case:S,Default:E}};const w=e=>r.createElement(r.Fragment,null,e.children),N=({children:e})=>r.createElement(r.Fragment,null,e),F=e=>r.createElement(r.Fragment,null,e.children);w.displayName="If_Then",N.displayName="If_Else",F.displayName="If_ElseIf";const p=({condition:e,children:t})=>{let n=null,l=null;const a=[];if(r.Children.forEach(t,o=>{if(!r.isValidElement(o))throw new Error("If component only accepts valid React elements");const i=o.type;if(i.displayName===w.displayName){if(n)throw new Error("If component can only have one Then child");n=o}else if(i.displayName===F.displayName)a.push(o);else if(i.displayName===N.displayName){if(l)throw new Error("If component can only have one Else child");l=o}else throw new Error(`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(i.displayName||i.name||i)}`)}),e)return n?r.createElement(r.Fragment,null,n.props.children):null;for(const o of a)if(o.props.condition)return r.createElement(r.Fragment,null,o.props.children);return l?r.createElement(r.Fragment,null,l.props.children):null};p.displayName="If",p.Then=w,p.ElseIf=F,p.Else=N,p.createTyped=function(){return{If:p,Then:w,ElseIf:F,Else:N}};const B=({condition:e,children:t})=>e?r.createElement(r.Fragment,null,t):null,P=({condition:e,children:t})=>e===!1?r.createElement(r.Fragment,null,t):null,W=({all:e,any:t,none:n,children:l,fallback:a})=>h(()=>(e&&(t||n)&&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)||n&&n.length>0&&n.every(o=>!o))),[e,t,n])?r.createElement(r.Fragment,null,l):r.createElement(r.Fragment,null,a||null),Z=({data:e,transform:t,render:n,fallback:l})=>{const a=h(()=>t.reduce((o,i)=>i(o),e),[e,t]);return a==null?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,n(a))},V=e=>{const{index:t=0,options:n,next:l,render:a}=e;J(()=>{if(n.length<t+1)throw new Error(`Index ${t} is out of bounds for options array of length ${n.length}. Defaulting to first option.`)},[t,n]);const[o,i]=b(t),c=()=>{i(u=>n.length?l?l(u,n):(u+1)%n.length:u)};return a(n[o],c)},$=e=>{const{children:t,h:n,w:l,size:a,height:o,width:i,className:c}=e;return r.createElement("div",{style:{width:a||l||i,height:a||n||o,flexShrink:0},className:c},t)};function j(e){const{items:t,renderItem:n,filter:l}=e;return t?r.createElement(O,null,t.map((a,o)=>l&&!l(a)?null:n(a,o))):(console.error("ArrayRender: items is null"),null)}const z=({let:e,props:t,children:n,fallback:l})=>{const a=h(()=>typeof e=="function"?e(t):e,[e,t]);return!n||!Object.keys(a).length?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,n(a))};function L({source:e,format:t,children:n}){const l=h(()=>{if(e instanceof Date)return e;if(typeof e=="string"||typeof e=="number"){const o=new Date(e);return isNaN(o.getTime())?null:o}return null},[e]),a=h(()=>l?t?t(l):l.toLocaleString():null,[l,t]);return!a||!n?null:r.createElement(r.Fragment,null,n(a))}const q="onChange",G="value";function K(e){const{defaultValue:t,onBeforeChange:n,trigger:l=q,valuePropName:a=G,props:o}=e,i=Object.prototype.hasOwnProperty.call(o,a),[c,u]=b(t),d=i?o[a]:c,s=h(()=>o[l],[o,l]),f=H(m=>{const y=typeof m=="function"?m(d):m;n&&n(y,d)===!1||(i||u(y),s&&s(y))},[i,n,l,d,s]);return[d,f]}export{j as ArrayRender,L as DateRender,P as False,p as If,Z as Pipe,z as Scope,$ as SizeBox,g as Switch,V as Toggle,B as True,W as When,I as childrenLoop,R as formatDate,K as useControlled};
1
+ import r,{useMemo as h,useEffect as I,isValidElement as T,Fragment as M,cloneElement as H,useState as A,useCallback as P}from"react";function Y(t,n){if(t===void 0)return;let e=0;if(Array.isArray(t)){for(const l of t)if(n(l,e++)===!1)break}else n(t,e)}function W(t,n){const e=n||new Date,l=e.getFullYear(),a=e.getMonth()+1,o=e.getDate(),s=e.getHours(),c=e.getMinutes(),u=e.getSeconds(),d=e.getMilliseconds(),i=e.getDay(),f=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],m=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],y=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],x=["January","February","March","April","May","June","July","August","September","October","November","December"],D=i===0?6:i-1,k=m[D],b=f[D],C=a-1,O=x[C],J=y[C],j={YY:l.toString().slice(2),YYYY:l.toString(),M:a.toString(),MM:a.toString().padStart(2,"0"),MMM:J,MMMM:O,D:o.toString(),DD:o.toString().padStart(2,"0"),d:i.toString(),dd:b,ddd:b,dddd:k,H:s.toString(),HH:s.toString().padStart(2,"0"),h:(s%12).toString(),hh:(s%12).toString().padStart(2,"0"),m:c.toString(),mm:c.toString().padStart(2,"0"),s:u.toString(),ss:u.toString().padStart(2,"0"),SSS:d.toString().padStart(3,"0"),Z:"+08:00",ZZ:"+0800",A:s<12?"AM":"PM",a:s<12?"am":"pm"};return t.replace(/YYYY|YY|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|SSS|Z{1,2}|A|a/g,R=>j[R])}class _{count=0;next(){return this.count++}}function v(...t){const n=new Set;for(const e of t)if(e){if(typeof e=="string")n.add(e);else if(Array.isArray(e))e.forEach(l=>n.add(l));else if(typeof e=="object")for(const[l,a]of Object.entries(e))a&&n.add(l)}return Array.from(n).join(" ")}const B=(t,n)=>t===n,S=t=>r.createElement(r.Fragment,null,t.children);S.displayName="Switch_Case";const E=t=>r.createElement(r.Fragment,null,t.children);E.displayName="Switch_Default";const g=t=>{const{value:n,compare:e=B,children:l,strict:a=!1}=t,o=new Set;let s=null,c=null,u=!1;return Y(l,(d,i)=>{if(!r.isValidElement(d))throw new Error(`Switch Children only accepts valid React elements at index ${i}`);const f=d.type;if(f.displayName===S.displayName){const m=d.props;if(o.has(m.value))throw new Error(`Switch found duplicate Case value at index ${i}: ${JSON.stringify(m.value)}${a?" (detected in strict mode)":""}`);if(o.add(m.value),!s&&e(n,m.value)&&(s=m.children,a===!1))return!1}else if(f.displayName===E.displayName){if(u)throw new Error(`Switch can only have one Default child at index ${i}`);if(u=!0,c=d.props.children,!a&&s)return!1}else throw new Error(`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(f.displayName||f.name||f)} at index ${i}`)}),r.createElement(r.Fragment,null,s??c)};g.displayName="Switch",g.Case=S,g.Default=E,g.createTyped=function(){return{Switch:g,Case:S,Default:E}};const w=t=>r.createElement(r.Fragment,null,t.children),N=({children:t})=>r.createElement(r.Fragment,null,t),F=t=>r.createElement(r.Fragment,null,t.children);w.displayName="If_Then",N.displayName="If_Else",F.displayName="If_ElseIf";const p=({condition:t,children:n})=>{let e=null,l=null;const a=[];if(r.Children.forEach(n,o=>{if(!r.isValidElement(o))throw new Error("If component only accepts valid React elements");const s=o.type;if(s.displayName===w.displayName){if(e)throw new Error("If component can only have one Then child");e=o}else if(s.displayName===F.displayName)a.push(o);else if(s.displayName===N.displayName){if(l)throw new Error("If component can only have one Else child");l=o}else throw new Error(`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(s.displayName||s.name||s)}`)}),t)return e?r.createElement(r.Fragment,null,e.props.children):null;for(const o of a)if(o.props.condition)return r.createElement(r.Fragment,null,o.props.children);return l?r.createElement(r.Fragment,null,l.props.children):null};p.displayName="If",p.Then=w,p.ElseIf=F,p.Else=N,p.createTyped=function(){return{If:p,Then:w,ElseIf:F,Else:N}};const V=({condition:t,children:n})=>t?r.createElement(r.Fragment,null,n):null,Z=({condition:t,children:n})=>t===!1?r.createElement(r.Fragment,null,n):null,$=({all:t,any:n,none:e,children:l,fallback:a})=>h(()=>(t&&(n||e)&&console.warn('When: Multiple condition types (all, any, none) provided; "all" takes precedence.'),!!(t&&t.length>0&&t.every(Boolean)||n&&n.length>0&&n.some(Boolean)||e&&e.length>0&&e.every(o=>!o))),[t,n,e])?r.createElement(r.Fragment,null,l):r.createElement(r.Fragment,null,a||null),z=({data:t,transform:n,render:e,fallback:l})=>{const a=h(()=>n.reduce((o,s)=>s(o),t),[t,n]);return a==null?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,e(a))},L=t=>{const{children:n,h:e,w:l,size:a,height:o,width:s,className:c}=t;return r.createElement("div",{style:{width:a||l||s,height:a||e||o,flexShrink:0},className:c},n)},q=({let:t,props:n,children:e,fallback:l})=>{const a=h(()=>typeof t=="function"?t(n):t,[t,n]);return!e||!Object.keys(a).length?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,e(a))},G=t=>{const{className:n,children:e,asWrapper:l}=t;if(I(()=>{T(e)===!1&&console.warn("<ClassName>: children is not a valid React element. Please check your code.")},[e]),!e)return null;if(!n)return r.createElement(M,null,e);const a=v(...Object.values(n));return l?r.createElement(typeof l=="string"?l:"div",{className:a},e):T(e)?H(e,{className:v(e.props.className,a)}):r.createElement(M,null,e)},K=t=>{const{index:n=0,options:e,next:l,render:a}=t;I(()=>{if(e.length<n+1)throw new Error(`Index ${n} is out of bounds for options array of length ${e.length}. Defaulting to first option.`)},[n,e]);const[o,s]=A(n),c=()=>{s(u=>e.length?l?l(u,e):(u+1)%e.length:u)};return a(e[o],c)};function Q(t){const{items:n,renderItem:e,filter:l}=t;return n?r.createElement(M,null,n.map((a,o)=>l&&!l(a)?null:e(a,o))):(console.error("ArrayRender: items is null"),null)}function U({source:t,format:n,children:e}){const l=h(()=>{if(t instanceof Date)return t;if(typeof t=="string"||typeof t=="number"){const o=new Date(t);return isNaN(o.getTime())?null:o}return null},[t]),a=h(()=>l?n?n(l):l.toLocaleString():null,[l,n]);return!a||!e?null:r.createElement(r.Fragment,null,e(a))}const X="onChange",ee="value";function te(t){const{defaultValue:n,onBeforeChange:e,trigger:l=X,valuePropName:a=ee,props:o}=t,s=Object.prototype.hasOwnProperty.call(o,a),[c,u]=A(n),d=s?o[a]:c,i=h(()=>o[l],[o,l]),f=P(m=>{const y=typeof m=="function"?m(d):m;e&&e(y,d)===!1||(s||u(y),i&&i(y))},[s,e,l,d,i]);return[d,f]}export{Q as ArrayRender,G as ClassName,_ as Counter,U as DateRender,Z as False,p as If,z as Pipe,q as Scope,L as SizeBox,g as Switch,K as Toggle,V as True,$ as When,Y as childrenLoop,v as cx,W as formatDate,te as useControlled};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wwog/react",
3
- "version": "1.2.3",
3
+ "version": "1.2.5",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "author": "wwog",
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { childrenLoop } from "../utils";
2
+ import { childrenLoop } from "../../utils";
3
3
 
4
4
  export interface SwitchProps<T> {
5
5
  value: T;
@@ -2,4 +2,3 @@ export * from "./Switch";
2
2
  export * from "./If";
3
3
  export * from "./When";
4
4
  export * from "./Pipe";
5
- export * from "./Toggle";
@@ -1,4 +1,2 @@
1
- export * from "./SizeBox";
2
1
  export * from "./ArrayRender";
3
- export * from "./Scope";
4
2
  export * from "./DateRender";
@@ -0,0 +1,86 @@
1
+ import React, {
2
+ cloneElement,
3
+ FC,
4
+ Fragment,
5
+ isValidElement,
6
+ useEffect,
7
+ type HTMLElementType,
8
+ } from "react";
9
+ import { cx, type CxInput } from "../../utils";
10
+
11
+ export interface ClassNameProps {
12
+ className?: {
13
+ base?: CxInput;
14
+ hover?: CxInput;
15
+ active?: CxInput;
16
+ focus?: CxInput;
17
+ disabled?: CxInput;
18
+ [key: string]: CxInput;
19
+ };
20
+ /**
21
+ * @description 传入容器标签名.是否生成包含所有 `className` 的 `wrapper`, 默认 false, 传递 `true` 为 `div。`
22
+ * @description_en Whether to generate a `wrapper` containing all `className`, default is false, and pass the container tag name, if `true` will be `div`.
23
+ */
24
+ asWrapper?: boolean | HTMLElementType;
25
+ children?: React.ReactNode;
26
+ }
27
+
28
+ /**
29
+ * @description 用于将 `className` 分类编写的组件,内置了类似`clsx`的功能,并且去除重复的 className。
30
+ * @description_en A component for `className` classification, built-in similar to `clsx` functionality, and removes duplicate className.
31
+ * @component
32
+ * @example
33
+ * ```tsx
34
+ * <ClassName className={{ base: "p-2 bg-red", hover: ["hover:bg-blue", { "hover:text-white": true }] }}>
35
+ * <button>Click me</button>
36
+ * </ClassName>
37
+ * ```
38
+ *
39
+ * @example
40
+ * ```tsx
41
+ * <ClassName
42
+ * className={{
43
+ * base: ["p-2", { "bg-red": condition }],
44
+ * hover: { "hover:bg-blue": true },
45
+ * }}
46
+ * asWrapper="span"
47
+ * >
48
+ * <button>Click me</button>
49
+ * </ClassName>
50
+ * ```
51
+ */
52
+ export const ClassName: FC<ClassNameProps> = (props) => {
53
+ const { className, children, asWrapper } = props;
54
+
55
+ useEffect(() => {
56
+ if (isValidElement(children) === false) {
57
+ console.warn(
58
+ "<ClassName>: children is not a valid React element. Please check your code."
59
+ );
60
+ }
61
+ }, [children]);
62
+
63
+ if (!children) {
64
+ return null;
65
+ }
66
+
67
+ if (!className) {
68
+ return <Fragment>{children}</Fragment>;
69
+ }
70
+
71
+ const generatedCls = cx(...Object.values(className));
72
+
73
+ if (asWrapper) {
74
+ const Wrapper = typeof asWrapper === "string" ? asWrapper : "div";
75
+ return <Wrapper className={generatedCls}>{children}</Wrapper>;
76
+ }
77
+
78
+ if (isValidElement(children)) {
79
+ return cloneElement(children, {
80
+ //@ts-expect-error type error
81
+ className: cx(children.props.className, generatedCls),
82
+ } as any);
83
+ }
84
+
85
+ return <Fragment>{children}</Fragment>;
86
+ };
@@ -0,0 +1,4 @@
1
+ export * from "./SizeBox";
2
+ export * from "./Scope";
3
+ export * from "./ClassName";
4
+ export * from "./Toggle";
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
- export * from "./ProcessControl";
2
- export * from "./Common";
1
+ export * from "./components/ProcessControl";
2
+ export * from "./components/Sundry";
3
+ export * from "./components/Struct";
3
4
  export * from "./utils";
4
5
  export * from "./hooks";
@@ -139,3 +139,47 @@ export function formatDate(schema: string, date?: Date): string {
139
139
  }
140
140
  );
141
141
  }
142
+
143
+ export class Counter {
144
+ count = 0;
145
+
146
+ /**
147
+ * @description 获取下一个计数值,不考虑越界。
148
+ * @description_en Get the next count value, without considering overflow.
149
+ */
150
+ next() {
151
+ return this.count++;
152
+ }
153
+ }
154
+
155
+ export type CxInput =
156
+ | string
157
+ | string[]
158
+ | Record<string, boolean>
159
+ | undefined
160
+ | null
161
+ | false;
162
+
163
+ export function cx(...args: CxInput[]): string {
164
+ const classes = new Set<string>();
165
+
166
+ for (const arg of args) {
167
+ if (!arg) {
168
+ continue;
169
+ }
170
+
171
+ if (typeof arg === "string") {
172
+ classes.add(arg);
173
+ } else if (Array.isArray(arg)) {
174
+ arg.forEach((item) => classes.add(item));
175
+ } else if (typeof arg === "object") {
176
+ for (const [key, value] of Object.entries(arg)) {
177
+ if (value) {
178
+ classes.add(key);
179
+ }
180
+ }
181
+ }
182
+ }
183
+
184
+ return Array.from(classes).join(" ");
185
+ }
File without changes
File without changes