@take-out/helpers 0.0.39 → 0.0.41

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.
Files changed (158) hide show
  1. package/dist/cjs/clipboard/clipboard.cjs +32 -0
  2. package/dist/cjs/clipboard/clipboard.js +27 -0
  3. package/dist/cjs/clipboard/clipboard.js.map +6 -0
  4. package/dist/cjs/clipboard/clipboard.native.js +41 -0
  5. package/dist/cjs/clipboard/clipboard.native.js.map +6 -0
  6. package/dist/cjs/clipboard/index.cjs +18 -0
  7. package/dist/cjs/clipboard/index.js +15 -0
  8. package/dist/cjs/clipboard/index.js.map +6 -0
  9. package/dist/cjs/clipboard/index.native.js +20 -0
  10. package/dist/cjs/clipboard/index.native.js.map +6 -0
  11. package/dist/cjs/color/extractOpacityFromColor.cjs +34 -0
  12. package/dist/cjs/color/extractOpacityFromColor.js +29 -0
  13. package/dist/cjs/color/extractOpacityFromColor.js.map +6 -0
  14. package/dist/cjs/color/extractOpacityFromColor.native.js +38 -0
  15. package/dist/cjs/color/extractOpacityFromColor.native.js.map +6 -0
  16. package/dist/cjs/color/generateColors.cjs +77 -0
  17. package/dist/cjs/color/generateColors.js +64 -0
  18. package/dist/cjs/color/generateColors.js.map +6 -0
  19. package/dist/cjs/color/generateColors.native.js +88 -0
  20. package/dist/cjs/color/generateColors.native.js.map +6 -0
  21. package/dist/cjs/color/index.cjs +21 -0
  22. package/dist/cjs/color/index.js +18 -0
  23. package/dist/cjs/color/index.js.map +6 -0
  24. package/dist/cjs/color/index.native.js +26 -0
  25. package/dist/cjs/color/index.native.js.map +6 -0
  26. package/dist/cjs/color/lum.cjs +75 -0
  27. package/dist/cjs/color/lum.js +61 -0
  28. package/dist/cjs/color/lum.js.map +6 -0
  29. package/dist/cjs/color/lum.native.js +70 -0
  30. package/dist/cjs/color/lum.native.js.map +6 -0
  31. package/dist/cjs/color/toHex.cjs +32 -0
  32. package/dist/cjs/color/toHex.js +27 -0
  33. package/dist/cjs/color/toHex.js.map +6 -0
  34. package/dist/cjs/color/toHex.native.js +33 -0
  35. package/dist/cjs/color/toHex.native.js.map +6 -0
  36. package/dist/cjs/index.cjs +4 -0
  37. package/dist/cjs/index.js +4 -0
  38. package/dist/cjs/index.js.map +1 -1
  39. package/dist/cjs/index.native.js +8 -0
  40. package/dist/cjs/index.native.js.map +1 -1
  41. package/dist/cjs/number/formatNumber.cjs +9 -1
  42. package/dist/cjs/number/formatNumber.js +9 -1
  43. package/dist/cjs/number/formatNumber.js.map +1 -1
  44. package/dist/cjs/number/formatNumber.native.js +12 -2
  45. package/dist/cjs/number/formatNumber.native.js.map +1 -1
  46. package/dist/cjs/time/formatDistanceToNow.cjs +32 -0
  47. package/dist/cjs/time/formatDistanceToNow.js +24 -0
  48. package/dist/cjs/time/formatDistanceToNow.js.map +6 -0
  49. package/dist/cjs/time/formatDistanceToNow.native.js +29 -0
  50. package/dist/cjs/time/formatDistanceToNow.native.js.map +6 -0
  51. package/dist/cjs/time/useTimer.cjs +55 -0
  52. package/dist/cjs/time/useTimer.js +51 -0
  53. package/dist/cjs/time/useTimer.js.map +6 -0
  54. package/dist/cjs/time/useTimer.native.js +67 -0
  55. package/dist/cjs/time/useTimer.native.js.map +6 -0
  56. package/dist/esm/clipboard/clipboard.js +11 -0
  57. package/dist/esm/clipboard/clipboard.js.map +6 -0
  58. package/dist/esm/clipboard/clipboard.mjs +9 -0
  59. package/dist/esm/clipboard/clipboard.mjs.map +1 -0
  60. package/dist/esm/clipboard/clipboard.native.js +11 -0
  61. package/dist/esm/clipboard/clipboard.native.js.map +1 -0
  62. package/dist/esm/clipboard/index.js +2 -0
  63. package/dist/esm/clipboard/index.js.map +6 -0
  64. package/dist/esm/clipboard/index.mjs +2 -0
  65. package/dist/esm/clipboard/index.mjs.map +1 -0
  66. package/dist/esm/clipboard/index.native.js +2 -0
  67. package/dist/esm/clipboard/index.native.js.map +1 -0
  68. package/dist/esm/color/extractOpacityFromColor.js +13 -0
  69. package/dist/esm/color/extractOpacityFromColor.js.map +6 -0
  70. package/dist/esm/color/extractOpacityFromColor.mjs +11 -0
  71. package/dist/esm/color/extractOpacityFromColor.mjs.map +1 -0
  72. package/dist/esm/color/extractOpacityFromColor.native.js +15 -0
  73. package/dist/esm/color/extractOpacityFromColor.native.js.map +1 -0
  74. package/dist/esm/color/generateColors.js +48 -0
  75. package/dist/esm/color/generateColors.js.map +6 -0
  76. package/dist/esm/color/generateColors.mjs +54 -0
  77. package/dist/esm/color/generateColors.mjs.map +1 -0
  78. package/dist/esm/color/generateColors.native.js +52 -0
  79. package/dist/esm/color/generateColors.native.js.map +1 -0
  80. package/dist/esm/color/index.js +5 -0
  81. package/dist/esm/color/index.js.map +6 -0
  82. package/dist/esm/color/index.mjs +5 -0
  83. package/dist/esm/color/index.mjs.map +1 -0
  84. package/dist/esm/color/index.native.js +5 -0
  85. package/dist/esm/color/index.native.js.map +1 -0
  86. package/dist/esm/color/lum.js +45 -0
  87. package/dist/esm/color/lum.js.map +6 -0
  88. package/dist/esm/color/lum.mjs +52 -0
  89. package/dist/esm/color/lum.mjs.map +1 -0
  90. package/dist/esm/color/lum.native.js +57 -0
  91. package/dist/esm/color/lum.native.js.map +1 -0
  92. package/dist/esm/color/toHex.js +11 -0
  93. package/dist/esm/color/toHex.js.map +6 -0
  94. package/dist/esm/color/toHex.mjs +8 -0
  95. package/dist/esm/color/toHex.mjs.map +1 -0
  96. package/dist/esm/color/toHex.native.js +8 -0
  97. package/dist/esm/color/toHex.native.js.map +1 -0
  98. package/dist/esm/index.js +4 -0
  99. package/dist/esm/index.js.map +1 -1
  100. package/dist/esm/index.mjs +4 -0
  101. package/dist/esm/index.mjs.map +1 -1
  102. package/dist/esm/index.native.js +4 -0
  103. package/dist/esm/index.native.js.map +1 -1
  104. package/dist/esm/number/formatNumber.js +9 -1
  105. package/dist/esm/number/formatNumber.js.map +1 -1
  106. package/dist/esm/number/formatNumber.mjs +7 -1
  107. package/dist/esm/number/formatNumber.mjs.map +1 -1
  108. package/dist/esm/number/formatNumber.native.js +7 -1
  109. package/dist/esm/number/formatNumber.native.js.map +1 -1
  110. package/dist/esm/time/formatDistanceToNow.js +8 -0
  111. package/dist/esm/time/formatDistanceToNow.js.map +6 -0
  112. package/dist/esm/time/formatDistanceToNow.mjs +9 -0
  113. package/dist/esm/time/formatDistanceToNow.mjs.map +1 -0
  114. package/dist/esm/time/formatDistanceToNow.native.js +10 -0
  115. package/dist/esm/time/formatDistanceToNow.native.js.map +1 -0
  116. package/dist/esm/time/useTimer.js +35 -0
  117. package/dist/esm/time/useTimer.js.map +6 -0
  118. package/dist/esm/time/useTimer.mjs +32 -0
  119. package/dist/esm/time/useTimer.mjs.map +1 -0
  120. package/dist/esm/time/useTimer.native.js +40 -0
  121. package/dist/esm/time/useTimer.native.js.map +1 -0
  122. package/package.json +2 -1
  123. package/src/clipboard/clipboard.native.ts +10 -0
  124. package/src/clipboard/clipboard.ts +8 -0
  125. package/src/clipboard/index.ts +1 -0
  126. package/src/color/extractOpacityFromColor.ts +18 -0
  127. package/src/color/generateColors.ts +72 -0
  128. package/src/color/index.ts +4 -0
  129. package/src/color/lum.ts +78 -0
  130. package/src/color/toHex.ts +10 -0
  131. package/src/index.ts +8 -0
  132. package/src/number/formatNumber.ts +15 -0
  133. package/src/time/formatDistanceToNow.ts +17 -0
  134. package/src/time/useTimer.ts +80 -0
  135. package/types/clipboard/clipboard.d.ts +3 -0
  136. package/types/clipboard/clipboard.d.ts.map +11 -0
  137. package/types/clipboard/clipboard.native.d.ts +3 -0
  138. package/types/clipboard/clipboard.native.d.ts.map +11 -0
  139. package/types/clipboard/index.d.ts +3 -0
  140. package/types/clipboard/index.d.ts.map +11 -0
  141. package/types/color/extractOpacityFromColor.d.ts +3 -0
  142. package/types/color/extractOpacityFromColor.d.ts.map +13 -0
  143. package/types/color/generateColors.d.ts +11 -0
  144. package/types/color/generateColors.d.ts.map +11 -0
  145. package/types/color/index.d.ts +6 -0
  146. package/types/color/index.d.ts.map +11 -0
  147. package/types/color/lum.d.ts +3 -0
  148. package/types/color/lum.d.ts.map +13 -0
  149. package/types/color/toHex.d.ts +6 -0
  150. package/types/color/toHex.d.ts.map +13 -0
  151. package/types/index.d.ts +6 -0
  152. package/types/index.d.ts.map +2 -2
  153. package/types/number/formatNumber.d.ts +2 -0
  154. package/types/number/formatNumber.d.ts.map +5 -3
  155. package/types/time/formatDistanceToNow.d.ts +3 -0
  156. package/types/time/formatDistanceToNow.d.ts.map +13 -0
  157. package/types/time/useTimer.d.ts +11 -0
  158. package/types/time/useTimer.d.ts.map +14 -0
@@ -0,0 +1,72 @@
1
+ import { toHex } from './toHex'
2
+
3
+ type ColorGenOptions = {
4
+ numColors?: number
5
+ minSaturation?: number
6
+ maxSaturation?: number
7
+ minLightness?: number
8
+ maxLightness?: number
9
+ }
10
+
11
+ // convert HSL to hex
12
+ function hslToHex(h: number, s: number, l: number): string {
13
+ // normalize values
14
+ h = h / 360
15
+ s = s / 100
16
+ l = l / 100
17
+
18
+ const a = s * Math.min(l, 1 - l)
19
+ const f = (n: number) => {
20
+ const k = (n + h * 12) % 12
21
+ const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1)
22
+ return Math.round(255 * color)
23
+ }
24
+
25
+ const r = f(0)
26
+ const g = f(8)
27
+ const b = f(4)
28
+
29
+ return toHex((r << 16) | (g << 8) | b)
30
+ }
31
+
32
+ export const generateColors = ({
33
+ numColors = 32,
34
+ minSaturation = 45,
35
+ maxSaturation = 85,
36
+ minLightness = 45,
37
+ maxLightness = 65,
38
+ }: ColorGenOptions = {}): string[] => {
39
+ const colors: string[] = []
40
+
41
+ // Define hue ranges for color groups
42
+ const hueRanges = [
43
+ [0, 30], // reds
44
+ [30, 60], // oranges
45
+ [60, 90], // yellows
46
+ [90, 150], // greens
47
+ [150, 180], // teals
48
+ [180, 240], // blues
49
+ [240, 270], // purples
50
+ [270, 330], // magentas
51
+ [330, 360], // pink-reds
52
+ ]
53
+
54
+ // Calculate colors per group
55
+ const colorsPerGroup = Math.ceil(numColors / hueRanges.length)
56
+
57
+ hueRanges.forEach(([start, end]) => {
58
+ const hueStep = (end! - start!) / colorsPerGroup
59
+
60
+ for (let i = 0; i < colorsPerGroup; i++) {
61
+ if (colors.length >= numColors) break
62
+
63
+ const hue = start! + hueStep * i
64
+ const saturation = minSaturation + Math.random() * (maxSaturation - minSaturation)
65
+ const lightness = minLightness + Math.random() * (maxLightness - minLightness)
66
+
67
+ colors.push(hslToHex(hue, saturation, lightness))
68
+ }
69
+ })
70
+
71
+ return colors
72
+ }
@@ -0,0 +1,4 @@
1
+ export * from './toHex'
2
+ export * from './generateColors'
3
+ export * from './lum'
4
+ export * from './extractOpacityFromColor'
@@ -0,0 +1,78 @@
1
+ // Helper to ensure color string is valid (simple fallback implementation)
2
+ const validColor = (color: string) => color
3
+
4
+ export function lum(color: string, luminance = 0.5): string {
5
+ // handle hsl/hsla
6
+ if (color.startsWith('hsl')) {
7
+ const match = color.match(/hsla?\((\d+),\s*(\d+)%,\s*(\d+)%(?:,\s*([\d.]+))?\)/)
8
+ if (match) {
9
+ const [, h, s, , a] = match
10
+ const newL = Math.round(luminance * 100)
11
+ if (a) {
12
+ return validColor(`hsla(${h}, ${s}%, ${newL}%, ${a})`)
13
+ }
14
+ return validColor(`hsl(${h}, ${s}%, ${newL}%)`)
15
+ }
16
+ }
17
+
18
+ // handle hex - convert to hsl and adjust
19
+ if (color.startsWith('#')) {
20
+ let hex = color.slice(1)
21
+
22
+ // expand shorthand hex
23
+ if (hex.length === 3) {
24
+ hex = hex
25
+ .split('')
26
+ .map((c) => c + c)
27
+ .join('')
28
+ }
29
+
30
+ // convert hex to rgb
31
+ const r = parseInt(hex.slice(0, 2), 16) / 255
32
+ const g = parseInt(hex.slice(2, 4), 16) / 255
33
+ const b = parseInt(hex.slice(4, 6), 16) / 255
34
+
35
+ // convert rgb to hsl
36
+ const max = Math.max(r, g, b)
37
+ const min = Math.min(r, g, b)
38
+ const l = (max + min) / 2
39
+
40
+ if (max === min) {
41
+ // achromatic
42
+ const newL = Math.round(luminance * 100)
43
+ return validColor(`hsl(0, 0%, ${newL}%)`)
44
+ }
45
+
46
+ const d = max - min
47
+ const s = l > 0.5 ? d / (2 - max - min) : d / (max + min)
48
+
49
+ let h: number
50
+ switch (max) {
51
+ case r:
52
+ h = ((g - b) / d + (g < b ? 6 : 0)) / 6
53
+ break
54
+ case g:
55
+ h = ((b - r) / d + 2) / 6
56
+ break
57
+ case b:
58
+ h = ((r - g) / d + 4) / 6
59
+ break
60
+ default:
61
+ h = 0
62
+ }
63
+
64
+ const newH = Math.round(h * 360)
65
+ const newS = Math.round(s * 100)
66
+ const newL = Math.round(luminance * 100)
67
+
68
+ // preserve alpha if present
69
+ if (hex.length === 8) {
70
+ const alpha = parseInt(hex.slice(6, 8), 16) / 255
71
+ return validColor(`hsla(${newH}, ${newS}%, ${newL}%, ${alpha.toFixed(2)})`)
72
+ }
73
+
74
+ return validColor(`hsl(${newH}, ${newS}%, ${newL}%)`)
75
+ }
76
+
77
+ return validColor(color)
78
+ }
@@ -0,0 +1,10 @@
1
+ // simple helper to ensure we always get a valid 6-digit hex color from a number
2
+ export function toHex(value: number): string {
3
+ // ensure we get a 6-digit hex string with leading zeros if needed
4
+ return '#' + value.toString(16).padStart(6, '0')
5
+ }
6
+
7
+ // generate a random hex color
8
+ export function randomHex(): string {
9
+ return toHex(Math.floor(Math.random() * 0xffffff))
10
+ }
package/src/index.ts CHANGED
@@ -23,6 +23,12 @@ export * from './async/useLazyValue'
23
23
 
24
24
  // browser
25
25
  export * from './browser/clearIndexedDB'
26
+
27
+ // clipboard
28
+ export * from './clipboard'
29
+
30
+ // color
31
+ export * from './color'
26
32
  export * from './browser/isActiveElementFormField'
27
33
  export * from './browser/openPopup'
28
34
 
@@ -92,7 +98,9 @@ export * from './string/truncateList'
92
98
  // time
93
99
  export * from './time/formatDate'
94
100
  export * from './time/formatDateRelative'
101
+ export * from './time/formatDistanceToNow'
95
102
  export * from './time/time'
103
+ export * from './time/useTimer'
96
104
 
97
105
  // types
98
106
  export type * from './types/NullToOptional'
@@ -38,3 +38,18 @@ export function formatCount(value: number): string {
38
38
  forceCompact: value >= 1000,
39
39
  })
40
40
  }
41
+
42
+ export function formatReactionCount(value: number | string): string {
43
+ if (typeof value === 'string') return value
44
+ if (value >= 1000000) {
45
+ return `${(value / 1000000).toFixed(1)}M`
46
+ }
47
+ if (value >= 1000) {
48
+ return `${(value / 1000).toFixed(1)}K`
49
+ }
50
+ return value.toString()
51
+ }
52
+
53
+ export function formatPhoneNumber(value: string): string {
54
+ return value
55
+ }
@@ -0,0 +1,17 @@
1
+ export function formatDistanceToNow(timestamp: number): string {
2
+ const now = Date.now()
3
+ const diff = now - timestamp
4
+
5
+ const minutes = Math.floor(diff / 60000)
6
+ const hours = Math.floor(diff / 3600000)
7
+ const days = Math.floor(diff / 86400000)
8
+
9
+ if (minutes < 1) return 'just now'
10
+ if (minutes < 60) return `${minutes}m`
11
+ if (hours < 24) return `${hours}h`
12
+ if (days < 7) return `${days}d`
13
+ if (days < 30) return `${Math.floor(days / 7)}w`
14
+ if (days < 365) return `${Math.floor(days / 30)}mo`
15
+
16
+ return `${Math.floor(days / 365)}y`
17
+ }
@@ -0,0 +1,80 @@
1
+ import { useState, useEffect, useRef, useMemo, useCallback } from 'react'
2
+
3
+ type UseTimerReturn = {
4
+ count: number
5
+ start: (time: number, start: boolean) => void
6
+ pause: () => void
7
+ resume: () => void
8
+ clear: () => void
9
+ }
10
+
11
+ export const useTimer = (): UseTimerReturn => {
12
+ const [timerCount, setTimer] = useState<number>(30)
13
+ const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)
14
+
15
+ const clearTimer = useCallback(() => {
16
+ if (intervalRef.current) {
17
+ clearInterval(intervalRef.current)
18
+ intervalRef.current = null
19
+ }
20
+ }, [])
21
+
22
+ const resetTimer = useCallback(
23
+ (time: number, start: boolean) => {
24
+ setTimer(time)
25
+ clearTimer()
26
+ if (start) {
27
+ intervalRef.current = setInterval(() => {
28
+ setTimer((lastTimerCount) => {
29
+ if (lastTimerCount <= 1) {
30
+ clearInterval(intervalRef.current!)
31
+ intervalRef.current = null
32
+ return 0
33
+ }
34
+ return lastTimerCount - 1
35
+ })
36
+ }, 1000)
37
+ }
38
+ },
39
+ [clearTimer]
40
+ )
41
+
42
+ const pauseTimer = useCallback(() => {
43
+ clearTimer()
44
+ }, [clearTimer])
45
+
46
+ const resumeTimer = useCallback(() => {
47
+ if (!intervalRef.current) {
48
+ intervalRef.current = setInterval(() => {
49
+ setTimer((lastTimerCount) => {
50
+ if (lastTimerCount <= 1) {
51
+ clearInterval(intervalRef.current!)
52
+ intervalRef.current = null
53
+ return 0
54
+ }
55
+ return lastTimerCount - 1
56
+ })
57
+ }, 1000)
58
+ }
59
+ }, [])
60
+
61
+ useEffect(() => {
62
+ return () => {
63
+ if (intervalRef.current) {
64
+ clearInterval(intervalRef.current)
65
+ intervalRef.current = null
66
+ }
67
+ }
68
+ }, [])
69
+
70
+ return useMemo(
71
+ () => ({
72
+ count: timerCount,
73
+ start: resetTimer,
74
+ pause: pauseTimer,
75
+ resume: resumeTimer,
76
+ clear: clearTimer,
77
+ }),
78
+ [timerCount, clearTimer, pauseTimer, resetTimer, resumeTimer]
79
+ )
80
+ }
@@ -0,0 +1,3 @@
1
+ export declare const getClipboardText: () => Promise<string | null>;
2
+
3
+ //# sourceMappingURL=clipboard.d.ts.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "mappings": "AAAA,OAAO,cAAM,wBAA6B",
3
+ "names": [],
4
+ "sources": [
5
+ "src/clipboard/clipboard.ts"
6
+ ],
7
+ "sourcesContent": [
8
+ "export const getClipboardText = async (): Promise<string | null> => {\n try {\n const text = await navigator.clipboard.readText()\n return text\n } catch {\n return null\n }\n}\n"
9
+ ],
10
+ "version": 3
11
+ }
@@ -0,0 +1,3 @@
1
+ export declare const getClipboardText: () => Promise<string | null>;
2
+
3
+ //# sourceMappingURL=clipboard.native.d.ts.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "mappings": "AAEA,OAAO,cAAM,wBAA6B",
3
+ "names": [],
4
+ "sources": [
5
+ "src/clipboard/clipboard.native.ts"
6
+ ],
7
+ "sourcesContent": [
8
+ "import * as Clipboard from 'expo-clipboard'\n\nexport const getClipboardText = async (): Promise<string | null> => {\n try {\n const text = await Clipboard.getStringAsync()\n return text\n } catch {\n return null\n }\n}\n"
9
+ ],
10
+ "version": 3
11
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./clipboard";
2
+
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "mappings": "AAAA,cAAc",
3
+ "names": [],
4
+ "sources": [
5
+ "src/clipboard/index.ts"
6
+ ],
7
+ "sourcesContent": [
8
+ "export * from './clipboard'\n"
9
+ ],
10
+ "version": 3
11
+ }
@@ -0,0 +1,3 @@
1
+ export declare function extractOpacityFromColor(color: string): number;
2
+
3
+ //# sourceMappingURL=extractOpacityFromColor.d.ts.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "mappings": "AAAA,OAAO,iBAAS,wBAAwBA",
3
+ "names": [
4
+ "color: string"
5
+ ],
6
+ "sources": [
7
+ "src/color/extractOpacityFromColor.ts"
8
+ ],
9
+ "sourcesContent": [
10
+ "export function extractOpacityFromColor(color: string): number {\n if (color === 'transparent') return 0\n\n // Match hex codes like #RRGGBBAA or #RRGGBB\n const hexMatch = color.match(/^#([0-9a-fA-F]{6})([0-9a-fA-F]{2})?$/)\n if (hexMatch) {\n const [, _rgb, alphaHex] = hexMatch\n if (alphaHex) {\n const alpha = parseInt(alphaHex, 16)\n return alpha / 255\n }\n return 1 // No alpha specified → fully opaque\n }\n\n // Could expand this to support rgba(), hsl(), etc. if needed\n console.warn(`Unsupported color format: ${color}`)\n return 1\n}\n"
11
+ ],
12
+ "version": 3
13
+ }
@@ -0,0 +1,11 @@
1
+ type ColorGenOptions = {
2
+ numColors?: number;
3
+ minSaturation?: number;
4
+ maxSaturation?: number;
5
+ minLightness?: number;
6
+ maxLightness?: number;
7
+ };
8
+ export declare const generateColors: ({ numColors, minSaturation, maxSaturation, minLightness, maxLightness }?: ColorGenOptions) => string[];
9
+ export {};
10
+
11
+ //# sourceMappingURL=generateColors.d.ts.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "mappings": "KAEK,kBAAkB;CACrB;CACA;CACA;CACA;CACA;AACD;AAuBD,OAAO,cAAM,iBAAkB,EAC7B,WACA,eACA,eACA,cACA,cACgB,GAAf",
3
+ "names": [],
4
+ "sources": [
5
+ "src/color/generateColors.ts"
6
+ ],
7
+ "sourcesContent": [
8
+ "import { toHex } from './toHex'\n\ntype ColorGenOptions = {\n numColors?: number\n minSaturation?: number\n maxSaturation?: number\n minLightness?: number\n maxLightness?: number\n}\n\n// convert HSL to hex\nfunction hslToHex(h: number, s: number, l: number): string {\n // normalize values\n h = h / 360\n s = s / 100\n l = l / 100\n\n const a = s * Math.min(l, 1 - l)\n const f = (n: number) => {\n const k = (n + h * 12) % 12\n const color = l - a * Math.max(Math.min(k - 3, 9 - k, 1), -1)\n return Math.round(255 * color)\n }\n\n const r = f(0)\n const g = f(8)\n const b = f(4)\n\n return toHex((r << 16) | (g << 8) | b)\n}\n\nexport const generateColors = ({\n numColors = 32,\n minSaturation = 45,\n maxSaturation = 85,\n minLightness = 45,\n maxLightness = 65,\n}: ColorGenOptions = {}): string[] => {\n const colors: string[] = []\n\n // Define hue ranges for color groups\n const hueRanges = [\n [0, 30], // reds\n [30, 60], // oranges\n [60, 90], // yellows\n [90, 150], // greens\n [150, 180], // teals\n [180, 240], // blues\n [240, 270], // purples\n [270, 330], // magentas\n [330, 360], // pink-reds\n ]\n\n // Calculate colors per group\n const colorsPerGroup = Math.ceil(numColors / hueRanges.length)\n\n hueRanges.forEach(([start, end]) => {\n const hueStep = (end! - start!) / colorsPerGroup\n\n for (let i = 0; i < colorsPerGroup; i++) {\n if (colors.length >= numColors) break\n\n const hue = start! + hueStep * i\n const saturation = minSaturation + Math.random() * (maxSaturation - minSaturation)\n const lightness = minLightness + Math.random() * (maxLightness - minLightness)\n\n colors.push(hslToHex(hue, saturation, lightness))\n }\n })\n\n return colors\n}\n"
9
+ ],
10
+ "version": 3
11
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./toHex";
2
+ export * from "./generateColors";
3
+ export * from "./lum";
4
+ export * from "./extractOpacityFromColor";
5
+
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,11 @@
1
+ {
2
+ "mappings": "AAAA,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc",
3
+ "names": [],
4
+ "sources": [
5
+ "src/color/index.ts"
6
+ ],
7
+ "sourcesContent": [
8
+ "export * from './toHex'\nexport * from './generateColors'\nexport * from './lum'\nexport * from './extractOpacityFromColor'\n"
9
+ ],
10
+ "version": 3
11
+ }
@@ -0,0 +1,3 @@
1
+ export declare function lum(color: string, luminance?: number): string;
2
+
3
+ //# sourceMappingURL=lum.d.ts.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "mappings": "AAGA,OAAO,iBAAS,IAAIA,eAAe",
3
+ "names": [
4
+ "color: string"
5
+ ],
6
+ "sources": [
7
+ "src/color/lum.ts"
8
+ ],
9
+ "sourcesContent": [
10
+ "// Helper to ensure color string is valid (simple fallback implementation)\nconst validColor = (color: string) => color\n\nexport function lum(color: string, luminance = 0.5): string {\n // handle hsl/hsla\n if (color.startsWith('hsl')) {\n const match = color.match(/hsla?\\((\\d+),\\s*(\\d+)%,\\s*(\\d+)%(?:,\\s*([\\d.]+))?\\)/)\n if (match) {\n const [, h, s, , a] = match\n const newL = Math.round(luminance * 100)\n if (a) {\n return validColor(`hsla(${h}, ${s}%, ${newL}%, ${a})`)\n }\n return validColor(`hsl(${h}, ${s}%, ${newL}%)`)\n }\n }\n\n // handle hex - convert to hsl and adjust\n if (color.startsWith('#')) {\n let hex = color.slice(1)\n\n // expand shorthand hex\n if (hex.length === 3) {\n hex = hex\n .split('')\n .map((c) => c + c)\n .join('')\n }\n\n // convert hex to rgb\n const r = parseInt(hex.slice(0, 2), 16) / 255\n const g = parseInt(hex.slice(2, 4), 16) / 255\n const b = parseInt(hex.slice(4, 6), 16) / 255\n\n // convert rgb to hsl\n const max = Math.max(r, g, b)\n const min = Math.min(r, g, b)\n const l = (max + min) / 2\n\n if (max === min) {\n // achromatic\n const newL = Math.round(luminance * 100)\n return validColor(`hsl(0, 0%, ${newL}%)`)\n }\n\n const d = max - min\n const s = l > 0.5 ? d / (2 - max - min) : d / (max + min)\n\n let h: number\n switch (max) {\n case r:\n h = ((g - b) / d + (g < b ? 6 : 0)) / 6\n break\n case g:\n h = ((b - r) / d + 2) / 6\n break\n case b:\n h = ((r - g) / d + 4) / 6\n break\n default:\n h = 0\n }\n\n const newH = Math.round(h * 360)\n const newS = Math.round(s * 100)\n const newL = Math.round(luminance * 100)\n\n // preserve alpha if present\n if (hex.length === 8) {\n const alpha = parseInt(hex.slice(6, 8), 16) / 255\n return validColor(`hsla(${newH}, ${newS}%, ${newL}%, ${alpha.toFixed(2)})`)\n }\n\n return validColor(`hsl(${newH}, ${newS}%, ${newL}%)`)\n }\n\n return validColor(color)\n}\n"
11
+ ],
12
+ "version": 3
13
+ }
@@ -0,0 +1,6 @@
1
+ // simple helper to ensure we always get a valid 6-digit hex color from a number
2
+ export declare function toHex(value: number): string;
3
+ // generate a random hex color
4
+ export declare function randomHex(): string;
5
+
6
+ //# sourceMappingURL=toHex.d.ts.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "mappings": ";AACA,OAAO,iBAAS,MAAMA;;AAMtB,OAAO,iBAAS",
3
+ "names": [
4
+ "value: number"
5
+ ],
6
+ "sources": [
7
+ "src/color/toHex.ts"
8
+ ],
9
+ "sourcesContent": [
10
+ "// simple helper to ensure we always get a valid 6-digit hex color from a number\nexport function toHex(value: number): string {\n // ensure we get a 6-digit hex string with leading zeros if needed\n return '#' + value.toString(16).padStart(6, '0')\n}\n\n// generate a random hex color\nexport function randomHex(): string {\n return toHex(Math.floor(Math.random() * 0xffffff))\n}\n"
11
+ ],
12
+ "version": 3
13
+ }
package/types/index.d.ts CHANGED
@@ -19,6 +19,10 @@ export * from "./async/useLazyMount";
19
19
  export * from "./async/useLazyValue";
20
20
  // browser
21
21
  export * from "./browser/clearIndexedDB";
22
+ // clipboard
23
+ export * from "./clipboard";
24
+ // color
25
+ export * from "./color";
22
26
  export * from "./browser/isActiveElementFormField";
23
27
  export * from "./browser/openPopup";
24
28
  // debug
@@ -75,7 +79,9 @@ export * from "./string/truncateList";
75
79
  // time
76
80
  export * from "./time/formatDate";
77
81
  export * from "./time/formatDateRelative";
82
+ export * from "./time/formatDistanceToNow";
78
83
  export * from "./time/time";
84
+ export * from "./time/useTimer";
79
85
  // types
80
86
  export type * from "./types/NullToOptional";
81
87
  export type * from "./types/object";
@@ -1,11 +1,11 @@
1
1
  {
2
- "mappings": "AAAA,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;;AAGd,cAAc;;;AAKd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;;AAGd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,SAAS,uBAAuB;AAChC,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,cAAc,qBAAqB;;AAGnC,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,mBAAmB;AACnB,mBAAmB;AACnB,mBAAmB;AACnB,mBAAmB;AACnB,mBAAmB;;AAGnB,cAAc;AACd,cAAc",
2
+ "mappings": "AAAA,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;;AAGd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;;AAGd,cAAc;;;AAKd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;;AAGd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,SAAS,uBAAuB;AAChC,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,cAAc,qBAAqB;;AAGnC,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;AACd,cAAc;;AAGd,mBAAmB;AACnB,mBAAmB;AACnB,mBAAmB;AACnB,mBAAmB;AACnB,mBAAmB;;AAGnB,cAAc;AACd,cAAc",
3
3
  "names": [],
4
4
  "sources": [
5
5
  "src/index.ts"
6
6
  ],
7
7
  "sourcesContent": [
8
- "export * from './constants'\nexport * from './emitter'\n\n// array\nexport * from './array/getRandomItem'\nexport * from './array/takeLast'\nexport * from './array/uniqBy'\n\n// assert\nexport * from './assert'\n\n// async\nexport * from './async/abortable'\nexport * from './async/asyncContext'\nexport * from './async/idle'\nexport * from './async/interval'\nexport * from './async/isAborted'\nexport * from './async/sleep'\nexport * from './async/useAsync'\nexport * from './async/useAsyncEffect'\nexport * from './async/useLazyMount'\nexport * from './async/useLazyValue'\n\n// browser\nexport * from './browser/clearIndexedDB'\nexport * from './browser/isActiveElementFormField'\nexport * from './browser/openPopup'\n\n// debug\nexport * from './debug/debugLog'\nexport * from './debug/debugUseState'\n\n// ensure\nexport * from './ensure/ensure'\nexport * from './ensure/ensureOne'\n\n// error\nexport * from './error/errors'\n\n// files\n\n// function\nexport * from './function/emptyFn'\nexport * from './function/identityFn'\nexport * from './function/throttle'\n\n// global\nexport * from './global/globalEffect'\nexport * from './global/globalValue'\n\n// number\nexport * from './number/formatNumber'\n\n// object\nexport * from './object/decorateObject'\nexport * from './object/isEqualDeep'\nexport { isEqualDeepLite } from './object/isEqualDeep'\nexport * from './object/isEqualIdentity'\nexport * from './object/isEqualJSON'\nexport * from './object/isEqualNever'\nexport * from './object/mapObject'\nexport * from './object/object'\nexport * from './object/objectUniqueKey'\n\n// react\nexport * from './react/createGlobalContext'\nexport * from './react/getCurrentComponentStack'\n\n// storage\nexport * from './storage/createStorage'\nexport * from './storage/driver'\nexport type { StorageDriver } from './storage/types'\n\n// server\nexport * from './server/ensureEnv'\nexport * from './server/getHeaders'\nexport * from './server/prettyPrintRequest'\nexport * from './server/prettyPrintResponse'\nexport * from './server/streamToString'\n\n// string\nexport * from './string/dedent'\nexport * from './string/ellipsis'\nexport * from './string/hash'\nexport * from './string/insertAtIndex'\nexport * from './string/pickLast'\nexport * from './string/pluralize'\nexport * from './string/randomId'\nexport * from './string/slugify'\nexport * from './string/truncateList'\n\n// time\nexport * from './time/formatDate'\nexport * from './time/formatDateRelative'\nexport * from './time/time'\n\n// types\nexport type * from './types/NullToOptional'\nexport type * from './types/object'\nexport type * from './types/react'\nexport type * from './types/timer'\nexport type * from './types/tuple'\n\n// url\nexport * from './url/urlSanitize'\nexport * from './url/urlValidate'\n"
8
+ "export * from './constants'\nexport * from './emitter'\n\n// array\nexport * from './array/getRandomItem'\nexport * from './array/takeLast'\nexport * from './array/uniqBy'\n\n// assert\nexport * from './assert'\n\n// async\nexport * from './async/abortable'\nexport * from './async/asyncContext'\nexport * from './async/idle'\nexport * from './async/interval'\nexport * from './async/isAborted'\nexport * from './async/sleep'\nexport * from './async/useAsync'\nexport * from './async/useAsyncEffect'\nexport * from './async/useLazyMount'\nexport * from './async/useLazyValue'\n\n// browser\nexport * from './browser/clearIndexedDB'\n\n// clipboard\nexport * from './clipboard'\n\n// color\nexport * from './color'\nexport * from './browser/isActiveElementFormField'\nexport * from './browser/openPopup'\n\n// debug\nexport * from './debug/debugLog'\nexport * from './debug/debugUseState'\n\n// ensure\nexport * from './ensure/ensure'\nexport * from './ensure/ensureOne'\n\n// error\nexport * from './error/errors'\n\n// files\n\n// function\nexport * from './function/emptyFn'\nexport * from './function/identityFn'\nexport * from './function/throttle'\n\n// global\nexport * from './global/globalEffect'\nexport * from './global/globalValue'\n\n// number\nexport * from './number/formatNumber'\n\n// object\nexport * from './object/decorateObject'\nexport * from './object/isEqualDeep'\nexport { isEqualDeepLite } from './object/isEqualDeep'\nexport * from './object/isEqualIdentity'\nexport * from './object/isEqualJSON'\nexport * from './object/isEqualNever'\nexport * from './object/mapObject'\nexport * from './object/object'\nexport * from './object/objectUniqueKey'\n\n// react\nexport * from './react/createGlobalContext'\nexport * from './react/getCurrentComponentStack'\n\n// storage\nexport * from './storage/createStorage'\nexport * from './storage/driver'\nexport type { StorageDriver } from './storage/types'\n\n// server\nexport * from './server/ensureEnv'\nexport * from './server/getHeaders'\nexport * from './server/prettyPrintRequest'\nexport * from './server/prettyPrintResponse'\nexport * from './server/streamToString'\n\n// string\nexport * from './string/dedent'\nexport * from './string/ellipsis'\nexport * from './string/hash'\nexport * from './string/insertAtIndex'\nexport * from './string/pickLast'\nexport * from './string/pluralize'\nexport * from './string/randomId'\nexport * from './string/slugify'\nexport * from './string/truncateList'\n\n// time\nexport * from './time/formatDate'\nexport * from './time/formatDateRelative'\nexport * from './time/formatDistanceToNow'\nexport * from './time/time'\nexport * from './time/useTimer'\n\n// types\nexport type * from './types/NullToOptional'\nexport type * from './types/object'\nexport type * from './types/react'\nexport type * from './types/timer'\nexport type * from './types/tuple'\n\n// url\nexport * from './url/urlSanitize'\nexport * from './url/urlValidate'\n"
9
9
  ],
10
10
  "version": 3
11
11
  }
@@ -7,5 +7,7 @@ export interface FormatNumberOptions {
7
7
  export declare function formatNumber(value: number, options?: FormatNumberOptions): string;
8
8
  export declare function abbreviateNumber(value: number): string;
9
9
  export declare function formatCount(value: number): string;
10
+ export declare function formatReactionCount(value: number | string): string;
11
+ export declare function formatPhoneNumber(value: string): string;
10
12
 
11
13
  //# sourceMappingURL=formatNumber.d.ts.map
@@ -1,14 +1,16 @@
1
1
  {
2
- "mappings": "AAAA,iBAAiB,oBAAoB;CACnC;CACA;CACA;CACA;AACD;AAED,OAAO,iBAAS,aAAaA,eAAeC,UAAS;AAuBrD,OAAO,iBAAS,iBAAiBD;AAIjC,OAAO,iBAAS,YAAYA",
2
+ "mappings": "AAAA,iBAAiB,oBAAoB;CACnC;CACA;CACA;CACA;AACD;AAED,OAAO,iBAAS,aAAaA,eAAeC,UAAS;AAuBrD,OAAO,iBAAS,iBAAiBD;AAIjC,OAAO,iBAAS,YAAYA;AAO5B,OAAO,iBAAS,oBAAoBE;AAWpC,OAAO,iBAAS,kBAAkBC",
3
3
  "names": [
4
4
  "value: number",
5
- "options: FormatNumberOptions"
5
+ "options: FormatNumberOptions",
6
+ "value: number | string",
7
+ "value: string"
6
8
  ],
7
9
  "sources": [
8
10
  "src/number/formatNumber.ts"
9
11
  ],
10
12
  "sourcesContent": [
11
- "export interface FormatNumberOptions {\n locale?: string\n maximumFractionDigits?: number\n minimumFractionDigits?: number\n forceCompact?: boolean\n}\n\nexport function formatNumber(value: number, options: FormatNumberOptions = {}): string {\n const {\n locale = 'en-US',\n maximumFractionDigits = 1,\n minimumFractionDigits = 0,\n forceCompact = false,\n } = options\n\n if (!forceCompact && Math.abs(value) < 1000) {\n return new Intl.NumberFormat(locale, {\n maximumFractionDigits,\n minimumFractionDigits,\n }).format(value)\n }\n\n return new Intl.NumberFormat(locale, {\n notation: 'compact',\n compactDisplay: 'short',\n maximumFractionDigits,\n minimumFractionDigits,\n }).format(value)\n}\n\nexport function abbreviateNumber(value: number): string {\n return formatNumber(value, { maximumFractionDigits: 1 })\n}\n\nexport function formatCount(value: number): string {\n return formatNumber(value, {\n maximumFractionDigits: 0,\n forceCompact: value >= 1000,\n })\n}\n"
13
+ "export interface FormatNumberOptions {\n locale?: string\n maximumFractionDigits?: number\n minimumFractionDigits?: number\n forceCompact?: boolean\n}\n\nexport function formatNumber(value: number, options: FormatNumberOptions = {}): string {\n const {\n locale = 'en-US',\n maximumFractionDigits = 1,\n minimumFractionDigits = 0,\n forceCompact = false,\n } = options\n\n if (!forceCompact && Math.abs(value) < 1000) {\n return new Intl.NumberFormat(locale, {\n maximumFractionDigits,\n minimumFractionDigits,\n }).format(value)\n }\n\n return new Intl.NumberFormat(locale, {\n notation: 'compact',\n compactDisplay: 'short',\n maximumFractionDigits,\n minimumFractionDigits,\n }).format(value)\n}\n\nexport function abbreviateNumber(value: number): string {\n return formatNumber(value, { maximumFractionDigits: 1 })\n}\n\nexport function formatCount(value: number): string {\n return formatNumber(value, {\n maximumFractionDigits: 0,\n forceCompact: value >= 1000,\n })\n}\n\nexport function formatReactionCount(value: number | string): string {\n if (typeof value === 'string') return value\n if (value >= 1000000) {\n return `${(value / 1000000).toFixed(1)}M`\n }\n if (value >= 1000) {\n return `${(value / 1000).toFixed(1)}K`\n }\n return value.toString()\n}\n\nexport function formatPhoneNumber(value: string): string {\n return value\n}\n"
12
14
  ],
13
15
  "version": 3
14
16
  }
@@ -0,0 +1,3 @@
1
+ export declare function formatDistanceToNow(timestamp: number): string;
2
+
3
+ //# sourceMappingURL=formatDistanceToNow.d.ts.map
@@ -0,0 +1,13 @@
1
+ {
2
+ "mappings": "AAAA,OAAO,iBAAS,oBAAoBA",
3
+ "names": [
4
+ "timestamp: number"
5
+ ],
6
+ "sources": [
7
+ "src/time/formatDistanceToNow.ts"
8
+ ],
9
+ "sourcesContent": [
10
+ "export function formatDistanceToNow(timestamp: number): string {\n const now = Date.now()\n const diff = now - timestamp\n\n const minutes = Math.floor(diff / 60000)\n const hours = Math.floor(diff / 3600000)\n const days = Math.floor(diff / 86400000)\n\n if (minutes < 1) return 'just now'\n if (minutes < 60) return `${minutes}m`\n if (hours < 24) return `${hours}h`\n if (days < 7) return `${days}d`\n if (days < 30) return `${Math.floor(days / 7)}w`\n if (days < 365) return `${Math.floor(days / 30)}mo`\n\n return `${Math.floor(days / 365)}y`\n}\n"
11
+ ],
12
+ "version": 3
13
+ }
@@ -0,0 +1,11 @@
1
+ type UseTimerReturn = {
2
+ count: number;
3
+ start: (time: number, start: boolean) => void;
4
+ pause: () => void;
5
+ resume: () => void;
6
+ clear: () => void;
7
+ };
8
+ export declare const useTimer: () => UseTimerReturn;
9
+ export {};
10
+
11
+ //# sourceMappingURL=useTimer.d.ts.map
@@ -0,0 +1,14 @@
1
+ {
2
+ "mappings": "KAEK,iBAAiB;CACpB;CACA,QAAQA,cAAcC;CACtB;CACA;CACA;AACD;AAED,OAAO,cAAM,gBAAe",
3
+ "names": [
4
+ "time: number",
5
+ "start: boolean"
6
+ ],
7
+ "sources": [
8
+ "src/time/useTimer.ts"
9
+ ],
10
+ "sourcesContent": [
11
+ "import { useState, useEffect, useRef, useMemo, useCallback } from 'react'\n\ntype UseTimerReturn = {\n count: number\n start: (time: number, start: boolean) => void\n pause: () => void\n resume: () => void\n clear: () => void\n}\n\nexport const useTimer = (): UseTimerReturn => {\n const [timerCount, setTimer] = useState<number>(30)\n const intervalRef = useRef<ReturnType<typeof setInterval> | null>(null)\n\n const clearTimer = useCallback(() => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current)\n intervalRef.current = null\n }\n }, [])\n\n const resetTimer = useCallback(\n (time: number, start: boolean) => {\n setTimer(time)\n clearTimer()\n if (start) {\n intervalRef.current = setInterval(() => {\n setTimer((lastTimerCount) => {\n if (lastTimerCount <= 1) {\n clearInterval(intervalRef.current!)\n intervalRef.current = null\n return 0\n }\n return lastTimerCount - 1\n })\n }, 1000)\n }\n },\n [clearTimer]\n )\n\n const pauseTimer = useCallback(() => {\n clearTimer()\n }, [clearTimer])\n\n const resumeTimer = useCallback(() => {\n if (!intervalRef.current) {\n intervalRef.current = setInterval(() => {\n setTimer((lastTimerCount) => {\n if (lastTimerCount <= 1) {\n clearInterval(intervalRef.current!)\n intervalRef.current = null\n return 0\n }\n return lastTimerCount - 1\n })\n }, 1000)\n }\n }, [])\n\n useEffect(() => {\n return () => {\n if (intervalRef.current) {\n clearInterval(intervalRef.current)\n intervalRef.current = null\n }\n }\n }, [])\n\n return useMemo(\n () => ({\n count: timerCount,\n start: resetTimer,\n pause: pauseTimer,\n resume: resumeTimer,\n clear: clearTimer,\n }),\n [timerCount, clearTimer, pauseTimer, resetTimer, resumeTimer]\n )\n}\n"
12
+ ],
13
+ "version": 3
14
+ }