@wwog/react 1.2.2 → 1.2.3

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
@@ -150,13 +150,16 @@ function Example({ isActive }) {
150
150
  ```tsx
151
151
  import { Toggle } from "@wwog/react";
152
152
 
153
- <Toggle options={["light", "dark"]} render={(value,toggle)=>{/* xxx */}}/>
153
+ <Toggle
154
+ options={["light", "dark"]}
155
+ render={(value, toggle) => {
156
+ /* xxx */
157
+ }}
158
+ />;
154
159
  ```
155
160
 
156
161
  - `options`:可切换的值数组。
157
162
  - `index`:默认:0。
158
- - `target`:传递切换值给子节点的属性名,默认 value。
159
- - `toggleTarget`:传递切换函数给子节点的属性名,默认 toggle。
160
163
  - `next`:自定义切换逻辑函数。
161
164
  - `render`:渲染函数。
162
165
 
@@ -253,6 +256,37 @@ function Example() {
253
256
  - `children`:作用域变量的渲染函数。
254
257
  - `fallback`:无内容时的兜底渲染。
255
258
 
259
+ #### `<DateRender>` (v1.2.3+)
260
+
261
+ 一个声明式组件,用于格式化并渲染日期,简单易用且支持自定义格式化。
262
+
263
+ ```tsx
264
+ import { DateRender } from "@wwog/react";
265
+
266
+ function Example() {
267
+ return (
268
+ <>
269
+ {/* 使用默认格式化 */}
270
+ <DateRender source="2025-05-06">
271
+ {(formatted) => <div>日期: {formatted}</div>}
272
+ </DateRender>
273
+
274
+ {/* 使用自定义格式化 */}
275
+ <DateRender
276
+ source={new Date()}
277
+ format={(date) => date.toLocaleDateString("zh-CN")}
278
+ >
279
+ {(formatted) => <div>日期: {formatted}</div>}
280
+ </DateRender>
281
+ </>
282
+ );
283
+ }
284
+ ```
285
+
286
+ - `source`:要渲染的输入日期(Date 对象、ISO 字符串或时间戳)。
287
+ - `format`:可选的格式化日期的函数,默认使用 `toLocaleString()`。
288
+ - `children`:渲染格式化后日期的函数,接收格式化后的日期作为参数。
289
+
256
290
  #### `<SizeBox>`
257
291
 
258
292
  创建固定尺寸的容器,用于布局调整和间距控制。
@@ -278,25 +312,20 @@ function Layout() {
278
312
  }
279
313
  ```
280
314
 
281
- ### Ideas
282
-
283
- > 需求不高,但有用的组件
284
-
285
- - Loop:灵活的迭代渲染,支持数组、对象和范围。
286
- - Try:封装异步逻辑,处理 Promise 状态。
287
- - Pick:轻量版值选择渲染,类似枚举匹配。
288
- - Render:动态渲染函数,简化复杂渲染逻辑。
289
- - Once:确保内容仅渲染一次,适合初始化。
290
- - Each:增强列表渲染,支持过滤和排序。
291
-
292
315
  ### hooks
293
316
 
294
- - 一些常用的hooks的封装
317
+ - 一些常用的 hooks 的封装
295
318
 
296
- ### useControlled (v1.2.0+)
319
+ #### useControlled (v1.2.0+)
297
320
 
298
321
  - 受控组件和非受控组件的切换,方便组件开发
299
322
 
323
+ ### method
324
+
325
+ - 用于部分组件的内部函数,如需要也可使用
326
+
327
+ formatDate 比较标准的格式化时间函数
328
+ childrenLoop 可以中断的子节点遍历,让一些分支流程拥有极致性能
300
329
 
301
330
  ## License
302
331
 
package/dist/index.d.mts CHANGED
@@ -306,11 +306,84 @@ interface ScopeProps {
306
306
  */
307
307
  declare const Scope: FC<ScopeProps>;
308
308
 
309
+ interface DateRenderProps<T = string> {
310
+ /**
311
+ * @description_en The input date to render (Date object, ISO string, or timestamp).
312
+ * @description_zh 要渲染的输入日期(Date 对象、ISO 字符串或时间戳)。
313
+ */
314
+ source: Date | string | number;
315
+ /**
316
+ * @description_en Function to format the date.
317
+ * @description_zh 格式化日期的函数。
318
+ * @optional
319
+ * @default toLocaleString
320
+ */
321
+ format?: (date: Date) => T;
322
+ /**
323
+ * @description_en Function to render the formatted date.
324
+ * @description_zh 渲染格式化后日期的函数。
325
+ */
326
+ children: (formatted: T) => React$1.ReactNode;
327
+ }
328
+ /**
329
+ * @description_zh 一个声明式组件,用于格式化并渲染日期,简单易用且支持自定义格式化。
330
+ * @description_en A declarative component for formatting and rendering dates, simple to use with support for custom formatting.
331
+ * @component
332
+ * @template T - The type of the formatted date value
333
+ * @example
334
+ * ```tsx
335
+ * <DateRender source="2025-05-06">
336
+ * {(formatted) => <div>日期: {formatted}</div>}
337
+ * </DateRender>
338
+ * ```
339
+ *
340
+ * @example
341
+ * ```tsx
342
+ * <DateRender<string>
343
+ * source={new Date()}
344
+ * format={(date) => date.toLocaleDateString("zh-CN")}
345
+ * >
346
+ * {(formatted) => <div>日期: {formatted}</div>}
347
+ * </DateRender>
348
+ * ```
349
+ */
350
+ declare function DateRender<T = string>({ source, format, children, }: DateRenderProps<T>): React$1.JSX.Element | null;
351
+
309
352
  /**
310
353
  * @description 性能优化,替代 React.Children.forEach, 回调可以返回 false 来中断循环
311
354
  * @description_en Replace React.Children.forEach, the callback can return false to interrupt the loop
312
355
  */
313
356
  declare function childrenLoop(children: React$1.ReactNode | undefined, callback: (child: React$1.ReactNode, index: number) => boolean | void): void;
357
+ /**
358
+ * @param schema
359
+ * @example
360
+ * YY | 18 | Two-digit year
361
+ * YYYY | 2018 | Four-digit year
362
+ * M | 1-12 | The month, beginning at 1
363
+ * MM | 01-12 | The month, 2-digits
364
+ * MMM | Jan-Dec | The abbreviated month name
365
+ * MMMM | January-December | The full month name
366
+ * D | 1-31 | The day of the month
367
+ * DD | 01-31 | The day of the month, 2-digits
368
+ * d | 0-6 | The day of the week, with Sunday as 0
369
+ * dd | Su-Sa | The min name of the day of the week
370
+ * ddd | Sun-Sat | The short name of the day of the week
371
+ * dddd | Sunday-Saturday | The name of the day of the week
372
+ * H | 0-23 | The hour
373
+ * HH | 00-23 | The hour, 2-digits
374
+ * h | 1-12 | The hour, 12-hour clock
375
+ * hh | 01-12 | The hour, 12-hour clock, 2-digits
376
+ * m | 0-59 | The minute
377
+ * mm | 00-59 | The minute, 2-digits
378
+ * s | 0-59 | The second
379
+ * ss | 00-59 | The second, 2-digits
380
+ * SSS | 000-999 | The millisecond, 3-digits
381
+ * Z | +05:00 | The offset from UTC, ±HH:mm
382
+ * ZZ | +0500 | The offset from UTC, ±HHmm
383
+ * A | AM | PM
384
+ * a | am | pm
385
+ */
386
+ declare function formatDate(schema: string, date?: Date): string;
314
387
 
315
388
  interface UseControlledOptions<T> {
316
389
  /**
@@ -339,5 +412,5 @@ interface UseControlledOptions<T> {
339
412
  }
340
413
  declare function useControlled<T>(options: UseControlledOptions<T>): [T, Dispatch<React.SetStateAction<T>>];
341
414
 
342
- export { ArrayRender, False, If, Pipe, Scope, SizeBox, Switch, Toggle, True, When, childrenLoop, useControlled };
343
- export type { ArrayRenderProps, ElseIfProps, ElseProps, FalseProps, IfProps, PipeProps, ScopeProps, SwitchCaseProps, SwitchDefaultProps, SwitchProps, ThenProps, ToggleProps, TrueProps, UseControlledOptions, WhenProps };
415
+ export { ArrayRender, DateRender, False, If, Pipe, Scope, SizeBox, Switch, Toggle, True, When, childrenLoop, formatDate, useControlled };
416
+ export type { ArrayRenderProps, DateRenderProps, ElseIfProps, ElseProps, FalseProps, IfProps, PipeProps, ScopeProps, SwitchCaseProps, SwitchDefaultProps, SwitchProps, ThenProps, ToggleProps, TrueProps, UseControlledOptions, WhenProps };
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- import n,{useMemo as y,useEffect as C,useState as S,Fragment as x,useCallback as T}from"react";function I(e,l){if(e===void 0)return;let t=0;if(Array.isArray(e)){for(const r of e)if(l(r,t++)===!1)break}else l(e,t)}const b=(e,l)=>e===l,E=e=>n.createElement(n.Fragment,null,e.children);E.displayName="Switch_Case";const g=e=>n.createElement(n.Fragment,null,e.children);g.displayName="Switch_Default";const p=e=>{const{value:l,compare:t=b,children:r,strict:o=!1}=e,a=new Set;let i=null,u=null,d=!1;return I(r,(s,m)=>{if(!n.isValidElement(s))throw new Error(`Switch Children only accepts valid React elements at index ${m}`);const f=s.type;if(f.displayName===E.displayName){const c=s.props;if(a.has(c.value))throw new Error(`Switch found duplicate Case value at index ${m}: ${JSON.stringify(c.value)}${o?" (detected in strict mode)":""}`);if(a.add(c.value),!i&&t(l,c.value)&&(i=c.children,o===!1))return!1}else if(f.displayName===g.displayName){if(d)throw new Error(`Switch can only have one Default child at index ${m}`);if(d=!0,u=s.props.children,!o&&i)return!1}else throw new Error(`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(f.displayName||f.name||f)} at index ${m}`)}),n.createElement(n.Fragment,null,i??u)};p.displayName="Switch",p.Case=E,p.Default=g,p.createTyped=function(){return{Switch:p,Case:E,Default:g}};const w=e=>n.createElement(n.Fragment,null,e.children),N=({children:e})=>n.createElement(n.Fragment,null,e),v=e=>n.createElement(n.Fragment,null,e.children);w.displayName="If_Then",N.displayName="If_Else",v.displayName="If_ElseIf";const h=({condition:e,children:l})=>{let t=null,r=null;const o=[];if(n.Children.forEach(l,a=>{if(!n.isValidElement(a))throw new Error("If component only accepts valid React elements");const i=a.type;if(i.displayName===w.displayName){if(t)throw new Error("If component can only have one Then child");t=a}else if(i.displayName===v.displayName)o.push(a);else if(i.displayName===N.displayName){if(r)throw new Error("If component can only have one Else child");r=a}else throw new Error(`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(i.displayName||i.name||i)}`)}),e)return t?n.createElement(n.Fragment,null,t.props.children):null;for(const a of o)if(a.props.condition)return n.createElement(n.Fragment,null,a.props.children);return r?n.createElement(n.Fragment,null,r.props.children):null};h.displayName="If",h.Then=w,h.ElseIf=v,h.Else=N,h.createTyped=function(){return{If:h,Then:w,ElseIf:v,Else:N}};const k=({condition:e,children:l})=>e?n.createElement(n.Fragment,null,l):null,$=({condition:e,children:l})=>e===!1?n.createElement(n.Fragment,null,l):null,D=({all:e,any:l,none:t,children:r,fallback:o})=>y(()=>(e&&(l||t)&&console.warn('When: Multiple condition types (all, any, none) provided; "all" takes precedence.'),!!(e&&e.length>0&&e.every(Boolean)||l&&l.length>0&&l.some(Boolean)||t&&t.length>0&&t.every(a=>!a))),[e,l,t])?n.createElement(n.Fragment,null,r):n.createElement(n.Fragment,null,o||null),_=({data:e,transform:l,render:t,fallback:r})=>{const o=y(()=>l.reduce((a,i)=>i(a),e),[e,l]);return o==null?n.createElement(n.Fragment,null,r||null):n.createElement(n.Fragment,null,t(o))},A=e=>{const{index:l=0,options:t,next:r,render:o}=e;C(()=>{if(t.length<l+1)throw new Error(`Index ${l} is out of bounds for options array of length ${t.length}. Defaulting to first option.`)},[l,t]);const[a,i]=S(l),u=()=>{i(d=>t.length?r?r(d,t):(d+1)%t.length:d)};return o(t[a],u)},B=e=>{const{children:l,h:t,w:r,size:o,height:a,width:i,className:u}=e;return n.createElement("div",{style:{width:o||r||i,height:o||t||a,flexShrink:0},className:u},l)};function O(e){const{items:l,renderItem:t,filter:r}=e;return l?n.createElement(x,null,l.map((o,a)=>r&&!r(o)?null:t(o,a))):(console.error("ArrayRender: items is null"),null)}const R=({let:e,props:l,children:t,fallback:r})=>{const o=y(()=>typeof e=="function"?e(l):e,[e,l]);return!t||!Object.keys(o).length?n.createElement(n.Fragment,null,r||null):n.createElement(n.Fragment,null,t(o))},P="onChange",V="value";function j(e){const{defaultValue:l,onBeforeChange:t,trigger:r=P,valuePropName:o=V,props:a}=e,i=Object.prototype.hasOwnProperty.call(a,o),[u,d]=S(l),s=i?a[o]:u,m=y(()=>a[r],[a,r]),f=T(c=>{const F=typeof c=="function"?c(s):c;t&&t(F,s)===!1||(i||d(F),m&&m(F))},[i,t,r,s,m]);return[s,f]}export{O as ArrayRender,$ as False,h as If,_ as Pipe,R as Scope,B as SizeBox,p as Switch,A as Toggle,k as True,D as When,I as childrenLoop,j as useControlled};
1
+ import r,{useMemo as h,useEffect as J,useState as b,Fragment as O,useCallback as H}from"react";function I(e,t){if(e===void 0)return;let n=0;if(Array.isArray(e)){for(const l of e)if(t(l,n++)===!1)break}else t(e,n)}function R(e,t){const n=t||new Date,l=n.getFullYear(),a=n.getMonth()+1,o=n.getDate(),i=n.getHours(),c=n.getMinutes(),u=n.getSeconds(),d=n.getMilliseconds(),s=n.getDay(),f=["Sun","Mon","Tue","Wed","Thu","Fri","Sat"],m=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"],y=["Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sep","Oct","Nov","Dec"],T=["January","February","March","April","May","June","July","August","September","October","November","December"],M=s===0?6:s-1,Y=m[M],v=f[M],D=a-1,C=T[D],A=y[D],x={YY:l.toString().slice(2),YYYY:l.toString(),M:a.toString(),MM:a.toString().padStart(2,"0"),MMM:A,MMMM:C,D:o.toString(),DD:o.toString().padStart(2,"0"),d:s.toString(),dd:v,ddd:v,dddd:Y,H:i.toString(),HH:i.toString().padStart(2,"0"),h:(i%12).toString(),hh:(i%12).toString().padStart(2,"0"),m:c.toString(),mm:c.toString().padStart(2,"0"),s:u.toString(),ss:u.toString().padStart(2,"0"),SSS:d.toString().padStart(3,"0"),Z:"+08:00",ZZ:"+0800",A:i<12?"AM":"PM",a:i<12?"am":"pm"};return e.replace(/YYYY|YY|M{1,4}|D{1,2}|d{1,4}|H{1,2}|h{1,2}|m{1,2}|s{1,2}|SSS|Z{1,2}|A|a/g,k=>x[k])}const _=(e,t)=>e===t,S=e=>r.createElement(r.Fragment,null,e.children);S.displayName="Switch_Case";const E=e=>r.createElement(r.Fragment,null,e.children);E.displayName="Switch_Default";const g=e=>{const{value:t,compare:n=_,children:l,strict:a=!1}=e,o=new Set;let i=null,c=null,u=!1;return I(l,(d,s)=>{if(!r.isValidElement(d))throw new Error(`Switch Children only accepts valid React elements at index ${s}`);const f=d.type;if(f.displayName===S.displayName){const m=d.props;if(o.has(m.value))throw new Error(`Switch found duplicate Case value at index ${s}: ${JSON.stringify(m.value)}${a?" (detected in strict mode)":""}`);if(o.add(m.value),!i&&n(t,m.value)&&(i=m.children,a===!1))return!1}else if(f.displayName===E.displayName){if(u)throw new Error(`Switch can only have one Default child at index ${s}`);if(u=!0,c=d.props.children,!a&&i)return!1}else throw new Error(`Switch Children only accepts 'Case' or 'Default' elements, found: ${String(f.displayName||f.name||f)} at index ${s}`)}),r.createElement(r.Fragment,null,i??c)};g.displayName="Switch",g.Case=S,g.Default=E,g.createTyped=function(){return{Switch:g,Case:S,Default:E}};const w=e=>r.createElement(r.Fragment,null,e.children),N=({children:e})=>r.createElement(r.Fragment,null,e),F=e=>r.createElement(r.Fragment,null,e.children);w.displayName="If_Then",N.displayName="If_Else",F.displayName="If_ElseIf";const p=({condition:e,children:t})=>{let n=null,l=null;const a=[];if(r.Children.forEach(t,o=>{if(!r.isValidElement(o))throw new Error("If component only accepts valid React elements");const i=o.type;if(i.displayName===w.displayName){if(n)throw new Error("If component can only have one Then child");n=o}else if(i.displayName===F.displayName)a.push(o);else if(i.displayName===N.displayName){if(l)throw new Error("If component can only have one Else child");l=o}else throw new Error(`If component only accepts 'Then', 'ElseIf', or 'Else' elements as children, found: ${String(i.displayName||i.name||i)}`)}),e)return n?r.createElement(r.Fragment,null,n.props.children):null;for(const o of a)if(o.props.condition)return r.createElement(r.Fragment,null,o.props.children);return l?r.createElement(r.Fragment,null,l.props.children):null};p.displayName="If",p.Then=w,p.ElseIf=F,p.Else=N,p.createTyped=function(){return{If:p,Then:w,ElseIf:F,Else:N}};const B=({condition:e,children:t})=>e?r.createElement(r.Fragment,null,t):null,P=({condition:e,children:t})=>e===!1?r.createElement(r.Fragment,null,t):null,W=({all:e,any:t,none:n,children:l,fallback:a})=>h(()=>(e&&(t||n)&&console.warn('When: Multiple condition types (all, any, none) provided; "all" takes precedence.'),!!(e&&e.length>0&&e.every(Boolean)||t&&t.length>0&&t.some(Boolean)||n&&n.length>0&&n.every(o=>!o))),[e,t,n])?r.createElement(r.Fragment,null,l):r.createElement(r.Fragment,null,a||null),Z=({data:e,transform:t,render:n,fallback:l})=>{const a=h(()=>t.reduce((o,i)=>i(o),e),[e,t]);return a==null?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,n(a))},V=e=>{const{index:t=0,options:n,next:l,render:a}=e;J(()=>{if(n.length<t+1)throw new Error(`Index ${t} is out of bounds for options array of length ${n.length}. Defaulting to first option.`)},[t,n]);const[o,i]=b(t),c=()=>{i(u=>n.length?l?l(u,n):(u+1)%n.length:u)};return a(n[o],c)},$=e=>{const{children:t,h:n,w:l,size:a,height:o,width:i,className:c}=e;return r.createElement("div",{style:{width:a||l||i,height:a||n||o,flexShrink:0},className:c},t)};function j(e){const{items:t,renderItem:n,filter:l}=e;return t?r.createElement(O,null,t.map((a,o)=>l&&!l(a)?null:n(a,o))):(console.error("ArrayRender: items is null"),null)}const z=({let:e,props:t,children:n,fallback:l})=>{const a=h(()=>typeof e=="function"?e(t):e,[e,t]);return!n||!Object.keys(a).length?r.createElement(r.Fragment,null,l||null):r.createElement(r.Fragment,null,n(a))};function L({source:e,format:t,children:n}){const l=h(()=>{if(e instanceof Date)return e;if(typeof e=="string"||typeof e=="number"){const o=new Date(e);return isNaN(o.getTime())?null:o}return null},[e]),a=h(()=>l?t?t(l):l.toLocaleString():null,[l,t]);return!a||!n?null:r.createElement(r.Fragment,null,n(a))}const q="onChange",G="value";function K(e){const{defaultValue:t,onBeforeChange:n,trigger:l=q,valuePropName:a=G,props:o}=e,i=Object.prototype.hasOwnProperty.call(o,a),[c,u]=b(t),d=i?o[a]:c,s=h(()=>o[l],[o,l]),f=H(m=>{const y=typeof m=="function"?m(d):m;n&&n(y,d)===!1||(i||u(y),s&&s(y))},[i,n,l,d,s]);return[d,f]}export{j as ArrayRender,L as DateRender,P as False,p as If,Z as Pipe,z as Scope,$ as SizeBox,g as Switch,V as Toggle,B as True,W as When,I as childrenLoop,R as formatDate,K as useControlled};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@wwog/react",
3
- "version": "1.2.2",
3
+ "version": "1.2.3",
4
4
  "description": "",
5
5
  "keywords": [],
6
6
  "author": "wwog",
@@ -0,0 +1,72 @@
1
+ import React, { useMemo } from "react";
2
+
3
+ export interface DateRenderProps<T = string> {
4
+ /**
5
+ * @description_en The input date to render (Date object, ISO string, or timestamp).
6
+ * @description_zh 要渲染的输入日期(Date 对象、ISO 字符串或时间戳)。
7
+ */
8
+ source: Date | string | number;
9
+ /**
10
+ * @description_en Function to format the date.
11
+ * @description_zh 格式化日期的函数。
12
+ * @optional
13
+ * @default toLocaleString
14
+ */
15
+ format?: (date: Date) => T;
16
+ /**
17
+ * @description_en Function to render the formatted date.
18
+ * @description_zh 渲染格式化后日期的函数。
19
+ */
20
+ children: (formatted: T) => React.ReactNode;
21
+ }
22
+
23
+ /**
24
+ * @description_zh 一个声明式组件,用于格式化并渲染日期,简单易用且支持自定义格式化。
25
+ * @description_en A declarative component for formatting and rendering dates, simple to use with support for custom formatting.
26
+ * @component
27
+ * @template T - The type of the formatted date value
28
+ * @example
29
+ * ```tsx
30
+ * <DateRender source="2025-05-06">
31
+ * {(formatted) => <div>日期: {formatted}</div>}
32
+ * </DateRender>
33
+ * ```
34
+ *
35
+ * @example
36
+ * ```tsx
37
+ * <DateRender<string>
38
+ * source={new Date()}
39
+ * format={(date) => date.toLocaleDateString("zh-CN")}
40
+ * >
41
+ * {(formatted) => <div>日期: {formatted}</div>}
42
+ * </DateRender>
43
+ * ```
44
+ */
45
+ export function DateRender<T = string>({
46
+ source,
47
+ format,
48
+ children,
49
+ }: DateRenderProps<T>) {
50
+ const date = useMemo(() => {
51
+ if (source instanceof Date) return source;
52
+ if (typeof source === "string" || typeof source === "number") {
53
+ const parsed = new Date(source);
54
+ return isNaN(parsed.getTime()) ? null : parsed;
55
+ }
56
+ return null;
57
+ }, [source]);
58
+
59
+ const formattedDate = useMemo(() => {
60
+ if (!date) return null;
61
+ if (format) {
62
+ return format(date);
63
+ }
64
+ return date.toLocaleString() as unknown as T;
65
+ }, [date, format]);
66
+
67
+ if (!formattedDate || !children) {
68
+ return null;
69
+ }
70
+
71
+ return <>{children(formattedDate)}</>;
72
+ }
@@ -1,3 +1,4 @@
1
1
  export * from "./SizeBox";
2
2
  export * from "./ArrayRender";
3
3
  export * from "./Scope";
4
+ export * from "./DateRender";
@@ -20,3 +20,122 @@ export function childrenLoop(
20
20
  callback(children, index);
21
21
  }
22
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
+ }