@wwog/react 1.2.5 → 1.2.7

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/dist/index.d.mts CHANGED
@@ -259,61 +259,49 @@ interface ScopeProps {
259
259
  */
260
260
  declare const Scope: FC<ScopeProps>;
261
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
262
  type CxInput = string | string[] | Record<string, boolean> | undefined | null | false;
306
263
  declare function cx(...args: CxInput[]): string;
307
264
 
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
- };
265
+ interface StylesDescriptor {
266
+ base?: CxInput;
267
+ hover?: CxInput;
268
+ active?: CxInput;
269
+ focus?: CxInput;
270
+ disabled?: CxInput;
271
+ color?: CxInput;
272
+ size?: CxInput;
273
+ layer?: CxInput;
274
+ wrapper?: CxInput;
275
+ dark?: CxInput;
276
+ light?: CxInput;
277
+ sundry?: CxInput;
278
+ [key: string]: CxInput;
279
+ }
280
+ type StylesType = StylesDescriptor | string;
281
+ interface StylesProps {
282
+ /**
283
+ * @description_en either as a simple string or a categorized object with predefined or custom keys.
284
+ * @description 可以是简单字符串或包含预定义或自定义键的分类对象。
285
+ * @example
286
+ * ```tsx
287
+ * // Simple string
288
+ * <Styles className="p-2 bg-red">
289
+ * <button>Click</button>
290
+ * </Styles>
291
+ *
292
+ * // Categorized object
293
+ * <Styles
294
+ * className={{
295
+ * base: ["p-2", { "bg-red": true }],
296
+ * hover: "hover:bg-blue",
297
+ * color: "text-blue",
298
+ * }}
299
+ * >
300
+ * <button>Click</button>
301
+ * </Styles>
302
+ * ```
303
+ */
304
+ className?: StylesType;
317
305
  /**
318
306
  * @description 传入容器标签名.是否生成包含所有 `className` 的 `wrapper`, 默认 false, 传递 `true` 为 `div。`
319
307
  * @description_en Whether to generate a `wrapper` containing all `className`, default is false, and pass the container tag name, if `true` will be `div`.
@@ -322,30 +310,27 @@ interface ClassNameProps {
322
310
  children?: React$1.ReactNode;
323
311
  }
324
312
  /**
325
- * @description 用于将 `className` 分类编写的组件,内置了类似`clsx`的功能,并且去除重复的 className。
326
- * @description_en A component for `className` classification, built-in similar to `clsx` functionality, and removes duplicate className.
313
+ * @description 分类编写样式和基本的string样式,内置类似 `clsx` 对类型描述对象的值进行组合,支持去除重复类名,支持嵌套。
314
+ * @description_en Categorized writing styles and basic string styles, built-in similar to `clsx` to combine the values of type description objects, support removing duplicate class names, and support nesting.
327
315
  * @component
328
316
  * @example
329
317
  * ```tsx
330
- * <ClassName className={{ base: "p-2 bg-red", hover: ["hover:bg-blue", { "hover:text-white": true }] }}>
318
+ * <Styles className={{ base: "p-2 bg-red", hover: ["hover:bg-blue", { "hover:text-white": true }] }}>
331
319
  * <button>Click me</button>
332
- * </ClassName>
320
+ * </Styles>
333
321
  * ```
334
322
  *
335
323
  * @example
336
324
  * ```tsx
337
- * <ClassName
338
- * className={{
339
- * base: ["p-2", { "bg-red": condition }],
340
- * hover: { "hover:bg-blue": true },
341
- * }}
325
+ * <Styles
326
+ * className="p-2 bg-red"
342
327
  * asWrapper="span"
343
328
  * >
344
329
  * <button>Click me</button>
345
- * </ClassName>
330
+ * </Styles>
346
331
  * ```
347
332
  */
348
- declare const ClassName: FC<ClassNameProps>;
333
+ declare const Styles: FC<StylesProps>;
349
334
 
350
335
  interface ToggleProps<T = boolean> {
351
336
  /**
@@ -464,5 +449,50 @@ interface UseControlledOptions<T> {
464
449
  }
465
450
  declare function useControlled<T>(options: UseControlledOptions<T>): [T, Dispatch<React.SetStateAction<T>>];
466
451
 
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 };
452
+ /**
453
+ * @param schema
454
+ * @example
455
+ * YY | 18 | Two-digit year
456
+ * YYYY | 2018 | Four-digit year
457
+ * M | 1-12 | The month, beginning at 1
458
+ * MM | 01-12 | The month, 2-digits
459
+ * MMM | Jan-Dec | The abbreviated month name
460
+ * MMMM | January-December | The full month name
461
+ * D | 1-31 | The day of the month
462
+ * DD | 01-31 | The day of the month, 2-digits
463
+ * d | 0-6 | The day of the week, with Sunday as 0
464
+ * dd | Su-Sa | The min name of the day of the week
465
+ * ddd | Sun-Sat | The short name of the day of the week
466
+ * dddd | Sunday-Saturday | The name of the day of the week
467
+ * H | 0-23 | The hour
468
+ * HH | 00-23 | The hour, 2-digits
469
+ * h | 1-12 | The hour, 12-hour clock
470
+ * hh | 01-12 | The hour, 12-hour clock, 2-digits
471
+ * m | 0-59 | The minute
472
+ * mm | 00-59 | The minute, 2-digits
473
+ * s | 0-59 | The second
474
+ * ss | 00-59 | The second, 2-digits
475
+ * SSS | 000-999 | The millisecond, 3-digits
476
+ * Z | +05:00 | The offset from UTC, ±HH:mm
477
+ * ZZ | +0500 | The offset from UTC, ±HHmm
478
+ * A | AM | PM
479
+ * a | am | pm
480
+ */
481
+ declare function formatDate(schema: string, date?: Date): string;
482
+ declare class Counter {
483
+ count: number;
484
+ /**
485
+ * @description 获取下一个计数值,不考虑越界。
486
+ * @description_en Get the next count value, without considering overflow.
487
+ */
488
+ next(): number;
489
+ }
490
+
491
+ /**
492
+ * @description 性能优化,替代 React.Children.forEach, 回调可以返回 false 来中断循环
493
+ * @description_en Replace React.Children.forEach, the callback can return false to interrupt the loop
494
+ */
495
+ declare function childrenLoop(children: React$1.ReactNode | undefined, callback: (child: React$1.ReactNode, index: number) => boolean | void): void;
496
+
497
+ export { ArrayRender, Counter, DateRender, False, If, Pipe, Scope, SizeBox, Styles, Switch, Toggle, True, When, childrenLoop, cx, formatDate, useControlled };
498
+ export type { ArrayRenderProps, CxInput, DateRenderProps, ElseIfProps, ElseProps, FalseProps, IfProps, PipeProps, ScopeProps, StylesDescriptor, StylesProps, StylesType, SwitchCaseProps, SwitchDefaultProps, SwitchProps, ThenProps, ToggleProps, TrueProps, UseControlledOptions, WhenProps };
package/dist/index.js CHANGED
@@ -1 +1 @@
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};
1
+ import r,{useMemo as h,Children as J,Fragment as S,isValidElement as P,cloneElement as R,useEffect as W,useState as I,useCallback as H}from"react";function T(e,n){if(e===void 0)return;let t=0;if(Array.isArray(e)){for(const l of e)if(n(l,t++)===!1)break}else n(e,t)}const _=(e,n)=>e===n,E=e=>r.createElement(r.Fragment,null,e.children);E.displayName="Switch_Case";const w=e=>r.createElement(r.Fragment,null,e.children);w.displayName="Switch_Default";const y=e=>{const{value:n,compare:t=_,children:l,strict:a=!1}=e,o=new Set;let s=null,c=null,u=!1;return T(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===E.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&&t(n,m.value)&&(s=m.children,a===!1))return!1}else if(f.displayName===w.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)};y.displayName="Switch",y.Case=E,y.Default=w,y.createTyped=function(){return{Switch:y,Case:E,Default:w}};const N=e=>r.createElement(r.Fragment,null,e.children),v=({children:e})=>r.createElement(r.Fragment,null,e),F=e=>r.createElement(r.Fragment,null,e.children);N.displayName="If_Then",v.displayName="If_Else",F.displayName="If_ElseIf";const p=({condition:e,children:n})=>{let t=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===N.displayName){if(t)throw new Error("If component can only have one Then child");t=o}else if(s.displayName===F.displayName)a.push(o);else if(s.displayName===v.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)}`)}),e)return t?r.createElement(r.Fragment,null,t.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=N,p.ElseIf=F,p.Else=v,p.createTyped=function(){return{If:p,Then:N,ElseIf:F,Else:v}};const B=({condition:e,children:n})=>e?r.createElement(r.Fragment,null,n):null,V=({condition:e,children:n})=>e===!1?r.createElement(r.Fragment,null,n):null,Z=({all:e,any:n,none:t,children:l,fallback:a})=>h(()=>(e&&(n||t)&&console.warn('When: Multiple condition types (all, any, none) provided; "all" takes precedence.'),!!(e&&e.length>0&&e.every(Boolean)||n&&n.length>0&&n.some(Boolean)||t&&t.length>0&&t.every(o=>!o))),[e,n,t])?r.createElement(r.Fragment,null,l):r.createElement(r.Fragment,null,a||null),z=({data:e,transform:n,render:t,fallback:l})=>{const a=h(()=>n.reduce((o,s)=>s(o),e),[e,n]);return a==null?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,t(a))},L=e=>{const{children:n,h:t,w:l,size:a,height:o,width:s,className:c}=e;return r.createElement("div",{style:{width:a||l||s,height:a||t||o,flexShrink:0},className:c},n)},q=({let:e,props:n,children:t,fallback:l})=>{const a=h(()=>typeof e=="function"?e(n):e,[e,n]);return!t||!Object.keys(a).length?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,t(a))};function M(...e){const n=new Set;for(const t of e)if(t){if(typeof t=="string")n.add(t);else if(Array.isArray(t))t.forEach(l=>n.add(l));else if(typeof t=="object")for(const[l,a]of Object.entries(t))a&&n.add(l)}return Array.from(n).join(" ")}const G=e=>typeof e=="object"&&!!e,b=({className:e,children:n,asWrapper:t=!1})=>{if(!n)return null;if(J.count(n)>1)return console.error("<Styles>: children has more than one child. Please check your code."),r.createElement(S,null,n);if(!e)return r.createElement(S,null,n);const l=typeof e=="string"?e:M(...Object.values(e));if(t)return r.createElement(t===!0?"div":t,{className:l},n);if(P(n)){const a=n;let o=a?.props?.className;return a?.type?.displayName===b.displayName&&G(o)&&(o=M(...Object.values(o))),R(n,{className:M(l,o)})}return console.error("<Styles>: children is not a valid React element. Please check your code."),r.createElement(S,null,n)};b.displayName="W/Styles";const K=e=>{const{index:n=0,options:t,next:l,render:a}=e;W(()=>{if(t.length<n+1)throw new Error(`Index ${n} is out of bounds for options array of length ${t.length}. Defaulting to first option.`)},[n,t]);const[o,s]=I(n),c=()=>{s(u=>t.length?l?l(u,t):(u+1)%t.length:u)};return a(t[o],c)};function Q(e){const{items:n,renderItem:t,filter:l}=e;return n?r.createElement(S,null,n.map((a,o)=>l&&!l(a)?null:t(a,o))):(console.error("ArrayRender: items is null"),null)}function U({source:e,format:n,children:t}){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?n?n(l):l.toLocaleString():null,[l,n]);return!a||!t?null:r.createElement(r.Fragment,null,t(a))}const X="onChange",$="value";function ee(e){const{defaultValue:n,onBeforeChange:t,trigger:l=X,valuePropName:a=$,props:o}=e,s=Object.prototype.hasOwnProperty.call(o,a),[c,u]=I(n),d=s?o[a]:c,i=h(()=>o[l],[o,l]),f=H(m=>{const g=typeof m=="function"?m(d):m;t&&t(g,d)===!1||(s||u(g),i&&i(g))},[s,t,d,i]);return[d,f]}function te(e,n){const t=n||new Date,l=t.getFullYear(),a=t.getMonth()+1,o=t.getDate(),s=t.getHours(),c=t.getMinutes(),u=t.getSeconds(),d=t.getMilliseconds(),i=t.getDay(),f=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],m=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],g=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],A=["January","February","March","April","May","June","July","August","September","October","November","December"],Y=m[i],D=f[i],C=a-1,x=A[C],k=g[C],O={YY:l.toString().slice(2),YYYY:l.toString(),M:a.toString(),MM:a.toString().padStart(2,"0"),MMM:k,MMMM:x,D:o.toString(),DD:o.toString().padStart(2,"0"),d:i.toString(),dd:D,ddd:D,dddd:Y,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 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,j=>O[j])}class ne{count=0;next(){return this.count++}}export{Q as ArrayRender,ne as Counter,U as DateRender,V as False,p as If,z as Pipe,q as Scope,L as SizeBox,b as Styles,y as Switch,K as Toggle,B as True,Z as When,T as childrenLoop,M as cx,te as formatDate,ee as useControlled};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wwog/react",
3
- "version": "1.2.5",
3
+ "version": "1.2.7",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "author": "wwog",
@@ -17,18 +17,24 @@
17
17
  "scripts": {
18
18
  "build": "unbuild",
19
19
  "format": "biome format --write src",
20
- "check": "biome check --apply src",
21
- "test": "pnpm check && pnpm test:types && vitest run --coverage",
22
- "test:types": "tsc --noEmit --skipLibCheck"
20
+ "check": "biome check --write src",
21
+ "test:unit": "vitest run",
22
+ "test:types": "tsc --noEmit --skipLibCheck",
23
+ "all-suites": "pnpm run format && pnpm run check && pnpm run test:types && pnpm run test:unit",
24
+ "test:watch": "vitest"
23
25
  },
24
26
  "devDependencies": {
25
27
  "@biomejs/biome": "^1.9.4",
26
28
  "@types/react": "^19.1.2",
27
29
  "@types/react-dom": "^19.1.2",
28
- "@vitest/coverage-v8": "^3.1.1",
30
+ "@vitejs/plugin-react": "^4.4.1",
31
+ "@vitest/browser": "^3.1.3",
32
+ "@vitest/coverage-v8": "^3.1.3",
33
+ "playwright": "^1.52.0",
29
34
  "typescript": "^5.8.3",
30
35
  "unbuild": "^3.5.0",
31
- "vitest": "^3.1.1"
36
+ "vitest": "^3.1.3",
37
+ "vitest-browser-react": "^0.1.1"
32
38
  },
33
39
  "peerDependencies": {
34
40
  "react": ">=16.8.0",
@@ -1,5 +1,5 @@
1
1
  import React from "react";
2
- import { childrenLoop } from "../../utils";
2
+ import { childrenLoop } from "../../utils/reactUtils";
3
3
 
4
4
  export interface SwitchProps<T> {
5
5
  value: T;
@@ -120,5 +120,5 @@ Switch.createTyped = function <T>() {
120
120
  Switch: (props: SwitchProps<T>) => React.ReactElement | null;
121
121
  Case: (props: SwitchCaseProps<T>) => React.ReactElement;
122
122
  Default: (props: SwitchDefaultProps) => React.ReactElement;
123
- }
123
+ };
124
124
  };
@@ -1,4 +1,4 @@
1
- export * from "./Switch";
2
- export * from "./If";
3
- export * from "./When";
4
- export * from "./Pipe";
1
+ export * from './Switch'
2
+ export * from './If'
3
+ export * from './When'
4
+ export * from './Pipe'
@@ -1,2 +1,2 @@
1
- export * from "./ArrayRender";
2
- export * from "./DateRender";
1
+ export * from './ArrayRender'
2
+ export * from './DateRender'
@@ -0,0 +1,128 @@
1
+ import { expect, describe, it } from "vitest";
2
+ import { render } from "vitest-browser-react";
3
+ import { Styles } from "./Styles";
4
+ import React from "react";
5
+
6
+ describe("Styles Component", () => {
7
+ it("测试传递styles的className StylesDescriptor", () => {
8
+ const { getByRole } = render(
9
+ <Styles
10
+ className={{
11
+ base: ["p-2"],
12
+ hover: "hover:bg-blue",
13
+ color: "text-blue",
14
+ }}
15
+ >
16
+ <button>Click</button>
17
+ </Styles>
18
+ );
19
+ const renderClassName = getByRole("button").elements()[0].className;
20
+ expect(renderClassName).toBe("p-2 hover:bg-blue text-blue");
21
+ });
22
+
23
+ it("测试传递styles的className string", () => {
24
+ const { getByRole } = render(
25
+ <Styles className="button_class">
26
+ <button>Click</button>
27
+ </Styles>
28
+ );
29
+ const renderClassName = getByRole("button").elements()[0].className;
30
+ expect(renderClassName).toBe("button_class");
31
+ });
32
+
33
+ it("测试嵌套的Styles组件,仅string", () => {
34
+ const { getByRole } = render(
35
+ <Styles className="outer">
36
+ <Styles className="inner">
37
+ <button>Click</button>
38
+ </Styles>
39
+ </Styles>
40
+ );
41
+ const renderClassName = getByRole("button").elements()[0].className;
42
+ expect(renderClassName).toBe("outer inner");
43
+ });
44
+
45
+ it("测试嵌套的Styles组件,含string和StylesDescriptor", () => {
46
+ const { getByRole } = render(
47
+ <Styles
48
+ className={{
49
+ base: ["p-2"],
50
+ hover: "hover:bg-blue",
51
+ color: "text-blue",
52
+ }}
53
+ >
54
+ <Styles className="button_class">
55
+ <button>Click</button>
56
+ </Styles>
57
+ </Styles>
58
+ );
59
+ const renderClassName = getByRole("button").elements()[0].className;
60
+ expect(renderClassName).toBe("p-2 hover:bg-blue text-blue button_class");
61
+ });
62
+
63
+ it("测试children本身含有样式的混入", () => {
64
+ const { getByRole } = render(
65
+ <Styles className={{ base: "wrapper" }}>
66
+ <Styles className="outer">
67
+ <button className="inner">Click</button>
68
+ </Styles>
69
+ </Styles>
70
+ );
71
+ const renderClassName = getByRole("button").elements()[0].className;
72
+ expect(renderClassName).toBe("wrapper outer inner");
73
+ });
74
+
75
+ it("测试传递属性空值", () => {
76
+ const { getByRole } = render(
77
+ <Styles className={undefined}>
78
+ <button>Click</button>
79
+ </Styles>
80
+ );
81
+ const renderClassName = getByRole("button").elements()[0].className;
82
+ expect(renderClassName).toBe("");
83
+ });
84
+
85
+ it("测试空值与有值嵌套", () => {
86
+ const { getByRole } = render(
87
+ <Styles className={undefined}>
88
+ <Styles className="inner">
89
+ <button>Click</button>
90
+ </Styles>
91
+ </Styles>
92
+ );
93
+ const renderClassName = getByRole("button").elements()[0].className;
94
+ expect(renderClassName).toBe("inner");
95
+ });
96
+
97
+ it("测试asWrapper为true", () => {
98
+ const { container } = render(
99
+ <Styles className="wrapper" asWrapper={true}>
100
+ <button>Click</button>
101
+ </Styles>
102
+ );
103
+ expect(container.querySelector("div")?.className).toBe("wrapper");
104
+ expect(container.querySelector("button")?.className).toBe("");
105
+ });
106
+
107
+ it("测试asWrapper为指定标签", () => {
108
+ const { container } = render(
109
+ <Styles className="wrapper" asWrapper="section">
110
+ <button>Click</button>
111
+ </Styles>
112
+ );
113
+ expect(container.querySelector("section")?.className).toBe("wrapper");
114
+ expect(container.querySelector("button")?.className).toBe("");
115
+ });
116
+
117
+ it("测试asWrapper与嵌套组合", () => {
118
+ const { container } = render(
119
+ <Styles className="outer" asWrapper="div">
120
+ <Styles className="inner">
121
+ <button className="btn">Click</button>
122
+ </Styles>
123
+ </Styles>
124
+ );
125
+ expect(container.querySelector("div")?.className).toBe("outer");
126
+ expect(container.querySelector("button")?.className).toBe("inner btn");
127
+ });
128
+ });
@@ -0,0 +1,131 @@
1
+ import React, {
2
+ Children,
3
+ cloneElement,
4
+ FC,
5
+ Fragment,
6
+ isValidElement,
7
+ type HTMLElementType,
8
+ } from "react";
9
+ import { cx, type CxInput } from "../../utils/cx";
10
+
11
+ export interface StylesDescriptor {
12
+ base?: CxInput;
13
+ hover?: CxInput;
14
+ active?: CxInput;
15
+ focus?: CxInput;
16
+ disabled?: CxInput;
17
+ color?: CxInput;
18
+ size?: CxInput;
19
+ layer?: CxInput;
20
+ wrapper?: CxInput;
21
+ dark?: CxInput;
22
+ light?: CxInput;
23
+ sundry?: CxInput;
24
+ [key: string]: CxInput;
25
+ }
26
+
27
+ const isStylesDescriptor = (obj: any): obj is StylesDescriptor => {
28
+ return typeof obj === "object" && !!obj;
29
+ };
30
+
31
+ export type StylesType = StylesDescriptor | string;
32
+
33
+ export interface StylesProps {
34
+ /**
35
+ * @description_en either as a simple string or a categorized object with predefined or custom keys.
36
+ * @description 可以是简单字符串或包含预定义或自定义键的分类对象。
37
+ * @example
38
+ * ```tsx
39
+ * // Simple string
40
+ * <Styles className="p-2 bg-red">
41
+ * <button>Click</button>
42
+ * </Styles>
43
+ *
44
+ * // Categorized object
45
+ * <Styles
46
+ * className={{
47
+ * base: ["p-2", { "bg-red": true }],
48
+ * hover: "hover:bg-blue",
49
+ * color: "text-blue",
50
+ * }}
51
+ * >
52
+ * <button>Click</button>
53
+ * </Styles>
54
+ * ```
55
+ */
56
+ className?: StylesType;
57
+ /**
58
+ * @description 传入容器标签名.是否生成包含所有 `className` 的 `wrapper`, 默认 false, 传递 `true` 为 `div。`
59
+ * @description_en Whether to generate a `wrapper` containing all `className`, default is false, and pass the container tag name, if `true` will be `div`.
60
+ */
61
+ asWrapper?: boolean | HTMLElementType;
62
+ children?: React.ReactNode;
63
+ }
64
+
65
+ /**
66
+ * @description 分类编写样式和基本的string样式,内置类似 `clsx` 对类型描述对象的值进行组合,支持去除重复类名,支持嵌套。
67
+ * @description_en Categorized writing styles and basic string styles, built-in similar to `clsx` to combine the values of type description objects, support removing duplicate class names, and support nesting.
68
+ * @component
69
+ * @example
70
+ * ```tsx
71
+ * <Styles className={{ base: "p-2 bg-red", hover: ["hover:bg-blue", { "hover:text-white": true }] }}>
72
+ * <button>Click me</button>
73
+ * </Styles>
74
+ * ```
75
+ *
76
+ * @example
77
+ * ```tsx
78
+ * <Styles
79
+ * className="p-2 bg-red"
80
+ * asWrapper="span"
81
+ * >
82
+ * <button>Click me</button>
83
+ * </Styles>
84
+ * ```
85
+ */
86
+ export const Styles: FC<StylesProps> = ({
87
+ className,
88
+ children,
89
+ asWrapper = false,
90
+ }) => {
91
+ if (!children) {
92
+ return null;
93
+ }
94
+ if (Children.count(children) > 1) {
95
+ console.error(
96
+ "<Styles>: children has more than one child. Please check your code."
97
+ );
98
+ return <Fragment>{children}</Fragment>;
99
+ }
100
+
101
+ if (!className) {
102
+ return <Fragment>{children}</Fragment>;
103
+ }
104
+
105
+ const generatedClassName =
106
+ typeof className === "string" ? className : cx(...Object.values(className));
107
+
108
+ if (asWrapper) {
109
+ const Tag = asWrapper === true ? "div" : asWrapper;
110
+ return <Tag className={generatedClassName}>{children}</Tag>;
111
+ }
112
+ if (isValidElement(children)) {
113
+ const typeChildren = children as any;
114
+ let processedChildClassName = typeChildren?.props?.className;
115
+
116
+ if (typeChildren?.type?.displayName === Styles.displayName) {
117
+ if (isStylesDescriptor(processedChildClassName)) {
118
+ processedChildClassName = cx(...Object.values(processedChildClassName));
119
+ }
120
+ }
121
+
122
+ return cloneElement(children, {
123
+ className: cx(generatedClassName, processedChildClassName),
124
+ } as any);
125
+ }
126
+ console.error(
127
+ "<Styles>: children is not a valid React element. Please check your code."
128
+ );
129
+ return <Fragment>{children}</Fragment>;
130
+ };
131
+ Styles.displayName = "W/Styles";
@@ -1,4 +1,4 @@
1
- export * from "./SizeBox";
2
- export * from "./Scope";
3
- export * from "./ClassName";
4
- export * from "./Toggle";
1
+ export * from './SizeBox'
2
+ export * from './Scope'
3
+ export * from './Styles'
4
+ export * from './Toggle'
@@ -1 +1 @@
1
- export * from "./useControlled";
1
+ export * from './useControlled'