@djangocfg/ui-tools 2.1.91

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 (174) hide show
  1. package/dist/LottiePlayer.client-LBEC2JKY.mjs +161 -0
  2. package/dist/LottiePlayer.client-LBEC2JKY.mjs.map +1 -0
  3. package/dist/LottiePlayer.client-WFMG2OOW.cjs +168 -0
  4. package/dist/LottiePlayer.client-WFMG2OOW.cjs.map +1 -0
  5. package/dist/Mermaid.client-4TU2TSH3.mjs +477 -0
  6. package/dist/Mermaid.client-4TU2TSH3.mjs.map +1 -0
  7. package/dist/Mermaid.client-SBYY364Q.cjs +483 -0
  8. package/dist/Mermaid.client-SBYY364Q.cjs.map +1 -0
  9. package/dist/PlaygroundLayout-3YVSAEAF.cjs +1003 -0
  10. package/dist/PlaygroundLayout-3YVSAEAF.cjs.map +1 -0
  11. package/dist/PlaygroundLayout-4DYBORAS.mjs +996 -0
  12. package/dist/PlaygroundLayout-4DYBORAS.mjs.map +1 -0
  13. package/dist/PrettyCode.client-LCBPPTIX.mjs +152 -0
  14. package/dist/PrettyCode.client-LCBPPTIX.mjs.map +1 -0
  15. package/dist/PrettyCode.client-PNPLXRH6.cjs +154 -0
  16. package/dist/PrettyCode.client-PNPLXRH6.cjs.map +1 -0
  17. package/dist/chunk-37ZI6VD4.mjs +12 -0
  18. package/dist/chunk-37ZI6VD4.mjs.map +1 -0
  19. package/dist/chunk-3HK2OE62.cjs +81 -0
  20. package/dist/chunk-3HK2OE62.cjs.map +1 -0
  21. package/dist/chunk-7DGDQVQW.cjs +591 -0
  22. package/dist/chunk-7DGDQVQW.cjs.map +1 -0
  23. package/dist/chunk-M6P2FU7L.mjs +572 -0
  24. package/dist/chunk-M6P2FU7L.mjs.map +1 -0
  25. package/dist/chunk-UQ3XI5MY.cjs +15 -0
  26. package/dist/chunk-UQ3XI5MY.cjs.map +1 -0
  27. package/dist/chunk-YFRNE2IR.mjs +79 -0
  28. package/dist/chunk-YFRNE2IR.mjs.map +1 -0
  29. package/dist/index.cjs +5042 -0
  30. package/dist/index.cjs.map +1 -0
  31. package/dist/index.d.cts +1591 -0
  32. package/dist/index.d.ts +1591 -0
  33. package/dist/index.mjs +4941 -0
  34. package/dist/index.mjs.map +1 -0
  35. package/package.json +86 -0
  36. package/src/components/markdown/MarkdownMessage.tsx +340 -0
  37. package/src/components/markdown/index.ts +5 -0
  38. package/src/index.ts +26 -0
  39. package/src/stores/index.ts +9 -0
  40. package/src/stores/mediaCache.ts +534 -0
  41. package/src/tools/AudioPlayer/README.md +206 -0
  42. package/src/tools/AudioPlayer/components/HybridAudioPlayer.tsx +216 -0
  43. package/src/tools/AudioPlayer/components/HybridSimplePlayer.tsx +280 -0
  44. package/src/tools/AudioPlayer/components/HybridWaveform.tsx +279 -0
  45. package/src/tools/AudioPlayer/components/ReactiveCover/AudioReactiveCover.tsx +149 -0
  46. package/src/tools/AudioPlayer/components/ReactiveCover/effects/GlowEffect.tsx +110 -0
  47. package/src/tools/AudioPlayer/components/ReactiveCover/effects/MeshEffect.tsx +58 -0
  48. package/src/tools/AudioPlayer/components/ReactiveCover/effects/OrbsEffect.tsx +45 -0
  49. package/src/tools/AudioPlayer/components/ReactiveCover/effects/SpotlightEffect.tsx +82 -0
  50. package/src/tools/AudioPlayer/components/ReactiveCover/effects/index.ts +8 -0
  51. package/src/tools/AudioPlayer/components/ReactiveCover/index.ts +6 -0
  52. package/src/tools/AudioPlayer/components/index.ts +22 -0
  53. package/src/tools/AudioPlayer/context/HybridAudioProvider.tsx +158 -0
  54. package/src/tools/AudioPlayer/context/index.ts +16 -0
  55. package/src/tools/AudioPlayer/effects/index.ts +412 -0
  56. package/src/tools/AudioPlayer/hooks/index.ts +35 -0
  57. package/src/tools/AudioPlayer/hooks/useHybridAudio.ts +387 -0
  58. package/src/tools/AudioPlayer/hooks/useHybridAudioAnalysis.ts +95 -0
  59. package/src/tools/AudioPlayer/hooks/useVisualization.tsx +207 -0
  60. package/src/tools/AudioPlayer/index.ts +133 -0
  61. package/src/tools/AudioPlayer/types/effects.ts +73 -0
  62. package/src/tools/AudioPlayer/types/index.ts +27 -0
  63. package/src/tools/AudioPlayer/utils/debug.ts +14 -0
  64. package/src/tools/AudioPlayer/utils/formatTime.ts +10 -0
  65. package/src/tools/AudioPlayer/utils/index.ts +6 -0
  66. package/src/tools/ImageViewer/@refactoring/00-PLAN.md +71 -0
  67. package/src/tools/ImageViewer/@refactoring/01-TYPES.md +121 -0
  68. package/src/tools/ImageViewer/@refactoring/02-UTILS.md +143 -0
  69. package/src/tools/ImageViewer/@refactoring/03-HOOKS.md +261 -0
  70. package/src/tools/ImageViewer/@refactoring/04-COMPONENTS.md +427 -0
  71. package/src/tools/ImageViewer/@refactoring/05-EXECUTION-CHECKLIST.md +126 -0
  72. package/src/tools/ImageViewer/README.md +200 -0
  73. package/src/tools/ImageViewer/components/ImageInfo.tsx +44 -0
  74. package/src/tools/ImageViewer/components/ImageToolbar.tsx +145 -0
  75. package/src/tools/ImageViewer/components/ImageViewer.tsx +241 -0
  76. package/src/tools/ImageViewer/components/index.ts +7 -0
  77. package/src/tools/ImageViewer/hooks/index.ts +9 -0
  78. package/src/tools/ImageViewer/hooks/useImageLoading.ts +204 -0
  79. package/src/tools/ImageViewer/hooks/useImageTransform.ts +101 -0
  80. package/src/tools/ImageViewer/index.ts +60 -0
  81. package/src/tools/ImageViewer/types.ts +81 -0
  82. package/src/tools/ImageViewer/utils/constants.ts +59 -0
  83. package/src/tools/ImageViewer/utils/debug.ts +14 -0
  84. package/src/tools/ImageViewer/utils/index.ts +17 -0
  85. package/src/tools/ImageViewer/utils/lqip.ts +47 -0
  86. package/src/tools/JsonForm/JsonSchemaForm.tsx +197 -0
  87. package/src/tools/JsonForm/examples/BotConfigExample.tsx +249 -0
  88. package/src/tools/JsonForm/examples/RealBotConfigExample.tsx +161 -0
  89. package/src/tools/JsonForm/index.ts +46 -0
  90. package/src/tools/JsonForm/templates/ArrayFieldItemTemplate.tsx +47 -0
  91. package/src/tools/JsonForm/templates/ArrayFieldTemplate.tsx +74 -0
  92. package/src/tools/JsonForm/templates/BaseInputTemplate.tsx +107 -0
  93. package/src/tools/JsonForm/templates/ErrorListTemplate.tsx +35 -0
  94. package/src/tools/JsonForm/templates/FieldTemplate.tsx +62 -0
  95. package/src/tools/JsonForm/templates/ObjectFieldTemplate.tsx +116 -0
  96. package/src/tools/JsonForm/templates/index.ts +12 -0
  97. package/src/tools/JsonForm/types.ts +83 -0
  98. package/src/tools/JsonForm/utils.ts +213 -0
  99. package/src/tools/JsonForm/widgets/CheckboxWidget.tsx +37 -0
  100. package/src/tools/JsonForm/widgets/ColorWidget.tsx +219 -0
  101. package/src/tools/JsonForm/widgets/NumberWidget.tsx +89 -0
  102. package/src/tools/JsonForm/widgets/SelectWidget.tsx +97 -0
  103. package/src/tools/JsonForm/widgets/SliderWidget.tsx +148 -0
  104. package/src/tools/JsonForm/widgets/SwitchWidget.tsx +35 -0
  105. package/src/tools/JsonForm/widgets/TextWidget.tsx +96 -0
  106. package/src/tools/JsonForm/widgets/index.ts +14 -0
  107. package/src/tools/JsonTree/index.tsx +243 -0
  108. package/src/tools/LottiePlayer/LottiePlayer.client.tsx +213 -0
  109. package/src/tools/LottiePlayer/index.tsx +56 -0
  110. package/src/tools/LottiePlayer/types.ts +108 -0
  111. package/src/tools/LottiePlayer/useLottie.ts +164 -0
  112. package/src/tools/Mermaid/Mermaid.client.tsx +82 -0
  113. package/src/tools/Mermaid/components/MermaidCodeViewer.tsx +95 -0
  114. package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +103 -0
  115. package/src/tools/Mermaid/hooks/index.ts +4 -0
  116. package/src/tools/Mermaid/hooks/useMermaidCleanup.ts +73 -0
  117. package/src/tools/Mermaid/hooks/useMermaidFullscreen.ts +46 -0
  118. package/src/tools/Mermaid/hooks/useMermaidRenderer.ts +226 -0
  119. package/src/tools/Mermaid/hooks/useMermaidValidation.ts +29 -0
  120. package/src/tools/Mermaid/index.tsx +44 -0
  121. package/src/tools/Mermaid/utils/mermaid-helpers.ts +33 -0
  122. package/src/tools/OpenapiViewer/components/EndpointInfo.tsx +149 -0
  123. package/src/tools/OpenapiViewer/components/EndpointsLibrary.tsx +263 -0
  124. package/src/tools/OpenapiViewer/components/PlaygroundLayout.tsx +125 -0
  125. package/src/tools/OpenapiViewer/components/PlaygroundStepper.tsx +100 -0
  126. package/src/tools/OpenapiViewer/components/RequestBuilder.tsx +157 -0
  127. package/src/tools/OpenapiViewer/components/RequestParametersForm.tsx +253 -0
  128. package/src/tools/OpenapiViewer/components/ResponseViewer.tsx +173 -0
  129. package/src/tools/OpenapiViewer/components/VersionSelector.tsx +68 -0
  130. package/src/tools/OpenapiViewer/components/index.ts +14 -0
  131. package/src/tools/OpenapiViewer/constants.ts +39 -0
  132. package/src/tools/OpenapiViewer/context/PlaygroundContext.tsx +337 -0
  133. package/src/tools/OpenapiViewer/hooks/index.ts +8 -0
  134. package/src/tools/OpenapiViewer/hooks/useMobile.ts +10 -0
  135. package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +199 -0
  136. package/src/tools/OpenapiViewer/index.tsx +37 -0
  137. package/src/tools/OpenapiViewer/types.ts +151 -0
  138. package/src/tools/OpenapiViewer/utils/apiKeyManager.ts +149 -0
  139. package/src/tools/OpenapiViewer/utils/formatters.ts +71 -0
  140. package/src/tools/OpenapiViewer/utils/index.ts +9 -0
  141. package/src/tools/OpenapiViewer/utils/versionManager.ts +161 -0
  142. package/src/tools/PrettyCode/PrettyCode.client.tsx +208 -0
  143. package/src/tools/PrettyCode/index.tsx +47 -0
  144. package/src/tools/VideoPlayer/@refactoring/00-PLAN.md +91 -0
  145. package/src/tools/VideoPlayer/@refactoring/01-TYPES.md +284 -0
  146. package/src/tools/VideoPlayer/@refactoring/02-UTILS.md +141 -0
  147. package/src/tools/VideoPlayer/@refactoring/03-HOOKS.md +178 -0
  148. package/src/tools/VideoPlayer/@refactoring/04-COMPONENTS.md +95 -0
  149. package/src/tools/VideoPlayer/@refactoring/05-EXECUTION-CHECKLIST.md +139 -0
  150. package/src/tools/VideoPlayer/README.md +264 -0
  151. package/src/tools/VideoPlayer/components/VideoControls.tsx +138 -0
  152. package/src/tools/VideoPlayer/components/VideoErrorFallback.tsx +172 -0
  153. package/src/tools/VideoPlayer/components/VideoPlayer.tsx +201 -0
  154. package/src/tools/VideoPlayer/components/index.ts +14 -0
  155. package/src/tools/VideoPlayer/context/VideoPlayerContext.tsx +52 -0
  156. package/src/tools/VideoPlayer/context/index.ts +8 -0
  157. package/src/tools/VideoPlayer/hooks/index.ts +12 -0
  158. package/src/tools/VideoPlayer/hooks/useVideoPlayerSettings.ts +70 -0
  159. package/src/tools/VideoPlayer/hooks/useVideoPositionCache.ts +116 -0
  160. package/src/tools/VideoPlayer/index.ts +77 -0
  161. package/src/tools/VideoPlayer/providers/NativeProvider.tsx +284 -0
  162. package/src/tools/VideoPlayer/providers/StreamProvider.tsx +505 -0
  163. package/src/tools/VideoPlayer/providers/VidstackProvider.tsx +400 -0
  164. package/src/tools/VideoPlayer/providers/index.ts +8 -0
  165. package/src/tools/VideoPlayer/types/index.ts +38 -0
  166. package/src/tools/VideoPlayer/types/player.ts +116 -0
  167. package/src/tools/VideoPlayer/types/provider.ts +93 -0
  168. package/src/tools/VideoPlayer/types/sources.ts +97 -0
  169. package/src/tools/VideoPlayer/utils/debug.ts +14 -0
  170. package/src/tools/VideoPlayer/utils/fileSource.ts +78 -0
  171. package/src/tools/VideoPlayer/utils/index.ts +12 -0
  172. package/src/tools/VideoPlayer/utils/resolvers.ts +75 -0
  173. package/src/tools/_shared.ts +29 -0
  174. package/src/tools/index.ts +172 -0
@@ -0,0 +1,96 @@
1
+ "use client"
2
+
3
+ import React, { useCallback, useMemo } from 'react';
4
+
5
+ import { Input } from '@djangocfg/ui-core/components';
6
+ import { WidgetProps } from '@rjsf/utils';
7
+
8
+ /**
9
+ * Text input widget for JSON Schema Form
10
+ * Handles string fields with optional textarea for multiline
11
+ */
12
+ export function TextWidget(props: WidgetProps) {
13
+ const {
14
+ id,
15
+ placeholder,
16
+ required,
17
+ disabled,
18
+ readonly,
19
+ autofocus,
20
+ value,
21
+ onChange,
22
+ onBlur,
23
+ onFocus,
24
+ options,
25
+ schema,
26
+ rawErrors,
27
+ } = props;
28
+
29
+ // Memoize widget configuration
30
+ const config = useMemo(() => ({
31
+ isTextarea: options?.widget === 'textarea',
32
+ rows: options?.rows || 3,
33
+ emptyValue: options?.emptyValue,
34
+ }), [options]);
35
+
36
+ // Ensure value is always a string
37
+ const safeValue = useMemo(() => {
38
+ if (value === null || value === undefined) return '';
39
+ return String(value);
40
+ }, [value]);
41
+
42
+ const hasError = useMemo(() => {
43
+ return rawErrors && rawErrors.length > 0;
44
+ }, [rawErrors]);
45
+
46
+ const handleChange = useCallback((event: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>) => {
47
+ const newValue = event.target.value;
48
+ onChange(newValue === '' ? config.emptyValue : newValue);
49
+ }, [onChange, config.emptyValue]);
50
+
51
+ const handleBlur = useCallback((event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => {
52
+ onBlur(id, event.target.value);
53
+ }, [id, onBlur]);
54
+
55
+ const handleFocus = useCallback((event: React.FocusEvent<HTMLInputElement | HTMLTextAreaElement>) => {
56
+ onFocus(id, event.target.value);
57
+ }, [id, onFocus]);
58
+
59
+ if (config.isTextarea) {
60
+ return (
61
+ <textarea
62
+ id={id}
63
+ className={`flex min-h-[80px] w-full rounded-md border ${
64
+ hasError ? 'border-destructive' : 'border-input'
65
+ } bg-transparent px-3 py-2 text-base shadow-sm transition-colors placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50 md:text-sm`}
66
+ placeholder={placeholder}
67
+ disabled={disabled || readonly}
68
+ readOnly={readonly}
69
+ autoFocus={autofocus}
70
+ value={safeValue}
71
+ required={required}
72
+ onChange={handleChange}
73
+ onBlur={handleBlur}
74
+ onFocus={handleFocus}
75
+ rows={config.rows}
76
+ />
77
+ );
78
+ }
79
+
80
+ return (
81
+ <Input
82
+ id={id}
83
+ type="text"
84
+ placeholder={placeholder}
85
+ disabled={disabled}
86
+ readOnly={readonly}
87
+ autoFocus={autofocus}
88
+ value={safeValue}
89
+ required={required}
90
+ onChange={handleChange}
91
+ onBlur={handleBlur}
92
+ onFocus={handleFocus}
93
+ className={hasError ? 'border-destructive' : ''}
94
+ />
95
+ );
96
+ }
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Custom widgets for JSON Schema Form
3
+ *
4
+ * Each widget is a React component that renders a specific form input type
5
+ * using components from @djangocfg/ui
6
+ */
7
+
8
+ export { TextWidget } from './TextWidget';
9
+ export { NumberWidget } from './NumberWidget';
10
+ export { CheckboxWidget } from './CheckboxWidget';
11
+ export { SelectWidget } from './SelectWidget';
12
+ export { SwitchWidget } from './SwitchWidget';
13
+ export { ColorWidget } from './ColorWidget';
14
+ export { SliderWidget } from './SliderWidget';
@@ -0,0 +1,243 @@
1
+ 'use client';
2
+
3
+ import { ChevronDown, ChevronUp, Download } from 'lucide-react';
4
+ import React, { useState } from 'react';
5
+ import { CommonExternalProps, JSONTree } from 'react-json-tree';
6
+
7
+ import { Button, CopyButton } from '@djangocfg/ui-core/components';
8
+
9
+ export type { Language } from 'prism-react-renderer';
10
+
11
+ export interface JsonTreeConfig {
12
+ /** Maximum depth to expand automatically (default: 2) */
13
+ maxAutoExpandDepth?: number;
14
+ /** Maximum items in array to auto-expand (default: 10) */
15
+ maxAutoExpandArrayItems?: number;
16
+ /** Maximum object keys to auto-expand (default: 5) */
17
+ maxAutoExpandObjectKeys?: number;
18
+ /** Maximum string length before truncation (default: 200) */
19
+ maxStringLength?: number;
20
+ /** Collection limit for performance (default: 50) */
21
+ collectionLimit?: number;
22
+ /** Whether to show collection info (array length, object keys count) */
23
+ showCollectionInfo?: boolean;
24
+ /** Whether to show expand/collapse all buttons */
25
+ showExpandControls?: boolean;
26
+ /** Whether to show copy/download buttons */
27
+ showActionButtons?: boolean;
28
+ /** Custom CSS classes for the container */
29
+ className?: string;
30
+ /** Whether to preserve object key order (default: true) */
31
+ preserveKeyOrder?: boolean;
32
+ }
33
+
34
+ interface JsonTreeComponentProps {
35
+ title?: string;
36
+ data: unknown;
37
+ config?: JsonTreeConfig;
38
+ /** Override for react-json-tree props */
39
+ jsonTreeProps?: Partial<CommonExternalProps>;
40
+ }
41
+
42
+ const JsonTreeComponent = ({ title, data, config = {}, jsonTreeProps = {} }: JsonTreeComponentProps) => {
43
+ // State for expand/collapse all
44
+ const [expandAll, setExpandAll] = useState<boolean | null>(null);
45
+ const [renderKey, setRenderKey] = useState(0);
46
+
47
+ // Default configuration
48
+ const {
49
+ maxAutoExpandDepth = 2,
50
+ maxAutoExpandArrayItems = 10,
51
+ maxAutoExpandObjectKeys = 5,
52
+ maxStringLength = 200,
53
+ collectionLimit = 50,
54
+ showCollectionInfo = true,
55
+ showExpandControls = true,
56
+ showActionButtons = true,
57
+ className = '',
58
+ preserveKeyOrder = true,
59
+ } = config;
60
+
61
+ // JSON Tree theme optimized for dark theme
62
+ const jsonTreeTheme = {
63
+ scheme: 'djangocfg-dark',
64
+ base00: 'transparent', // Background
65
+ base01: '#1a1a1a', // Lighter background
66
+ base02: '#2a2a2a', // Selection background
67
+ base03: '#6b7280', // Comments, invisibles
68
+ base04: '#9ca3af', // Dark foreground
69
+ base05: '#e5e7eb', // Default foreground
70
+ base06: '#f3f4f6', // Light foreground
71
+ base07: '#ffffff', // Lightest foreground
72
+ base08: '#ef4444', // Red - for null, undefined
73
+ base09: '#f97316', // Orange - for numbers
74
+ base0A: '#eab308', // Yellow - for strings
75
+ base0B: '#22c55e', // Green - for booleans (true)
76
+ base0C: '#06b6d4', // Cyan - for dates, regex
77
+ base0D: '#3b82f6', // Blue - for keys
78
+ base0E: '#a855f7', // Purple - for functions
79
+ base0F: '#f43f5e', // Pink - for deprecations
80
+ };
81
+
82
+ // Smart expansion logic
83
+ const shouldExpandNodeInitially = (keyPath: readonly (string | number)[], nodeData: unknown, level: number) => {
84
+ // If user explicitly clicked "Expand All", expand everything
85
+ if (expandAll === true) return true;
86
+
87
+ // If user explicitly clicked "Collapse All", collapse everything
88
+ if (expandAll === false) return false;
89
+
90
+ // Default auto-expansion (expandAll === null)
91
+ // Always expand up to maxAutoExpandDepth
92
+ if (level <= maxAutoExpandDepth) return true;
93
+
94
+ // For arrays, expand if they have less than maxAutoExpandArrayItems
95
+ if (Array.isArray(nodeData) && nodeData.length <= maxAutoExpandArrayItems) return true;
96
+
97
+ // For objects, expand if they have less than maxAutoExpandObjectKeys
98
+ if (nodeData && typeof nodeData === 'object' && !Array.isArray(nodeData)) {
99
+ const keys = Object.keys(nodeData);
100
+ return keys.length <= maxAutoExpandObjectKeys;
101
+ }
102
+
103
+ return false;
104
+ };
105
+
106
+ // Collection info display
107
+ const getItemString = showCollectionInfo
108
+ ? (nodeType: string, nodeData: unknown) => {
109
+ if (nodeType === 'Array') {
110
+ const length = Array.isArray(nodeData) ? nodeData.length : 0;
111
+ return length > 0 ? <span className="text-muted-foreground text-sm">({length} items)</span> : null;
112
+ }
113
+ if (nodeType === 'Object') {
114
+ const keys = nodeData && typeof nodeData === 'object' ? Object.keys(nodeData) : [];
115
+ return keys.length > 0 ? <span className="text-muted-foreground text-sm">({keys.length} keys)</span> : null;
116
+ }
117
+ return null;
118
+ }
119
+ : () => null;
120
+
121
+ // Value processing for better display
122
+ const postprocessValue = (value: unknown) => {
123
+ // Truncate very long strings
124
+ if (typeof value === 'string' && value.length > maxStringLength) {
125
+ return value.substring(0, maxStringLength) + '... (truncated)';
126
+ }
127
+ return value;
128
+ };
129
+
130
+ // Custom node detection for special formatting
131
+ const isCustomNode = (value: unknown) => {
132
+ // Mark URLs as custom nodes for special styling
133
+ if (typeof value === 'string' && (value.startsWith('http://') || value.startsWith('https://'))) {
134
+ return true;
135
+ }
136
+ return false;
137
+ };
138
+
139
+ // JSON string for copy/download
140
+ const jsonString = JSON.stringify(data, null, 2);
141
+
142
+ // Action handlers
143
+ const handleDownload = () => {
144
+ const blob = new Blob([jsonString], { type: 'application/json' });
145
+ const url = URL.createObjectURL(blob);
146
+ const a = document.createElement('a');
147
+ a.href = url;
148
+ a.download = 'data.json';
149
+ document.body.appendChild(a);
150
+ a.click();
151
+ document.body.removeChild(a);
152
+ URL.revokeObjectURL(url);
153
+ };
154
+
155
+ return (
156
+ <div className={`relative border border-border rounded-sm h-full overflow-hidden ${className}`}>
157
+ {/* Header with title and controls */}
158
+ {(title || showExpandControls || showActionButtons) && (
159
+ <div className="p-4 border-b border-border bg-muted/50 rounded-t-sm">
160
+ <div className="flex items-center justify-between">
161
+ {title && (
162
+ <h6 className="text-lg font-semibold text-foreground">{title}</h6>
163
+ )}
164
+
165
+ {(showExpandControls || showActionButtons) && (
166
+ <div className="flex items-center space-x-2">
167
+ {/* Expand/Collapse Controls */}
168
+ {showExpandControls && (
169
+ <Button
170
+ variant={expandAll === true ? "default" : "outline"}
171
+ size="sm"
172
+ onClick={() => {
173
+ const newState = expandAll === true ? false : true;
174
+ setExpandAll(newState);
175
+ setRenderKey(prev => prev + 1);
176
+ }}
177
+ className="h-8 px-2"
178
+ >
179
+ {expandAll === true ? (
180
+ <>
181
+ <ChevronUp className="h-3 w-3" />
182
+ <span className="ml-1 text-xs">Collapse All</span>
183
+ </>
184
+ ) : (
185
+ <>
186
+ <ChevronDown className="h-3 w-3" />
187
+ <span className="ml-1 text-xs">Expand All</span>
188
+ </>
189
+ )}
190
+ </Button>
191
+ )}
192
+
193
+ {/* Action Buttons */}
194
+ {showActionButtons && (
195
+ <>
196
+ <CopyButton
197
+ value={jsonString}
198
+ variant="outline"
199
+ size="sm"
200
+ className="h-8 px-2"
201
+ iconClassName="h-3 w-3"
202
+ >
203
+ Copy
204
+ </CopyButton>
205
+ <Button
206
+ variant="outline"
207
+ size="sm"
208
+ onClick={handleDownload}
209
+ className="h-8 px-2"
210
+ >
211
+ <Download className="h-3 w-3" />
212
+ <span className="ml-1 text-xs hidden sm:inline">Download</span>
213
+ </Button>
214
+ </>
215
+ )}
216
+ </div>
217
+ )}
218
+ </div>
219
+ </div>
220
+ )}
221
+
222
+ {/* JSON Tree Content */}
223
+ <div className="h-full overflow-auto p-4">
224
+ <JSONTree
225
+ key={renderKey} // Force re-render when expand/collapse state changes
226
+ data={data}
227
+ theme={jsonTreeTheme}
228
+ invertTheme={false}
229
+ hideRoot={true}
230
+ shouldExpandNodeInitially={shouldExpandNodeInitially}
231
+ getItemString={getItemString}
232
+ postprocessValue={postprocessValue}
233
+ isCustomNode={isCustomNode}
234
+ collectionLimit={collectionLimit}
235
+ sortObjectKeys={!preserveKeyOrder}
236
+ {...jsonTreeProps}
237
+ />
238
+ </div>
239
+ </div>
240
+ );
241
+ };
242
+
243
+ export default JsonTreeComponent;
@@ -0,0 +1,213 @@
1
+ /**
2
+ * LottiePlayer Component
3
+ *
4
+ * Universal Lottie animation player component
5
+ */
6
+
7
+ 'use client';
8
+
9
+ import React from 'react';
10
+ import Lottie from 'react-lottie-player';
11
+
12
+ import { LottiePlayerProps } from './types';
13
+ import { useLottie } from './useLottie';
14
+
15
+ // Size presets mapping
16
+ const SIZE_PRESETS = {
17
+ xs: { width: 64, height: 64 },
18
+ sm: { width: 128, height: 128 },
19
+ md: { width: 256, height: 256 },
20
+ lg: { width: 384, height: 384 },
21
+ xl: { width: 512, height: 512 },
22
+ full: { width: '100%', height: '100%' },
23
+ } as const;
24
+
25
+ /**
26
+ * LottiePlayer component for displaying Lottie animations
27
+ *
28
+ * Features:
29
+ * - Loads animations from URLs or objects
30
+ * - Size presets or custom dimensions
31
+ * - Playback controls (speed, direction, loop)
32
+ * - Loading and error states
33
+ * - Event callbacks
34
+ *
35
+ * Usage:
36
+ * ```tsx
37
+ * // From URL with size preset
38
+ * <LottiePlayer
39
+ * src="https://example.com/animation.json"
40
+ * size="md"
41
+ * autoplay
42
+ * loop
43
+ * />
44
+ *
45
+ * // From object with custom size
46
+ * <LottiePlayer
47
+ * src={animationData}
48
+ * width={300}
49
+ * height={300}
50
+ * speed={1.5}
51
+ * controls
52
+ * />
53
+ *
54
+ * // With callbacks
55
+ * <LottiePlayer
56
+ * src="https://example.com/animation.json"
57
+ * onLoad={() => console.log('Animation loaded')}
58
+ * onComplete={() => console.log('Animation completed')}
59
+ * onError={(err) => console.error('Error:', err)}
60
+ * />
61
+ * ```
62
+ */
63
+ export function LottiePlayer({
64
+ src,
65
+ size = 'md',
66
+ width,
67
+ height,
68
+ autoplay = true,
69
+ loop = true,
70
+ speed = 1,
71
+ direction = 1,
72
+ controls = false,
73
+ background,
74
+ className,
75
+ showLoading = true,
76
+ onComplete,
77
+ onLoad,
78
+ onError,
79
+ }: LottiePlayerProps) {
80
+ // Load animation data using our custom hook
81
+ const { animationData, isLoading, error, retry } = useLottie({ src });
82
+
83
+ // Notify parent about load state
84
+ React.useEffect(() => {
85
+ if (animationData && onLoad) {
86
+ onLoad();
87
+ }
88
+ }, [animationData, onLoad]);
89
+
90
+ // Notify parent about errors
91
+ React.useEffect(() => {
92
+ if (error && onError) {
93
+ onError(error);
94
+ }
95
+ }, [error, onError]);
96
+
97
+ // Determine dimensions
98
+ const dimensions = React.useMemo(() => {
99
+ // Custom dimensions override size preset
100
+ if (width !== undefined || height !== undefined) {
101
+ return {
102
+ width: width ?? SIZE_PRESETS[size].width,
103
+ height: height ?? SIZE_PRESETS[size].height,
104
+ };
105
+ }
106
+
107
+ // Use size preset
108
+ return SIZE_PRESETS[size];
109
+ }, [size, width, height]);
110
+
111
+ // Handle complete event
112
+ const handleComplete = React.useCallback(() => {
113
+ if (onComplete) {
114
+ onComplete();
115
+ }
116
+ }, [onComplete]);
117
+
118
+ // Loading state
119
+ if (isLoading && showLoading) {
120
+ return (
121
+ <div
122
+ className={`flex items-center justify-center ${className || ''}`}
123
+ style={{
124
+ width: dimensions.width,
125
+ height: dimensions.height,
126
+ background: background || 'transparent',
127
+ }}
128
+ >
129
+ <div className="flex flex-col items-center gap-2">
130
+ <div className="h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-gray-900" />
131
+ <span className="text-sm text-gray-500">Loading animation...</span>
132
+ </div>
133
+ </div>
134
+ );
135
+ }
136
+
137
+ // Error state
138
+ if (error) {
139
+ return (
140
+ <div
141
+ className={`flex items-center justify-center ${className || ''}`}
142
+ style={{
143
+ width: dimensions.width,
144
+ height: dimensions.height,
145
+ background: background || 'transparent',
146
+ }}
147
+ >
148
+ <div className="flex flex-col items-center gap-2 p-4 text-center">
149
+ <svg
150
+ className="h-8 w-8 text-red-500"
151
+ fill="none"
152
+ stroke="currentColor"
153
+ viewBox="0 0 24 24"
154
+ >
155
+ <path
156
+ strokeLinecap="round"
157
+ strokeLinejoin="round"
158
+ strokeWidth={2}
159
+ d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"
160
+ />
161
+ </svg>
162
+ <div className="text-sm text-red-600">{error.message}</div>
163
+ <button
164
+ onClick={retry}
165
+ className="rounded bg-red-100 px-3 py-1 text-sm text-red-700 hover:bg-red-200"
166
+ >
167
+ Retry
168
+ </button>
169
+ </div>
170
+ </div>
171
+ );
172
+ }
173
+
174
+ // No animation data
175
+ if (!animationData) {
176
+ return null;
177
+ }
178
+
179
+ // Render the Lottie player
180
+ return (
181
+ <div
182
+ className={className}
183
+ style={{
184
+ width: dimensions.width,
185
+ height: dimensions.height,
186
+ background: background || 'transparent',
187
+ }}
188
+ >
189
+ <Lottie
190
+ animationData={animationData}
191
+ play={autoplay}
192
+ loop={loop}
193
+ speed={speed}
194
+ direction={direction}
195
+ style={{
196
+ width: '100%',
197
+ height: '100%',
198
+ }}
199
+ onComplete={handleComplete}
200
+ rendererSettings={{
201
+ preserveAspectRatio: 'xMidYMid meet',
202
+ }}
203
+ />
204
+ {controls && (
205
+ <div className="mt-2 flex items-center justify-center gap-2">
206
+ <span className="text-xs text-gray-500">
207
+ Speed: {speed}x | Direction: {direction === 1 ? 'Forward' : 'Reverse'}
208
+ </span>
209
+ </div>
210
+ )}
211
+ </div>
212
+ );
213
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * LottiePlayer - Dynamic Import Wrapper
3
+ *
4
+ * Lazy loads the LottiePlayer component for optimal bundle size
5
+ */
6
+
7
+ 'use client';
8
+
9
+ import React, { lazy, Suspense } from 'react';
10
+ import { LottiePlayerProps } from './types';
11
+
12
+ // Lazy load the client component
13
+ const LottiePlayerClient = lazy(() =>
14
+ import('./LottiePlayer.client').then((mod) => ({ default: mod.LottiePlayer }))
15
+ );
16
+
17
+ // Loading fallback component
18
+ const LoadingFallback = () => (
19
+ <div className="flex items-center justify-center p-8">
20
+ <div className="flex flex-col items-center gap-2">
21
+ <div className="h-8 w-8 animate-spin rounded-full border-4 border-gray-300 border-t-gray-900" />
22
+ <span className="text-sm text-gray-500">Loading player...</span>
23
+ </div>
24
+ </div>
25
+ );
26
+
27
+ /**
28
+ * LottiePlayer component wrapper with dynamic import
29
+ *
30
+ * This component automatically handles code splitting and lazy loading
31
+ * of the Lottie player to optimize bundle size.
32
+ *
33
+ * Usage:
34
+ * ```tsx
35
+ * import { LottiePlayer } from '@djangocfg/ui-core/tools';
36
+ *
37
+ * <LottiePlayer
38
+ * src="https://example.com/animation.json"
39
+ * size="md"
40
+ * autoplay
41
+ * loop
42
+ * />
43
+ * ```
44
+ */
45
+ export function LottiePlayer(props: LottiePlayerProps) {
46
+ return (
47
+ <Suspense fallback={<LoadingFallback />}>
48
+ <LottiePlayerClient {...props} />
49
+ </Suspense>
50
+ );
51
+ }
52
+
53
+ // Re-export types for convenience
54
+ export type { LottiePlayerProps, LottieSize, LottieSpeed, LottieDirection } from './types';
55
+ export { useLottie } from './useLottie';
56
+ export type { UseLottieOptions, UseLottieReturn } from './useLottie';