@wwog/react 1.2.5 → 1.2.6

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
@@ -314,7 +314,7 @@ function Layout() {
314
314
 
315
315
  #### `<ClassName>` (v1.2.5+)
316
316
 
317
- 用于将 CSS 类名分类编写的组件,内置类似`clsx`的功能,并且可以去除重复的 className。支持基础类、悬停态、激活态等多种状态的类名管理。
317
+ 用于将 CSS 类名分类编写的组件,内置类似`clsx`的功能,并且可以去除重复的 className
318
318
 
319
319
  ```tsx
320
320
  import { ClassName } from "@wwog/react";
@@ -327,6 +327,7 @@ function Example() {
327
327
  hover: "hover:bg-gray-100",
328
328
  active: "active:bg-gray-200",
329
329
  focus: "focus:ring-2",
330
+ other: "button",
330
331
  }}
331
332
  >
332
333
  <button>点击我</button>
package/dist/index.d.mts CHANGED
@@ -259,49 +259,6 @@ 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
 
@@ -464,5 +421,50 @@ interface UseControlledOptions<T> {
464
421
  }
465
422
  declare function useControlled<T>(options: UseControlledOptions<T>): [T, Dispatch<React.SetStateAction<T>>];
466
423
 
424
+ /**
425
+ * @param schema
426
+ * @example
427
+ * YY | 18 | Two-digit year
428
+ * YYYY | 2018 | Four-digit year
429
+ * M | 1-12 | The month, beginning at 1
430
+ * MM | 01-12 | The month, 2-digits
431
+ * MMM | Jan-Dec | The abbreviated month name
432
+ * MMMM | January-December | The full month name
433
+ * D | 1-31 | The day of the month
434
+ * DD | 01-31 | The day of the month, 2-digits
435
+ * d | 0-6 | The day of the week, with Sunday as 0
436
+ * dd | Su-Sa | The min name of the day of the week
437
+ * ddd | Sun-Sat | The short name of the day of the week
438
+ * dddd | Sunday-Saturday | The name of the day of the week
439
+ * H | 0-23 | The hour
440
+ * HH | 00-23 | The hour, 2-digits
441
+ * h | 1-12 | The hour, 12-hour clock
442
+ * hh | 01-12 | The hour, 12-hour clock, 2-digits
443
+ * m | 0-59 | The minute
444
+ * mm | 00-59 | The minute, 2-digits
445
+ * s | 0-59 | The second
446
+ * ss | 00-59 | The second, 2-digits
447
+ * SSS | 000-999 | The millisecond, 3-digits
448
+ * Z | +05:00 | The offset from UTC, ±HH:mm
449
+ * ZZ | +0500 | The offset from UTC, ±HHmm
450
+ * A | AM | PM
451
+ * a | am | pm
452
+ */
453
+ declare function formatDate(schema: string, date?: Date): string;
454
+ declare class Counter {
455
+ count: number;
456
+ /**
457
+ * @description 获取下一个计数值,不考虑越界。
458
+ * @description_en Get the next count value, without considering overflow.
459
+ */
460
+ next(): number;
461
+ }
462
+
463
+ /**
464
+ * @description 性能优化,替代 React.Children.forEach, 回调可以返回 false 来中断循环
465
+ * @description_en Replace React.Children.forEach, the callback can return false to interrupt the loop
466
+ */
467
+ declare function childrenLoop(children: React$1.ReactNode | undefined, callback: (child: React$1.ReactNode, index: number) => boolean | void): void;
468
+
467
469
  export { ArrayRender, ClassName, Counter, DateRender, False, If, Pipe, Scope, SizeBox, Switch, Toggle, True, When, childrenLoop, cx, formatDate, useControlled };
468
470
  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 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,useEffect as C,isValidElement as I,Fragment as M,cloneElement as R,useState as T,useCallback as H}from"react";function A(n,t){if(n===void 0)return;let e=0;if(Array.isArray(n)){for(const l of n)if(t(l,e++)===!1)break}else t(n,e)}const P=(n,t)=>n===t,S=n=>r.createElement(r.Fragment,null,n.children);S.displayName="Switch_Case";const E=n=>r.createElement(r.Fragment,null,n.children);E.displayName="Switch_Default";const g=n=>{const{value:t,compare:e=P,children:l,strict:a=!1}=n,o=new Set;let s=null,c=null,u=!1;return A(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(t,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=n=>r.createElement(r.Fragment,null,n.children),N=({children:n})=>r.createElement(r.Fragment,null,n),F=n=>r.createElement(r.Fragment,null,n.children);w.displayName="If_Then",N.displayName="If_Else",F.displayName="If_ElseIf";const p=({condition:n,children:t})=>{let e=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 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)}`)}),n)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 W=({condition:n,children:t})=>n?r.createElement(r.Fragment,null,t):null,_=({condition:n,children:t})=>n===!1?r.createElement(r.Fragment,null,t):null,B=({all:n,any:t,none:e,children:l,fallback:a})=>h(()=>(n&&(t||e)&&console.warn('When: Multiple condition types (all, any, none) provided; "all" takes precedence.'),!!(n&&n.length>0&&n.every(Boolean)||t&&t.length>0&&t.some(Boolean)||e&&e.length>0&&e.every(o=>!o))),[n,t,e])?r.createElement(r.Fragment,null,l):r.createElement(r.Fragment,null,a||null),V=({data:n,transform:t,render:e,fallback:l})=>{const a=h(()=>t.reduce((o,s)=>s(o),n),[n,t]);return a==null?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,e(a))},Z=n=>{const{children:t,h:e,w:l,size:a,height:o,width:s,className:c}=n;return r.createElement("div",{style:{width:a||l||s,height:a||e||o,flexShrink:0},className:c},t)},$=({let:n,props:t,children:e,fallback:l})=>{const a=h(()=>typeof n=="function"?n(t):n,[n,t]);return!e||!Object.keys(a).length?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,e(a))};function v(...n){const t=new Set;for(const e of n)if(e){if(typeof e=="string")t.add(e);else if(Array.isArray(e))e.forEach(l=>t.add(l));else if(typeof e=="object")for(const[l,a]of Object.entries(e))a&&t.add(l)}return Array.from(t).join(" ")}const z=n=>{const{className:t,children:e,asWrapper:l}=n;if(C(()=>{I(e)===!1&&console.warn("<ClassName>: children is not a valid React element. Please check your code.")},[e]),!e)return null;if(!t)return r.createElement(M,null,e);const a=v(...Object.values(t));return l?r.createElement(typeof l=="string"?l:"div",{className:a},e):I(e)?R(e,{className:v(e.props.className,a)}):r.createElement(M,null,e)},L=n=>{const{index:t=0,options:e,next:l,render:a}=n;C(()=>{if(e.length<t+1)throw new Error(`Index ${t} is out of bounds for options array of length ${e.length}. Defaulting to first option.`)},[t,e]);const[o,s]=T(t),c=()=>{s(u=>e.length?l?l(u,e):(u+1)%e.length:u)};return a(e[o],c)};function q(n){const{items:t,renderItem:e,filter:l}=n;return t?r.createElement(M,null,t.map((a,o)=>l&&!l(a)?null:e(a,o))):(console.error("ArrayRender: items is null"),null)}function G({source:n,format:t,children:e}){const l=h(()=>{if(n instanceof Date)return n;if(typeof n=="string"||typeof n=="number"){const o=new Date(n);return isNaN(o.getTime())?null:o}return null},[n]),a=h(()=>l?t?t(l):l.toLocaleString():null,[l,t]);return!a||!e?null:r.createElement(r.Fragment,null,e(a))}const K="onChange",Q="value";function U(n){const{defaultValue:t,onBeforeChange:e,trigger:l=K,valuePropName:a=Q,props:o}=n,s=Object.prototype.hasOwnProperty.call(o,a),[c,u]=T(t),d=s?o[a]:c,i=h(()=>o[l],[o,l]),f=H(m=>{const y=typeof m=="function"?m(d):m;e&&e(y,d)===!1||(s||u(y),i&&i(y))},[s,e,d,i]);return[d,f]}function X(n,t){const e=t||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"],Y=["January","February","March","April","May","June","July","August","September","October","November","December"],x=m[i],D=f[i],b=a-1,k=Y[b],O=y[b],J={YY:l.toString().slice(2),YYYY:l.toString(),M:a.toString(),MM:a.toString().padStart(2,"0"),MMM:O,MMMM:k,D:o.toString(),DD:o.toString().padStart(2,"0"),d:i.toString(),dd:D,ddd:D,dddd:x,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 n.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=>J[j])}class ee{count=0;next(){return this.count++}}export{q as ArrayRender,z as ClassName,ee as Counter,G as DateRender,_ as False,p as If,V as Pipe,$ as Scope,Z as SizeBox,g as Switch,L as Toggle,W as True,B as When,A as childrenLoop,v as cx,X as formatDate,U 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.6",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "author": "wwog",
@@ -17,9 +17,10 @@
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": "vitest run",
22
+ "test:types": "tsc --noEmit --skipLibCheck",
23
+ "all-suites": "pnpm run format && pnpm run check && pnpm run test:types && pnpm run test"
23
24
  },
24
25
  "devDependencies": {
25
26
  "@biomejs/biome": "^1.9.4",
@@ -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'
@@ -6,7 +6,7 @@ import React, {
6
6
  useEffect,
7
7
  type HTMLElementType,
8
8
  } from "react";
9
- import { cx, type CxInput } from "../../utils";
9
+ import { cx, type CxInput } from "../../utils/cx";
10
10
 
11
11
  export interface ClassNameProps {
12
12
  className?: {
@@ -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 './ClassName'
4
+ export * from './Toggle'
@@ -1 +1 @@
1
- export * from "./useControlled";
1
+ export * from './useControlled'
@@ -1,36 +1,36 @@
1
- import { useCallback, useMemo, useState, type Dispatch } from "react";
1
+ import {type Dispatch, useCallback, useMemo, useState} from 'react'
2
2
 
3
- const DEFAULT_TRIGGER = "onChange";
4
- const DEFAULT_VALUE_PROP_NAME = "value";
3
+ const DEFAULT_TRIGGER = 'onChange'
4
+ const DEFAULT_VALUE_PROP_NAME = 'value'
5
5
 
6
6
  export interface UseControlledOptions<T> {
7
7
  /**
8
8
  * @description - 非受控模式下的默认值,会被受控模式下的值覆盖
9
9
  * @description_en - Default value in uncontrolled mode, will be overridden by the value in controlled mode
10
10
  */
11
- defaultValue: T;
11
+ defaultValue: T
12
12
  /**
13
13
  * @description - 值变更前的回调函数,可用于拦截或修改新值
14
14
  * @description_en - Callback function before the value changes, can be used to intercept or modify the new value
15
15
  */
16
- onBeforeChange?: (newValue: T, currentValue: T) => boolean | void;
16
+ onBeforeChange?: (newValue: T, currentValue: T) => boolean | void
17
17
  /**
18
18
  * @description - 当值发生变化时触发的回调函数名
19
19
  * @description_en - Callback function name triggered when the value changes
20
20
  * @default - onChange
21
21
  */
22
- trigger?: string;
22
+ trigger?: string
23
23
  /**
24
24
  * @description - 值的属性名
25
25
  * @description_en - Property name of the value
26
26
  * @default - value
27
27
  */
28
- valuePropName?: string;
29
- props: Record<string, any>;
28
+ valuePropName?: string
29
+ props: Record<string, any>
30
30
  }
31
31
 
32
32
  export function useControlled<T>(
33
- options: UseControlledOptions<T>
33
+ options: UseControlledOptions<T>,
34
34
  ): [T, Dispatch<React.SetStateAction<T>>] {
35
35
  const {
36
36
  defaultValue,
@@ -38,36 +38,31 @@ export function useControlled<T>(
38
38
  trigger = DEFAULT_TRIGGER,
39
39
  valuePropName = DEFAULT_VALUE_PROP_NAME,
40
40
  props,
41
- } = options;
42
- const isControlled = Object.prototype.hasOwnProperty.call(
43
- props,
44
- valuePropName
45
- );
46
- const [internalValue, setInternalValue] = useState<T>(defaultValue);
47
- const value = isControlled ? props[valuePropName] : internalValue;
48
- const onChange = useMemo(() => props[trigger], [props, trigger]);
41
+ } = options
42
+ const isControlled = Object.prototype.hasOwnProperty.call(props, valuePropName)
43
+ const [internalValue, setInternalValue] = useState<T>(defaultValue)
44
+ const value = isControlled ? props[valuePropName] : internalValue
45
+ const onChange = useMemo(() => props[trigger], [props, trigger])
49
46
 
50
47
  const setValue = useCallback<Dispatch<React.SetStateAction<T>>>(
51
48
  (newValue) => {
52
49
  const resolvedValue =
53
- typeof newValue === "function"
54
- ? (newValue as (prev: T) => T)(value)
55
- : newValue;
50
+ typeof newValue === 'function' ? (newValue as (prev: T) => T)(value) : newValue
56
51
  if (onBeforeChange) {
57
- const shouldProceed = onBeforeChange(resolvedValue, value);
52
+ const shouldProceed = onBeforeChange(resolvedValue, value)
58
53
  if (shouldProceed === false) {
59
- return;
54
+ return
60
55
  }
61
56
  }
62
57
  if (!isControlled) {
63
- setInternalValue(resolvedValue);
58
+ setInternalValue(resolvedValue)
64
59
  }
65
60
  if (onChange) {
66
- onChange(resolvedValue);
61
+ onChange(resolvedValue)
67
62
  }
68
63
  },
69
- [isControlled, onBeforeChange, trigger, value, onChange]
70
- );
64
+ [isControlled, onBeforeChange, value, onChange],
65
+ )
71
66
 
72
- return [value, setValue];
67
+ return [value, setValue]
73
68
  }
package/src/index.ts CHANGED
@@ -1,5 +1,9 @@
1
- export * from "./components/ProcessControl";
2
- export * from "./components/Sundry";
3
- export * from "./components/Struct";
4
- export * from "./utils";
5
- export * from "./hooks";
1
+ export * from './components/ProcessControl'
2
+ export * from './components/Sundry'
3
+ export * from './components/Struct'
4
+
5
+ export * from './hooks'
6
+
7
+ export * from './utils/sundry'
8
+ export * from './utils/cx'
9
+ export * from './utils/reactUtils'
@@ -0,0 +1,33 @@
1
+ import {describe, expect, it} from 'vitest'
2
+ import {cx} from './cx'
3
+
4
+ describe('cx', () => {
5
+ it('应该正确合并字符串类名', () => {
6
+ expect(cx('foo', 'bar')).toBe('foo bar')
7
+ expect(cx('foo', 'bar', 'baz')).toBe('foo bar baz')
8
+ })
9
+
10
+ it('应该过滤掉 falsy 值', () => {
11
+ expect(cx('foo', null, 'bar', undefined, false)).toBe('foo bar')
12
+ })
13
+
14
+ it('应该正确处理数组', () => {
15
+ expect(cx(['foo', 'bar'])).toBe('foo bar')
16
+ expect(cx('baz', ['foo', 'bar'])).toBe('baz foo bar')
17
+ })
18
+
19
+ it('应该正确处理对象', () => {
20
+ expect(cx({foo: true, bar: false})).toBe('foo')
21
+ expect(cx({foo: true, bar: true})).toBe('foo bar')
22
+ })
23
+
24
+ it('应该正确处理混合输入', () => {
25
+ expect(cx('foo', ['bar', 'baz'], {qux: true, quux: false})).toBe('foo bar baz qux')
26
+ })
27
+
28
+ it('应该去除重复的类名', () => {
29
+ expect(cx('foo', 'foo', 'bar')).toBe('foo bar')
30
+ expect(cx('foo', ['foo', 'bar'])).toBe('foo bar')
31
+ expect(cx('foo', {foo: true})).toBe('foo')
32
+ })
33
+ })
@@ -0,0 +1,25 @@
1
+ export type CxInput = string | string[] | Record<string, boolean> | undefined | null | false
2
+
3
+ export function cx(...args: CxInput[]): string {
4
+ const classes = new Set<string>()
5
+
6
+ for (const arg of args) {
7
+ if (!arg) {
8
+ continue
9
+ }
10
+
11
+ if (typeof arg === 'string') {
12
+ classes.add(arg)
13
+ } else if (Array.isArray(arg)) {
14
+ arg.forEach((item) => classes.add(item))
15
+ } else if (typeof arg === 'object') {
16
+ for (const [key, value] of Object.entries(arg)) {
17
+ if (value) {
18
+ classes.add(key)
19
+ }
20
+ }
21
+ }
22
+ }
23
+
24
+ return Array.from(classes).join(' ')
25
+ }
@@ -0,0 +1,22 @@
1
+ import type React from 'react'
2
+ /**
3
+ * @description 性能优化,替代 React.Children.forEach, 回调可以返回 false 来中断循环
4
+ * @description_en Replace React.Children.forEach, the callback can return false to interrupt the loop
5
+ */
6
+ export function childrenLoop(
7
+ children: React.ReactNode | undefined,
8
+ callback: (child: React.ReactNode, index: number) => boolean | void,
9
+ ): void {
10
+ if (children === undefined) return
11
+ let index = 0
12
+ if (Array.isArray(children)) {
13
+ for (const child of children) {
14
+ const shouldContinue = callback(child, index++)
15
+ if (shouldContinue === false) {
16
+ break
17
+ }
18
+ }
19
+ } else {
20
+ callback(children, index)
21
+ }
22
+ }
@@ -0,0 +1,130 @@
1
+ import {describe, expect, it} from 'vitest'
2
+ import {Counter, formatDate} from './sundry'
3
+
4
+ describe('formatDate', () => {
5
+ // 使用一个固定的日期来测试,以避免时间差异导致的测试失败
6
+ // 2023-04-15 14:30:45.678 星期六
7
+ const testDate = new Date(2023, 3, 15, 14, 30, 45, 678)
8
+
9
+ it('应该正确格式化年份', () => {
10
+ expect(formatDate('YY', testDate)).toBe('23')
11
+ expect(formatDate('YYYY', testDate)).toBe('2023')
12
+ })
13
+
14
+ it('应该正确格式化月份', () => {
15
+ expect(formatDate('M', testDate)).toBe('4')
16
+ expect(formatDate('MM', testDate)).toBe('04')
17
+ expect(formatDate('MMM', testDate)).toBe('Apr')
18
+ expect(formatDate('MMMM', testDate)).toBe('April')
19
+ })
20
+
21
+ it('应该正确格式化日期', () => {
22
+ expect(formatDate('D', testDate)).toBe('15')
23
+ expect(formatDate('DD', testDate)).toBe('15')
24
+ })
25
+
26
+ it('应该正确格式化星期', () => {
27
+ expect(formatDate('d', testDate)).toBe('6')
28
+ expect(formatDate('dd', testDate)).toBe('Sat')
29
+ expect(formatDate('ddd', testDate)).toBe('Sat')
30
+ expect(formatDate('dddd', testDate)).toBe('Saturday')
31
+ })
32
+
33
+ it('应该正确格式化小时', () => {
34
+ expect(formatDate('H', testDate)).toBe('14')
35
+ expect(formatDate('HH', testDate)).toBe('14')
36
+ expect(formatDate('h', testDate)).toBe('2')
37
+ expect(formatDate('hh', testDate)).toBe('02')
38
+ })
39
+
40
+ it('应该正确格式化分钟和秒', () => {
41
+ expect(formatDate('m', testDate)).toBe('30')
42
+ expect(formatDate('mm', testDate)).toBe('30')
43
+ expect(formatDate('s', testDate)).toBe('45')
44
+ expect(formatDate('ss', testDate)).toBe('45')
45
+ })
46
+
47
+ it('应该正确格式化毫秒', () => {
48
+ expect(formatDate('SSS', testDate)).toBe('678')
49
+ })
50
+
51
+ it('应该正确格式化上午/下午', () => {
52
+ expect(formatDate('A', testDate)).toBe('PM')
53
+ expect(formatDate('a', testDate)).toBe('pm')
54
+
55
+ const morningDate = new Date(2023, 3, 15, 9, 30, 45, 678)
56
+ expect(formatDate('A', morningDate)).toBe('AM')
57
+ expect(formatDate('a', morningDate)).toBe('am')
58
+ })
59
+
60
+ it('应该正确组合多种格式', () => {
61
+ expect(formatDate('YYYY-MM-DD', testDate)).toBe('2023-04-15')
62
+ expect(formatDate('YYYY/MM/DD HH:mm:ss', testDate)).toBe('2023/04/15 14:30:45')
63
+ expect(formatDate('YYYY年MM月DD日 HH时mm分ss秒', testDate)).toBe('2023年04月15日 14时30分45秒')
64
+ expect(formatDate('YY-MM-DD hh:mm:ss A', testDate)).toBe('23-04-15 02:30:45 PM')
65
+ })
66
+
67
+ it('当不传入日期时应该使用当前日期', () => {
68
+ // 由于测试时间不确定,这里只测试格式是否正确,不测试具体的值
69
+ const result = formatDate('YYYY-MM-DD')
70
+ expect(result).toMatch(/^\d{4}-\d{2}-\d{2}$/)
71
+ })
72
+ })
73
+
74
+ describe('Counter', () => {
75
+ it('应该从0开始计数', () => {
76
+ const counter = new Counter()
77
+ expect(counter.count).toBe(0)
78
+ })
79
+
80
+ it('next() 方法应该返回当前计数并递增', () => {
81
+ const counter = new Counter()
82
+ expect(counter.next()).toBe(0)
83
+ expect(counter.count).toBe(1)
84
+ expect(counter.next()).toBe(1)
85
+ expect(counter.count).toBe(2)
86
+ })
87
+
88
+ it('连续调用 next() 应该正确递增', () => {
89
+ const counter = new Counter()
90
+ expect(counter.next()).toBe(0)
91
+ expect(counter.next()).toBe(1)
92
+ expect(counter.next()).toBe(2)
93
+ expect(counter.next()).toBe(3)
94
+ expect(counter.count).toBe(4)
95
+ })
96
+ })
97
+
98
+ /* describe("cx", () => {
99
+ it("应该正确合并字符串类名", () => {
100
+ expect(cx("foo", "bar")).toBe("foo bar");
101
+ expect(cx("foo", "bar", "baz")).toBe("foo bar baz");
102
+ });
103
+
104
+ it("应该过滤掉 falsy 值", () => {
105
+ expect(cx("foo", null, "bar", undefined, false)).toBe("foo bar");
106
+ });
107
+
108
+ it("应该正确处理数组", () => {
109
+ expect(cx(["foo", "bar"])).toBe("foo bar");
110
+ expect(cx("baz", ["foo", "bar"])).toBe("baz foo bar");
111
+ });
112
+
113
+ it("应该正确处理对象", () => {
114
+ expect(cx({ foo: true, bar: false })).toBe("foo");
115
+ expect(cx({ foo: true, bar: true })).toBe("foo bar");
116
+ });
117
+
118
+ it("应该正确处理混合输入", () => {
119
+ expect(cx("foo", ["bar", "baz"], { qux: true, quux: false })).toBe(
120
+ "foo bar baz qux"
121
+ );
122
+ });
123
+
124
+ it("应该去除重复的类名", () => {
125
+ expect(cx("foo", "foo", "bar")).toBe("foo bar");
126
+ expect(cx("foo", ["foo", "bar"])).toBe("foo bar");
127
+ expect(cx("foo", { foo: true })).toBe("foo");
128
+ });
129
+ });
130
+ */
@@ -0,0 +1,130 @@
1
+ /**
2
+ * @param schema
3
+ * @example
4
+ * YY | 18 | Two-digit year
5
+ * YYYY | 2018 | Four-digit year
6
+ * M | 1-12 | The month, beginning at 1
7
+ * MM | 01-12 | The month, 2-digits
8
+ * MMM | Jan-Dec | The abbreviated month name
9
+ * MMMM | January-December | The full month name
10
+ * D | 1-31 | The day of the month
11
+ * DD | 01-31 | The day of the month, 2-digits
12
+ * d | 0-6 | The day of the week, with Sunday as 0
13
+ * dd | Su-Sa | The min name of the day of the week
14
+ * ddd | Sun-Sat | The short name of the day of the week
15
+ * dddd | Sunday-Saturday | The name of the day of the week
16
+ * H | 0-23 | The hour
17
+ * HH | 00-23 | The hour, 2-digits
18
+ * h | 1-12 | The hour, 12-hour clock
19
+ * hh | 01-12 | The hour, 12-hour clock, 2-digits
20
+ * m | 0-59 | The minute
21
+ * mm | 00-59 | The minute, 2-digits
22
+ * s | 0-59 | The second
23
+ * ss | 00-59 | The second, 2-digits
24
+ * SSS | 000-999 | The millisecond, 3-digits
25
+ * Z | +05:00 | The offset from UTC, ±HH:mm
26
+ * ZZ | +0500 | The offset from UTC, ±HHmm
27
+ * A | AM | PM
28
+ * a | am | pm
29
+ */
30
+ export function formatDate(schema: string, date?: Date): string {
31
+ const d = date || new Date()
32
+ const year = d.getFullYear()
33
+ const month = d.getMonth() + 1
34
+ const day = d.getDate()
35
+ const hour = d.getHours()
36
+ const minute = d.getMinutes()
37
+ const second = d.getSeconds()
38
+ const millisecond = d.getMilliseconds()
39
+ const week = d.getDay()
40
+ const weekName = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat']
41
+ const weekFullName = [
42
+ 'Sunday',
43
+ 'Monday',
44
+ 'Tuesday',
45
+ 'Wednesday',
46
+ 'Thursday',
47
+ 'Friday',
48
+ 'Saturday',
49
+ ]
50
+ const monthName = [
51
+ 'Jan',
52
+ 'Feb',
53
+ 'Mar',
54
+ 'Apr',
55
+ 'May',
56
+ 'Jun',
57
+ 'Jul',
58
+ 'Aug',
59
+ 'Sep',
60
+ 'Oct',
61
+ 'Nov',
62
+ 'Dec',
63
+ ]
64
+ const monthFullName = [
65
+ 'January',
66
+ 'February',
67
+ 'March',
68
+ 'April',
69
+ 'May',
70
+ 'June',
71
+ 'July',
72
+ 'August',
73
+ 'September',
74
+ 'October',
75
+ 'November',
76
+ 'December',
77
+ ]
78
+ // 直接使用 week 索引,不进行转换,因为 getDay() 已经返回了正确的星期索引 (0-6)
79
+ const weekFull = weekFullName[week]!
80
+ const weekShort = weekName[week]!
81
+ const monthIndex = month - 1
82
+ const monthFull = monthFullName[monthIndex]!
83
+ const monthShort = monthName[monthIndex]!
84
+ const map: Record<string, string> = {
85
+ YY: year.toString().slice(2),
86
+ YYYY: year.toString(),
87
+ M: month.toString(),
88
+ MM: month.toString().padStart(2, '0'),
89
+ MMM: monthShort,
90
+ MMMM: monthFull,
91
+ D: day.toString(),
92
+ DD: day.toString().padStart(2, '0'),
93
+ d: week.toString(),
94
+ dd: weekShort,
95
+ ddd: weekShort,
96
+ dddd: weekFull,
97
+ H: hour.toString(),
98
+ HH: hour.toString().padStart(2, '0'),
99
+ h: (hour % 12).toString(),
100
+ hh: (hour % 12).toString().padStart(2, '0'),
101
+ m: minute.toString(),
102
+ mm: minute.toString().padStart(2, '0'),
103
+ s: second.toString(),
104
+ ss: second.toString().padStart(2, '0'),
105
+ SSS: millisecond.toString().padStart(3, '0'),
106
+ Z: '+08:00',
107
+ ZZ: '+0800',
108
+ A: hour < 12 ? 'AM' : 'PM',
109
+ a: hour < 12 ? 'am' : 'pm',
110
+ }
111
+
112
+ return schema.replace(
113
+ /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,
114
+ (match) => {
115
+ return map[match]!
116
+ },
117
+ )
118
+ }
119
+
120
+ export class Counter {
121
+ count = 0
122
+
123
+ /**
124
+ * @description 获取下一个计数值,不考虑越界。
125
+ * @description_en Get the next count value, without considering overflow.
126
+ */
127
+ next() {
128
+ return this.count++
129
+ }
130
+ }
@@ -1,185 +0,0 @@
1
- import React from "react";
2
- /**
3
- * @description 性能优化,替代 React.Children.forEach, 回调可以返回 false 来中断循环
4
- * @description_en Replace React.Children.forEach, the callback can return false to interrupt the loop
5
- */
6
- export function childrenLoop(
7
- children: React.ReactNode | undefined,
8
- callback: (child: React.ReactNode, index: number) => boolean | void
9
- ): void {
10
- if (children === undefined) return;
11
- let index = 0;
12
- if (Array.isArray(children)) {
13
- for (const child of children) {
14
- const shouldContinue = callback(child, index++);
15
- if (shouldContinue === false) {
16
- break;
17
- }
18
- }
19
- } else {
20
- callback(children, index);
21
- }
22
- }
23
-
24
- /**
25
- * @param schema
26
- * @example
27
- * YY | 18 | Two-digit year
28
- * YYYY | 2018 | Four-digit year
29
- * M | 1-12 | The month, beginning at 1
30
- * MM | 01-12 | The month, 2-digits
31
- * MMM | Jan-Dec | The abbreviated month name
32
- * MMMM | January-December | The full month name
33
- * D | 1-31 | The day of the month
34
- * DD | 01-31 | The day of the month, 2-digits
35
- * d | 0-6 | The day of the week, with Sunday as 0
36
- * dd | Su-Sa | The min name of the day of the week
37
- * ddd | Sun-Sat | The short name of the day of the week
38
- * dddd | Sunday-Saturday | The name of the day of the week
39
- * H | 0-23 | The hour
40
- * HH | 00-23 | The hour, 2-digits
41
- * h | 1-12 | The hour, 12-hour clock
42
- * hh | 01-12 | The hour, 12-hour clock, 2-digits
43
- * m | 0-59 | The minute
44
- * mm | 00-59 | The minute, 2-digits
45
- * s | 0-59 | The second
46
- * ss | 00-59 | The second, 2-digits
47
- * SSS | 000-999 | The millisecond, 3-digits
48
- * Z | +05:00 | The offset from UTC, ±HH:mm
49
- * ZZ | +0500 | The offset from UTC, ±HHmm
50
- * A | AM | PM
51
- * a | am | pm
52
- */
53
- export function formatDate(schema: string, date?: Date): string {
54
- const d = date || new Date();
55
- const year = d.getFullYear();
56
- const month = d.getMonth() + 1;
57
- const day = d.getDate();
58
- const hour = d.getHours();
59
- const minute = d.getMinutes();
60
- const second = d.getSeconds();
61
- const millisecond = d.getMilliseconds();
62
- const week = d.getDay();
63
- const weekName = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"];
64
- const weekFullName = [
65
- "Sunday",
66
- "Monday",
67
- "Tuesday",
68
- "Wednesday",
69
- "Thursday",
70
- "Friday",
71
- "Saturday",
72
- ];
73
- const monthName = [
74
- "Jan",
75
- "Feb",
76
- "Mar",
77
- "Apr",
78
- "May",
79
- "Jun",
80
- "Jul",
81
- "Aug",
82
- "Sep",
83
- "Oct",
84
- "Nov",
85
- "Dec",
86
- ];
87
- const monthFullName = [
88
- "January",
89
- "February",
90
- "March",
91
- "April",
92
- "May",
93
- "June",
94
- "July",
95
- "August",
96
- "September",
97
- "October",
98
- "November",
99
- "December",
100
- ];
101
- const weekIndex = week === 0 ? 6 : week - 1;
102
- const weekFull = weekFullName[weekIndex]!;
103
- const weekShort = weekName[weekIndex]!;
104
- const monthIndex = month - 1;
105
- const monthFull = monthFullName[monthIndex]!;
106
- const monthShort = monthName[monthIndex]!;
107
- const map: Record<string, string> = {
108
- YY: year.toString().slice(2),
109
- YYYY: year.toString(),
110
- M: month.toString(),
111
- MM: month.toString().padStart(2, "0"),
112
- MMM: monthShort,
113
- MMMM: monthFull,
114
- D: day.toString(),
115
- DD: day.toString().padStart(2, "0"),
116
- d: week.toString(),
117
- dd: weekShort,
118
- ddd: weekShort,
119
- dddd: weekFull,
120
- H: hour.toString(),
121
- HH: hour.toString().padStart(2, "0"),
122
- h: (hour % 12).toString(),
123
- hh: (hour % 12).toString().padStart(2, "0"),
124
- m: minute.toString(),
125
- mm: minute.toString().padStart(2, "0"),
126
- s: second.toString(),
127
- ss: second.toString().padStart(2, "0"),
128
- SSS: millisecond.toString().padStart(3, "0"),
129
- Z: "+08:00",
130
- ZZ: "+0800",
131
- A: hour < 12 ? "AM" : "PM",
132
- a: hour < 12 ? "am" : "pm",
133
- };
134
-
135
- return schema.replace(
136
- /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,
137
- (match) => {
138
- return map[match]!;
139
- }
140
- );
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
- }