@djangocfg/ui-tools 2.1.407 → 2.1.409
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 +9 -10
- package/dist/file-icon/index.cjs +449 -61
- package/dist/file-icon/index.cjs.map +1 -1
- package/dist/file-icon/index.d.cts +56 -18
- package/dist/file-icon/index.d.ts +56 -18
- package/dist/file-icon/index.mjs +448 -62
- package/dist/file-icon/index.mjs.map +1 -1
- package/dist/tree/index.cjs +49 -22
- package/dist/tree/index.cjs.map +1 -1
- package/dist/tree/index.d.cts +9 -3
- package/dist/tree/index.d.ts +9 -3
- package/dist/tree/index.mjs +49 -22
- package/dist/tree/index.mjs.map +1 -1
- package/dist/{types-B_zhyAqR.d.cts → types-eEu8SeiQ.d.cts} +4 -0
- package/dist/{types-B_zhyAqR.d.ts → types-eEu8SeiQ.d.ts} +4 -0
- package/package.json +8 -13
- package/src/components/FloatingToolbar/index.tsx +37 -3
- package/src/lib/page-snapshot/__tests__/capture-integration.test.ts +85 -0
- package/src/lib/page-snapshot/__tests__/engine.test.ts +36 -0
- package/src/lib/page-snapshot/__tests__/redaction-integration.test.ts +99 -0
- package/src/lib/page-snapshot/__tests__/tokens.test.ts +17 -0
- package/src/lib/page-snapshot/capture/__tests__/budget.test.ts +49 -0
- package/src/lib/page-snapshot/capture/__tests__/chrome-filter.test.ts +47 -0
- package/src/lib/page-snapshot/capture/__tests__/fold.test.ts +66 -0
- package/src/lib/page-snapshot/capture/__tests__/scope.test.ts +74 -0
- package/src/lib/page-snapshot/capture/__tests__/walk.test.ts +129 -0
- package/src/lib/page-snapshot/capture/accessible-name.ts +73 -0
- package/src/lib/page-snapshot/capture/budget.ts +95 -0
- package/src/lib/page-snapshot/capture/chrome-filter.ts +81 -0
- package/src/lib/page-snapshot/capture/classify.ts +111 -0
- package/src/lib/page-snapshot/capture/dom-utils.ts +111 -0
- package/src/lib/page-snapshot/capture/fold.ts +96 -0
- package/src/lib/page-snapshot/capture/scope.ts +169 -0
- package/src/lib/page-snapshot/capture/walk.ts +250 -0
- package/src/lib/page-snapshot/cst/__tests__/serialize.test.ts +50 -0
- package/src/lib/page-snapshot/cst/directives.ts +47 -0
- package/src/lib/page-snapshot/cst/payload.ts +50 -0
- package/src/lib/page-snapshot/cst/serialize.ts +84 -0
- package/src/lib/page-snapshot/cst/types.ts +115 -0
- package/src/lib/page-snapshot/engine.ts +176 -0
- package/src/lib/page-snapshot/index.ts +93 -0
- package/src/lib/page-snapshot/react/PageSnapshotChip.tsx +72 -0
- package/src/lib/page-snapshot/react/PageSnapshotPreview.tsx +78 -0
- package/src/lib/page-snapshot/react/__tests__/PageSnapshotChip.test.tsx +54 -0
- package/src/lib/page-snapshot/react/__tests__/provider.test.tsx +103 -0
- package/src/lib/page-snapshot/react/__tests__/use-page-snapshot-toggle.test.tsx +62 -0
- package/src/lib/page-snapshot/react/provider.tsx +162 -0
- package/src/lib/page-snapshot/react/use-page-snapshot-toggle.ts +47 -0
- package/src/lib/page-snapshot/react/use-page-snapshot.ts +67 -0
- package/src/lib/page-snapshot/redaction/__tests__/audit.test.ts +25 -0
- package/src/lib/page-snapshot/redaction/__tests__/heuristics.test.ts +73 -0
- package/src/lib/page-snapshot/redaction/__tests__/luhn.test.ts +26 -0
- package/src/lib/page-snapshot/redaction/__tests__/patterns.test.ts +60 -0
- package/src/lib/page-snapshot/redaction/audit.ts +58 -0
- package/src/lib/page-snapshot/redaction/heuristics.ts +75 -0
- package/src/lib/page-snapshot/redaction/index.ts +75 -0
- package/src/lib/page-snapshot/redaction/luhn.ts +25 -0
- package/src/lib/page-snapshot/redaction/patterns.ts +111 -0
- package/src/lib/page-snapshot/refs/__tests__/registry.test.ts +24 -0
- package/src/lib/page-snapshot/refs/registry.ts +46 -0
- package/src/lib/page-snapshot/staleness/__tests__/hash.test.ts +34 -0
- package/src/lib/page-snapshot/staleness/hash.ts +20 -0
- package/src/lib/page-snapshot/tokens.ts +15 -0
- package/src/tools/AudioPlayer/context/PlayerProvider.tsx +13 -14
- package/src/tools/AudioPlayer/hooks/useAudioElementEvents.ts +55 -6
- package/src/tools/AudioPlayer/parts/Meta/TimeDisplay.tsx +2 -5
- package/src/tools/Chat/README.md +277 -39
- package/src/tools/Chat/composer/Composer.tsx +471 -0
- package/src/tools/Chat/composer/ComposerActionBar.tsx +65 -0
- package/src/tools/Chat/composer/ComposerBanner.tsx +128 -0
- package/src/tools/Chat/composer/ComposerButton.tsx +64 -0
- package/src/tools/Chat/composer/ComposerFooter.tsx +90 -0
- package/src/tools/Chat/composer/ComposerMenuButton.tsx +62 -0
- package/src/tools/Chat/composer/ComposerModelPicker.tsx +104 -0
- package/src/tools/Chat/composer/ComposerRichTextarea.tsx +88 -0
- package/src/tools/Chat/composer/ComposerToolPill.tsx +95 -0
- package/src/tools/Chat/composer/index.ts +45 -0
- package/src/tools/Chat/composer/size-context.tsx +26 -0
- package/src/tools/Chat/composer/types.ts +143 -0
- package/src/tools/Chat/composer/useComposerActions.tsx +164 -0
- package/src/tools/Chat/context/ChatProvider.tsx +54 -3
- package/src/tools/Chat/core/__tests__/metadata.test.ts +69 -0
- package/src/tools/Chat/core/index.ts +23 -1
- package/src/tools/Chat/core/markdown.ts +1 -1
- package/src/tools/Chat/core/metadata.ts +47 -0
- package/src/tools/Chat/core/payload-dispatch.ts +1 -1
- package/src/tools/Chat/core/transport/http.ts +71 -32
- package/src/tools/Chat/core/transport/sse.ts +18 -10
- package/src/tools/Chat/highlight/HighlightOverlay.tsx +101 -0
- package/src/tools/Chat/highlight/README.md +103 -0
- package/src/tools/Chat/highlight/SpotlightCanvas.tsx +153 -0
- package/src/tools/Chat/highlight/__tests__/HighlightOverlay.test.tsx +112 -0
- package/src/tools/Chat/highlight/__tests__/resolveRef.test.ts +55 -0
- package/src/tools/Chat/highlight/index.ts +21 -0
- package/src/tools/Chat/highlight/resolveRef.ts +42 -0
- package/src/tools/Chat/highlight/types.ts +49 -0
- package/src/tools/Chat/highlight/useHighlightTargets.ts +128 -0
- package/src/tools/Chat/hooks/index.ts +0 -5
- package/src/tools/Chat/hooks/useAutoFocusOnStreamEnd.ts +28 -47
- package/src/tools/Chat/hooks/useChat.ts +47 -14
- package/src/tools/Chat/hooks/useChatComposer.ts +2 -2
- package/src/tools/Chat/hooks/useChatLayout.ts +1 -1
- package/src/tools/Chat/hooks/useStreamEndFocus.ts +54 -0
- package/src/tools/Chat/index.ts +25 -219
- package/src/tools/Chat/launcher/ChatDock.tsx +1 -1
- package/src/tools/Chat/launcher/ChatLauncher.tsx +1 -1
- package/src/tools/Chat/launcher/{ChatHeader.tsx → header/ChatHeader.tsx} +24 -11
- package/src/tools/Chat/launcher/{ChatHeaderActionButton.tsx → header/ChatHeaderActionButton.tsx} +34 -3
- package/src/tools/Chat/launcher/{ChatHeaderLanguageButton.tsx → header/ChatHeaderLanguageButton.tsx} +2 -2
- package/src/tools/Chat/launcher/{ChatHeaderModeToggle.tsx → header/ChatHeaderModeToggle.tsx} +1 -1
- package/src/tools/Chat/launcher/{ChatHeaderResetButton.tsx → header/ChatHeaderResetButton.tsx} +2 -1
- package/src/tools/Chat/launcher/{HeaderSlots.tsx → header/HeaderSlots.tsx} +3 -3
- package/src/tools/Chat/launcher/header/index.ts +26 -0
- package/src/tools/Chat/launcher/index.ts +3 -10
- package/src/tools/Chat/lazy.tsx +38 -284
- package/src/tools/Chat/{components → messages}/MessageBubble.tsx +58 -5
- package/src/tools/Chat/{components → messages}/MessageList.tsx +8 -25
- package/src/tools/Chat/messages/blocks/MessageBlocks.tsx +131 -0
- package/src/tools/Chat/messages/blocks/builtin.tsx +91 -0
- package/src/tools/Chat/messages/blocks/index.ts +12 -0
- package/src/tools/Chat/messages/blocks/registry.tsx +42 -0
- package/src/tools/Chat/messages/blocks/renderers/AudioBlock.tsx +20 -0
- package/src/tools/Chat/messages/blocks/renderers/CodeBlock.tsx +19 -0
- package/src/tools/Chat/messages/blocks/renderers/GalleryBlock.tsx +26 -0
- package/src/tools/Chat/messages/blocks/renderers/ImageBlock.tsx +27 -0
- package/src/tools/Chat/messages/blocks/renderers/JsonBlock.tsx +12 -0
- package/src/tools/Chat/messages/blocks/renderers/LottieBlock.tsx +11 -0
- package/src/tools/Chat/messages/blocks/renderers/MapBlock.tsx +36 -0
- package/src/tools/Chat/messages/blocks/renderers/MermaidBlock.tsx +11 -0
- package/src/tools/Chat/messages/blocks/renderers/VideoBlock.tsx +24 -0
- package/src/tools/Chat/messages/blocks/renderers/types.ts +8 -0
- package/src/tools/Chat/{components → messages}/index.ts +11 -5
- package/src/tools/Chat/public.ts +212 -0
- package/src/tools/Chat/shell/ChatRoot.tsx +345 -0
- package/src/tools/Chat/{components → shell}/EmptyState.tsx +4 -2
- package/src/tools/Chat/shell/index.ts +15 -0
- package/src/tools/Chat/types/block.ts +120 -0
- package/src/tools/Chat/types/config.ts +0 -5
- package/src/tools/Chat/types/index.ts +17 -0
- package/src/tools/Chat/types/message.ts +3 -0
- package/src/tools/Chat/utils/index.ts +4 -0
- package/src/tools/CodeEditor/README.md +4 -6
- package/src/tools/CodeEditor/components/DiffEditor.tsx +48 -13
- package/src/tools/CodeEditor/components/Editor.tsx +96 -44
- package/src/tools/CodeEditor/context/EditorProvider.tsx +34 -17
- package/src/tools/CodeEditor/hooks/useEditorTheme.ts +92 -99
- package/src/tools/CodeEditor/hooks/useMonaco.ts +37 -22
- package/src/tools/CodeEditor/lazy.tsx +6 -0
- package/src/tools/CodeEditor/lib/index.ts +1 -1
- package/src/tools/CodeEditor/lib/themes.ts +3 -39
- package/src/tools/CronScheduler/CronScheduler.client.tsx +230 -61
- package/src/tools/CronScheduler/components/CustomInput.tsx +21 -4
- package/src/tools/CronScheduler/components/DayChips.tsx +13 -11
- package/src/tools/CronScheduler/components/MonthDayGrid.tsx +4 -4
- package/src/tools/CronScheduler/components/SchedulePreview.tsx +7 -3
- package/src/tools/CronScheduler/components/TimeSelector.tsx +1 -1
- package/src/tools/CronScheduler/index.tsx +1 -1
- package/src/tools/CronScheduler/types/index.ts +8 -3
- package/src/tools/CronScheduler/utils/cron-humanize.ts +61 -16
- package/src/tools/CronScheduler/utils/cron-parser.ts +13 -4
- package/src/tools/FileIcon/FileIcon.tsx +24 -39
- package/src/tools/FileIcon/get-file-icon.ts +73 -0
- package/src/tools/FileIcon/icons/icon-data.ts +399 -0
- package/src/tools/FileIcon/index.ts +4 -0
- package/src/tools/FileIcon/loader.ts +17 -35
- package/src/tools/FileIcon/specialFolders.ts +18 -0
- package/src/tools/Gallery/components/lightbox/GalleryLightbox.tsx +112 -35
- package/src/tools/Gallery/components/media/GalleryVideo.tsx +21 -2
- package/src/tools/Gallery/components/preview/GalleryCarousel.tsx +11 -1
- package/src/tools/Gallery/hooks/usePreloadImages.ts +54 -7
- package/src/tools/ImageViewer/components/ImageInfo.tsx +12 -1
- package/src/tools/ImageViewer/components/ImageToolbar.tsx +51 -43
- package/src/tools/ImageViewer/components/ImageViewer.tsx +96 -24
- package/src/tools/ImageViewer/hooks/useImageLoading.ts +13 -0
- package/src/tools/ImageViewer/utils/constants.ts +3 -0
- package/src/tools/ImageViewer/utils/index.ts +1 -0
- package/src/tools/JsonForm/JsonSchemaForm.tsx +4 -1
- package/src/tools/JsonForm/templates/ArrayFieldTemplate.tsx +5 -3
- package/src/tools/JsonForm/templates/BaseInputTemplate.tsx +7 -4
- package/src/tools/JsonForm/templates/ErrorListTemplate.tsx +3 -1
- package/src/tools/JsonForm/templates/ObjectFieldTemplate.tsx +23 -3
- package/src/tools/JsonForm/widgets/ColorWidget.tsx +20 -12
- package/src/tools/JsonForm/widgets/NumberWidget.tsx +14 -9
- package/src/tools/JsonForm/widgets/RadioWidget.tsx +78 -0
- package/src/tools/JsonForm/widgets/SelectWidget.tsx +1 -0
- package/src/tools/JsonForm/widgets/SliderWidget.tsx +7 -4
- package/src/tools/JsonForm/widgets/TextWidget.tsx +41 -17
- package/src/tools/JsonForm/widgets/index.ts +1 -0
- package/src/tools/JsonTree/components/JsonContent.tsx +115 -40
- package/src/tools/LottiePlayer/LottiePlayer.client.tsx +177 -72
- package/src/tools/LottiePlayer/index.tsx +14 -4
- package/src/tools/LottiePlayer/lazy.tsx +11 -3
- package/src/tools/LottiePlayer/types.ts +31 -1
- package/src/tools/LottiePlayer/useLottie.ts +32 -9
- package/src/tools/LottiePlayer/usePrefersReducedMotion.ts +46 -0
- package/src/tools/Map/components/LayerSwitcher.tsx +54 -21
- package/src/tools/Map/components/MapCluster.tsx +28 -21
- package/src/tools/Map/components/MapContainer.tsx +11 -4
- package/src/tools/Map/components/MapLegend.tsx +46 -15
- package/src/tools/Map/components/MapMarker.tsx +31 -2
- package/src/tools/Map/hooks/useMapEvents.ts +64 -105
- package/src/tools/MarkdownEditor/MarkdownEditor.tsx +61 -6
- package/src/tools/MarkdownEditor/MentionList.tsx +37 -4
- package/src/tools/MarkdownEditor/createMentionSuggestion.ts +11 -0
- package/src/tools/MarkdownEditor/lazy.tsx +32 -7
- package/src/tools/MarkdownEditor/styles.css +13 -0
- package/src/tools/MarkdownMessage/CodeBlock.tsx +40 -17
- package/src/tools/MarkdownMessage/MarkdownMessage.tsx +26 -6
- package/src/tools/MarkdownMessage/components.tsx +22 -9
- package/src/tools/MarkdownMessage/types.ts +24 -1
- package/src/tools/Mermaid/Mermaid.client.tsx +27 -5
- package/src/tools/Mermaid/components/MermaidErrorPanel.tsx +31 -0
- package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +14 -17
- package/src/tools/Mermaid/hooks/useMermaidRenderer.ts +264 -168
- package/src/tools/Mermaid/hooks/useMermaidValidation.ts +76 -10
- package/src/tools/Mermaid/index.tsx +6 -0
- package/src/tools/Mermaid/utils/mermaid-helpers.ts +141 -18
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +11 -1
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +49 -20
- package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/index.tsx +7 -0
- package/src/tools/OpenapiViewer/components/DocsLayout/grouping.ts +7 -4
- package/src/tools/OpenapiViewer/constants.ts +3 -0
- package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +73 -11
- package/src/tools/OpenapiViewer/utils/schemaExport.ts +26 -6
- package/src/tools/PrettyCode/PrettyCode.client.tsx +23 -16
- package/src/tools/PrettyCode/lazy.tsx +1 -1
- package/src/tools/SpeechRecognition/README.md +1 -1
- package/src/tools/SpeechRecognition/__tests__/language.test.ts +9 -3
- package/src/tools/SpeechRecognition/components/RecordingPulse.tsx +59 -0
- package/src/tools/SpeechRecognition/components/index.ts +2 -0
- package/src/tools/SpeechRecognition/core/engine/external.ts +24 -7
- package/src/tools/SpeechRecognition/core/language.ts +23 -6
- package/src/tools/SpeechRecognition/hooks/usePushToTalk.ts +36 -5
- package/src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts +18 -11
- package/src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx +94 -26
- package/src/tools/SpeechRecognition/widgets/index.ts +1 -1
- package/src/tools/Tree/README.md +4 -8
- package/src/tools/Tree/TreeRoot.tsx +22 -10
- package/src/tools/Tree/components/TreeContent.tsx +24 -4
- package/src/tools/Tree/components/TreeLabel.tsx +8 -2
- package/src/tools/Tree/components/TreeRow.tsx +16 -6
- package/src/tools/Tree/data/flatten.ts +10 -4
- package/src/tools/Tree/types.ts +4 -0
- package/src/tools/Uploader/components/UploadAddButton.tsx +29 -6
- package/src/tools/Uploader/components/UploadDropzone.tsx +63 -7
- package/src/tools/Uploader/components/UploadPageDropOverlay.tsx +19 -5
- package/src/tools/Uploader/components/UploadPreviewItem.tsx +47 -17
- package/src/tools/Uploader/components/UploadPreviewList.tsx +24 -12
- package/src/tools/Uploader/utils/formatters.ts +8 -3
- package/src/tools/VideoPlayer/canvas/hls-canvas.tsx +1 -0
- package/src/tools/VideoPlayer/canvas/{jsx.d.ts → jsx-augmentation.ts} +12 -19
- package/src/tools/VideoPlayer/canvas/vimeo-canvas.tsx +1 -0
- package/src/tools/VideoPlayer/canvas/youtube-canvas.tsx +1 -0
- package/src/tools/VideoPlayer/parts/fullscreen.tsx +1 -1
- package/src/tools/VideoPlayer/parts/pip.tsx +1 -1
- package/src/tools/VideoPlayer/parts/playback-rate.tsx +1 -1
- package/src/tools/VideoPlayer/parts/seek-bar.tsx +2 -2
- package/src/tools/VideoPlayer/parts/volume.tsx +2 -2
- package/src/tools/index.ts +2 -1
- package/src/tools/Chat/components/AudioToggle.tsx +0 -78
- package/src/tools/Chat/components/ChatRoot.tsx +0 -305
- package/src/tools/Chat/components/Composer.tsx +0 -216
- package/src/tools/Chat/hooks/useChatScroll.ts +0 -145
- package/src/tools/Chat/types.ts +0 -9
- package/src/tools/JsonTree/components/JsonToolbar.tsx +0 -95
- package/src/tools/JsonTree/hooks/useElementCorner.ts +0 -84
- package/src/tools/JsonTree/hooks/useNavbarHeight.ts +0 -83
- package/src/tools/OpenapiViewer/components/DocsLayout/schemaFields.ts +0 -121
- package/src/tools/Tour/README.md +0 -373
- package/src/tools/Tour/components/Tour.tsx +0 -12
- package/src/tools/Tour/components/TourContent.tsx +0 -171
- package/src/tools/Tour/components/TourNavigation.tsx +0 -77
- package/src/tools/Tour/components/TourProgress.tsx +0 -88
- package/src/tools/Tour/components/TourSpotlight.tsx +0 -199
- package/src/tools/Tour/components/index.ts +0 -5
- package/src/tools/Tour/context/TourContext.ts +0 -19
- package/src/tools/Tour/context/TourProvider.tsx +0 -292
- package/src/tools/Tour/context/index.ts +0 -2
- package/src/tools/Tour/hooks/index.ts +0 -3
- package/src/tools/Tour/hooks/useKeyboardNavigation.ts +0 -59
- package/src/tools/Tour/hooks/useStepTarget.ts +0 -121
- package/src/tools/Tour/hooks/useTour.ts +0 -42
- package/src/tools/Tour/index.ts +0 -38
- package/src/tools/Tour/types/index.ts +0 -224
- package/src/tools/Tour/utils/dom.ts +0 -98
- package/src/tools/Tour/utils/index.ts +0 -3
- package/src/tools/Tour/utils/logger.ts +0 -3
- package/src/tools/Tour/utils/scrollIntoView.ts +0 -24
- /package/src/tools/Chat/{config.ts → constants.ts} +0 -0
- /package/src/tools/Chat/launcher/{ChatHeaderAudioToggle.tsx → header/ChatHeaderAudioToggle.tsx} +0 -0
- /package/src/tools/Chat/{components → messages}/Attachments.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/JumpToLatest.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/MessageActions.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/Sources.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/StreamingIndicator.tsx +0 -0
- /package/src/tools/Chat/{components → messages}/ToolCalls.tsx +0 -0
- /package/src/tools/Chat/{components → shell}/ErrorBanner.tsx +0 -0
|
@@ -1,121 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Flatten a JSON Schema node into a flat list of (name, type, required,
|
|
3
|
-
* description) rows, ready to feed into the existing parameter table.
|
|
4
|
-
*
|
|
5
|
-
* Rules:
|
|
6
|
-
* - ``type: object`` → one row per property.
|
|
7
|
-
* - ``type: array`` → recurse into items; prefix each row with ``[].``
|
|
8
|
-
* so the caller sees "[].username string" etc.
|
|
9
|
-
* - Nested objects → dot-joined path: ``category.name``. We expand
|
|
10
|
-
* one level; deeper graphs get a single summary
|
|
11
|
-
* row ("category.* (object)") to keep the table
|
|
12
|
-
* readable.
|
|
13
|
-
*
|
|
14
|
-
* This is a presentation helper — not a full form generator. It powers
|
|
15
|
-
* the read-only fields table shown in the docs longread.
|
|
16
|
-
*/
|
|
17
|
-
|
|
18
|
-
type JsonSchemaNode = Record<string, unknown> & {
|
|
19
|
-
type?: string;
|
|
20
|
-
properties?: Record<string, JsonSchemaNode>;
|
|
21
|
-
required?: string[];
|
|
22
|
-
items?: JsonSchemaNode;
|
|
23
|
-
enum?: unknown[];
|
|
24
|
-
description?: string;
|
|
25
|
-
format?: string;
|
|
26
|
-
};
|
|
27
|
-
|
|
28
|
-
export interface SchemaField {
|
|
29
|
-
name: string;
|
|
30
|
-
type: string;
|
|
31
|
-
required: boolean;
|
|
32
|
-
description?: string;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const MAX_DEPTH = 2;
|
|
36
|
-
|
|
37
|
-
function describeType(node: JsonSchemaNode): string {
|
|
38
|
-
if (!node.type && node.properties) return 'object';
|
|
39
|
-
const base = node.type || 'any';
|
|
40
|
-
if (base === 'array') {
|
|
41
|
-
const itemType = node.items ? describeType(node.items) : 'any';
|
|
42
|
-
return `array<${itemType}>`;
|
|
43
|
-
}
|
|
44
|
-
if (Array.isArray(node.enum) && node.enum.length > 0) {
|
|
45
|
-
return `${base} enum`;
|
|
46
|
-
}
|
|
47
|
-
if (node.format) return `${base} (${node.format})`;
|
|
48
|
-
return base;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
export function schemaToFields(
|
|
52
|
-
schema: JsonSchemaNode | undefined,
|
|
53
|
-
prefix = '',
|
|
54
|
-
depth = 0,
|
|
55
|
-
): SchemaField[] {
|
|
56
|
-
if (!schema || depth > MAX_DEPTH) return [];
|
|
57
|
-
|
|
58
|
-
// Unwrap arrays: show the inner item's fields, prefixed with ``[]``.
|
|
59
|
-
if (schema.type === 'array') {
|
|
60
|
-
if (!schema.items) {
|
|
61
|
-
return [{ name: prefix || '[]', type: 'array', required: false }];
|
|
62
|
-
}
|
|
63
|
-
const inner = schemaToFields(schema.items, prefix ? `${prefix}[]` : '[]', depth);
|
|
64
|
-
if (inner.length === 0) {
|
|
65
|
-
return [
|
|
66
|
-
{
|
|
67
|
-
name: prefix || '[]',
|
|
68
|
-
type: describeType(schema),
|
|
69
|
-
required: false,
|
|
70
|
-
description: schema.description,
|
|
71
|
-
},
|
|
72
|
-
];
|
|
73
|
-
}
|
|
74
|
-
return inner;
|
|
75
|
-
}
|
|
76
|
-
|
|
77
|
-
// Primitives and unknown — single row.
|
|
78
|
-
if (schema.type !== 'object' && !schema.properties) {
|
|
79
|
-
return [
|
|
80
|
-
{
|
|
81
|
-
name: prefix || '(body)',
|
|
82
|
-
type: describeType(schema),
|
|
83
|
-
required: false,
|
|
84
|
-
description: schema.description,
|
|
85
|
-
},
|
|
86
|
-
];
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
// Objects — one row per property, recurse for nested objects/arrays.
|
|
90
|
-
const required = new Set(schema.required ?? []);
|
|
91
|
-
const rows: SchemaField[] = [];
|
|
92
|
-
const props = schema.properties ?? {};
|
|
93
|
-
for (const [key, node] of Object.entries(props)) {
|
|
94
|
-
const fullName = prefix ? `${prefix}.${key}` : key;
|
|
95
|
-
const isRequired = required.has(key);
|
|
96
|
-
|
|
97
|
-
const isNestedExpandable =
|
|
98
|
-
(node.type === 'object' && node.properties) ||
|
|
99
|
-
(node.type === 'array' && node.items);
|
|
100
|
-
|
|
101
|
-
if (isNestedExpandable && depth < MAX_DEPTH) {
|
|
102
|
-
// Leaf summary row for the parent itself (so the user sees
|
|
103
|
-
// its description) + recurse for inner fields.
|
|
104
|
-
rows.push({
|
|
105
|
-
name: fullName,
|
|
106
|
-
type: describeType(node),
|
|
107
|
-
required: isRequired,
|
|
108
|
-
description: node.description,
|
|
109
|
-
});
|
|
110
|
-
rows.push(...schemaToFields(node, fullName, depth + 1));
|
|
111
|
-
} else {
|
|
112
|
-
rows.push({
|
|
113
|
-
name: fullName,
|
|
114
|
-
type: describeType(node),
|
|
115
|
-
required: isRequired,
|
|
116
|
-
description: node.description,
|
|
117
|
-
});
|
|
118
|
-
}
|
|
119
|
-
}
|
|
120
|
-
return rows;
|
|
121
|
-
}
|
package/src/tools/Tour/README.md
DELETED
|
@@ -1,373 +0,0 @@
|
|
|
1
|
-
# Tour
|
|
2
|
-
|
|
3
|
-
Smart, decomposed onboarding Tour component with spotlight highlighting, animations, and i18n support.
|
|
4
|
-
|
|
5
|
-
## Installation
|
|
6
|
-
|
|
7
|
-
```bash
|
|
8
|
-
pnpm add @djangocfg/ui-tools
|
|
9
|
-
```
|
|
10
|
-
|
|
11
|
-
## Basic Usage
|
|
12
|
-
|
|
13
|
-
```tsx
|
|
14
|
-
import { Tour, useTour } from '@djangocfg/ui-tools/tour';
|
|
15
|
-
|
|
16
|
-
const tours = [
|
|
17
|
-
{
|
|
18
|
-
id: 'onboarding',
|
|
19
|
-
steps: [
|
|
20
|
-
{
|
|
21
|
-
id: 'welcome',
|
|
22
|
-
target: '[data-tour-step-id="header"]',
|
|
23
|
-
title: 'Welcome',
|
|
24
|
-
content: 'Let us show you around!',
|
|
25
|
-
position: 'bottom',
|
|
26
|
-
},
|
|
27
|
-
{
|
|
28
|
-
id: 'sidebar',
|
|
29
|
-
target: '#sidebar',
|
|
30
|
-
title: 'Navigation',
|
|
31
|
-
content: 'Use the sidebar to navigate.',
|
|
32
|
-
position: 'right',
|
|
33
|
-
},
|
|
34
|
-
],
|
|
35
|
-
},
|
|
36
|
-
];
|
|
37
|
-
|
|
38
|
-
function App() {
|
|
39
|
-
return (
|
|
40
|
-
<Tour
|
|
41
|
-
tours={tours}
|
|
42
|
-
onComplete={(tourId) => console.log('Tour completed:', tourId)}
|
|
43
|
-
>
|
|
44
|
-
<Layout />
|
|
45
|
-
</Tour>
|
|
46
|
-
);
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
function StartButton() {
|
|
50
|
-
const { start } = useTour();
|
|
51
|
-
return <button onClick={() => start('onboarding')}>Start Tour</button>;
|
|
52
|
-
}
|
|
53
|
-
```
|
|
54
|
-
|
|
55
|
-
## Components
|
|
56
|
-
|
|
57
|
-
### Tour
|
|
58
|
-
|
|
59
|
-
All-in-one wrapper combining TourProvider with overlay rendering.
|
|
60
|
-
|
|
61
|
-
```tsx
|
|
62
|
-
<Tour
|
|
63
|
-
tours={tours} // Array of tour configs
|
|
64
|
-
spotlight={true} // Enable spotlight (default: true)
|
|
65
|
-
spotlightOpacity={0.5} // Overlay opacity (default: 0.5)
|
|
66
|
-
spotlightBlur={0} // Backdrop blur in px (default: 0)
|
|
67
|
-
animated={true} // Enable animations (default: true)
|
|
68
|
-
pulseRing={false} // Pulsing ring around target (default: false)
|
|
69
|
-
showArrow={false} // Show arrow to target (default: false)
|
|
70
|
-
keyboardNavigation={true} // Arrow keys navigation (default: true)
|
|
71
|
-
closeOnEscape={true} // Close on Escape (default: true)
|
|
72
|
-
closeOnOverlayClick={false} // Close on overlay click (default: false)
|
|
73
|
-
scrollToTarget={true} // Scroll target into view (default: true)
|
|
74
|
-
progressVariant="dots" // 'dots' | 'bar' | 'fraction' | 'none'
|
|
75
|
-
showSkipButton={true} // Show skip button (default: true)
|
|
76
|
-
onStart={(tourId) => {}}
|
|
77
|
-
onComplete={(tourId) => {}}
|
|
78
|
-
onSkip={(tourId, stepIndex) => {}}
|
|
79
|
-
onStepChange={(index, step, direction) => {}}
|
|
80
|
-
>
|
|
81
|
-
{children}
|
|
82
|
-
</Tour>
|
|
83
|
-
```
|
|
84
|
-
|
|
85
|
-
### Custom Composition
|
|
86
|
-
|
|
87
|
-
```tsx
|
|
88
|
-
import {
|
|
89
|
-
TourProvider,
|
|
90
|
-
TourSpotlight,
|
|
91
|
-
TourContent,
|
|
92
|
-
TourProgress,
|
|
93
|
-
TourNavigation,
|
|
94
|
-
useTour,
|
|
95
|
-
} from '@djangocfg/ui-tools/tour';
|
|
96
|
-
|
|
97
|
-
function CustomTourOverlay() {
|
|
98
|
-
const { currentStep, currentStepIndex, totalSteps, next, previous, close } = useTour();
|
|
99
|
-
// ... render custom UI
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
<TourProvider tours={tours}>
|
|
103
|
-
<App />
|
|
104
|
-
<CustomTourOverlay />
|
|
105
|
-
</TourProvider>
|
|
106
|
-
```
|
|
107
|
-
|
|
108
|
-
## Step Configuration
|
|
109
|
-
|
|
110
|
-
```tsx
|
|
111
|
-
interface TourStep {
|
|
112
|
-
id: string; // Unique identifier
|
|
113
|
-
target?: string; // CSS selector or data-tour-step-id
|
|
114
|
-
title: ReactNode; // Step title
|
|
115
|
-
content: ReactNode; // Step content
|
|
116
|
-
position?: 'top' | 'bottom' | 'left' | 'right' | 'auto';
|
|
117
|
-
offset?: number; // Popover offset (default: 12)
|
|
118
|
-
align?: 'start' | 'center' | 'end';
|
|
119
|
-
|
|
120
|
-
// Multi-page tours
|
|
121
|
-
nextRoute?: string; // Navigate on Next
|
|
122
|
-
previousRoute?: string; // Navigate on Previous
|
|
123
|
-
|
|
124
|
-
// Labels (overrides i18n)
|
|
125
|
-
nextLabel?: ReactNode;
|
|
126
|
-
previousLabel?: ReactNode;
|
|
127
|
-
|
|
128
|
-
// Spotlight
|
|
129
|
-
disableSpotlight?: boolean;
|
|
130
|
-
spotlightPadding?: number; // Padding around target (default: 8)
|
|
131
|
-
spotlightRadius?: number; // Border radius (default: 4)
|
|
132
|
-
allowTargetInteraction?: boolean;
|
|
133
|
-
|
|
134
|
-
// Auto-advance
|
|
135
|
-
autoAdvance?: number; // Auto-advance after ms
|
|
136
|
-
|
|
137
|
-
// Custom actions
|
|
138
|
-
actions?: TourAction[];
|
|
139
|
-
|
|
140
|
-
// Callbacks
|
|
141
|
-
onBeforeShow?: () => boolean | Promise<boolean>;
|
|
142
|
-
onShow?: () => void;
|
|
143
|
-
onHide?: () => void;
|
|
144
|
-
}
|
|
145
|
-
```
|
|
146
|
-
|
|
147
|
-
## Targeting Elements
|
|
148
|
-
|
|
149
|
-
### Option 1: CSS Selector
|
|
150
|
-
|
|
151
|
-
```tsx
|
|
152
|
-
{
|
|
153
|
-
id: 'step-1',
|
|
154
|
-
target: '#my-button', // or '.my-class', '[data-id="123"]'
|
|
155
|
-
title: 'Click here',
|
|
156
|
-
content: '...',
|
|
157
|
-
}
|
|
158
|
-
```
|
|
159
|
-
|
|
160
|
-
### Option 2: data-tour-step-id Attribute
|
|
161
|
-
|
|
162
|
-
```tsx
|
|
163
|
-
// In your component
|
|
164
|
-
<header data-tour-step-id="header">...</header>
|
|
165
|
-
|
|
166
|
-
// In tour config
|
|
167
|
-
{
|
|
168
|
-
id: 'header', // matches data-tour-step-id
|
|
169
|
-
title: 'Welcome',
|
|
170
|
-
content: '...',
|
|
171
|
-
}
|
|
172
|
-
```
|
|
173
|
-
|
|
174
|
-
## Hooks
|
|
175
|
-
|
|
176
|
-
### useTour
|
|
177
|
-
|
|
178
|
-
```tsx
|
|
179
|
-
const {
|
|
180
|
-
start, // (tourId, startIndex?) => void
|
|
181
|
-
next, // () => void
|
|
182
|
-
previous, // () => void
|
|
183
|
-
goTo, // (index) => void
|
|
184
|
-
close, // () => void
|
|
185
|
-
isActive, // boolean
|
|
186
|
-
activeTourId, // string | null
|
|
187
|
-
currentStep, // TourStep | null
|
|
188
|
-
currentStepIndex,// number
|
|
189
|
-
totalSteps, // number
|
|
190
|
-
isFirstStep, // boolean
|
|
191
|
-
isLastStep, // boolean
|
|
192
|
-
progress, // number (0-1)
|
|
193
|
-
} = useTour();
|
|
194
|
-
```
|
|
195
|
-
|
|
196
|
-
## Keyboard Navigation
|
|
197
|
-
|
|
198
|
-
When enabled (default):
|
|
199
|
-
- `ArrowRight` / `ArrowDown`: Next step
|
|
200
|
-
- `ArrowLeft` / `ArrowUp`: Previous step
|
|
201
|
-
- `Escape`: Close tour
|
|
202
|
-
|
|
203
|
-
## Multi-page Tours
|
|
204
|
-
|
|
205
|
-
```tsx
|
|
206
|
-
const tours = [{
|
|
207
|
-
id: 'walkthrough',
|
|
208
|
-
steps: [
|
|
209
|
-
{
|
|
210
|
-
id: 'home',
|
|
211
|
-
target: '#hero',
|
|
212
|
-
title: 'Welcome',
|
|
213
|
-
content: 'Start here.',
|
|
214
|
-
nextRoute: '/dashboard', // Navigate on Next
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
id: 'dashboard',
|
|
218
|
-
target: '#stats',
|
|
219
|
-
title: 'Dashboard',
|
|
220
|
-
content: 'View your metrics.',
|
|
221
|
-
previousRoute: '/', // Navigate on Previous
|
|
222
|
-
},
|
|
223
|
-
],
|
|
224
|
-
}];
|
|
225
|
-
```
|
|
226
|
-
|
|
227
|
-
## Callbacks
|
|
228
|
-
|
|
229
|
-
```tsx
|
|
230
|
-
<Tour
|
|
231
|
-
tours={tours}
|
|
232
|
-
onStart={(tourId) => {
|
|
233
|
-
analytics.track('tour_started', { tourId });
|
|
234
|
-
}}
|
|
235
|
-
onComplete={(tourId) => {
|
|
236
|
-
localStorage.setItem(`tour_${tourId}_completed`, 'true');
|
|
237
|
-
}}
|
|
238
|
-
onSkip={(tourId, stepIndex) => {
|
|
239
|
-
analytics.track('tour_skipped', { tourId, stepIndex });
|
|
240
|
-
}}
|
|
241
|
-
onStepChange={(index, step, direction) => {
|
|
242
|
-
analytics.track('tour_step', { stepId: step.id, direction });
|
|
243
|
-
}}
|
|
244
|
-
onBeforeStepChange={async (currentIndex, nextIndex) => {
|
|
245
|
-
// Return false to prevent navigation
|
|
246
|
-
return true;
|
|
247
|
-
}}
|
|
248
|
-
>
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
## Progress Variants
|
|
252
|
-
|
|
253
|
-
- `dots` - Clickable dots (default)
|
|
254
|
-
- `bar` - Progress bar
|
|
255
|
-
- `fraction` - "1 / 5" text
|
|
256
|
-
- `none` - No progress indicator
|
|
257
|
-
|
|
258
|
-
## Visual Options
|
|
259
|
-
|
|
260
|
-
### Arrow
|
|
261
|
-
|
|
262
|
-
Shows an arrow pointing from the popover to the target element:
|
|
263
|
-
|
|
264
|
-
```tsx
|
|
265
|
-
<Tour tours={tours} showArrow>
|
|
266
|
-
{children}
|
|
267
|
-
</Tour>
|
|
268
|
-
```
|
|
269
|
-
|
|
270
|
-
### Backdrop Blur
|
|
271
|
-
|
|
272
|
-
Blurs the background outside the spotlight for enhanced focus:
|
|
273
|
-
|
|
274
|
-
```tsx
|
|
275
|
-
<Tour tours={tours} spotlightBlur={4} spotlightOpacity={0.3}>
|
|
276
|
-
{children}
|
|
277
|
-
</Tour>
|
|
278
|
-
```
|
|
279
|
-
|
|
280
|
-
### Pulse Ring
|
|
281
|
-
|
|
282
|
-
Animated pulsing ring around the target to draw attention:
|
|
283
|
-
|
|
284
|
-
```tsx
|
|
285
|
-
<Tour tours={tours} pulseRing>
|
|
286
|
-
{children}
|
|
287
|
-
</Tour>
|
|
288
|
-
```
|
|
289
|
-
|
|
290
|
-
### Disable Animations
|
|
291
|
-
|
|
292
|
-
Turn off all spotlight transition animations:
|
|
293
|
-
|
|
294
|
-
```tsx
|
|
295
|
-
<Tour tours={tours} animated={false}>
|
|
296
|
-
{children}
|
|
297
|
-
</Tour>
|
|
298
|
-
```
|
|
299
|
-
|
|
300
|
-
### Full Featured
|
|
301
|
-
|
|
302
|
-
Combine all visual enhancements:
|
|
303
|
-
|
|
304
|
-
```tsx
|
|
305
|
-
<Tour
|
|
306
|
-
tours={tours}
|
|
307
|
-
showArrow
|
|
308
|
-
spotlightBlur={2}
|
|
309
|
-
spotlightOpacity={0.4}
|
|
310
|
-
pulseRing
|
|
311
|
-
>
|
|
312
|
-
{children}
|
|
313
|
-
</Tour>
|
|
314
|
-
```
|
|
315
|
-
|
|
316
|
-
## Internationalization (i18n)
|
|
317
|
-
|
|
318
|
-
Tour uses `@djangocfg/i18n` for translations. Default labels come from `tools.tour.*` keys:
|
|
319
|
-
|
|
320
|
-
- `tools.tour.next` - "Next"
|
|
321
|
-
- `tools.tour.previous` - "Back"
|
|
322
|
-
- `tools.tour.skip` - "Skip"
|
|
323
|
-
- `tools.tour.finish` - "Finish"
|
|
324
|
-
- `tools.tour.close` - "Close"
|
|
325
|
-
- `tools.tour.stepOf` - "Step {current} of {total}"
|
|
326
|
-
|
|
327
|
-
To customize, either:
|
|
328
|
-
|
|
329
|
-
1. **Override via props** (per-step or global):
|
|
330
|
-
```tsx
|
|
331
|
-
<Tour
|
|
332
|
-
tours={tours}
|
|
333
|
-
skipLabel="Exit tour"
|
|
334
|
-
finishLabel="Done!"
|
|
335
|
-
>
|
|
336
|
-
```
|
|
337
|
-
|
|
338
|
-
2. **Provide custom translations** via I18nProvider:
|
|
339
|
-
```tsx
|
|
340
|
-
import { I18nProvider, mergeTranslations, en } from '@djangocfg/i18n';
|
|
341
|
-
|
|
342
|
-
const customEn = mergeTranslations(en, {
|
|
343
|
-
tools: {
|
|
344
|
-
tour: {
|
|
345
|
-
next: 'Continue',
|
|
346
|
-
skip: 'Exit',
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
<I18nProvider locale="en" translations={customEn}>
|
|
352
|
-
<Tour tours={tours}>
|
|
353
|
-
<App />
|
|
354
|
-
</Tour>
|
|
355
|
-
</I18nProvider>
|
|
356
|
-
```
|
|
357
|
-
|
|
358
|
-
## Features
|
|
359
|
-
|
|
360
|
-
- Spotlight highlighting with SVG mask
|
|
361
|
-
- Optional backdrop blur for enhanced focus
|
|
362
|
-
- Arrow pointing from popover to target
|
|
363
|
-
- Smooth animations between steps
|
|
364
|
-
- Pulse ring effect for attention
|
|
365
|
-
- Keyboard navigation
|
|
366
|
-
- Multi-step and multi-page tours
|
|
367
|
-
- Progress indicators (dots, bar, fraction)
|
|
368
|
-
- Auto-scroll to target
|
|
369
|
-
- Lifecycle callbacks
|
|
370
|
-
- Custom positioning
|
|
371
|
-
- Skip/close functionality
|
|
372
|
-
- ARIA accessibility
|
|
373
|
-
- Full i18n support
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { TourProvider } from '../context/TourProvider';
|
|
4
|
-
import type { TourProps } from '../types';
|
|
5
|
-
|
|
6
|
-
/**
|
|
7
|
-
* All-in-one Tour component.
|
|
8
|
-
* Combines TourProvider with children for simple usage.
|
|
9
|
-
*/
|
|
10
|
-
export function Tour({ children, ...providerProps }: TourProps) {
|
|
11
|
-
return <TourProvider {...providerProps}>{children}</TourProvider>;
|
|
12
|
-
}
|
|
@@ -1,171 +0,0 @@
|
|
|
1
|
-
'use client';
|
|
2
|
-
|
|
3
|
-
import { useMemo } from 'react';
|
|
4
|
-
import { X } from 'lucide-react';
|
|
5
|
-
import { cn } from '@djangocfg/ui-core/lib';
|
|
6
|
-
import {
|
|
7
|
-
Popover,
|
|
8
|
-
PopoverAnchor,
|
|
9
|
-
PopoverContent,
|
|
10
|
-
PopoverArrow,
|
|
11
|
-
Button,
|
|
12
|
-
} from '@djangocfg/ui-core/components';
|
|
13
|
-
import { TourProgress } from './TourProgress';
|
|
14
|
-
import { TourNavigation } from './TourNavigation';
|
|
15
|
-
import type { TourContentProps, TourPosition } from '../types';
|
|
16
|
-
|
|
17
|
-
/**
|
|
18
|
-
* Map TourPosition to Radix Popover side.
|
|
19
|
-
*/
|
|
20
|
-
function mapPositionToSide(
|
|
21
|
-
position: TourPosition | undefined
|
|
22
|
-
): 'top' | 'bottom' | 'left' | 'right' {
|
|
23
|
-
if (!position || position === 'auto') return 'bottom';
|
|
24
|
-
return position;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
/**
|
|
28
|
-
* Tour step content popover.
|
|
29
|
-
* Uses Radix Popover with virtual anchor for positioning.
|
|
30
|
-
*/
|
|
31
|
-
export function TourContent({
|
|
32
|
-
step,
|
|
33
|
-
targetRect,
|
|
34
|
-
currentIndex,
|
|
35
|
-
totalSteps,
|
|
36
|
-
onNext,
|
|
37
|
-
onPrevious,
|
|
38
|
-
onClose,
|
|
39
|
-
progressVariant = 'dots',
|
|
40
|
-
showSkipButton = true,
|
|
41
|
-
showArrow = false,
|
|
42
|
-
skipLabel,
|
|
43
|
-
finishLabel,
|
|
44
|
-
onGoTo,
|
|
45
|
-
className,
|
|
46
|
-
}: TourContentProps) {
|
|
47
|
-
// Prepare virtual ref for Popover anchor
|
|
48
|
-
const virtualRef = useMemo(
|
|
49
|
-
() => ({
|
|
50
|
-
current: {
|
|
51
|
-
getBoundingClientRect: () => targetRect,
|
|
52
|
-
},
|
|
53
|
-
}),
|
|
54
|
-
[targetRect]
|
|
55
|
-
);
|
|
56
|
-
|
|
57
|
-
// Prepare positioning
|
|
58
|
-
const side = mapPositionToSide(step.position);
|
|
59
|
-
const sideOffset = step.offset ?? 12;
|
|
60
|
-
const align = step.align ?? 'center';
|
|
61
|
-
const alignOffset = step.alignOffset ?? 0;
|
|
62
|
-
|
|
63
|
-
// Prepare step state
|
|
64
|
-
const isFirstStep = currentIndex === 0;
|
|
65
|
-
const isLastStep = currentIndex === totalSteps - 1;
|
|
66
|
-
|
|
67
|
-
// Prepare styles
|
|
68
|
-
const popoverContentClassName = cn('z-[51] w-72', step.className, className);
|
|
69
|
-
|
|
70
|
-
// Prepare custom actions
|
|
71
|
-
const hasCustomActions = step.actions && step.actions.length > 0;
|
|
72
|
-
const customActions = useMemo(() => {
|
|
73
|
-
if (!step.actions) return [];
|
|
74
|
-
|
|
75
|
-
return step.actions.map((action, index) => ({
|
|
76
|
-
key: index,
|
|
77
|
-
label: action.label,
|
|
78
|
-
onClick: action.onClick,
|
|
79
|
-
className: cn(
|
|
80
|
-
'text-sm font-medium underline-offset-4 hover:underline',
|
|
81
|
-
action.variant === 'destructive' && 'text-destructive'
|
|
82
|
-
),
|
|
83
|
-
}));
|
|
84
|
-
}, [step.actions]);
|
|
85
|
-
|
|
86
|
-
// Event handlers
|
|
87
|
-
const preventAutoFocus = (e: Event) => e.preventDefault();
|
|
88
|
-
|
|
89
|
-
return (
|
|
90
|
-
<Popover open>
|
|
91
|
-
<PopoverAnchor virtualRef={virtualRef as React.RefObject<Element>} />
|
|
92
|
-
<PopoverContent
|
|
93
|
-
side={side}
|
|
94
|
-
sideOffset={sideOffset}
|
|
95
|
-
align={align}
|
|
96
|
-
alignOffset={alignOffset}
|
|
97
|
-
avoidCollisions
|
|
98
|
-
collisionPadding={16}
|
|
99
|
-
className={popoverContentClassName}
|
|
100
|
-
onOpenAutoFocus={preventAutoFocus}
|
|
101
|
-
onCloseAutoFocus={preventAutoFocus}
|
|
102
|
-
role="dialog"
|
|
103
|
-
aria-labelledby="tour-title"
|
|
104
|
-
aria-describedby="tour-description"
|
|
105
|
-
>
|
|
106
|
-
{showArrow && <PopoverArrow className="fill-popover" />}
|
|
107
|
-
|
|
108
|
-
{/* Header with close button */}
|
|
109
|
-
<div className="flex items-start justify-between gap-2 pb-2">
|
|
110
|
-
<h3 id="tour-title" className="text-base font-semibold leading-none tracking-tight">
|
|
111
|
-
{step.title}
|
|
112
|
-
</h3>
|
|
113
|
-
{showSkipButton && (
|
|
114
|
-
<Button
|
|
115
|
-
variant="ghost"
|
|
116
|
-
size="icon"
|
|
117
|
-
className="h-6 w-6 shrink-0 -mt-1 -mr-2"
|
|
118
|
-
onClick={onClose}
|
|
119
|
-
>
|
|
120
|
-
<X className="h-4 w-4" />
|
|
121
|
-
</Button>
|
|
122
|
-
)}
|
|
123
|
-
</div>
|
|
124
|
-
|
|
125
|
-
{/* Content */}
|
|
126
|
-
<p id="tour-description" className="text-sm text-muted-foreground">
|
|
127
|
-
{step.content}
|
|
128
|
-
</p>
|
|
129
|
-
|
|
130
|
-
{/* Footer */}
|
|
131
|
-
<div className="mt-4 flex flex-col gap-3">
|
|
132
|
-
<TourProgress
|
|
133
|
-
current={currentIndex}
|
|
134
|
-
total={totalSteps}
|
|
135
|
-
variant={progressVariant}
|
|
136
|
-
onDotClick={onGoTo}
|
|
137
|
-
/>
|
|
138
|
-
|
|
139
|
-
<TourNavigation
|
|
140
|
-
onNext={onNext}
|
|
141
|
-
onPrevious={onPrevious}
|
|
142
|
-
isFirstStep={isFirstStep}
|
|
143
|
-
isLastStep={isLastStep}
|
|
144
|
-
nextLabel={step.nextLabel}
|
|
145
|
-
previousLabel={step.previousLabel}
|
|
146
|
-
finishLabel={finishLabel}
|
|
147
|
-
nextRoute={step.nextRoute}
|
|
148
|
-
previousRoute={step.previousRoute}
|
|
149
|
-
className="w-full"
|
|
150
|
-
/>
|
|
151
|
-
|
|
152
|
-
{/* Custom actions */}
|
|
153
|
-
{hasCustomActions && (
|
|
154
|
-
<div className="flex w-full flex-wrap gap-2">
|
|
155
|
-
{customActions.map((action) => (
|
|
156
|
-
<button
|
|
157
|
-
key={action.key}
|
|
158
|
-
type="button"
|
|
159
|
-
onClick={action.onClick}
|
|
160
|
-
className={action.className}
|
|
161
|
-
>
|
|
162
|
-
{action.label}
|
|
163
|
-
</button>
|
|
164
|
-
))}
|
|
165
|
-
</div>
|
|
166
|
-
)}
|
|
167
|
-
</div>
|
|
168
|
-
</PopoverContent>
|
|
169
|
-
</Popover>
|
|
170
|
-
);
|
|
171
|
-
}
|