@wwog/react 1.3.12 → 1.3.14
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 +91 -3
- package/dist/index.d.mts +116 -19
- package/dist/index.js +1 -1
- package/package.json +1 -1
- package/src/algorithm/date/zellersKongruenz.ts +9 -14
- package/src/algorithm/date.ts +2 -2
- package/src/components/Layout/index.ts +2 -2
- package/src/components/ProcessControl/If.tsx +2 -2
- package/src/components/Sundry/Boundary.tsx +67 -0
- package/src/components/Sundry/Portal.tsx +59 -0
- package/src/components/Sundry/Repeat.tsx +34 -0
- package/src/components/Sundry/index.ts +8 -5
- package/src/hooks/index.ts +1 -1
- package/src/hooks/useScreen.ts +54 -65
- package/src/index.ts +5 -5
- package/src/utils/constants.ts +7 -16
- package/src/utils/createExternalState.test.tsx +36 -17
- package/src/utils/createExternalState.ts +93 -95
- package/src/utils/index.ts +7 -7
- package/src/utils/promise.ts +18 -18
- package/src/utils/ruleChecker.test.ts +336 -349
- package/src/utils/ruleChecker.ts +98 -181
- package/src/utils/sundry.ts +66 -67
package/README.md
CHANGED
|
@@ -387,6 +387,84 @@ function InfiniteList({ items, onLoadMore }) {
|
|
|
387
387
|
- `className`: CSS class name for the wrapper element.
|
|
388
388
|
- `style`: Inline styles for the wrapper element.
|
|
389
389
|
|
|
390
|
+
#### `<Repeat>` (v1.3.13+)
|
|
391
|
+
|
|
392
|
+
A declarative repeat-render component, commonly used for skeleton screens and placeholders.
|
|
393
|
+
|
|
394
|
+
```tsx
|
|
395
|
+
import { Repeat } from "@wwog/react";
|
|
396
|
+
|
|
397
|
+
function SkeletonList() {
|
|
398
|
+
return (
|
|
399
|
+
<Repeat times={5}>
|
|
400
|
+
{(i) => <SkeletonItem key={i} />}
|
|
401
|
+
</Repeat>
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
- `times`: Number of times to repeat. Renders nothing when `<= 0`.
|
|
407
|
+
- `children`: Render function receiving the current 0-based index.
|
|
408
|
+
|
|
409
|
+
#### `<Portal>` (v1.3.13+)
|
|
410
|
+
|
|
411
|
+
A declarative `createPortal` wrapper that renders children into a specified DOM node. Handles SSR safely by deferring mount until the client is ready.
|
|
412
|
+
|
|
413
|
+
```tsx
|
|
414
|
+
import { Portal } from "@wwog/react";
|
|
415
|
+
|
|
416
|
+
// Render into document.body (default)
|
|
417
|
+
function Modal({ children }) {
|
|
418
|
+
return <Portal>{children}</Portal>;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Render into a specific element
|
|
422
|
+
function Tooltip({ children }) {
|
|
423
|
+
return (
|
|
424
|
+
<Portal to={document.getElementById("overlay-root")}>
|
|
425
|
+
{children}
|
|
426
|
+
</Portal>
|
|
427
|
+
);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Disable portal and render inline
|
|
431
|
+
function ConditionalPortal({ usePortal, children }) {
|
|
432
|
+
return <Portal disabled={!usePortal}>{children}</Portal>;
|
|
433
|
+
}
|
|
434
|
+
```
|
|
435
|
+
|
|
436
|
+
- `to`: Target DOM element to mount into. Defaults to `document.body`.
|
|
437
|
+
- `disabled`: When `true`, renders children inline without a portal. Defaults to `false`.
|
|
438
|
+
- `children`: Child elements to render into the portal.
|
|
439
|
+
|
|
440
|
+
#### `<Boundary>` (v1.3.13+)
|
|
441
|
+
|
|
442
|
+
A declarative Error Boundary wrapper with render-prop fallback and reset capability.
|
|
443
|
+
|
|
444
|
+
```tsx
|
|
445
|
+
import { Boundary } from "@wwog/react";
|
|
446
|
+
|
|
447
|
+
function App() {
|
|
448
|
+
return (
|
|
449
|
+
<Boundary
|
|
450
|
+
fallback={(error, reset) => (
|
|
451
|
+
<div>
|
|
452
|
+
<p>Something went wrong: {error.message}</p>
|
|
453
|
+
<button onClick={reset}>Retry</button>
|
|
454
|
+
</div>
|
|
455
|
+
)}
|
|
456
|
+
onError={(error, info) => reportError(error, info)}
|
|
457
|
+
>
|
|
458
|
+
<RiskyComponent />
|
|
459
|
+
</Boundary>
|
|
460
|
+
);
|
|
461
|
+
}
|
|
462
|
+
```
|
|
463
|
+
|
|
464
|
+
- `fallback`: Render function called when an error is caught. Receives `(error: Error, reset: () => void)`.
|
|
465
|
+
- `onError`: Optional callback for error reporting (e.g. logging to Sentry).
|
|
466
|
+
- `children`: Child elements to protect.
|
|
467
|
+
|
|
390
468
|
#### `<SizeBox>`
|
|
391
469
|
|
|
392
470
|
Create a fixed-size container for layout adjustment and spacing control.
|
|
@@ -571,6 +649,7 @@ const result = ruleChecker(registrationData, rules);
|
|
|
571
649
|
|
|
572
650
|
> v1.2.21: Refactor the API to move sideeffects into options and enhance support for the transform interface
|
|
573
651
|
> v1.2.13: add useGetter
|
|
652
|
+
> Breaking: `sideEffect` replaced by `onSet` and `onChange` for clearer callback semantics
|
|
574
653
|
|
|
575
654
|
> A lightweight external state management utility that allows you to create and manage state outside the React component tree while maintaining perfect integration with components.
|
|
576
655
|
|
|
@@ -578,12 +657,18 @@ const result = ruleChecker(registrationData, rules);
|
|
|
578
657
|
|
|
579
658
|
> Extends from createExternalState and uses storage to persist state, supports `localStorage` and `sessionStorage`
|
|
580
659
|
|
|
660
|
+
- `createStorageState<T>(key, initialState, options?)`: Creates persisted state
|
|
661
|
+
- `options.onSet`: Invoked on every `set()` (storage write happens first, then the user callback)
|
|
662
|
+
- `options.onChange`: Invoked only when the value actually changes
|
|
663
|
+
- `options.storageType`: `'local'` | `'session'`, defaults to `'local'`
|
|
664
|
+
- `options.transform`: Same as `createExternalState`
|
|
665
|
+
|
|
581
666
|
```tsx
|
|
582
667
|
import { createExternalState } from "@wwog/react";
|
|
583
668
|
|
|
584
669
|
// Create a global theme state
|
|
585
670
|
const themeState = createExternalState("light", {
|
|
586
|
-
|
|
671
|
+
onChange: (newTheme, oldTheme) => {
|
|
587
672
|
console.log(`Theme changed from ${oldTheme} to ${newTheme}`);
|
|
588
673
|
},
|
|
589
674
|
});
|
|
@@ -617,14 +702,17 @@ function ReadOnlyThemeConsumer() {
|
|
|
617
702
|
- `createExternalState<T>(initialState, options?)`: Creates a state accessible outside components
|
|
618
703
|
|
|
619
704
|
- `initialState`: Initial state value
|
|
620
|
-
- `options.
|
|
705
|
+
- `options.onSet`: Optional callback invoked on every `set()` call, even when the value is unchanged
|
|
706
|
+
- `options.onChange`: Optional callback invoked only when the stored value actually changes (compared via `Object.is`)
|
|
707
|
+
- Both callbacks receive `(newState, prevState)` as the raw internal values (type `T`, before `transform.get`)
|
|
621
708
|
- Returns an object with methods:
|
|
622
709
|
- `get()`: Get the current state value
|
|
623
710
|
- `set(newState)`: Update the state value
|
|
624
711
|
- `use()`: React Hook, returns `[state, setState]` for using this state in components
|
|
625
712
|
- `useGetter()`: React Hook that only returns the state value, useful when you only need to read the state
|
|
626
713
|
- `options.transform`: - `get` - `set`
|
|
627
|
-
|
|
714
|
+
|
|
715
|
+
Use cases:
|
|
628
716
|
|
|
629
717
|
- Global state management (themes, user settings, etc.)
|
|
630
718
|
- Cross-component communication
|
package/dist/index.d.mts
CHANGED
|
@@ -461,6 +461,103 @@ interface ObserverProps {
|
|
|
461
461
|
*/
|
|
462
462
|
declare const Observer: React$1.FC<ObserverProps>;
|
|
463
463
|
|
|
464
|
+
interface RepeatProps {
|
|
465
|
+
/**
|
|
466
|
+
* @description_en Number of times to repeat.
|
|
467
|
+
* @description_zh 重复次数。
|
|
468
|
+
*/
|
|
469
|
+
times: number;
|
|
470
|
+
/**
|
|
471
|
+
* @description_en Render function receiving the current index (0-based).
|
|
472
|
+
* @description_zh 渲染函数,接收当前索引(从 0 开始)。
|
|
473
|
+
*/
|
|
474
|
+
children: (index: number) => ReactNode;
|
|
475
|
+
}
|
|
476
|
+
/**
|
|
477
|
+
* @description_zh 声明式重复渲染组件,常用于骨架屏、占位符等场景。
|
|
478
|
+
* @description_en Declarative repeat-render component, commonly used for skeleton screens and placeholders.
|
|
479
|
+
* @component
|
|
480
|
+
* @example
|
|
481
|
+
* ```tsx
|
|
482
|
+
* <Repeat times={3}>
|
|
483
|
+
* {(i) => <Skeleton key={i} />}
|
|
484
|
+
* </Repeat>
|
|
485
|
+
* ```
|
|
486
|
+
*/
|
|
487
|
+
declare function Repeat({ times, children }: RepeatProps): ReactNode;
|
|
488
|
+
|
|
489
|
+
interface PortalProps {
|
|
490
|
+
/**
|
|
491
|
+
* @description_en Target DOM element to mount into. Defaults to document.body.
|
|
492
|
+
* @description_zh 挂载目标 DOM 元素,默认为 document.body。
|
|
493
|
+
* @default document.body
|
|
494
|
+
*/
|
|
495
|
+
to?: Element | null;
|
|
496
|
+
/**
|
|
497
|
+
* @description_en Child elements to render into the portal.
|
|
498
|
+
* @description_zh 要渲染到 portal 中的子元素。
|
|
499
|
+
*/
|
|
500
|
+
children?: ReactNode;
|
|
501
|
+
/**
|
|
502
|
+
* @description_en Whether to disable the portal and render children inline. Default is false.
|
|
503
|
+
* @description_zh 是否禁用 portal,直接内联渲染子元素。默认为 false。
|
|
504
|
+
* @default false
|
|
505
|
+
*/
|
|
506
|
+
disabled?: boolean;
|
|
507
|
+
}
|
|
508
|
+
/**
|
|
509
|
+
* @description_zh 声明式 Portal 组件,将子元素渲染到指定 DOM 节点,常用于模态框、浮层等场景。
|
|
510
|
+
* @description_en Declarative Portal component that renders children into a specified DOM node, commonly used for modals and overlays.
|
|
511
|
+
* @component
|
|
512
|
+
* @example
|
|
513
|
+
* ```tsx
|
|
514
|
+
* <Portal>
|
|
515
|
+
* <Modal />
|
|
516
|
+
* </Portal>
|
|
517
|
+
*
|
|
518
|
+
* <Portal to={document.getElementById('overlay-root')}>
|
|
519
|
+
* <Tooltip />
|
|
520
|
+
* </Portal>
|
|
521
|
+
* ```
|
|
522
|
+
*/
|
|
523
|
+
declare function Portal({ to, children, disabled }: PortalProps): ReactNode;
|
|
524
|
+
|
|
525
|
+
interface BoundaryProps {
|
|
526
|
+
/**
|
|
527
|
+
* @description_en Fallback UI to render when an error is caught. Receives the error and a reset function.
|
|
528
|
+
* @description_zh 捕获到错误时渲染的降级 UI,接收错误对象和重置函数。
|
|
529
|
+
*/
|
|
530
|
+
fallback: (error: Error, reset: () => void) => ReactNode;
|
|
531
|
+
/**
|
|
532
|
+
* @description_en Called when an error is caught, useful for logging.
|
|
533
|
+
* @description_zh 捕获到错误时的回调,可用于上报日志。
|
|
534
|
+
* @optional
|
|
535
|
+
*/
|
|
536
|
+
onError?: (error: Error, info: React$1.ErrorInfo) => void;
|
|
537
|
+
/**
|
|
538
|
+
* @description_en Child elements to protect.
|
|
539
|
+
* @description_zh 需要保护的子元素。
|
|
540
|
+
*/
|
|
541
|
+
children?: ReactNode;
|
|
542
|
+
}
|
|
543
|
+
/**
|
|
544
|
+
* @description_zh Error Boundary 的声明式封装,通过 render prop 提供降级 UI 和重置能力。
|
|
545
|
+
* @description_en Declarative Error Boundary wrapper with render-prop fallback and reset capability.
|
|
546
|
+
* @component
|
|
547
|
+
* @example
|
|
548
|
+
* ```tsx
|
|
549
|
+
* <Boundary fallback={(error, reset) => (
|
|
550
|
+
* <div>
|
|
551
|
+
* <p>出错了: {error.message}</p>
|
|
552
|
+
* <button onClick={reset}>重试</button>
|
|
553
|
+
* </div>
|
|
554
|
+
* )}>
|
|
555
|
+
* <RiskyComponent />
|
|
556
|
+
* </Boundary>
|
|
557
|
+
* ```
|
|
558
|
+
*/
|
|
559
|
+
declare function Boundary(props: BoundaryProps): ReactNode;
|
|
560
|
+
|
|
464
561
|
interface ArrayRenderProps<T> {
|
|
465
562
|
items: T[];
|
|
466
563
|
renderItem: (item: T, index: number) => React$1.ReactNode;
|
|
@@ -541,19 +638,13 @@ interface UseControlledOptions<T> {
|
|
|
541
638
|
declare function useControlled<T>(options: UseControlledOptions<T>): [T, Dispatch<React.SetStateAction<T>>];
|
|
542
639
|
|
|
543
640
|
/**
|
|
544
|
-
* @
|
|
545
|
-
* @
|
|
546
|
-
* @template T The type of the state / 状态的类型
|
|
547
|
-
*/
|
|
548
|
-
type CreateStateListener<T> = (state: T) => void;
|
|
549
|
-
/**
|
|
550
|
-
* @zh 如果需要在变更状态时执行副作用,可以传入函数,对于异步函数,会在更改状态后执行,不会阻塞状态更新, 尽可能在外部使用useEffect处理异步副作用
|
|
551
|
-
* @en If you need to perform side effects when changing the state, you can pass a function. For asynchronous functions, it will be executed after the state changes without blocking the state update, so it's best to use useEffect for handling asynchronous side effects.
|
|
641
|
+
* @zh 状态回调函数。对于异步函数,会在状态更新后执行,不会阻塞状态更新,尽可能在外部使用 useEffect 处理异步副作用。
|
|
642
|
+
* @en State callback function. Async callbacks run after the state update without blocking it; prefer useEffect for async side effects.
|
|
552
643
|
* @template T The type of the state / 状态的类型
|
|
553
644
|
* @param newState The new state value / 新的状态值
|
|
554
645
|
* @param prevState The previous state value / 之前的状态值
|
|
555
646
|
*/
|
|
556
|
-
type
|
|
647
|
+
type ExternalStateCallback<T> = (newState: T, prevState: T) => any | Promise<any>;
|
|
557
648
|
/**
|
|
558
649
|
* @en Transform functions for getting and setting state
|
|
559
650
|
* @zh 用于获取和设置状态的转换函数
|
|
@@ -580,10 +671,15 @@ interface Transform<T, U = T> {
|
|
|
580
671
|
*/
|
|
581
672
|
interface ExternalStateOptions<T, U = T> {
|
|
582
673
|
/**
|
|
583
|
-
* @en
|
|
584
|
-
* @zh
|
|
674
|
+
* @en Callback invoked on every `set` call, even when the value is unchanged
|
|
675
|
+
* @zh 每次调用 `set` 后触发,即使值未发生变化
|
|
676
|
+
*/
|
|
677
|
+
onSet?: ExternalStateCallback<T>;
|
|
678
|
+
/**
|
|
679
|
+
* @en Callback invoked only when the stored value actually changes
|
|
680
|
+
* @zh 仅在内部存储值发生变化时触发
|
|
585
681
|
*/
|
|
586
|
-
|
|
682
|
+
onChange?: ExternalStateCallback<T>;
|
|
587
683
|
/**
|
|
588
684
|
* @en Transform functions for getting and setting state
|
|
589
685
|
* @zh 用于获取和设置状态的转换函数
|
|
@@ -612,7 +708,7 @@ interface ExternalState<T, U = T> {
|
|
|
612
708
|
/**
|
|
613
709
|
* @en React Hook for using external state in components
|
|
614
710
|
* @zh 在组件中使用外部状态的 React Hook
|
|
615
|
-
* @returns Array containing current
|
|
711
|
+
* @returns Array containing current state and update function, similar to useState / 包含当前状态和更新函数的数组,类似于 useState
|
|
616
712
|
*/
|
|
617
713
|
use: () => [U, (newState: U | ((prevState: U) => U)) => void];
|
|
618
714
|
/**
|
|
@@ -622,7 +718,7 @@ interface ExternalState<T, U = T> {
|
|
|
622
718
|
useGetter: () => U;
|
|
623
719
|
}
|
|
624
720
|
interface ExternalWithKernel<T, U = T> extends ExternalState<T, U> {
|
|
625
|
-
__listeners:
|
|
721
|
+
__listeners: (() => void)[];
|
|
626
722
|
}
|
|
627
723
|
/**
|
|
628
724
|
*
|
|
@@ -630,7 +726,7 @@ interface ExternalWithKernel<T, U = T> extends ExternalState<T, U> {
|
|
|
630
726
|
* ```tsx
|
|
631
727
|
* // Create an app-level theme state with options
|
|
632
728
|
* const themeState = createExternalState('light', {
|
|
633
|
-
*
|
|
729
|
+
* onChange: (newState, prevState) => console.log(`Theme changed from ${prevState} to ${newState}`),
|
|
634
730
|
* transform: {
|
|
635
731
|
* get: (state) => state.toUpperCase(),
|
|
636
732
|
* set: (value) => value.toLowerCase()
|
|
@@ -657,9 +753,10 @@ interface ExternalWithKernel<T, U = T> extends ExternalState<T, U> {
|
|
|
657
753
|
*/
|
|
658
754
|
declare function createExternalState<T, U = T>(initialState: T | (() => T), options?: ExternalStateOptions<T, U>): ExternalState<T, U>;
|
|
659
755
|
interface StorageStateOptions<T, U> {
|
|
660
|
-
|
|
756
|
+
onSet?: ExternalStateCallback<T>;
|
|
757
|
+
onChange?: ExternalStateCallback<T>;
|
|
661
758
|
transform?: Transform<T, U>;
|
|
662
|
-
storageType:
|
|
759
|
+
storageType: 'local' | 'session';
|
|
663
760
|
}
|
|
664
761
|
declare function createStorageState<T, U = T>(key: string, initialState: T, options?: StorageStateOptions<T, U>): ExternalState<T, U>;
|
|
665
762
|
|
|
@@ -774,5 +871,5 @@ declare function ruleChecker<T extends Record<string, unknown>, R extends RuleDe
|
|
|
774
871
|
declare function getCurrentBreakpoint(breakpointDesc: BreakpointDesc, width: number): BreakpointName;
|
|
775
872
|
declare function useScreen(breakpointDesc?: BreakpointDesc): "base" | "xs" | "sm" | "md" | "lg" | "xl" | "2xl" | "3xl";
|
|
776
873
|
|
|
777
|
-
export { ArrayRender, Counter, DateRender, DefBreakpointDesc, False, If, Observer, Pipe, Scope, SizeBox, Styles, Switch, Toggle, True, When, breakpoints, childrenLoop, createExternalState, createStorageState, cx, formatDate, getCurrentBreakpoint, ruleChecker, safePromiseTry, safePromiseWithResolvers, useControlled, useScreen };
|
|
778
|
-
export type { ApplyRules, ArrayRenderProps, ArrayRule, ArraySpecificProps, BaseRule, BooleanRule, BreakpointDesc, BreakpointName,
|
|
874
|
+
export { ArrayRender, Boundary, Counter, DateRender, DefBreakpointDesc, False, If, Observer, Pipe, Portal, Repeat, Scope, SizeBox, Styles, Switch, Toggle, True, When, breakpoints, childrenLoop, createExternalState, createStorageState, cx, formatDate, getCurrentBreakpoint, ruleChecker, safePromiseTry, safePromiseWithResolvers, useControlled, useScreen };
|
|
875
|
+
export type { ApplyRules, ArrayRenderProps, ArrayRule, ArraySpecificProps, BaseRule, BooleanRule, BoundaryProps, BreakpointDesc, BreakpointName, CxInput, DateRenderProps, ElseIfProps, ElseProps, ExternalState, ExternalStateCallback, ExternalStateOptions, ExternalWithKernel, FalseProps, FieldRule, IfProps, LengthRuleProps, NumberRangeProps, NumberRule, ObserverProps, PipeProps, PortalProps, RepeatProps, Responsive, RuleDescription, ScopeProps, StorageStateOptions, StringRule, StringSpecificProps, StylesDescriptor, StylesProps, StylesType, SwitchCaseProps, SwitchDefaultProps, SwitchProps, ThenProps, ToggleProps, Transform, TrueProps, UseControlledOptions, WhenProps };
|
package/dist/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import i,{useMemo as S,Fragment as v,Children as Z,isValidElement as q,cloneElement as G,useEffect as b,useState as I,useRef as O,useCallback as U}from"react";function j(e,n){if(e===void 0)return;let t=0;if(Array.isArray(e)){for(const o of e)if(n(o,t++)===!1)break}else n(e,t)}const K=(e,n)=>e===n,N=e=>i.createElement(i.Fragment,null,e.children);N.displayName="Switch_Case";const D=e=>i.createElement(i.Fragment,null,e.children);D.displayName="Switch_Default";const w=e=>{const{value:n,compare:t=K,children:o,strict:r=!1}=e,l=new Set;let a=null,c=null,u=!1;return j(o,(s,f)=>{if(!i.isValidElement(s))throw new Error(`Switch Children only accepts valid React elements at index ${f}`);const d=s.type;if(d.displayName===N.displayName){const m=s.props;if(l.has(m.value))throw new Error(`Switch found duplicate Case value at index ${f}: ${JSON.stringify(m.value)}${r?" (detected in strict mode)":""}`);if(l.add(m.value),!a&&t(n,m.value)&&(a=m.children,r===!1))return!1}else if(d.displayName===D.displayName){if(u)throw new Error(`Switch can only have one Default child at index ${f}`);if(u=!0,c=s.props.children,!r&&a)return!1}else throw new Error(`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(d.displayName||d.name||d)} at index ${f}`)}),i.createElement(i.Fragment,null,a??c)};w.displayName="Switch",w.Case=N,w.Default=D,w.createTyped=function(){return{Switch:w,Case:N,Default:D}};const A=e=>i.createElement(i.Fragment,null,e.children),x=({children:e})=>i.createElement(i.Fragment,null,e),M=e=>i.createElement(i.Fragment,null,e.children);A.displayName="If_Then",x.displayName="If_Else",M.displayName="If_ElseIf";const E=({condition:e,children:n})=>{let t=null,o=null;const r=[];if(i.Children.forEach(n,l=>{if(!i.isValidElement(l))throw new Error("If component only accepts valid React elements");const a=l.type;if(a.displayName===A.displayName){if(t)throw new Error("If component can only have one Then child");t=l}else if(a.displayName===M.displayName)r.push(l);else if(a.displayName===x.displayName){if(o)throw new Error("If component can only have one Else child");o=l}else throw new Error(`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(a.displayName||a.name||a)}`)}),e)return t?i.createElement(i.Fragment,null,t.props.children):null;for(const l of r)if(l.props.condition)return i.createElement(i.Fragment,null,l.props.children);return o?i.createElement(i.Fragment,null,o.props.children):null};E.displayName="If",E.Then=A,E.ElseIf=M,E.Else=x,E.createTyped=function(){return{If:E,Then:A,ElseIf:M,Else:x}};const Q=({condition:e,children:n})=>e?i.createElement(i.Fragment,null,n):null,X=({condition:e,children:n})=>e===!1?i.createElement(i.Fragment,null,n):null,ee=({all:e,any:n,none:t,children:o,fallback:r})=>S(()=>(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(l=>!l))),[e,n,t])?i.createElement(i.Fragment,null,o):i.createElement(i.Fragment,null,r||null),te=({data:e,transform:n,render:t,fallback:o})=>{const r=S(()=>n.reduce((l,a)=>a(l),e),[e,n]);return r==null?i.createElement(i.Fragment,null,o||null):i.createElement(i.Fragment,null,t(r))},ne=e=>{const{children:n,h:t,w:o,size:r,height:l,width:a,className:c}=e;return i.createElement("div",{style:{width:r||o||a,height:r||t||l,flexShrink:0},className:c},n)},re=({let:e,props:n,children:t,fallback:o})=>{const r=S(()=>typeof e=="function"?e(n):e,[e,n]);return!t||!Object.keys(r).length?i.createElement(i.Fragment,null,o||null):i.createElement(i.Fragment,null,t(r))};function C(...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(o=>n.add(o));else if(typeof t=="object")for(const[o,r]of Object.entries(t))r&&n.add(o)}return Array.from(n).join(" ")}const oe=e=>typeof e=="object"&&!!e,P=({className:e,children:n,asWrapper:t=!1})=>{if(!n)return null;if(!e)return i.createElement(v,null,n);const o=typeof e=="string"?e:C(...Object.values(e));if(t)return i.createElement(t===!0?"div":t,{className:o},n);if(Z.count(n)>1)return console.error("<Styles>: children has more than one child. Please check your code."),i.createElement(v,null,n);if(q(n)){const r=n;let l=r?.props?.className;return r?.type?.displayName===P.displayName&&oe(l)&&(l=C(...Object.values(l))),G(n,{className:C(o,l)})}return console.error("<Styles>: children is not a valid React element. Please check your code."),i.createElement(v,null,n)};P.displayName="W/Styles";const le=e=>{const{index:n=0,options:t,next:o,render:r}=e;b(()=>{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[l,a]=I(n),c=()=>{a(u=>t.length?o?o(u,t):(u+1)%t.length:u)};return r(t[l],c)},se=({onIntersect:e,threshold:n=.1,root:t=null,rootMargin:o="0px",triggerOnce:r=!1,disabled:l=!1,children:a,className:c,style:u})=>{const s=O(null),f=O(null),d=O(!1);return b(()=>{if(l||!s.current)return;if(!window.IntersectionObserver){console.warn("IntersectionObserver is not supported in this browser");return}const m=s.current,g=y=>{y.forEach($=>{r&&d.current||(e($,f.current),r&&(d.current=!0,f.current?.unobserve(m)))})};return f.current=new IntersectionObserver(g,{root:t,rootMargin:o,threshold:n}),f.current.observe(m),()=>{f.current&&f.current.disconnect()}},[e,n,t,o,r,l]),b(()=>{r||(d.current=!1)},[r]),i.createElement("div",{ref:s,className:c,style:u},a)};function ae(e){const{items:n,renderItem:t,filter:o,renderEmpty:r,sort:l}=e;if(!n)return console.error("ArrayRender: items is null"),null;if(n.length===0)return r?r():null;if(l){let u=[...n];return o&&(u=u.filter(o)),u=u.sort(l),u.length===0?r?r():null:i.createElement(v,null,u.map((s,f)=>t(s,f)))}let a=0;const c=n.map((u,s)=>o&&!o(u)?(a++,null):t(u,s));return i.createElement(v,null,a===n.length?r?r():null:c)}function ie({source:e,format:n,children:t}){const o=S(()=>{if(e instanceof Date)return e;if(typeof e=="string"||typeof e=="number"){const l=new Date(e);return isNaN(l.getTime())?null:l}return null},[e]),r=S(()=>o?n?n(o):o.toLocaleString():null,[o,n]);return!r||!t?null:i.createElement(i.Fragment,null,t(r))}const ue="onChange",ce="value";function fe(e){const{defaultValue:n,onBeforeChange:t,trigger:o=ue,valuePropName:r=ce,props:l}=e,a=Object.prototype.hasOwnProperty.call(l,r),[c,u]=I(n),s=a?l[r]:c,f=S(()=>l[o],[l,o]),d=U(m=>{const g=typeof m=="function"?m(s):m;t&&t(g,s)===!1||(a||u(g),f&&f(g))},[a,t,s,f]);return[s,d]}function de(e,...n){try{const t=e(...n);return t instanceof Promise?t:Promise.resolve(t)}catch(t){return Promise.reject(t)}}const J=typeof Promise.try=="function"?Promise.try.bind(Promise):de,me=typeof Promise.withResolvers=="function"?Promise.withResolvers.bind(Promise):()=>{let e,n;return{promise:new Promise((t,o)=>{e=t,n=o}),resolve:e,reject:n}};function W(e,n={}){let t=typeof e=="function"?e():e;const o=[],{sideEffect:r,transform:l}=n,a=()=>{const s=t;return l?.get?l.get(s):s},c=s=>{const f=t,d=l?.get?l.get(f):f;t=l?.set?l.set(typeof s=="function"?s(d):s):typeof s=="function"?s(d):s,o.forEach(m=>m(t)),r&&J(r,t,f).catch(m=>{console.error("Error in external state side effect, Please do it within side effects:",m)})},u=()=>{const[s,f]=i.useState(t);return i.useEffect(()=>(o.push(f),()=>{const d=o.indexOf(f);d>-1&&o.splice(d,1)}),[]),[l?.get?l.get(s):s,c]};return{get:a,set:c,use:u,useGetter:()=>{const[s]=u();return s},__listeners:o}}function he(e,n,t){const{storageType:o="local",sideEffect:r,transform:l}=t??{};let a=n;if(typeof window<"u"){const c=(o==="local"?localStorage:sessionStorage).getItem(e);if(c)try{a=JSON.parse(c)}catch(u){console.warn(`Failed to parse ${o}Storage value for key "${e}", using initial state:`,u),a=n}}return W(a,{sideEffect:c=>{typeof window<"u"&&(o==="local"?localStorage:sessionStorage).setItem(e,JSON.stringify(c)),r?.(c)},transform:l})}function pe(e,n){const t=n||new Date,o=t.getFullYear(),r=t.getMonth()+1,l=t.getDate(),a=t.getHours(),c=t.getMinutes(),u=t.getSeconds(),s=t.getMilliseconds(),f=t.getDay(),d=["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"],y=["January","February","March","April","May","June","July","August","September","October","November","December"],$=m[f],Y=d[f],R=r-1,_=y[R],H=g[R],z={YY:o.toString().slice(2),YYYY:o.toString(),M:r.toString(),MM:r.toString().padStart(2,"0"),MMM:H,MMMM:_,D:l.toString(),DD:l.toString().padStart(2,"0"),d:f.toString(),dd:Y,ddd:Y,dddd:$,H:a.toString(),HH:a.toString().padStart(2,"0"),h:(a%12).toString(),hh:(a%12).toString().padStart(2,"0"),m:c.toString(),mm:c.toString().padStart(2,"0"),s:u.toString(),ss:u.toString().padStart(2,"0"),SSS:s.toString().padStart(3,"0"),Z:"+08:00",ZZ:"+0800",A:a<12?"AM":"PM",a:a<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,V=>z[V])}class ge{count=0;next(){return this.count++}}const F=["base","xs","sm","md","lg","xl","2xl","3xl"],L={xs:475,sm:640,md:768,lg:1024,xl:1280,"2xl":1536,"3xl":1920};function h(e,n,t){t&&(e[n]||(e[n]=[]),e[n].push(t))}function ye(e){return e==null?!1:typeof e=="string"?e.trim().length>0:Array.isArray(e)?e.length>0:!0}function Ee(e){return e!=null}function p(e,n){return`${String(e)} ${n}`}const T={email:/^[^\s@]+@[^\s@]+\.[^\s@]+$/,url:/^(https?:\/\/)?([\w.-]+)\.([a-z]{2,6})([/\w .-]*)*\/?$/,phone:/^1[3-9]\d{9}$/};function k(e,n,t,o,r){const l=ye(n),a=Ee(n);if(t.required&&!l){h(r,e,t.message??p(e,"\u4E3A\u5FC5\u586B\u9879"));return}if(!(!a&&!t.required)){if(t.dependsOn){const c=t.dependsOn(o);c===!1?h(r,e,t.message??p(e,"\u4F9D\u8D56\u6761\u4EF6\u672A\u6EE1\u8DB3")):typeof c=="string"&&h(r,e,c)}if(typeof n=="string"){const c=t,{len:u,min:s,max:f,regex:d,email:m,url:g,phone:y}=c;typeof u=="number"&&n.length!==u&&h(r,e,t.message??p(e,`\u957F\u5EA6\u5FC5\u987B\u4E3A ${u}`)),typeof s=="number"&&n.length<s&&h(r,e,t.message??p(e,`\u957F\u5EA6\u4E0D\u80FD\u5C11\u4E8E ${s}`)),typeof f=="number"&&n.length>f&&h(r,e,t.message??p(e,`\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7 ${f}`)),d&&!d.test(n)&&h(r,e,t.message??p(e,"\u683C\u5F0F\u4E0D\u6B63\u786E")),m&&!T.email.test(n)&&h(r,e,t.message??p(e,"\u4E0D\u662F\u6709\u6548\u7684\u90AE\u7BB1")),g&&!T.url.test(n)&&h(r,e,t.message??p(e,"\u4E0D\u662F\u6709\u6548\u7684URL")),y&&!T.phone.test(n)&&h(r,e,t.message??p(e,"\u4E0D\u662F\u6709\u6548\u7684\u624B\u673A\u53F7"))}if(typeof n=="number"){const c=t,{min:u,max:s}=c;typeof u=="number"&&n<u&&h(r,e,t.message??p(e,`\u4E0D\u80FD\u5C0F\u4E8E ${u}`)),typeof s=="number"&&n>s&&h(r,e,t.message??p(e,`\u4E0D\u80FD\u5927\u4E8E ${s}`))}if(Array.isArray(n)){const c=t,{len:u,min:s,max:f,unique:d,elementRule:m}=c;typeof u=="number"&&n.length!==u&&h(r,e,t.message??p(e,`\u957F\u5EA6\u5FC5\u987B\u4E3A ${u}`)),typeof s=="number"&&n.length<s&&h(r,e,t.message??p(e,`\u957F\u5EA6\u4E0D\u80FD\u5C0F\u4E8E ${s}`)),typeof f=="number"&&n.length>f&&h(r,e,t.message??p(e,`\u957F\u5EA6\u4E0D\u80FD\u5927\u4E8E ${f}`)),d&&new Set(n).size!==n.length&&h(r,e,t.message??p(e,"\u5143\u7D20\u5FC5\u987B\u552F\u4E00")),m&&n.forEach((g,y)=>{k(`${String(e)}[${y}]`,g,m,o,r)})}if(t.validator){const c=t.validator?.(n,o);c===!1?h(r,e,t.message??p(e,"\u6821\u9A8C\u672A\u901A\u8FC7")):typeof c=="string"&&h(r,e,c)}}}function Se(e,n){const t={};for(const r in n){const l=r,a=n[l];if(!a)continue;const c=e[l];if(Array.isArray(a))for(const u of a)k(l,c,u,e,t);else k(l,c,a,e,t)}const o=Object.values(t).reduce((r,l)=>(l&&r.push(...l),r),[]);return o.length>0?{valid:!1,errors:o,fieldErrors:t}:{valid:!0,data:e}}const we=[...F].reverse();function B(e,n){for(const t of we){const o=e[t];if(o!==void 0&&!Number.isNaN(o)&&n>=o)return t}return"base"}function ve(e=L){const[n,t]=I(B(e,window.innerWidth));return b(()=>{let o=[],r=[];const l=()=>{r.forEach(f=>f()),o=[],r=[];const a=B(e,window.innerWidth);t(a);const c=F.indexOf(a),u=F[c+1];if(u&&e[u]!==void 0){const f=e[u];if(Number.isNaN(f))throw new Error(`Invalid breakpoint value for ${u}: ${e[u]}`);{const d=window.matchMedia(`(min-width: ${f}px)`);o.push(d);const m=()=>l();d.addEventListener("change",m),r.push(()=>d.removeEventListener("change",m))}}const s=F[c-1];if(s&&e[s]!==void 0){const f=e[s];if(Number.isNaN(f))throw new Error(`Invalid breakpoint value for ${s}: ${e[s]}`);{const d=window.matchMedia(`(max-width: ${f-1}px)`);o.push(d);const m=()=>l();d.addEventListener("change",m),r.push(()=>d.removeEventListener("change",m))}}};return l(),()=>{r.forEach(a=>a())}},[e]),n}export{ae as ArrayRender,ge as Counter,ie as DateRender,L as DefBreakpointDesc,X as False,E as If,se as Observer,te as Pipe,re as Scope,ne as SizeBox,P as Styles,w as Switch,le as Toggle,Q as True,ee as When,F as breakpoints,j as childrenLoop,W as createExternalState,he as createStorageState,C as cx,pe as formatDate,B as getCurrentBreakpoint,Se as ruleChecker,J as safePromiseTry,me as safePromiseWithResolvers,fe as useControlled,ve as useScreen};
|
|
1
|
+
import u,{useMemo as S,Fragment as v,Children as q,isValidElement as U,cloneElement as G,useEffect as b,useState as A,useRef as N,Component as K,useCallback as Q,useSyncExternalStore as X}from"react";import{createPortal as ee}from"react-dom";function j(e,n){if(e===void 0)return;let t=0;if(Array.isArray(e)){for(const o of e)if(n(o,t++)===!1)break}else n(e,t)}const te=(e,n)=>e===n,x=e=>u.createElement(u.Fragment,null,e.children);x.displayName="Switch_Case";const C=e=>u.createElement(u.Fragment,null,e.children);C.displayName="Switch_Default";const F=e=>{const{value:n,compare:t=te,children:o,strict:r=!1}=e,l=new Set;let s=null,i=null,a=!1;return j(o,(f,m)=>{if(!u.isValidElement(f))throw new Error(`Switch Children only accepts valid React elements at index ${m}`);const c=f.type;if(c.displayName===x.displayName){const d=f.props;if(l.has(d.value))throw new Error(`Switch found duplicate Case value at index ${m}: ${JSON.stringify(d.value)}${r?" (detected in strict mode)":""}`);if(l.add(d.value),!s&&t(n,d.value)&&(s=d.children,r===!1))return!1}else if(c.displayName===C.displayName){if(a)throw new Error(`Switch can only have one Default child at index ${m}`);if(a=!0,i=f.props.children,!r&&s)return!1}else throw new Error(`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(c.displayName||c.name||c)} at index ${m}`)}),u.createElement(u.Fragment,null,s??i)};F.displayName="Switch",F.Case=x,F.Default=C,F.createTyped=function(){return{Switch:F,Case:x,Default:C}};const M=e=>u.createElement(u.Fragment,null,e.children),I=({children:e})=>u.createElement(u.Fragment,null,e),O=e=>u.createElement(u.Fragment,null,e.children);M.displayName="If_Then",I.displayName="If_Else",O.displayName="If_ElseIf";const w=({condition:e,children:n})=>{let t=null,o=null;const r=[];if(u.Children.forEach(n,l=>{if(!u.isValidElement(l))throw new Error("If component only accepts valid React elements");const s=l.type;if(s.displayName===M.displayName){if(t)throw new Error("If component can only have one Then child");t=l}else if(s.displayName===O.displayName)r.push(l);else if(s.displayName===I.displayName){if(o)throw new Error("If component can only have one Else child");o=l}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?u.createElement(u.Fragment,null,t.props.children):null;for(const l of r)if(l.props.condition)return u.createElement(u.Fragment,null,l.props.children);return o?u.createElement(u.Fragment,null,o.props.children):null};w.displayName="If",w.Then=M,w.ElseIf=O,w.Else=I,w.createTyped=function(){return{If:w,Then:M,ElseIf:O,Else:I}};const ne=({condition:e,children:n})=>e?u.createElement(u.Fragment,null,n):null,re=({condition:e,children:n})=>e===!1?u.createElement(u.Fragment,null,n):null,oe=({all:e,any:n,none:t,children:o,fallback:r})=>S(()=>(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(l=>!l))),[e,n,t])?u.createElement(u.Fragment,null,o):u.createElement(u.Fragment,null,r||null),le=({data:e,transform:n,render:t,fallback:o})=>{const r=S(()=>n.reduce((l,s)=>s(l),e),[e,n]);return r==null?u.createElement(u.Fragment,null,o||null):u.createElement(u.Fragment,null,t(r))},se=e=>{const{children:n,h:t,w:o,size:r,height:l,width:s,className:i}=e;return u.createElement("div",{style:{width:r||o||s,height:r||t||l,flexShrink:0},className:i},n)},ae=({let:e,props:n,children:t,fallback:o})=>{const r=S(()=>typeof e=="function"?e(n):e,[e,n]);return!t||!Object.keys(r).length?u.createElement(u.Fragment,null,o||null):u.createElement(u.Fragment,null,t(r))};function $(...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(o=>n.add(o));else if(typeof t=="object")for(const[o,r]of Object.entries(t))r&&n.add(o)}return Array.from(n).join(" ")}const ue=e=>typeof e=="object"&&!!e,P=({className:e,children:n,asWrapper:t=!1})=>{if(!n)return null;if(!e)return u.createElement(v,null,n);const o=typeof e=="string"?e:$(...Object.values(e));if(t)return u.createElement(t===!0?"div":t,{className:o},n);if(q.count(n)>1)return console.error("<Styles>: children has more than one child. Please check your code."),u.createElement(v,null,n);if(U(n)){const r=n;let l=r?.props?.className;return r?.type?.displayName===P.displayName&&ue(l)&&(l=$(...Object.values(l))),G(n,{className:$(o,l)})}return console.error("<Styles>: children is not a valid React element. Please check your code."),u.createElement(v,null,n)};P.displayName="W/Styles";const ie=e=>{const{index:n=0,options:t,next:o,render:r}=e;b(()=>{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[l,s]=A(n),i=()=>{s(a=>t.length?o?o(a,t):(a+1)%t.length:a)};return r(t[l],i)},ce=({onIntersect:e,threshold:n=.1,root:t=null,rootMargin:o="0px",triggerOnce:r=!1,disabled:l=!1,children:s,className:i,style:a})=>{const f=N(null),m=N(null),c=N(!1);return b(()=>{if(l||!f.current)return;if(!window.IntersectionObserver){console.warn("IntersectionObserver is not supported in this browser");return}const d=f.current,h=p=>{p.forEach(E=>{r&&c.current||(e(E,m.current),r&&(c.current=!0,m.current?.unobserve(d)))})};return m.current=new IntersectionObserver(h,{root:t,rootMargin:o,threshold:n}),m.current.observe(d),()=>{m.current&&m.current.disconnect()}},[e,n,t,o,r,l]),b(()=>{r||(c.current=!1)},[r]),u.createElement("div",{ref:f,className:i,style:a},s)};function fe({times:e,children:n}){if(e<=0)return null;const t=[];for(let o=0;o<e;o++)t.push(n(o));return u.createElement(v,null,t)}function de({to:e,children:n,disabled:t=!1}){const[o,r]=A(!1),l=N(e);if(l.current=e,b(()=>{r(!0)},[]),t)return u.createElement(u.Fragment,null,n);if(!o)return null;const s=l.current??document.body;return ee(n,s)}class me extends K{state={error:null};static getDerivedStateFromError(n){return{error:n}}componentDidCatch(n,t){this.props.onError?.(n,t)}reset=()=>{this.setState({error:null})};render(){return this.state.error?this.props.fallback(this.state.error,this.reset):this.props.children}}function he(e){return u.createElement(me,{...e})}function pe(e){const{items:n,renderItem:t,filter:o,renderEmpty:r,sort:l}=e;if(!n)return console.error("ArrayRender: items is null"),null;if(n.length===0)return r?r():null;if(l){let a=[...n];return o&&(a=a.filter(o)),a=a.sort(l),a.length===0?r?r():null:u.createElement(v,null,a.map((f,m)=>t(f,m)))}let s=0;const i=n.map((a,f)=>o&&!o(a)?(s++,null):t(a,f));return u.createElement(v,null,s===n.length?r?r():null:i)}function ge({source:e,format:n,children:t}){const o=S(()=>{if(e instanceof Date)return e;if(typeof e=="string"||typeof e=="number"){const l=new Date(e);return isNaN(l.getTime())?null:l}return null},[e]),r=S(()=>o?n?n(o):o.toLocaleString():null,[o,n]);return!r||!t?null:u.createElement(u.Fragment,null,t(r))}const ye="onChange",Ee="value";function Se(e){const{defaultValue:n,onBeforeChange:t,trigger:o=ye,valuePropName:r=Ee,props:l}=e,s=Object.prototype.hasOwnProperty.call(l,r),[i,a]=A(n),f=s?l[r]:i,m=S(()=>l[o],[l,o]),c=Q(d=>{const h=typeof d=="function"?d(f):d;t&&t(h,f)===!1||(s||a(h),m&&m(h))},[s,t,f,m]);return[f,c]}function we(e,...n){try{const t=e(...n);return t instanceof Promise?t:Promise.resolve(t)}catch(t){return Promise.reject(t)}}const J=typeof Promise.try=="function"?Promise.try.bind(Promise):we,ve=typeof Promise.withResolvers=="function"?Promise.withResolvers.bind(Promise):()=>{let e,n;return{promise:new Promise((t,o)=>{e=t,n=o}),resolve:e,reject:n}};function W(e,n={}){let t=typeof e=="function"?e():e;const o=[],{onSet:r,onChange:l,transform:s}=n,i=(c,d,h)=>{c&&J(c,d,h).catch(p=>{console.error("Error in external state callback, Please do it within side effects:",p)})},a=()=>{const c=t;return s?.get?s.get(c):c},f=c=>{const d=t,h=s?.get?s.get(d):d;t=s?.set?s.set(typeof c=="function"?c(h):c):typeof c=="function"?c(h):c,o.forEach(p=>p()),i(r,t,d),Object.is(t,d)||i(l,t,d)},m=()=>{const c=X(d=>(o.push(d),()=>{const h=o.indexOf(d);h>-1&&o.splice(h,1)}),()=>t,()=>t);return[s?.get?s.get(c):c,f]};return{get:a,set:f,use:m,useGetter:()=>{const[c]=m();return c},__listeners:o}}function Fe(e,n,t){const{storageType:o="local",onSet:r,onChange:l,transform:s}=t??{};let i=n;if(typeof window<"u"){const a=(o==="local"?localStorage:sessionStorage).getItem(e);if(a)try{i=JSON.parse(a)}catch(f){console.warn(`Failed to parse ${o}Storage value for key "${e}", using initial state:`,f),i=n}}return W(i,{onSet:(a,f)=>{typeof window<"u"&&(o==="local"?localStorage:sessionStorage).setItem(e,JSON.stringify(a)),r?.(a,f)},onChange:l,transform:s})}function be(e,n){const t=n||new Date,o=t.getFullYear(),r=t.getMonth()+1,l=t.getDate(),s=t.getHours(),i=t.getMinutes(),a=t.getSeconds(),f=t.getMilliseconds(),m=t.getDay(),c=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],d=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],h=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],p=["January","February","March","April","May","June","July","August","September","October","November","December"],E=d[m],R=c[m],Y=r-1,H=p[Y],z=h[Y],V={YY:o.toString().slice(2),YYYY:o.toString(),M:r.toString(),MM:r.toString().padStart(2,"0"),MMM:z,MMMM:H,D:l.toString(),DD:l.toString().padStart(2,"0"),d:m.toString(),dd:R,ddd:R,dddd:E,H:s.toString(),HH:s.toString().padStart(2,"0"),h:(s%12).toString(),hh:(s%12).toString().padStart(2,"0"),m:i.toString(),mm:i.toString().padStart(2,"0"),s:a.toString(),ss:a.toString().padStart(2,"0"),SSS:f.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,Z=>V[Z])}class Ne{count=0;next(){return this.count++}}const D=["base","xs","sm","md","lg","xl","2xl","3xl"],L={xs:475,sm:640,md:768,lg:1024,xl:1280,"2xl":1536,"3xl":1920};function g(e,n,t){t&&(e[n]||(e[n]=[]),e[n].push(t))}function De(e){return e==null?!1:typeof e=="string"?e.trim().length>0:Array.isArray(e)?e.length>0:!0}function Ae(e){return e!=null}function y(e,n){return`${String(e)} ${n}`}const T={email:/^[^\s@]+@[^\s@]+\.[^\s@]+$/,url:/^(https?:\/\/)?([\w.-]+)\.([a-z]{2,6})([/\w .-]*)*\/?$/,phone:/^1[3-9]\d{9}$/};function k(e,n,t,o,r){const l=De(n),s=Ae(n);if(t.required&&!l){g(r,e,t.message??y(e,"\u4E3A\u5FC5\u586B\u9879"));return}if(!(!s&&!t.required)){if(t.dependsOn){const i=t.dependsOn(o);i===!1?g(r,e,t.message??y(e,"\u4F9D\u8D56\u6761\u4EF6\u672A\u6EE1\u8DB3")):typeof i=="string"&&g(r,e,i)}if(typeof n=="string"){const i=t,{len:a,min:f,max:m,regex:c,email:d,url:h,phone:p}=i;typeof a=="number"&&n.length!==a&&g(r,e,t.message??y(e,`\u957F\u5EA6\u5FC5\u987B\u4E3A ${a}`)),typeof f=="number"&&n.length<f&&g(r,e,t.message??y(e,`\u957F\u5EA6\u4E0D\u80FD\u5C11\u4E8E ${f}`)),typeof m=="number"&&n.length>m&&g(r,e,t.message??y(e,`\u957F\u5EA6\u4E0D\u80FD\u8D85\u8FC7 ${m}`)),c&&!c.test(n)&&g(r,e,t.message??y(e,"\u683C\u5F0F\u4E0D\u6B63\u786E")),d&&!T.email.test(n)&&g(r,e,t.message??y(e,"\u4E0D\u662F\u6709\u6548\u7684\u90AE\u7BB1")),h&&!T.url.test(n)&&g(r,e,t.message??y(e,"\u4E0D\u662F\u6709\u6548\u7684URL")),p&&!T.phone.test(n)&&g(r,e,t.message??y(e,"\u4E0D\u662F\u6709\u6548\u7684\u624B\u673A\u53F7"))}if(typeof n=="number"){const i=t,{min:a,max:f}=i;typeof a=="number"&&n<a&&g(r,e,t.message??y(e,`\u4E0D\u80FD\u5C0F\u4E8E ${a}`)),typeof f=="number"&&n>f&&g(r,e,t.message??y(e,`\u4E0D\u80FD\u5927\u4E8E ${f}`))}if(Array.isArray(n)){const i=t,{len:a,min:f,max:m,unique:c,elementRule:d}=i;typeof a=="number"&&n.length!==a&&g(r,e,t.message??y(e,`\u957F\u5EA6\u5FC5\u987B\u4E3A ${a}`)),typeof f=="number"&&n.length<f&&g(r,e,t.message??y(e,`\u957F\u5EA6\u4E0D\u80FD\u5C0F\u4E8E ${f}`)),typeof m=="number"&&n.length>m&&g(r,e,t.message??y(e,`\u957F\u5EA6\u4E0D\u80FD\u5927\u4E8E ${m}`)),c&&new Set(n).size!==n.length&&g(r,e,t.message??y(e,"\u5143\u7D20\u5FC5\u987B\u552F\u4E00")),d&&n.forEach((h,p)=>{k(`${String(e)}[${p}]`,h,d,o,r)})}if(t.validator){const i=t.validator?.(n,o);i===!1?g(r,e,t.message??y(e,"\u6821\u9A8C\u672A\u901A\u8FC7")):typeof i=="string"&&g(r,e,i)}}}function xe(e,n){const t={};for(const r in n){const l=r,s=n[l];if(!s)continue;const i=e[l];if(Array.isArray(s))for(const a of s)k(l,i,a,e,t);else k(l,i,s,e,t)}const o=Object.values(t).reduce((r,l)=>(l&&r.push(...l),r),[]);return o.length>0?{valid:!1,errors:o,fieldErrors:t}:{valid:!0,data:e}}const Ce=[...D].reverse(),_=typeof window<"u";function B(e,n){for(const t of Ce){const o=e[t];if(o!==void 0&&!Number.isNaN(o)&&n>=o)return t}return"base"}function Me(e=L){const n=S(()=>JSON.stringify(e),[e]),t=N(e);t.current=e;const[o,r]=A(()=>_?B(e,window.innerWidth):"base");return b(()=>{if(!_)return;let l=[],s=[];const i=()=>{s.forEach(h=>h()),l=[],s=[];const a=t.current,f=B(a,window.innerWidth);r(f);const m=D.indexOf(f),c=D[m+1];if(c&&a[c]!==void 0){const h=a[c];if(Number.isNaN(h))throw new Error(`Invalid breakpoint value for ${c}: ${a[c]}`);{const p=window.matchMedia(`(min-width: ${h}px)`);l.push(p);const E=()=>i();p.addEventListener("change",E),s.push(()=>p.removeEventListener("change",E))}}const d=D[m-1];if(d&&a[d]!==void 0){const h=a[d];if(Number.isNaN(h))throw new Error(`Invalid breakpoint value for ${d}: ${a[d]}`);{const p=window.matchMedia(`(max-width: ${h-1}px)`);l.push(p);const E=()=>i();p.addEventListener("change",E),s.push(()=>p.removeEventListener("change",E))}}};return i(),()=>{s.forEach(a=>a())}},[n]),o}export{pe as ArrayRender,he as Boundary,Ne as Counter,ge as DateRender,L as DefBreakpointDesc,re as False,w as If,ce as Observer,le as Pipe,de as Portal,fe as Repeat,ae as Scope,se as SizeBox,P as Styles,F as Switch,ie as Toggle,ne as True,oe as When,D as breakpoints,j as childrenLoop,W as createExternalState,Fe as createStorageState,$ as cx,be as formatDate,B as getCurrentBreakpoint,xe as ruleChecker,J as safePromiseTry,ve as safePromiseWithResolvers,Se as useControlled,Me as useScreen};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@wwog/react",
|
|
3
|
-
"version": "1.3.
|
|
3
|
+
"version": "1.3.14",
|
|
4
4
|
"description": "A practical React component library providing declarative flow control and common UI utility components to make your React code more concise and readable.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"react",
|
|
@@ -42,8 +42,8 @@ h = (q + [13(m + 1)/5] + Y + [Y / 4] + 5) mod 7
|
|
|
42
42
|
* @returns 星期几(0=周日, 1=周一, ..., 6=周六)
|
|
43
43
|
*/
|
|
44
44
|
export function weekday(y: number, m: number, d: number): number {
|
|
45
|
-
const adjustedY = m < 3 ? y - 1 : y - 2
|
|
46
|
-
const adjustedD = d + adjustedY
|
|
45
|
+
const adjustedY = m < 3 ? y - 1 : y - 2
|
|
46
|
+
const adjustedD = d + adjustedY
|
|
47
47
|
return (
|
|
48
48
|
(Math.floor((23 * m) / 9) +
|
|
49
49
|
adjustedD +
|
|
@@ -52,7 +52,7 @@ export function weekday(y: number, m: number, d: number): number {
|
|
|
52
52
|
Math.floor(y / 100) +
|
|
53
53
|
Math.floor(y / 400)) %
|
|
54
54
|
7
|
|
55
|
-
)
|
|
55
|
+
)
|
|
56
56
|
}
|
|
57
57
|
|
|
58
58
|
/**
|
|
@@ -63,18 +63,13 @@ export function weekday(y: number, m: number, d: number): number {
|
|
|
63
63
|
* @returns 星期几(0=周六, 1=周日, ..., 6=周五)
|
|
64
64
|
*/
|
|
65
65
|
export function weekdayJulian(y: number, m: number, d: number): number {
|
|
66
|
-
const adjustedY = m < 3 ? y - 1 : y
|
|
67
|
-
const adjustedD = d + adjustedY
|
|
66
|
+
const adjustedY = m < 3 ? y - 1 : y
|
|
67
|
+
const adjustedD = d + adjustedY
|
|
68
68
|
if (m < 3) {
|
|
69
|
-
m += 12
|
|
70
|
-
y
|
|
69
|
+
m += 12
|
|
70
|
+
y--
|
|
71
71
|
}
|
|
72
72
|
return (
|
|
73
|
-
(adjustedD +
|
|
74
|
-
|
|
75
|
-
adjustedY +
|
|
76
|
-
Math.floor(adjustedY / 4) +
|
|
77
|
-
5) %
|
|
78
|
-
7
|
|
79
|
-
);
|
|
73
|
+
(adjustedD + Math.floor((13 * (m + 1)) / 5) + adjustedY + Math.floor(adjustedY / 4) + 5) % 7
|
|
74
|
+
)
|
|
80
75
|
}
|
package/src/algorithm/date.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
1
|
+
export * from './constant'
|
|
2
|
+
export * from './Flex'
|
|
@@ -62,8 +62,8 @@ export const If = ({
|
|
|
62
62
|
} else {
|
|
63
63
|
throw new Error(
|
|
64
64
|
`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(
|
|
65
|
-
type.displayName || type.name || type
|
|
66
|
-
)}
|
|
65
|
+
type.displayName || type.name || type,
|
|
66
|
+
)}`,
|
|
67
67
|
);
|
|
68
68
|
}
|
|
69
69
|
});
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import React, { Component, type ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
export interface BoundaryProps {
|
|
4
|
+
/**
|
|
5
|
+
* @description_en Fallback UI to render when an error is caught. Receives the error and a reset function.
|
|
6
|
+
* @description_zh 捕获到错误时渲染的降级 UI,接收错误对象和重置函数。
|
|
7
|
+
*/
|
|
8
|
+
fallback: (error: Error, reset: () => void) => ReactNode;
|
|
9
|
+
/**
|
|
10
|
+
* @description_en Called when an error is caught, useful for logging.
|
|
11
|
+
* @description_zh 捕获到错误时的回调,可用于上报日志。
|
|
12
|
+
* @optional
|
|
13
|
+
*/
|
|
14
|
+
onError?: (error: Error, info: React.ErrorInfo) => void;
|
|
15
|
+
/**
|
|
16
|
+
* @description_en Child elements to protect.
|
|
17
|
+
* @description_zh 需要保护的子元素。
|
|
18
|
+
*/
|
|
19
|
+
children?: ReactNode;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
interface BoundaryState {
|
|
23
|
+
error: Error | null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
class ErrorBoundaryCore extends Component<BoundaryProps, BoundaryState> {
|
|
27
|
+
state: BoundaryState = { error: null };
|
|
28
|
+
|
|
29
|
+
static getDerivedStateFromError(error: Error): BoundaryState {
|
|
30
|
+
return { error };
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
componentDidCatch(error: Error, info: React.ErrorInfo) {
|
|
34
|
+
this.props.onError?.(error, info);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
reset = () => {
|
|
38
|
+
this.setState({ error: null });
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
render() {
|
|
42
|
+
if (this.state.error) {
|
|
43
|
+
return this.props.fallback(this.state.error, this.reset);
|
|
44
|
+
}
|
|
45
|
+
return this.props.children;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* @description_zh Error Boundary 的声明式封装,通过 render prop 提供降级 UI 和重置能力。
|
|
51
|
+
* @description_en Declarative Error Boundary wrapper with render-prop fallback and reset capability.
|
|
52
|
+
* @component
|
|
53
|
+
* @example
|
|
54
|
+
* ```tsx
|
|
55
|
+
* <Boundary fallback={(error, reset) => (
|
|
56
|
+
* <div>
|
|
57
|
+
* <p>出错了: {error.message}</p>
|
|
58
|
+
* <button onClick={reset}>重试</button>
|
|
59
|
+
* </div>
|
|
60
|
+
* )}>
|
|
61
|
+
* <RiskyComponent />
|
|
62
|
+
* </Boundary>
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
export function Boundary(props: BoundaryProps): ReactNode {
|
|
66
|
+
return <ErrorBoundaryCore {...props} />;
|
|
67
|
+
}
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import React, { useEffect, useRef, useState, type ReactNode } from "react";
|
|
2
|
+
import { createPortal } from "react-dom";
|
|
3
|
+
|
|
4
|
+
export interface PortalProps {
|
|
5
|
+
/**
|
|
6
|
+
* @description_en Target DOM element to mount into. Defaults to document.body.
|
|
7
|
+
* @description_zh 挂载目标 DOM 元素,默认为 document.body。
|
|
8
|
+
* @default document.body
|
|
9
|
+
*/
|
|
10
|
+
to?: Element | null;
|
|
11
|
+
/**
|
|
12
|
+
* @description_en Child elements to render into the portal.
|
|
13
|
+
* @description_zh 要渲染到 portal 中的子元素。
|
|
14
|
+
*/
|
|
15
|
+
children?: ReactNode;
|
|
16
|
+
/**
|
|
17
|
+
* @description_en Whether to disable the portal and render children inline. Default is false.
|
|
18
|
+
* @description_zh 是否禁用 portal,直接内联渲染子元素。默认为 false。
|
|
19
|
+
* @default false
|
|
20
|
+
*/
|
|
21
|
+
disabled?: boolean;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* @description_zh 声明式 Portal 组件,将子元素渲染到指定 DOM 节点,常用于模态框、浮层等场景。
|
|
26
|
+
* @description_en Declarative Portal component that renders children into a specified DOM node, commonly used for modals and overlays.
|
|
27
|
+
* @component
|
|
28
|
+
* @example
|
|
29
|
+
* ```tsx
|
|
30
|
+
* <Portal>
|
|
31
|
+
* <Modal />
|
|
32
|
+
* </Portal>
|
|
33
|
+
*
|
|
34
|
+
* <Portal to={document.getElementById('overlay-root')}>
|
|
35
|
+
* <Tooltip />
|
|
36
|
+
* </Portal>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function Portal({ to, children, disabled = false }: PortalProps): ReactNode {
|
|
40
|
+
const [mounted, setMounted] = useState(false);
|
|
41
|
+
// to 可能在首次渲染时为 null(SSR 或 ref 未就绪),延迟到客户端挂载后再渲染
|
|
42
|
+
const toRef = useRef(to);
|
|
43
|
+
toRef.current = to;
|
|
44
|
+
|
|
45
|
+
useEffect(() => {
|
|
46
|
+
setMounted(true);
|
|
47
|
+
}, []);
|
|
48
|
+
|
|
49
|
+
if (disabled) {
|
|
50
|
+
return <>{children}</>;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (!mounted) {
|
|
54
|
+
return null;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const target = toRef.current ?? document.body;
|
|
58
|
+
return createPortal(children, target);
|
|
59
|
+
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import React, { Fragment, type ReactNode } from "react";
|
|
2
|
+
|
|
3
|
+
export interface RepeatProps {
|
|
4
|
+
/**
|
|
5
|
+
* @description_en Number of times to repeat.
|
|
6
|
+
* @description_zh 重复次数。
|
|
7
|
+
*/
|
|
8
|
+
times: number;
|
|
9
|
+
/**
|
|
10
|
+
* @description_en Render function receiving the current index (0-based).
|
|
11
|
+
* @description_zh 渲染函数,接收当前索引(从 0 开始)。
|
|
12
|
+
*/
|
|
13
|
+
children: (index: number) => ReactNode;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* @description_zh 声明式重复渲染组件,常用于骨架屏、占位符等场景。
|
|
18
|
+
* @description_en Declarative repeat-render component, commonly used for skeleton screens and placeholders.
|
|
19
|
+
* @component
|
|
20
|
+
* @example
|
|
21
|
+
* ```tsx
|
|
22
|
+
* <Repeat times={3}>
|
|
23
|
+
* {(i) => <Skeleton key={i} />}
|
|
24
|
+
* </Repeat>
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export function Repeat({ times, children }: RepeatProps): ReactNode {
|
|
28
|
+
if (times <= 0) return null;
|
|
29
|
+
const items: ReactNode[] = [];
|
|
30
|
+
for (let i = 0; i < times; i++) {
|
|
31
|
+
items.push(children(i));
|
|
32
|
+
}
|
|
33
|
+
return <Fragment>{items}</Fragment>;
|
|
34
|
+
}
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
-
export * from
|
|
2
|
-
export * from
|
|
3
|
-
export * from
|
|
4
|
-
export * from
|
|
5
|
-
export * from
|
|
1
|
+
export * from './SizeBox'
|
|
2
|
+
export * from './Scope'
|
|
3
|
+
export * from './Styles'
|
|
4
|
+
export * from './Toggle'
|
|
5
|
+
export * from './Observer'
|
|
6
|
+
export * from './Repeat'
|
|
7
|
+
export * from './Portal'
|
|
8
|
+
export * from './Boundary'
|
package/src/hooks/index.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
export * from './useControlled'
|
|
2
|
-
export * from './useScreen'
|
|
2
|
+
export * from './useScreen'
|