@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.
- package/dist/LottiePlayer.client-LBEC2JKY.mjs +161 -0
- package/dist/LottiePlayer.client-LBEC2JKY.mjs.map +1 -0
- package/dist/LottiePlayer.client-WFMG2OOW.cjs +168 -0
- package/dist/LottiePlayer.client-WFMG2OOW.cjs.map +1 -0
- package/dist/Mermaid.client-4TU2TSH3.mjs +477 -0
- package/dist/Mermaid.client-4TU2TSH3.mjs.map +1 -0
- package/dist/Mermaid.client-SBYY364Q.cjs +483 -0
- package/dist/Mermaid.client-SBYY364Q.cjs.map +1 -0
- package/dist/PlaygroundLayout-3YVSAEAF.cjs +1003 -0
- package/dist/PlaygroundLayout-3YVSAEAF.cjs.map +1 -0
- package/dist/PlaygroundLayout-4DYBORAS.mjs +996 -0
- package/dist/PlaygroundLayout-4DYBORAS.mjs.map +1 -0
- package/dist/PrettyCode.client-LCBPPTIX.mjs +152 -0
- package/dist/PrettyCode.client-LCBPPTIX.mjs.map +1 -0
- package/dist/PrettyCode.client-PNPLXRH6.cjs +154 -0
- package/dist/PrettyCode.client-PNPLXRH6.cjs.map +1 -0
- package/dist/chunk-37ZI6VD4.mjs +12 -0
- package/dist/chunk-37ZI6VD4.mjs.map +1 -0
- package/dist/chunk-3HK2OE62.cjs +81 -0
- package/dist/chunk-3HK2OE62.cjs.map +1 -0
- package/dist/chunk-7DGDQVQW.cjs +591 -0
- package/dist/chunk-7DGDQVQW.cjs.map +1 -0
- package/dist/chunk-M6P2FU7L.mjs +572 -0
- package/dist/chunk-M6P2FU7L.mjs.map +1 -0
- package/dist/chunk-UQ3XI5MY.cjs +15 -0
- package/dist/chunk-UQ3XI5MY.cjs.map +1 -0
- package/dist/chunk-YFRNE2IR.mjs +79 -0
- package/dist/chunk-YFRNE2IR.mjs.map +1 -0
- package/dist/index.cjs +5042 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +1591 -0
- package/dist/index.d.ts +1591 -0
- package/dist/index.mjs +4941 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +86 -0
- package/src/components/markdown/MarkdownMessage.tsx +340 -0
- package/src/components/markdown/index.ts +5 -0
- package/src/index.ts +26 -0
- package/src/stores/index.ts +9 -0
- package/src/stores/mediaCache.ts +534 -0
- package/src/tools/AudioPlayer/README.md +206 -0
- package/src/tools/AudioPlayer/components/HybridAudioPlayer.tsx +216 -0
- package/src/tools/AudioPlayer/components/HybridSimplePlayer.tsx +280 -0
- package/src/tools/AudioPlayer/components/HybridWaveform.tsx +279 -0
- package/src/tools/AudioPlayer/components/ReactiveCover/AudioReactiveCover.tsx +149 -0
- package/src/tools/AudioPlayer/components/ReactiveCover/effects/GlowEffect.tsx +110 -0
- package/src/tools/AudioPlayer/components/ReactiveCover/effects/MeshEffect.tsx +58 -0
- package/src/tools/AudioPlayer/components/ReactiveCover/effects/OrbsEffect.tsx +45 -0
- package/src/tools/AudioPlayer/components/ReactiveCover/effects/SpotlightEffect.tsx +82 -0
- package/src/tools/AudioPlayer/components/ReactiveCover/effects/index.ts +8 -0
- package/src/tools/AudioPlayer/components/ReactiveCover/index.ts +6 -0
- package/src/tools/AudioPlayer/components/index.ts +22 -0
- package/src/tools/AudioPlayer/context/HybridAudioProvider.tsx +158 -0
- package/src/tools/AudioPlayer/context/index.ts +16 -0
- package/src/tools/AudioPlayer/effects/index.ts +412 -0
- package/src/tools/AudioPlayer/hooks/index.ts +35 -0
- package/src/tools/AudioPlayer/hooks/useHybridAudio.ts +387 -0
- package/src/tools/AudioPlayer/hooks/useHybridAudioAnalysis.ts +95 -0
- package/src/tools/AudioPlayer/hooks/useVisualization.tsx +207 -0
- package/src/tools/AudioPlayer/index.ts +133 -0
- package/src/tools/AudioPlayer/types/effects.ts +73 -0
- package/src/tools/AudioPlayer/types/index.ts +27 -0
- package/src/tools/AudioPlayer/utils/debug.ts +14 -0
- package/src/tools/AudioPlayer/utils/formatTime.ts +10 -0
- package/src/tools/AudioPlayer/utils/index.ts +6 -0
- package/src/tools/ImageViewer/@refactoring/00-PLAN.md +71 -0
- package/src/tools/ImageViewer/@refactoring/01-TYPES.md +121 -0
- package/src/tools/ImageViewer/@refactoring/02-UTILS.md +143 -0
- package/src/tools/ImageViewer/@refactoring/03-HOOKS.md +261 -0
- package/src/tools/ImageViewer/@refactoring/04-COMPONENTS.md +427 -0
- package/src/tools/ImageViewer/@refactoring/05-EXECUTION-CHECKLIST.md +126 -0
- package/src/tools/ImageViewer/README.md +200 -0
- package/src/tools/ImageViewer/components/ImageInfo.tsx +44 -0
- package/src/tools/ImageViewer/components/ImageToolbar.tsx +145 -0
- package/src/tools/ImageViewer/components/ImageViewer.tsx +241 -0
- package/src/tools/ImageViewer/components/index.ts +7 -0
- package/src/tools/ImageViewer/hooks/index.ts +9 -0
- package/src/tools/ImageViewer/hooks/useImageLoading.ts +204 -0
- package/src/tools/ImageViewer/hooks/useImageTransform.ts +101 -0
- package/src/tools/ImageViewer/index.ts +60 -0
- package/src/tools/ImageViewer/types.ts +81 -0
- package/src/tools/ImageViewer/utils/constants.ts +59 -0
- package/src/tools/ImageViewer/utils/debug.ts +14 -0
- package/src/tools/ImageViewer/utils/index.ts +17 -0
- package/src/tools/ImageViewer/utils/lqip.ts +47 -0
- package/src/tools/JsonForm/JsonSchemaForm.tsx +197 -0
- package/src/tools/JsonForm/examples/BotConfigExample.tsx +249 -0
- package/src/tools/JsonForm/examples/RealBotConfigExample.tsx +161 -0
- package/src/tools/JsonForm/index.ts +46 -0
- package/src/tools/JsonForm/templates/ArrayFieldItemTemplate.tsx +47 -0
- package/src/tools/JsonForm/templates/ArrayFieldTemplate.tsx +74 -0
- package/src/tools/JsonForm/templates/BaseInputTemplate.tsx +107 -0
- package/src/tools/JsonForm/templates/ErrorListTemplate.tsx +35 -0
- package/src/tools/JsonForm/templates/FieldTemplate.tsx +62 -0
- package/src/tools/JsonForm/templates/ObjectFieldTemplate.tsx +116 -0
- package/src/tools/JsonForm/templates/index.ts +12 -0
- package/src/tools/JsonForm/types.ts +83 -0
- package/src/tools/JsonForm/utils.ts +213 -0
- package/src/tools/JsonForm/widgets/CheckboxWidget.tsx +37 -0
- package/src/tools/JsonForm/widgets/ColorWidget.tsx +219 -0
- package/src/tools/JsonForm/widgets/NumberWidget.tsx +89 -0
- package/src/tools/JsonForm/widgets/SelectWidget.tsx +97 -0
- package/src/tools/JsonForm/widgets/SliderWidget.tsx +148 -0
- package/src/tools/JsonForm/widgets/SwitchWidget.tsx +35 -0
- package/src/tools/JsonForm/widgets/TextWidget.tsx +96 -0
- package/src/tools/JsonForm/widgets/index.ts +14 -0
- package/src/tools/JsonTree/index.tsx +243 -0
- package/src/tools/LottiePlayer/LottiePlayer.client.tsx +213 -0
- package/src/tools/LottiePlayer/index.tsx +56 -0
- package/src/tools/LottiePlayer/types.ts +108 -0
- package/src/tools/LottiePlayer/useLottie.ts +164 -0
- package/src/tools/Mermaid/Mermaid.client.tsx +82 -0
- package/src/tools/Mermaid/components/MermaidCodeViewer.tsx +95 -0
- package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +103 -0
- package/src/tools/Mermaid/hooks/index.ts +4 -0
- package/src/tools/Mermaid/hooks/useMermaidCleanup.ts +73 -0
- package/src/tools/Mermaid/hooks/useMermaidFullscreen.ts +46 -0
- package/src/tools/Mermaid/hooks/useMermaidRenderer.ts +226 -0
- package/src/tools/Mermaid/hooks/useMermaidValidation.ts +29 -0
- package/src/tools/Mermaid/index.tsx +44 -0
- package/src/tools/Mermaid/utils/mermaid-helpers.ts +33 -0
- package/src/tools/OpenapiViewer/components/EndpointInfo.tsx +149 -0
- package/src/tools/OpenapiViewer/components/EndpointsLibrary.tsx +263 -0
- package/src/tools/OpenapiViewer/components/PlaygroundLayout.tsx +125 -0
- package/src/tools/OpenapiViewer/components/PlaygroundStepper.tsx +100 -0
- package/src/tools/OpenapiViewer/components/RequestBuilder.tsx +157 -0
- package/src/tools/OpenapiViewer/components/RequestParametersForm.tsx +253 -0
- package/src/tools/OpenapiViewer/components/ResponseViewer.tsx +173 -0
- package/src/tools/OpenapiViewer/components/VersionSelector.tsx +68 -0
- package/src/tools/OpenapiViewer/components/index.ts +14 -0
- package/src/tools/OpenapiViewer/constants.ts +39 -0
- package/src/tools/OpenapiViewer/context/PlaygroundContext.tsx +337 -0
- package/src/tools/OpenapiViewer/hooks/index.ts +8 -0
- package/src/tools/OpenapiViewer/hooks/useMobile.ts +10 -0
- package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +199 -0
- package/src/tools/OpenapiViewer/index.tsx +37 -0
- package/src/tools/OpenapiViewer/types.ts +151 -0
- package/src/tools/OpenapiViewer/utils/apiKeyManager.ts +149 -0
- package/src/tools/OpenapiViewer/utils/formatters.ts +71 -0
- package/src/tools/OpenapiViewer/utils/index.ts +9 -0
- package/src/tools/OpenapiViewer/utils/versionManager.ts +161 -0
- package/src/tools/PrettyCode/PrettyCode.client.tsx +208 -0
- package/src/tools/PrettyCode/index.tsx +47 -0
- package/src/tools/VideoPlayer/@refactoring/00-PLAN.md +91 -0
- package/src/tools/VideoPlayer/@refactoring/01-TYPES.md +284 -0
- package/src/tools/VideoPlayer/@refactoring/02-UTILS.md +141 -0
- package/src/tools/VideoPlayer/@refactoring/03-HOOKS.md +178 -0
- package/src/tools/VideoPlayer/@refactoring/04-COMPONENTS.md +95 -0
- package/src/tools/VideoPlayer/@refactoring/05-EXECUTION-CHECKLIST.md +139 -0
- package/src/tools/VideoPlayer/README.md +264 -0
- package/src/tools/VideoPlayer/components/VideoControls.tsx +138 -0
- package/src/tools/VideoPlayer/components/VideoErrorFallback.tsx +172 -0
- package/src/tools/VideoPlayer/components/VideoPlayer.tsx +201 -0
- package/src/tools/VideoPlayer/components/index.ts +14 -0
- package/src/tools/VideoPlayer/context/VideoPlayerContext.tsx +52 -0
- package/src/tools/VideoPlayer/context/index.ts +8 -0
- package/src/tools/VideoPlayer/hooks/index.ts +12 -0
- package/src/tools/VideoPlayer/hooks/useVideoPlayerSettings.ts +70 -0
- package/src/tools/VideoPlayer/hooks/useVideoPositionCache.ts +116 -0
- package/src/tools/VideoPlayer/index.ts +77 -0
- package/src/tools/VideoPlayer/providers/NativeProvider.tsx +284 -0
- package/src/tools/VideoPlayer/providers/StreamProvider.tsx +505 -0
- package/src/tools/VideoPlayer/providers/VidstackProvider.tsx +400 -0
- package/src/tools/VideoPlayer/providers/index.ts +8 -0
- package/src/tools/VideoPlayer/types/index.ts +38 -0
- package/src/tools/VideoPlayer/types/player.ts +116 -0
- package/src/tools/VideoPlayer/types/provider.ts +93 -0
- package/src/tools/VideoPlayer/types/sources.ts +97 -0
- package/src/tools/VideoPlayer/utils/debug.ts +14 -0
- package/src/tools/VideoPlayer/utils/fileSource.ts +78 -0
- package/src/tools/VideoPlayer/utils/index.ts +12 -0
- package/src/tools/VideoPlayer/utils/resolvers.ts +75 -0
- package/src/tools/_shared.ts +29 -0
- 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';
|