@dilipod/ui 0.4.27 → 0.4.28

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.
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Formatting Utilities
3
+ *
4
+ * Shared text, currency, duration, and date formatting used across apps.
5
+ */
6
+ /**
7
+ * Convert a cent value to euros (numeric).
8
+ * e.g. 2900 → 29
9
+ */
10
+ export declare function formatCentsToEuros(cents: number): string;
11
+ /**
12
+ * Format a euro value as a display string.
13
+ * e.g. 299 → "€299" or 299.5 with decimals=2 → "€299.50"
14
+ */
15
+ export declare function formatEuros(euros: number, decimals?: number): string;
16
+ /**
17
+ * Format milliseconds as a human-readable duration.
18
+ * e.g. 1500 → "1.5s"
19
+ */
20
+ export declare function formatDuration(ms: number): string;
21
+ /**
22
+ * Format a date into a compact relative-time string.
23
+ * Returns "—" for null/undefined, "5m" for < 1 hour, "2h" for < 48 hours,
24
+ * or a relative distance like "3 days" for older dates.
25
+ */
26
+ export declare function formatRelativeTime(date: Date | string | null): string;
27
+ //# sourceMappingURL=formatting.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"formatting.d.ts","sourceRoot":"","sources":["../../src/lib/formatting.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAExD;AAED;;;GAGG;AACH,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAKpE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAEjD;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,IAAI,GAAG,MAAM,GAAG,IAAI,GAAG,MAAM,CAUrE"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@dilipod/ui",
3
- "version": "0.4.27",
3
+ "version": "0.4.28",
4
4
  "description": "Dilipod Design System - Shared UI components and styles",
5
5
  "author": "Dilipod <hello@dilipod.com>",
6
6
  "license": "MIT",
@@ -81,6 +81,7 @@
81
81
  "@xyflow/react": "^12.10.0",
82
82
  "class-variance-authority": "^0.7.1",
83
83
  "clsx": "^2.1.1",
84
+ "date-fns": "^4.1.0",
84
85
  "tailwind-merge": "^3.3.0",
85
86
  "tailwindcss-animate": "^1.0.7"
86
87
  },
@@ -0,0 +1,106 @@
1
+ 'use client'
2
+
3
+ import * as React from 'react'
4
+ import { cn } from '../lib/utils'
5
+
6
+ export interface ExpandableSectionProps {
7
+ /** Unique key for this section */
8
+ sectionKey: string
9
+ /** Section label text */
10
+ label: string
11
+ /** Optional icon rendered before the label */
12
+ icon?: React.ReactNode
13
+ /** Optional count badge after the label */
14
+ count?: number
15
+ /** Whether the section is currently expanded */
16
+ expanded: boolean
17
+ /** Toggle callback */
18
+ onToggle: (key: string) => void
19
+ /** Section content */
20
+ children: React.ReactNode
21
+ /** Additional class for the outer container */
22
+ className?: string
23
+ /** Additional class for the content area */
24
+ contentClassName?: string
25
+ /** Whether to render the section at all (default: true) */
26
+ show?: boolean
27
+ }
28
+
29
+ export function ExpandableSection({
30
+ sectionKey,
31
+ label,
32
+ icon,
33
+ count,
34
+ expanded,
35
+ onToggle,
36
+ children,
37
+ className,
38
+ contentClassName,
39
+ show = true,
40
+ }: ExpandableSectionProps) {
41
+ if (!show) return null
42
+
43
+ return (
44
+ <div className={className}>
45
+ <button
46
+ onClick={() => onToggle(sectionKey)}
47
+ className="flex items-center gap-1.5 text-xs font-medium text-muted-foreground hover:text-foreground transition-colors w-full text-left"
48
+ type="button"
49
+ >
50
+ <svg
51
+ width="12"
52
+ height="12"
53
+ viewBox="0 0 12 12"
54
+ fill="none"
55
+ className={cn(
56
+ 'shrink-0 transition-transform',
57
+ expanded ? 'rotate-0' : '-rotate-90'
58
+ )}
59
+ >
60
+ <path d="M2.5 4.5L6 8L9.5 4.5" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" />
61
+ </svg>
62
+ {icon}
63
+ <span>{label}</span>
64
+ {count !== undefined && (
65
+ <span className="text-muted-foreground/60">({count})</span>
66
+ )}
67
+ </button>
68
+ {expanded && (
69
+ <div className={cn('mt-2 pl-4', contentClassName)}>
70
+ {children}
71
+ </div>
72
+ )}
73
+ </div>
74
+ )
75
+ }
76
+
77
+ /**
78
+ * Hook to manage expandable section state.
79
+ * Returns [expandedSections, toggleSection] tuple.
80
+ */
81
+ export function useExpandedSections(initialExpanded: string[] = []) {
82
+ const [expanded, setExpanded] = React.useState<Set<string>>(
83
+ () => new Set(initialExpanded)
84
+ )
85
+
86
+ const toggle = React.useCallback((key: string) => {
87
+ setExpanded(prev => {
88
+ const next = new Set(prev)
89
+ if (next.has(key)) {
90
+ next.delete(key)
91
+ } else {
92
+ next.add(key)
93
+ }
94
+ return next
95
+ })
96
+ }, [])
97
+
98
+ const isExpanded = React.useCallback(
99
+ (key: string) => expanded.has(key),
100
+ [expanded]
101
+ )
102
+
103
+ return { expanded, toggle, isExpanded } as const
104
+ }
105
+
106
+ ExpandableSection.displayName = 'ExpandableSection'
@@ -0,0 +1,23 @@
1
+ 'use client'
2
+
3
+ import { useEffect } from 'react'
4
+
5
+ export function useServiceWorker() {
6
+ useEffect(() => {
7
+ if ('serviceWorker' in navigator) {
8
+ navigator.serviceWorker
9
+ .register('/sw.js')
10
+ .then((registration) => {
11
+ console.log('Service Worker registered:', registration.scope)
12
+
13
+ // Check for updates periodically
14
+ setInterval(() => {
15
+ registration.update()
16
+ }, 60 * 60 * 1000) // Check every hour
17
+ })
18
+ .catch((error) => {
19
+ console.error('Service Worker registration failed:', error)
20
+ })
21
+ }
22
+ }, [])
23
+ }
package/src/index.ts CHANGED
@@ -305,6 +305,16 @@ export type { WorkerSpecProps, WorkerSpecDocumentation, AnalysisSource, Analysis
305
305
  export { FlowchartDiagram } from './components/flowchart-diagram'
306
306
  export type { FlowchartDiagramProps } from './components/flowchart-diagram'
307
307
 
308
+ // Expandable Section
309
+ export { ExpandableSection, useExpandedSections } from './components/expandable-section'
310
+ export type { ExpandableSectionProps } from './components/expandable-section'
311
+
312
+ // Hooks
313
+ export { useServiceWorker } from './hooks/use-service-worker'
314
+
315
+ // Formatting Utilities
316
+ export { formatCentsToEuros, formatEuros, formatDuration, formatRelativeTime } from './lib/formatting'
317
+
308
318
  // Utilities
309
319
  export { cn } from './lib/utils'
310
320
 
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Formatting Utilities
3
+ *
4
+ * Shared text, currency, duration, and date formatting used across apps.
5
+ */
6
+
7
+ import { differenceInHours, differenceInMinutes, formatDistanceToNow } from 'date-fns'
8
+
9
+ /**
10
+ * Convert a cent value to euros (numeric).
11
+ * e.g. 2900 → 29
12
+ */
13
+ export function formatCentsToEuros(cents: number): string {
14
+ return `€${(cents / 100).toLocaleString()}`
15
+ }
16
+
17
+ /**
18
+ * Format a euro value as a display string.
19
+ * e.g. 299 → "€299" or 299.5 with decimals=2 → "€299.50"
20
+ */
21
+ export function formatEuros(euros: number, decimals?: number): string {
22
+ if (decimals !== undefined) {
23
+ return `€${euros.toFixed(decimals)}`
24
+ }
25
+ return `€${euros.toLocaleString()}`
26
+ }
27
+
28
+ /**
29
+ * Format milliseconds as a human-readable duration.
30
+ * e.g. 1500 → "1.5s"
31
+ */
32
+ export function formatDuration(ms: number): string {
33
+ return `${(ms / 1000).toFixed(1)}s`
34
+ }
35
+
36
+ /**
37
+ * Format a date into a compact relative-time string.
38
+ * Returns "—" for null/undefined, "5m" for < 1 hour, "2h" for < 48 hours,
39
+ * or a relative distance like "3 days" for older dates.
40
+ */
41
+ export function formatRelativeTime(date: Date | string | null): string {
42
+ if (!date) return '—'
43
+ const d = typeof date === 'string' ? new Date(date) : date
44
+ const hours = differenceInHours(new Date(), d)
45
+ if (hours < 1) {
46
+ const mins = differenceInMinutes(new Date(), d)
47
+ return `${mins}m`
48
+ }
49
+ if (hours < 48) return `${hours}h`
50
+ return formatDistanceToNow(d, { addSuffix: false })
51
+ }