@djangocfg/ui-tools 2.1.110 → 2.1.111
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 +242 -49
- package/dist/JsonSchemaForm-65NLLK56.mjs +4 -0
- package/dist/JsonSchemaForm-65NLLK56.mjs.map +1 -0
- package/dist/JsonSchemaForm-PY6DH3HE.cjs +13 -0
- package/dist/JsonSchemaForm-PY6DH3HE.cjs.map +1 -0
- package/dist/JsonTree-6RYAOPSS.mjs +4 -0
- package/dist/JsonTree-6RYAOPSS.mjs.map +1 -0
- package/dist/JsonTree-7OH6CIHT.cjs +10 -0
- package/dist/JsonTree-7OH6CIHT.cjs.map +1 -0
- package/dist/MapContainer-GXQLP5WY.mjs +214 -0
- package/dist/MapContainer-GXQLP5WY.mjs.map +1 -0
- package/dist/MapContainer-RYG4HPH4.cjs +221 -0
- package/dist/MapContainer-RYG4HPH4.cjs.map +1 -0
- package/dist/{Mermaid.client-4OCKJ6QD.mjs → Mermaid.client-OKACITCW.mjs} +16 -7
- package/dist/Mermaid.client-OKACITCW.mjs.map +1 -0
- package/dist/{Mermaid.client-ZP6OE46Z.cjs → Mermaid.client-PNXEC6YL.cjs} +16 -7
- package/dist/Mermaid.client-PNXEC6YL.cjs.map +1 -0
- package/dist/{PlaygroundLayout-XXVBU4WZ.cjs → PlaygroundLayout-SYMEAG3J.cjs} +25 -24
- package/dist/PlaygroundLayout-SYMEAG3J.cjs.map +1 -0
- package/dist/{PlaygroundLayout-LMQTVXSP.mjs → PlaygroundLayout-UQRBU5RH.mjs} +4 -3
- package/dist/PlaygroundLayout-UQRBU5RH.mjs.map +1 -0
- package/dist/{PrettyCode.client-2CLSV2VD.cjs → PrettyCode.client-DANYYQYO.cjs} +11 -4
- package/dist/PrettyCode.client-DANYYQYO.cjs.map +1 -0
- package/dist/{PrettyCode.client-Y2BVON7R.mjs → PrettyCode.client-RS5ZTNBT.mjs} +11 -4
- package/dist/PrettyCode.client-RS5ZTNBT.mjs.map +1 -0
- package/dist/chunk-2DSR7V2L.mjs +561 -0
- package/dist/chunk-2DSR7V2L.mjs.map +1 -0
- package/dist/chunk-47T5ECYV.cjs +1357 -0
- package/dist/chunk-47T5ECYV.cjs.map +1 -0
- package/dist/chunk-5QT3QYFZ.cjs +189 -0
- package/dist/chunk-5QT3QYFZ.cjs.map +1 -0
- package/dist/chunk-7IIRYG4S.mjs +1057 -0
- package/dist/chunk-7IIRYG4S.mjs.map +1 -0
- package/dist/{chunk-FB5QBSI3.cjs → chunk-DI3HUXHK.cjs} +15 -195
- package/dist/chunk-DI3HUXHK.cjs.map +1 -0
- package/dist/chunk-EVGWYASL.cjs +1528 -0
- package/dist/chunk-EVGWYASL.cjs.map +1 -0
- package/dist/chunk-F2N7P5XU.cjs +30 -0
- package/dist/chunk-F2N7P5XU.cjs.map +1 -0
- package/dist/{chunk-L6UHASYQ.mjs → chunk-G6PRZP5I.mjs} +7 -186
- package/dist/chunk-G6PRZP5I.mjs.map +1 -0
- package/dist/chunk-JWB2EWQO.mjs +5 -0
- package/dist/chunk-JWB2EWQO.mjs.map +1 -0
- package/dist/chunk-LTJX2JXE.mjs +338 -0
- package/dist/chunk-LTJX2JXE.mjs.map +1 -0
- package/dist/chunk-OVNC4KW6.mjs +1494 -0
- package/dist/chunk-OVNC4KW6.mjs.map +1 -0
- package/dist/chunk-PNZSJN6T.cjs +1086 -0
- package/dist/chunk-PNZSJN6T.cjs.map +1 -0
- package/dist/chunk-TEFRA7GW.cjs +565 -0
- package/dist/chunk-TEFRA7GW.cjs.map +1 -0
- package/dist/chunk-UOMPPIED.mjs +1343 -0
- package/dist/chunk-UOMPPIED.mjs.map +1 -0
- package/dist/chunk-W6YHQI4F.mjs +187 -0
- package/dist/chunk-W6YHQI4F.mjs.map +1 -0
- package/dist/chunk-XTBRWVIV.cjs +346 -0
- package/dist/chunk-XTBRWVIV.cjs.map +1 -0
- package/dist/components-C7ZL7OMY.mjs +5 -0
- package/dist/components-C7ZL7OMY.mjs.map +1 -0
- package/dist/components-CJ2IB65O.cjs +27 -0
- package/dist/components-CJ2IB65O.cjs.map +1 -0
- package/dist/components-EASJYK45.mjs +6 -0
- package/dist/components-EASJYK45.mjs.map +1 -0
- package/dist/components-LDRFDV4A.cjs +22 -0
- package/dist/components-LDRFDV4A.cjs.map +1 -0
- package/dist/components-VZKUTDJK.mjs +5 -0
- package/dist/components-VZKUTDJK.mjs.map +1 -0
- package/dist/components-Y64GTIMQ.cjs +42 -0
- package/dist/components-Y64GTIMQ.cjs.map +1 -0
- package/dist/index.cjs +701 -4813
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1274 -1026
- package/dist/index.d.ts +1274 -1026
- package/dist/index.mjs +358 -4730
- package/dist/index.mjs.map +1 -1
- package/package.json +27 -4
- package/src/components/index.ts +17 -0
- package/src/components/lazy-wrapper.tsx +281 -0
- package/src/index.ts +92 -7
- package/src/tools/AudioPlayer/components/HybridAudioPlayer.tsx +14 -5
- package/src/tools/AudioPlayer/lazy.tsx +85 -0
- package/src/tools/Gallery/components/Gallery.tsx +182 -0
- package/src/tools/Gallery/components/GalleryCarousel.tsx +251 -0
- package/src/tools/Gallery/components/GalleryCompact.tsx +173 -0
- package/src/tools/Gallery/components/GalleryGrid.tsx +493 -0
- package/src/tools/Gallery/components/GalleryImage.tsx +66 -0
- package/src/tools/Gallery/components/GalleryLightbox.tsx +331 -0
- package/src/tools/Gallery/components/GalleryMedia.tsx +66 -0
- package/src/tools/Gallery/components/GalleryThumbnails.tsx +173 -0
- package/src/tools/Gallery/components/GalleryThumbnailsVirtual.tsx +138 -0
- package/src/tools/Gallery/components/GalleryVideo.tsx +222 -0
- package/src/tools/Gallery/components/index.ts +13 -0
- package/src/tools/Gallery/hooks/index.ts +23 -0
- package/src/tools/Gallery/hooks/useGallery.ts +137 -0
- package/src/tools/Gallery/hooks/useImageDimensions.ts +223 -0
- package/src/tools/Gallery/hooks/usePinchZoom.ts +234 -0
- package/src/tools/Gallery/hooks/usePreloadImages.ts +71 -0
- package/src/tools/Gallery/hooks/useSwipe.ts +86 -0
- package/src/tools/Gallery/hooks/useVirtualList.ts +129 -0
- package/src/tools/Gallery/hooks/useZoom.ts +316 -0
- package/src/tools/Gallery/index.ts +66 -0
- package/src/tools/Gallery/types.ts +183 -0
- package/src/tools/Gallery/utils/imageAnalysis.ts +52 -0
- package/src/tools/Gallery/utils/index.ts +11 -0
- package/src/tools/ImageViewer/components/ImageToolbar.tsx +20 -8
- package/src/tools/ImageViewer/components/ImageViewer.tsx +12 -4
- package/src/tools/ImageViewer/lazy.tsx +37 -0
- package/src/tools/JsonForm/lazy.tsx +43 -0
- package/src/tools/JsonForm/widgets/ColorWidget.tsx +4 -1
- package/src/tools/JsonTree/lazy.tsx +45 -0
- package/src/tools/LottiePlayer/lazy.tsx +57 -0
- package/src/tools/Map/components/CustomOverlay.tsx +54 -0
- package/src/tools/Map/components/DrawControl.tsx +36 -0
- package/src/tools/Map/components/GeocoderControl.tsx +70 -0
- package/src/tools/Map/components/LayerSwitcher.tsx +225 -0
- package/src/tools/Map/components/MapCluster.tsx +273 -0
- package/src/tools/Map/components/MapContainer.tsx +191 -0
- package/src/tools/Map/components/MapControls.tsx +44 -0
- package/src/tools/Map/components/MapLegend.tsx +161 -0
- package/src/tools/Map/components/MapMarker.tsx +102 -0
- package/src/tools/Map/components/MapPopup.tsx +46 -0
- package/src/tools/Map/components/MapSource.tsx +30 -0
- package/src/tools/Map/components/index.ts +20 -0
- package/src/tools/Map/context/MapContext.tsx +89 -0
- package/src/tools/Map/context/index.ts +2 -0
- package/src/tools/Map/hooks/index.ts +9 -0
- package/src/tools/Map/hooks/useMap.ts +11 -0
- package/src/tools/Map/hooks/useMapControl.ts +99 -0
- package/src/tools/Map/hooks/useMapEvents.ts +147 -0
- package/src/tools/Map/hooks/useMapLayers.ts +83 -0
- package/src/tools/Map/hooks/useMapViewport.ts +62 -0
- package/src/tools/Map/hooks/useMarkers.ts +85 -0
- package/src/tools/Map/index.ts +116 -0
- package/src/tools/Map/layers/cluster.ts +94 -0
- package/src/tools/Map/layers/index.ts +15 -0
- package/src/tools/Map/layers/line.ts +93 -0
- package/src/tools/Map/layers/point.ts +61 -0
- package/src/tools/Map/layers/polygon.ts +73 -0
- package/src/tools/Map/lazy.tsx +56 -0
- package/src/tools/Map/styles/index.ts +15 -0
- package/src/tools/Map/types.ts +259 -0
- package/src/tools/Map/utils/geo.ts +88 -0
- package/src/tools/Map/utils/index.ts +16 -0
- package/src/tools/Map/utils/transform.ts +107 -0
- package/src/tools/Mermaid/Mermaid.client.tsx +12 -4
- package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +6 -2
- package/src/tools/Mermaid/lazy.tsx +46 -0
- package/src/tools/OpenapiViewer/lazy.tsx +72 -0
- package/src/tools/PrettyCode/PrettyCode.client.tsx +10 -3
- package/src/tools/PrettyCode/lazy.tsx +64 -0
- package/src/tools/VideoPlayer/lazy.tsx +63 -0
- package/dist/Mermaid.client-4OCKJ6QD.mjs.map +0 -1
- package/dist/Mermaid.client-ZP6OE46Z.cjs.map +0 -1
- package/dist/PlaygroundLayout-LMQTVXSP.mjs.map +0 -1
- package/dist/PlaygroundLayout-XXVBU4WZ.cjs.map +0 -1
- package/dist/PrettyCode.client-2CLSV2VD.cjs.map +0 -1
- package/dist/PrettyCode.client-Y2BVON7R.mjs.map +0 -1
- package/dist/chunk-FB5QBSI3.cjs.map +0 -1
- package/dist/chunk-L6UHASYQ.mjs.map +0 -1
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@djangocfg/ui-tools",
|
|
3
|
-
"version": "2.1.
|
|
3
|
+
"version": "2.1.111",
|
|
4
4
|
"description": "Heavy React tools with lazy loading - for Electron, Vite, CRA, Next.js apps",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"ui-tools",
|
|
@@ -10,7 +10,11 @@
|
|
|
10
10
|
"audio-player",
|
|
11
11
|
"json-form",
|
|
12
12
|
"mermaid",
|
|
13
|
-
"code-highlight"
|
|
13
|
+
"code-highlight",
|
|
14
|
+
"gallery",
|
|
15
|
+
"lightbox",
|
|
16
|
+
"map",
|
|
17
|
+
"maplibre"
|
|
14
18
|
],
|
|
15
19
|
"author": {
|
|
16
20
|
"name": "DjangoCFG",
|
|
@@ -32,6 +36,16 @@
|
|
|
32
36
|
"types": "./dist/index.d.ts",
|
|
33
37
|
"import": "./dist/index.mjs",
|
|
34
38
|
"require": "./dist/index.cjs"
|
|
39
|
+
},
|
|
40
|
+
"./gallery": {
|
|
41
|
+
"types": "./src/tools/Gallery/index.ts",
|
|
42
|
+
"import": "./src/tools/Gallery/index.ts",
|
|
43
|
+
"require": "./src/tools/Gallery/index.ts"
|
|
44
|
+
},
|
|
45
|
+
"./map": {
|
|
46
|
+
"types": "./src/tools/Map/index.ts",
|
|
47
|
+
"import": "./src/tools/Map/index.ts",
|
|
48
|
+
"require": "./src/tools/Map/index.ts"
|
|
35
49
|
}
|
|
36
50
|
},
|
|
37
51
|
"files": [
|
|
@@ -47,7 +61,8 @@
|
|
|
47
61
|
"check": "tsc --noEmit"
|
|
48
62
|
},
|
|
49
63
|
"peerDependencies": {
|
|
50
|
-
"@djangocfg/
|
|
64
|
+
"@djangocfg/i18n": "^2.1.111",
|
|
65
|
+
"@djangocfg/ui-core": "^2.1.111",
|
|
51
66
|
"lucide-react": "^0.545.0",
|
|
52
67
|
"react": "^19.0.0",
|
|
53
68
|
"react-dom": "^19.0.0",
|
|
@@ -61,19 +76,27 @@
|
|
|
61
76
|
"@rjsf/validator-ajv8": "^6.1.2",
|
|
62
77
|
"@vidstack/react": "next",
|
|
63
78
|
"@wavesurfer/react": "^1.0.12",
|
|
79
|
+
"maplibre-gl": "^4.7.1",
|
|
64
80
|
"media-icons": "next",
|
|
65
81
|
"mermaid": "^11.12.0",
|
|
66
82
|
"prism-react-renderer": "^2.4.1",
|
|
67
83
|
"react-json-tree": "^0.20.0",
|
|
68
84
|
"react-lottie-player": "^2.1.0",
|
|
85
|
+
"react-map-gl": "^8.1.0",
|
|
69
86
|
"react-markdown": "10.1.0",
|
|
70
87
|
"react-zoom-pan-pinch": "^3.7.0",
|
|
71
88
|
"remark-gfm": "4.0.1",
|
|
72
89
|
"vidstack": "next",
|
|
73
90
|
"wavesurfer.js": "^7.12.1"
|
|
74
91
|
},
|
|
92
|
+
"optionalDependencies": {
|
|
93
|
+
"@mapbox/mapbox-gl-draw": "^1.4.3",
|
|
94
|
+
"@maplibre/maplibre-gl-geocoder": "^1.7.0"
|
|
95
|
+
},
|
|
75
96
|
"devDependencies": {
|
|
76
|
-
"@djangocfg/
|
|
97
|
+
"@djangocfg/i18n": "^2.1.111",
|
|
98
|
+
"@djangocfg/typescript-config": "^2.1.111",
|
|
99
|
+
"@types/mapbox__mapbox-gl-draw": "^1.4.8",
|
|
77
100
|
"@types/node": "^24.7.2",
|
|
78
101
|
"@types/react": "^19.1.0",
|
|
79
102
|
"@types/react-dom": "^19.1.0",
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// Lazy loading utilities
|
|
2
|
+
export {
|
|
3
|
+
LazyWrapper,
|
|
4
|
+
LoadingFallback,
|
|
5
|
+
CardLoadingFallback,
|
|
6
|
+
MapLoadingFallback,
|
|
7
|
+
Spinner,
|
|
8
|
+
createLazyComponent,
|
|
9
|
+
} from './lazy-wrapper';
|
|
10
|
+
export type {
|
|
11
|
+
LazyWrapperProps,
|
|
12
|
+
LoadingFallbackProps,
|
|
13
|
+
CreateLazyComponentOptions,
|
|
14
|
+
} from './lazy-wrapper';
|
|
15
|
+
|
|
16
|
+
// Markdown
|
|
17
|
+
export * from './markdown';
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import * as React from 'react';
|
|
4
|
+
import { Suspense, type ReactNode, type ComponentType } from 'react';
|
|
5
|
+
import { cn } from '@djangocfg/ui-core/lib';
|
|
6
|
+
import { useTypedT, type I18nTranslations } from '@djangocfg/i18n';
|
|
7
|
+
|
|
8
|
+
// ============================================================================
|
|
9
|
+
// Loading Fallback Components
|
|
10
|
+
// ============================================================================
|
|
11
|
+
|
|
12
|
+
export interface LoadingFallbackProps {
|
|
13
|
+
/** Minimum height of the loading container */
|
|
14
|
+
minHeight?: string | number;
|
|
15
|
+
/** Show loading text */
|
|
16
|
+
showText?: boolean;
|
|
17
|
+
/** Custom loading text */
|
|
18
|
+
text?: string;
|
|
19
|
+
/** Additional CSS classes */
|
|
20
|
+
className?: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Spinner - Simple spinning loader
|
|
25
|
+
*/
|
|
26
|
+
export function Spinner({ className }: { className?: string }) {
|
|
27
|
+
const t = useTypedT<I18nTranslations>();
|
|
28
|
+
const loadingLabel = t('ui.states.loading');
|
|
29
|
+
|
|
30
|
+
return (
|
|
31
|
+
<div
|
|
32
|
+
className={cn(
|
|
33
|
+
'inline-block h-8 w-8 animate-spin rounded-full',
|
|
34
|
+
'border-4 border-solid border-current border-r-transparent',
|
|
35
|
+
'align-[-0.125em] motion-reduce:animate-[spin_1.5s_linear_infinite]',
|
|
36
|
+
className
|
|
37
|
+
)}
|
|
38
|
+
role="status"
|
|
39
|
+
aria-label={loadingLabel}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* LoadingFallback - Default loading state for lazy components
|
|
46
|
+
*/
|
|
47
|
+
export function LoadingFallback({
|
|
48
|
+
minHeight = 200,
|
|
49
|
+
showText = true,
|
|
50
|
+
text,
|
|
51
|
+
className,
|
|
52
|
+
}: LoadingFallbackProps) {
|
|
53
|
+
const t = useTypedT<I18nTranslations>();
|
|
54
|
+
const loadingText = text ?? t('ui.form.loading');
|
|
55
|
+
const height = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;
|
|
56
|
+
|
|
57
|
+
return (
|
|
58
|
+
<div
|
|
59
|
+
className={cn(
|
|
60
|
+
'flex items-center justify-center bg-muted/30 rounded-lg',
|
|
61
|
+
className
|
|
62
|
+
)}
|
|
63
|
+
style={{ minHeight: height }}
|
|
64
|
+
>
|
|
65
|
+
<div className="text-center">
|
|
66
|
+
<Spinner className="text-primary" />
|
|
67
|
+
{showText && (
|
|
68
|
+
<p className="mt-3 text-sm text-muted-foreground">{loadingText}</p>
|
|
69
|
+
)}
|
|
70
|
+
</div>
|
|
71
|
+
</div>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* CardLoadingFallback - Loading state styled as a card
|
|
77
|
+
*/
|
|
78
|
+
export function CardLoadingFallback({
|
|
79
|
+
title,
|
|
80
|
+
description,
|
|
81
|
+
minHeight = 200,
|
|
82
|
+
className,
|
|
83
|
+
}: {
|
|
84
|
+
title?: string;
|
|
85
|
+
description?: string;
|
|
86
|
+
minHeight?: string | number;
|
|
87
|
+
className?: string;
|
|
88
|
+
}) {
|
|
89
|
+
const t = useTypedT<I18nTranslations>();
|
|
90
|
+
const cardTitle = title ?? t('ui.states.loading');
|
|
91
|
+
const height = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;
|
|
92
|
+
|
|
93
|
+
return (
|
|
94
|
+
<div
|
|
95
|
+
className={cn(
|
|
96
|
+
'relative bg-card rounded-lg border border-border overflow-hidden',
|
|
97
|
+
className
|
|
98
|
+
)}
|
|
99
|
+
>
|
|
100
|
+
<div className="p-4 border-b border-border bg-muted/50">
|
|
101
|
+
<h6 className="text-sm font-semibold text-foreground">{cardTitle}</h6>
|
|
102
|
+
{description && (
|
|
103
|
+
<p className="text-xs text-muted-foreground mt-1">{description}</p>
|
|
104
|
+
)}
|
|
105
|
+
</div>
|
|
106
|
+
<div className="p-4">
|
|
107
|
+
<div
|
|
108
|
+
className="flex justify-center items-center"
|
|
109
|
+
style={{ minHeight: height }}
|
|
110
|
+
>
|
|
111
|
+
<Spinner className="text-primary" />
|
|
112
|
+
</div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* MapLoadingFallback - Loading state for map components
|
|
120
|
+
*/
|
|
121
|
+
export function MapLoadingFallback({
|
|
122
|
+
minHeight = 400,
|
|
123
|
+
className,
|
|
124
|
+
}: {
|
|
125
|
+
minHeight?: string | number;
|
|
126
|
+
className?: string;
|
|
127
|
+
}) {
|
|
128
|
+
const t = useTypedT<I18nTranslations>();
|
|
129
|
+
const loadingText = t('ui.form.loading');
|
|
130
|
+
const height = typeof minHeight === 'number' ? `${minHeight}px` : minHeight;
|
|
131
|
+
|
|
132
|
+
return (
|
|
133
|
+
<div
|
|
134
|
+
className={cn(
|
|
135
|
+
'relative bg-muted/50 rounded-lg overflow-hidden',
|
|
136
|
+
'flex items-center justify-center',
|
|
137
|
+
className
|
|
138
|
+
)}
|
|
139
|
+
style={{ minHeight: height }}
|
|
140
|
+
>
|
|
141
|
+
<div className="text-center">
|
|
142
|
+
<div className="relative">
|
|
143
|
+
<Spinner className="text-primary h-10 w-10" />
|
|
144
|
+
<div className="absolute inset-0 flex items-center justify-center">
|
|
145
|
+
<svg
|
|
146
|
+
className="h-5 w-5 text-muted-foreground"
|
|
147
|
+
fill="none"
|
|
148
|
+
viewBox="0 0 24 24"
|
|
149
|
+
stroke="currentColor"
|
|
150
|
+
>
|
|
151
|
+
<path
|
|
152
|
+
strokeLinecap="round"
|
|
153
|
+
strokeLinejoin="round"
|
|
154
|
+
strokeWidth={2}
|
|
155
|
+
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
|
|
156
|
+
/>
|
|
157
|
+
<path
|
|
158
|
+
strokeLinecap="round"
|
|
159
|
+
strokeLinejoin="round"
|
|
160
|
+
strokeWidth={2}
|
|
161
|
+
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
|
|
162
|
+
/>
|
|
163
|
+
</svg>
|
|
164
|
+
</div>
|
|
165
|
+
</div>
|
|
166
|
+
<p className="mt-3 text-sm text-muted-foreground">{loadingText}</p>
|
|
167
|
+
</div>
|
|
168
|
+
</div>
|
|
169
|
+
);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ============================================================================
|
|
173
|
+
// Lazy Wrapper
|
|
174
|
+
// ============================================================================
|
|
175
|
+
|
|
176
|
+
export interface LazyWrapperProps {
|
|
177
|
+
children: ReactNode;
|
|
178
|
+
/** Custom fallback component */
|
|
179
|
+
fallback?: ReactNode;
|
|
180
|
+
/** Use card-style fallback */
|
|
181
|
+
card?: boolean;
|
|
182
|
+
/** Card title (when card=true) */
|
|
183
|
+
cardTitle?: string;
|
|
184
|
+
/** Card description (when card=true) */
|
|
185
|
+
cardDescription?: string;
|
|
186
|
+
/** Minimum height for fallback */
|
|
187
|
+
minHeight?: string | number;
|
|
188
|
+
/** Additional CSS classes for fallback */
|
|
189
|
+
className?: string;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* LazyWrapper - Suspense wrapper with configurable fallbacks
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* // Basic usage
|
|
197
|
+
* <LazyWrapper>
|
|
198
|
+
* <LazyComponent />
|
|
199
|
+
* </LazyWrapper>
|
|
200
|
+
*
|
|
201
|
+
* @example
|
|
202
|
+
* // Card style fallback
|
|
203
|
+
* <LazyWrapper card cardTitle="Diagram" minHeight={300}>
|
|
204
|
+
* <Mermaid chart={...} />
|
|
205
|
+
* </LazyWrapper>
|
|
206
|
+
*
|
|
207
|
+
* @example
|
|
208
|
+
* // Custom fallback
|
|
209
|
+
* <LazyWrapper fallback={<MyCustomLoader />}>
|
|
210
|
+
* <HeavyComponent />
|
|
211
|
+
* </LazyWrapper>
|
|
212
|
+
*/
|
|
213
|
+
export function LazyWrapper({
|
|
214
|
+
children,
|
|
215
|
+
fallback,
|
|
216
|
+
card = false,
|
|
217
|
+
cardTitle,
|
|
218
|
+
cardDescription,
|
|
219
|
+
minHeight = 200,
|
|
220
|
+
className,
|
|
221
|
+
}: LazyWrapperProps) {
|
|
222
|
+
const defaultFallback = card ? (
|
|
223
|
+
<CardLoadingFallback
|
|
224
|
+
title={cardTitle}
|
|
225
|
+
description={cardDescription}
|
|
226
|
+
minHeight={minHeight}
|
|
227
|
+
className={className}
|
|
228
|
+
/>
|
|
229
|
+
) : (
|
|
230
|
+
<LoadingFallback minHeight={minHeight} className={className} />
|
|
231
|
+
);
|
|
232
|
+
|
|
233
|
+
return <Suspense fallback={fallback ?? defaultFallback}>{children}</Suspense>;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// ============================================================================
|
|
237
|
+
// Lazy Component Factory
|
|
238
|
+
// ============================================================================
|
|
239
|
+
|
|
240
|
+
export interface CreateLazyComponentOptions<P> {
|
|
241
|
+
/** Loading fallback component */
|
|
242
|
+
fallback?: ReactNode | ((props: P) => ReactNode);
|
|
243
|
+
/** Display name for the component */
|
|
244
|
+
displayName?: string;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
/**
|
|
248
|
+
* createLazyComponent - Factory for creating lazy-loaded components
|
|
249
|
+
*
|
|
250
|
+
* @example
|
|
251
|
+
* const LazyMermaid = createLazyComponent(
|
|
252
|
+
* () => import('./Mermaid.client'),
|
|
253
|
+
* {
|
|
254
|
+
* displayName: 'Mermaid',
|
|
255
|
+
* fallback: <CardLoadingFallback title="Diagram" />,
|
|
256
|
+
* }
|
|
257
|
+
* );
|
|
258
|
+
*/
|
|
259
|
+
export function createLazyComponent<P extends object>(
|
|
260
|
+
loader: () => Promise<{ default: ComponentType<P> }>,
|
|
261
|
+
options: CreateLazyComponentOptions<P> = {}
|
|
262
|
+
): ComponentType<P> {
|
|
263
|
+
const LazyComponent = React.lazy(loader);
|
|
264
|
+
|
|
265
|
+
const WrappedComponent = (props: P) => {
|
|
266
|
+
const fallback =
|
|
267
|
+
typeof options.fallback === 'function'
|
|
268
|
+
? options.fallback(props)
|
|
269
|
+
: options.fallback ?? <LoadingFallback />;
|
|
270
|
+
|
|
271
|
+
return (
|
|
272
|
+
<Suspense fallback={fallback}>
|
|
273
|
+
<LazyComponent {...props} />
|
|
274
|
+
</Suspense>
|
|
275
|
+
);
|
|
276
|
+
};
|
|
277
|
+
|
|
278
|
+
WrappedComponent.displayName = options.displayName ?? 'LazyComponent';
|
|
279
|
+
|
|
280
|
+
return WrappedComponent;
|
|
281
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -5,24 +5,109 @@
|
|
|
5
5
|
* Works in any React environment: Next.js, Vite, Wails, CRA
|
|
6
6
|
*
|
|
7
7
|
* Bundle sizes:
|
|
8
|
+
* - Map: ~800KB (MapLibre GL maps)
|
|
8
9
|
* - Mermaid: ~800KB (diagram rendering)
|
|
9
10
|
* - PrettyCode: ~500KB (code syntax highlighting)
|
|
10
|
-
* - JsonTree: ~100KB (JSON visualization)
|
|
11
|
-
* - LottiePlayer: ~200KB (Lottie animation player)
|
|
12
|
-
* - JsonForm: ~300KB (JSON Schema form generator)
|
|
13
11
|
* - OpenapiViewer: ~400KB (OpenAPI schema viewer & playground)
|
|
14
|
-
* -
|
|
12
|
+
* - JsonForm: ~300KB (JSON Schema form generator)
|
|
13
|
+
* - LottiePlayer: ~200KB (Lottie animation player)
|
|
15
14
|
* - AudioPlayer: ~200KB (Audio player with WaveSurfer.js)
|
|
15
|
+
* - VideoPlayer: ~150KB (Professional video player with Vidstack)
|
|
16
|
+
* - JsonTree: ~100KB (JSON visualization)
|
|
17
|
+
* - Gallery: ~50KB (Image/video gallery)
|
|
16
18
|
* - ImageViewer: ~50KB (Image viewer with zoom/pan/rotate)
|
|
19
|
+
*
|
|
20
|
+
* For tree-shaking, use subpath imports:
|
|
21
|
+
* - import { Gallery } from '@djangocfg/ui-tools/gallery'
|
|
22
|
+
* - import { MapContainer } from '@djangocfg/ui-tools/map'
|
|
17
23
|
*/
|
|
18
24
|
|
|
19
25
|
'use client';
|
|
20
26
|
|
|
21
|
-
//
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// Lazy Loading Utilities
|
|
29
|
+
// ============================================================================
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
// Components
|
|
33
|
+
LazyWrapper,
|
|
34
|
+
LoadingFallback,
|
|
35
|
+
CardLoadingFallback,
|
|
36
|
+
MapLoadingFallback,
|
|
37
|
+
Spinner,
|
|
38
|
+
// Factory
|
|
39
|
+
createLazyComponent,
|
|
40
|
+
} from './components';
|
|
41
|
+
export type {
|
|
42
|
+
LazyWrapperProps,
|
|
43
|
+
LoadingFallbackProps,
|
|
44
|
+
CreateLazyComponentOptions,
|
|
45
|
+
} from './components';
|
|
46
|
+
|
|
47
|
+
// ============================================================================
|
|
48
|
+
// Lazy Components (heavy tools, loaded on demand)
|
|
49
|
+
// ============================================================================
|
|
50
|
+
|
|
51
|
+
// Map (~800KB)
|
|
52
|
+
export {
|
|
53
|
+
LazyMapContainer,
|
|
54
|
+
LazyMapView,
|
|
55
|
+
} from './tools/Map/lazy';
|
|
56
|
+
export type {
|
|
57
|
+
MapContainerProps,
|
|
58
|
+
MapViewport,
|
|
59
|
+
MapStyleKey,
|
|
60
|
+
MarkerData,
|
|
61
|
+
} from './tools/Map/lazy';
|
|
62
|
+
|
|
63
|
+
// Mermaid (~800KB)
|
|
64
|
+
export { LazyMermaid } from './tools/Mermaid/lazy';
|
|
65
|
+
export type { MermaidProps } from './tools/Mermaid/lazy';
|
|
66
|
+
|
|
67
|
+
// PrettyCode (~500KB)
|
|
68
|
+
export { LazyPrettyCode } from './tools/PrettyCode/lazy';
|
|
69
|
+
export type { PrettyCodeProps } from './tools/PrettyCode/lazy';
|
|
70
|
+
|
|
71
|
+
// OpenapiViewer (~400KB)
|
|
72
|
+
export { LazyOpenapiViewer } from './tools/OpenapiViewer/lazy';
|
|
73
|
+
export type { PlaygroundConfig, SchemaSource, PlaygroundProps } from './tools/OpenapiViewer/lazy';
|
|
74
|
+
|
|
75
|
+
// JsonForm (~300KB)
|
|
76
|
+
export { LazyJsonSchemaForm } from './tools/JsonForm/lazy';
|
|
77
|
+
|
|
78
|
+
// LottiePlayer (~200KB)
|
|
79
|
+
export { LazyLottiePlayer } from './tools/LottiePlayer/lazy';
|
|
80
|
+
|
|
81
|
+
// AudioPlayer (~200KB)
|
|
82
|
+
export {
|
|
83
|
+
LazyHybridAudioPlayer,
|
|
84
|
+
LazyHybridSimplePlayer,
|
|
85
|
+
} from './tools/AudioPlayer/lazy';
|
|
86
|
+
|
|
87
|
+
// VideoPlayer (~150KB)
|
|
88
|
+
export { LazyVideoPlayer } from './tools/VideoPlayer/lazy';
|
|
89
|
+
|
|
90
|
+
// JsonTree (~100KB)
|
|
91
|
+
export { LazyJsonTree } from './tools/JsonTree/lazy';
|
|
92
|
+
export type { JsonTreeProps } from './tools/JsonTree/lazy';
|
|
93
|
+
|
|
94
|
+
// ImageViewer (~50KB)
|
|
95
|
+
export { LazyImageViewer } from './tools/ImageViewer/lazy';
|
|
96
|
+
|
|
97
|
+
// ============================================================================
|
|
98
|
+
// Tools (from ./tools/index.ts) - Direct imports (no lazy loading)
|
|
99
|
+
// ============================================================================
|
|
100
|
+
|
|
22
101
|
export * from './tools';
|
|
23
102
|
|
|
24
|
-
//
|
|
103
|
+
// ============================================================================
|
|
104
|
+
// Components (Markdown, etc.)
|
|
105
|
+
// ============================================================================
|
|
106
|
+
|
|
25
107
|
export * from './components/markdown';
|
|
26
108
|
|
|
27
|
-
//
|
|
109
|
+
// ============================================================================
|
|
110
|
+
// Stores
|
|
111
|
+
// ============================================================================
|
|
112
|
+
|
|
28
113
|
export * from './stores/mediaCache';
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
* - Timer display
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
|
-
import { memo } from 'react';
|
|
18
|
+
import { memo, useMemo } from 'react';
|
|
19
19
|
import {
|
|
20
20
|
Play,
|
|
21
21
|
Pause,
|
|
@@ -27,6 +27,7 @@ import {
|
|
|
27
27
|
Loader2,
|
|
28
28
|
Repeat,
|
|
29
29
|
} from 'lucide-react';
|
|
30
|
+
import { useTypedT, type I18nTranslations } from '@djangocfg/i18n';
|
|
30
31
|
import { Button, Slider, cn } from '../../_shared';
|
|
31
32
|
import { useHybridAudioContext } from '../context/HybridAudioProvider';
|
|
32
33
|
import { HybridWaveform } from './HybridWaveform';
|
|
@@ -72,8 +73,16 @@ export const HybridAudioPlayer = memo(function HybridAudioPlayer({
|
|
|
72
73
|
className,
|
|
73
74
|
style,
|
|
74
75
|
}: HybridAudioPlayerProps) {
|
|
76
|
+
const t = useTypedT<I18nTranslations>();
|
|
75
77
|
const { state, controls } = useHybridAudioContext();
|
|
76
78
|
|
|
79
|
+
const labels = useMemo(() => ({
|
|
80
|
+
restart: t('tools.audio.restart'),
|
|
81
|
+
back: t('tools.audio.back'),
|
|
82
|
+
forward: t('tools.audio.forward'),
|
|
83
|
+
volume: t('tools.audio.volume'),
|
|
84
|
+
}), [t]);
|
|
85
|
+
|
|
77
86
|
const isLoading = !state.isReady;
|
|
78
87
|
|
|
79
88
|
const handleVolumeChange = (value: number[]) => {
|
|
@@ -119,7 +128,7 @@ export const HybridAudioPlayer = memo(function HybridAudioPlayer({
|
|
|
119
128
|
className="h-9 w-9"
|
|
120
129
|
onClick={controls.restart}
|
|
121
130
|
disabled={!state.isReady}
|
|
122
|
-
title=
|
|
131
|
+
title={labels.restart}
|
|
123
132
|
>
|
|
124
133
|
<RotateCcw className="h-4 w-4" />
|
|
125
134
|
</Button>
|
|
@@ -131,7 +140,7 @@ export const HybridAudioPlayer = memo(function HybridAudioPlayer({
|
|
|
131
140
|
className="h-9 w-9"
|
|
132
141
|
onClick={() => controls.skip(-5)}
|
|
133
142
|
disabled={!state.isReady}
|
|
134
|
-
title=
|
|
143
|
+
title={labels.back}
|
|
135
144
|
>
|
|
136
145
|
<SkipBack className="h-4 w-4" />
|
|
137
146
|
</Button>
|
|
@@ -161,7 +170,7 @@ export const HybridAudioPlayer = memo(function HybridAudioPlayer({
|
|
|
161
170
|
className="h-9 w-9"
|
|
162
171
|
onClick={() => controls.skip(5)}
|
|
163
172
|
disabled={!state.isReady}
|
|
164
|
-
title=
|
|
173
|
+
title={labels.forward}
|
|
165
174
|
>
|
|
166
175
|
<SkipForward className="h-4 w-4" />
|
|
167
176
|
</Button>
|
|
@@ -189,7 +198,7 @@ export const HybridAudioPlayer = memo(function HybridAudioPlayer({
|
|
|
189
198
|
step={1}
|
|
190
199
|
onValueChange={handleVolumeChange}
|
|
191
200
|
className="w-20"
|
|
192
|
-
aria-label=
|
|
201
|
+
aria-label={labels.volume}
|
|
193
202
|
/>
|
|
194
203
|
</>
|
|
195
204
|
)}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Lazy-loaded AudioPlayer Components
|
|
5
|
+
*
|
|
6
|
+
* Heavy WaveSurfer.js (~200KB) is loaded only when component is rendered.
|
|
7
|
+
* Use this for automatic code-splitting with Suspense fallback.
|
|
8
|
+
*
|
|
9
|
+
* For direct imports without lazy loading, use:
|
|
10
|
+
* import { HybridAudioPlayer } from '@djangocfg/ui-tools/audio'
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
import { createLazyComponent, LoadingFallback } from '../../components';
|
|
14
|
+
import type {
|
|
15
|
+
HybridAudioPlayerProps,
|
|
16
|
+
HybridSimplePlayerProps,
|
|
17
|
+
} from './components';
|
|
18
|
+
|
|
19
|
+
// ============================================================================
|
|
20
|
+
// Re-export types
|
|
21
|
+
// ============================================================================
|
|
22
|
+
|
|
23
|
+
export type { HybridAudioPlayerProps, HybridSimplePlayerProps };
|
|
24
|
+
|
|
25
|
+
// ============================================================================
|
|
26
|
+
// Audio Loading Fallback
|
|
27
|
+
// ============================================================================
|
|
28
|
+
|
|
29
|
+
function AudioLoadingFallback() {
|
|
30
|
+
return (
|
|
31
|
+
<div className="flex items-center justify-center p-6 bg-muted/30 rounded-lg">
|
|
32
|
+
<div className="flex flex-col items-center gap-2">
|
|
33
|
+
<div className="relative">
|
|
34
|
+
<div className="h-10 w-10 animate-spin rounded-full border-4 border-muted border-t-primary" />
|
|
35
|
+
<div className="absolute inset-0 flex items-center justify-center">
|
|
36
|
+
<svg
|
|
37
|
+
className="h-5 w-5 text-muted-foreground"
|
|
38
|
+
fill="none"
|
|
39
|
+
viewBox="0 0 24 24"
|
|
40
|
+
stroke="currentColor"
|
|
41
|
+
>
|
|
42
|
+
<path
|
|
43
|
+
strokeLinecap="round"
|
|
44
|
+
strokeLinejoin="round"
|
|
45
|
+
strokeWidth={2}
|
|
46
|
+
d="M9 19V6l12-3v13M9 19c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zm12-3c0 1.105-1.343 2-3 2s-3-.895-3-2 1.343-2 3-2 3 .895 3 2zM9 10l12-3"
|
|
47
|
+
/>
|
|
48
|
+
</svg>
|
|
49
|
+
</div>
|
|
50
|
+
</div>
|
|
51
|
+
<span className="text-sm text-muted-foreground">Loading audio player...</span>
|
|
52
|
+
</div>
|
|
53
|
+
</div>
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// ============================================================================
|
|
58
|
+
// Lazy Components
|
|
59
|
+
// ============================================================================
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* LazyHybridAudioPlayer - Lazy-loaded full-featured audio player
|
|
63
|
+
*
|
|
64
|
+
* Automatically shows loading state while WaveSurfer loads (~200KB)
|
|
65
|
+
*/
|
|
66
|
+
export const LazyHybridAudioPlayer = createLazyComponent<HybridAudioPlayerProps>(
|
|
67
|
+
() => import('./components').then((mod) => ({ default: mod.HybridAudioPlayer })),
|
|
68
|
+
{
|
|
69
|
+
displayName: 'LazyHybridAudioPlayer',
|
|
70
|
+
fallback: <AudioLoadingFallback />,
|
|
71
|
+
}
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* LazyHybridSimplePlayer - Lazy-loaded simple audio player
|
|
76
|
+
*
|
|
77
|
+
* Automatically shows loading state while WaveSurfer loads (~200KB)
|
|
78
|
+
*/
|
|
79
|
+
export const LazyHybridSimplePlayer = createLazyComponent<HybridSimplePlayerProps>(
|
|
80
|
+
() => import('./components').then((mod) => ({ default: mod.HybridSimplePlayer })),
|
|
81
|
+
{
|
|
82
|
+
displayName: 'LazyHybridSimplePlayer',
|
|
83
|
+
fallback: <AudioLoadingFallback />,
|
|
84
|
+
}
|
|
85
|
+
);
|