@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.
Files changed (297) hide show
  1. package/README.md +9 -10
  2. package/dist/file-icon/index.cjs +449 -61
  3. package/dist/file-icon/index.cjs.map +1 -1
  4. package/dist/file-icon/index.d.cts +56 -18
  5. package/dist/file-icon/index.d.ts +56 -18
  6. package/dist/file-icon/index.mjs +448 -62
  7. package/dist/file-icon/index.mjs.map +1 -1
  8. package/dist/tree/index.cjs +49 -22
  9. package/dist/tree/index.cjs.map +1 -1
  10. package/dist/tree/index.d.cts +9 -3
  11. package/dist/tree/index.d.ts +9 -3
  12. package/dist/tree/index.mjs +49 -22
  13. package/dist/tree/index.mjs.map +1 -1
  14. package/dist/{types-B_zhyAqR.d.cts → types-eEu8SeiQ.d.cts} +4 -0
  15. package/dist/{types-B_zhyAqR.d.ts → types-eEu8SeiQ.d.ts} +4 -0
  16. package/package.json +8 -13
  17. package/src/components/FloatingToolbar/index.tsx +37 -3
  18. package/src/lib/page-snapshot/__tests__/capture-integration.test.ts +85 -0
  19. package/src/lib/page-snapshot/__tests__/engine.test.ts +36 -0
  20. package/src/lib/page-snapshot/__tests__/redaction-integration.test.ts +99 -0
  21. package/src/lib/page-snapshot/__tests__/tokens.test.ts +17 -0
  22. package/src/lib/page-snapshot/capture/__tests__/budget.test.ts +49 -0
  23. package/src/lib/page-snapshot/capture/__tests__/chrome-filter.test.ts +47 -0
  24. package/src/lib/page-snapshot/capture/__tests__/fold.test.ts +66 -0
  25. package/src/lib/page-snapshot/capture/__tests__/scope.test.ts +74 -0
  26. package/src/lib/page-snapshot/capture/__tests__/walk.test.ts +129 -0
  27. package/src/lib/page-snapshot/capture/accessible-name.ts +73 -0
  28. package/src/lib/page-snapshot/capture/budget.ts +95 -0
  29. package/src/lib/page-snapshot/capture/chrome-filter.ts +81 -0
  30. package/src/lib/page-snapshot/capture/classify.ts +111 -0
  31. package/src/lib/page-snapshot/capture/dom-utils.ts +111 -0
  32. package/src/lib/page-snapshot/capture/fold.ts +96 -0
  33. package/src/lib/page-snapshot/capture/scope.ts +169 -0
  34. package/src/lib/page-snapshot/capture/walk.ts +250 -0
  35. package/src/lib/page-snapshot/cst/__tests__/serialize.test.ts +50 -0
  36. package/src/lib/page-snapshot/cst/directives.ts +47 -0
  37. package/src/lib/page-snapshot/cst/payload.ts +50 -0
  38. package/src/lib/page-snapshot/cst/serialize.ts +84 -0
  39. package/src/lib/page-snapshot/cst/types.ts +115 -0
  40. package/src/lib/page-snapshot/engine.ts +176 -0
  41. package/src/lib/page-snapshot/index.ts +93 -0
  42. package/src/lib/page-snapshot/react/PageSnapshotChip.tsx +72 -0
  43. package/src/lib/page-snapshot/react/PageSnapshotPreview.tsx +78 -0
  44. package/src/lib/page-snapshot/react/__tests__/PageSnapshotChip.test.tsx +54 -0
  45. package/src/lib/page-snapshot/react/__tests__/provider.test.tsx +103 -0
  46. package/src/lib/page-snapshot/react/__tests__/use-page-snapshot-toggle.test.tsx +62 -0
  47. package/src/lib/page-snapshot/react/provider.tsx +162 -0
  48. package/src/lib/page-snapshot/react/use-page-snapshot-toggle.ts +47 -0
  49. package/src/lib/page-snapshot/react/use-page-snapshot.ts +67 -0
  50. package/src/lib/page-snapshot/redaction/__tests__/audit.test.ts +25 -0
  51. package/src/lib/page-snapshot/redaction/__tests__/heuristics.test.ts +73 -0
  52. package/src/lib/page-snapshot/redaction/__tests__/luhn.test.ts +26 -0
  53. package/src/lib/page-snapshot/redaction/__tests__/patterns.test.ts +60 -0
  54. package/src/lib/page-snapshot/redaction/audit.ts +58 -0
  55. package/src/lib/page-snapshot/redaction/heuristics.ts +75 -0
  56. package/src/lib/page-snapshot/redaction/index.ts +75 -0
  57. package/src/lib/page-snapshot/redaction/luhn.ts +25 -0
  58. package/src/lib/page-snapshot/redaction/patterns.ts +111 -0
  59. package/src/lib/page-snapshot/refs/__tests__/registry.test.ts +24 -0
  60. package/src/lib/page-snapshot/refs/registry.ts +46 -0
  61. package/src/lib/page-snapshot/staleness/__tests__/hash.test.ts +34 -0
  62. package/src/lib/page-snapshot/staleness/hash.ts +20 -0
  63. package/src/lib/page-snapshot/tokens.ts +15 -0
  64. package/src/tools/AudioPlayer/context/PlayerProvider.tsx +13 -14
  65. package/src/tools/AudioPlayer/hooks/useAudioElementEvents.ts +55 -6
  66. package/src/tools/AudioPlayer/parts/Meta/TimeDisplay.tsx +2 -5
  67. package/src/tools/Chat/README.md +277 -39
  68. package/src/tools/Chat/composer/Composer.tsx +471 -0
  69. package/src/tools/Chat/composer/ComposerActionBar.tsx +65 -0
  70. package/src/tools/Chat/composer/ComposerBanner.tsx +128 -0
  71. package/src/tools/Chat/composer/ComposerButton.tsx +64 -0
  72. package/src/tools/Chat/composer/ComposerFooter.tsx +90 -0
  73. package/src/tools/Chat/composer/ComposerMenuButton.tsx +62 -0
  74. package/src/tools/Chat/composer/ComposerModelPicker.tsx +104 -0
  75. package/src/tools/Chat/composer/ComposerRichTextarea.tsx +88 -0
  76. package/src/tools/Chat/composer/ComposerToolPill.tsx +95 -0
  77. package/src/tools/Chat/composer/index.ts +45 -0
  78. package/src/tools/Chat/composer/size-context.tsx +26 -0
  79. package/src/tools/Chat/composer/types.ts +143 -0
  80. package/src/tools/Chat/composer/useComposerActions.tsx +164 -0
  81. package/src/tools/Chat/context/ChatProvider.tsx +54 -3
  82. package/src/tools/Chat/core/__tests__/metadata.test.ts +69 -0
  83. package/src/tools/Chat/core/index.ts +23 -1
  84. package/src/tools/Chat/core/markdown.ts +1 -1
  85. package/src/tools/Chat/core/metadata.ts +47 -0
  86. package/src/tools/Chat/core/payload-dispatch.ts +1 -1
  87. package/src/tools/Chat/core/transport/http.ts +71 -32
  88. package/src/tools/Chat/core/transport/sse.ts +18 -10
  89. package/src/tools/Chat/highlight/HighlightOverlay.tsx +101 -0
  90. package/src/tools/Chat/highlight/README.md +103 -0
  91. package/src/tools/Chat/highlight/SpotlightCanvas.tsx +153 -0
  92. package/src/tools/Chat/highlight/__tests__/HighlightOverlay.test.tsx +112 -0
  93. package/src/tools/Chat/highlight/__tests__/resolveRef.test.ts +55 -0
  94. package/src/tools/Chat/highlight/index.ts +21 -0
  95. package/src/tools/Chat/highlight/resolveRef.ts +42 -0
  96. package/src/tools/Chat/highlight/types.ts +49 -0
  97. package/src/tools/Chat/highlight/useHighlightTargets.ts +128 -0
  98. package/src/tools/Chat/hooks/index.ts +0 -5
  99. package/src/tools/Chat/hooks/useAutoFocusOnStreamEnd.ts +28 -47
  100. package/src/tools/Chat/hooks/useChat.ts +47 -14
  101. package/src/tools/Chat/hooks/useChatComposer.ts +2 -2
  102. package/src/tools/Chat/hooks/useChatLayout.ts +1 -1
  103. package/src/tools/Chat/hooks/useStreamEndFocus.ts +54 -0
  104. package/src/tools/Chat/index.ts +25 -219
  105. package/src/tools/Chat/launcher/ChatDock.tsx +1 -1
  106. package/src/tools/Chat/launcher/ChatLauncher.tsx +1 -1
  107. package/src/tools/Chat/launcher/{ChatHeader.tsx → header/ChatHeader.tsx} +24 -11
  108. package/src/tools/Chat/launcher/{ChatHeaderActionButton.tsx → header/ChatHeaderActionButton.tsx} +34 -3
  109. package/src/tools/Chat/launcher/{ChatHeaderLanguageButton.tsx → header/ChatHeaderLanguageButton.tsx} +2 -2
  110. package/src/tools/Chat/launcher/{ChatHeaderModeToggle.tsx → header/ChatHeaderModeToggle.tsx} +1 -1
  111. package/src/tools/Chat/launcher/{ChatHeaderResetButton.tsx → header/ChatHeaderResetButton.tsx} +2 -1
  112. package/src/tools/Chat/launcher/{HeaderSlots.tsx → header/HeaderSlots.tsx} +3 -3
  113. package/src/tools/Chat/launcher/header/index.ts +26 -0
  114. package/src/tools/Chat/launcher/index.ts +3 -10
  115. package/src/tools/Chat/lazy.tsx +38 -284
  116. package/src/tools/Chat/{components → messages}/MessageBubble.tsx +58 -5
  117. package/src/tools/Chat/{components → messages}/MessageList.tsx +8 -25
  118. package/src/tools/Chat/messages/blocks/MessageBlocks.tsx +131 -0
  119. package/src/tools/Chat/messages/blocks/builtin.tsx +91 -0
  120. package/src/tools/Chat/messages/blocks/index.ts +12 -0
  121. package/src/tools/Chat/messages/blocks/registry.tsx +42 -0
  122. package/src/tools/Chat/messages/blocks/renderers/AudioBlock.tsx +20 -0
  123. package/src/tools/Chat/messages/blocks/renderers/CodeBlock.tsx +19 -0
  124. package/src/tools/Chat/messages/blocks/renderers/GalleryBlock.tsx +26 -0
  125. package/src/tools/Chat/messages/blocks/renderers/ImageBlock.tsx +27 -0
  126. package/src/tools/Chat/messages/blocks/renderers/JsonBlock.tsx +12 -0
  127. package/src/tools/Chat/messages/blocks/renderers/LottieBlock.tsx +11 -0
  128. package/src/tools/Chat/messages/blocks/renderers/MapBlock.tsx +36 -0
  129. package/src/tools/Chat/messages/blocks/renderers/MermaidBlock.tsx +11 -0
  130. package/src/tools/Chat/messages/blocks/renderers/VideoBlock.tsx +24 -0
  131. package/src/tools/Chat/messages/blocks/renderers/types.ts +8 -0
  132. package/src/tools/Chat/{components → messages}/index.ts +11 -5
  133. package/src/tools/Chat/public.ts +212 -0
  134. package/src/tools/Chat/shell/ChatRoot.tsx +345 -0
  135. package/src/tools/Chat/{components → shell}/EmptyState.tsx +4 -2
  136. package/src/tools/Chat/shell/index.ts +15 -0
  137. package/src/tools/Chat/types/block.ts +120 -0
  138. package/src/tools/Chat/types/config.ts +0 -5
  139. package/src/tools/Chat/types/index.ts +17 -0
  140. package/src/tools/Chat/types/message.ts +3 -0
  141. package/src/tools/Chat/utils/index.ts +4 -0
  142. package/src/tools/CodeEditor/README.md +4 -6
  143. package/src/tools/CodeEditor/components/DiffEditor.tsx +48 -13
  144. package/src/tools/CodeEditor/components/Editor.tsx +96 -44
  145. package/src/tools/CodeEditor/context/EditorProvider.tsx +34 -17
  146. package/src/tools/CodeEditor/hooks/useEditorTheme.ts +92 -99
  147. package/src/tools/CodeEditor/hooks/useMonaco.ts +37 -22
  148. package/src/tools/CodeEditor/lazy.tsx +6 -0
  149. package/src/tools/CodeEditor/lib/index.ts +1 -1
  150. package/src/tools/CodeEditor/lib/themes.ts +3 -39
  151. package/src/tools/CronScheduler/CronScheduler.client.tsx +230 -61
  152. package/src/tools/CronScheduler/components/CustomInput.tsx +21 -4
  153. package/src/tools/CronScheduler/components/DayChips.tsx +13 -11
  154. package/src/tools/CronScheduler/components/MonthDayGrid.tsx +4 -4
  155. package/src/tools/CronScheduler/components/SchedulePreview.tsx +7 -3
  156. package/src/tools/CronScheduler/components/TimeSelector.tsx +1 -1
  157. package/src/tools/CronScheduler/index.tsx +1 -1
  158. package/src/tools/CronScheduler/types/index.ts +8 -3
  159. package/src/tools/CronScheduler/utils/cron-humanize.ts +61 -16
  160. package/src/tools/CronScheduler/utils/cron-parser.ts +13 -4
  161. package/src/tools/FileIcon/FileIcon.tsx +24 -39
  162. package/src/tools/FileIcon/get-file-icon.ts +73 -0
  163. package/src/tools/FileIcon/icons/icon-data.ts +399 -0
  164. package/src/tools/FileIcon/index.ts +4 -0
  165. package/src/tools/FileIcon/loader.ts +17 -35
  166. package/src/tools/FileIcon/specialFolders.ts +18 -0
  167. package/src/tools/Gallery/components/lightbox/GalleryLightbox.tsx +112 -35
  168. package/src/tools/Gallery/components/media/GalleryVideo.tsx +21 -2
  169. package/src/tools/Gallery/components/preview/GalleryCarousel.tsx +11 -1
  170. package/src/tools/Gallery/hooks/usePreloadImages.ts +54 -7
  171. package/src/tools/ImageViewer/components/ImageInfo.tsx +12 -1
  172. package/src/tools/ImageViewer/components/ImageToolbar.tsx +51 -43
  173. package/src/tools/ImageViewer/components/ImageViewer.tsx +96 -24
  174. package/src/tools/ImageViewer/hooks/useImageLoading.ts +13 -0
  175. package/src/tools/ImageViewer/utils/constants.ts +3 -0
  176. package/src/tools/ImageViewer/utils/index.ts +1 -0
  177. package/src/tools/JsonForm/JsonSchemaForm.tsx +4 -1
  178. package/src/tools/JsonForm/templates/ArrayFieldTemplate.tsx +5 -3
  179. package/src/tools/JsonForm/templates/BaseInputTemplate.tsx +7 -4
  180. package/src/tools/JsonForm/templates/ErrorListTemplate.tsx +3 -1
  181. package/src/tools/JsonForm/templates/ObjectFieldTemplate.tsx +23 -3
  182. package/src/tools/JsonForm/widgets/ColorWidget.tsx +20 -12
  183. package/src/tools/JsonForm/widgets/NumberWidget.tsx +14 -9
  184. package/src/tools/JsonForm/widgets/RadioWidget.tsx +78 -0
  185. package/src/tools/JsonForm/widgets/SelectWidget.tsx +1 -0
  186. package/src/tools/JsonForm/widgets/SliderWidget.tsx +7 -4
  187. package/src/tools/JsonForm/widgets/TextWidget.tsx +41 -17
  188. package/src/tools/JsonForm/widgets/index.ts +1 -0
  189. package/src/tools/JsonTree/components/JsonContent.tsx +115 -40
  190. package/src/tools/LottiePlayer/LottiePlayer.client.tsx +177 -72
  191. package/src/tools/LottiePlayer/index.tsx +14 -4
  192. package/src/tools/LottiePlayer/lazy.tsx +11 -3
  193. package/src/tools/LottiePlayer/types.ts +31 -1
  194. package/src/tools/LottiePlayer/useLottie.ts +32 -9
  195. package/src/tools/LottiePlayer/usePrefersReducedMotion.ts +46 -0
  196. package/src/tools/Map/components/LayerSwitcher.tsx +54 -21
  197. package/src/tools/Map/components/MapCluster.tsx +28 -21
  198. package/src/tools/Map/components/MapContainer.tsx +11 -4
  199. package/src/tools/Map/components/MapLegend.tsx +46 -15
  200. package/src/tools/Map/components/MapMarker.tsx +31 -2
  201. package/src/tools/Map/hooks/useMapEvents.ts +64 -105
  202. package/src/tools/MarkdownEditor/MarkdownEditor.tsx +61 -6
  203. package/src/tools/MarkdownEditor/MentionList.tsx +37 -4
  204. package/src/tools/MarkdownEditor/createMentionSuggestion.ts +11 -0
  205. package/src/tools/MarkdownEditor/lazy.tsx +32 -7
  206. package/src/tools/MarkdownEditor/styles.css +13 -0
  207. package/src/tools/MarkdownMessage/CodeBlock.tsx +40 -17
  208. package/src/tools/MarkdownMessage/MarkdownMessage.tsx +26 -6
  209. package/src/tools/MarkdownMessage/components.tsx +22 -9
  210. package/src/tools/MarkdownMessage/types.ts +24 -1
  211. package/src/tools/Mermaid/Mermaid.client.tsx +27 -5
  212. package/src/tools/Mermaid/components/MermaidErrorPanel.tsx +31 -0
  213. package/src/tools/Mermaid/components/MermaidFullscreenModal.tsx +14 -17
  214. package/src/tools/Mermaid/hooks/useMermaidRenderer.ts +264 -168
  215. package/src/tools/Mermaid/hooks/useMermaidValidation.ts +76 -10
  216. package/src/tools/Mermaid/index.tsx +6 -0
  217. package/src/tools/Mermaid/utils/mermaid-helpers.ts +141 -18
  218. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/FieldRow.tsx +11 -1
  219. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/SchemaFields/buildTree.ts +49 -20
  220. package/src/tools/OpenapiViewer/components/DocsLayout/EndpointDoc/index.tsx +7 -0
  221. package/src/tools/OpenapiViewer/components/DocsLayout/grouping.ts +7 -4
  222. package/src/tools/OpenapiViewer/constants.ts +3 -0
  223. package/src/tools/OpenapiViewer/hooks/useOpenApiSchema.ts +73 -11
  224. package/src/tools/OpenapiViewer/utils/schemaExport.ts +26 -6
  225. package/src/tools/PrettyCode/PrettyCode.client.tsx +23 -16
  226. package/src/tools/PrettyCode/lazy.tsx +1 -1
  227. package/src/tools/SpeechRecognition/README.md +1 -1
  228. package/src/tools/SpeechRecognition/__tests__/language.test.ts +9 -3
  229. package/src/tools/SpeechRecognition/components/RecordingPulse.tsx +59 -0
  230. package/src/tools/SpeechRecognition/components/index.ts +2 -0
  231. package/src/tools/SpeechRecognition/core/engine/external.ts +24 -7
  232. package/src/tools/SpeechRecognition/core/language.ts +23 -6
  233. package/src/tools/SpeechRecognition/hooks/usePushToTalk.ts +36 -5
  234. package/src/tools/SpeechRecognition/hooks/useSpeechRecognition.ts +18 -11
  235. package/src/tools/SpeechRecognition/widgets/VoiceComposerSlot.tsx +94 -26
  236. package/src/tools/SpeechRecognition/widgets/index.ts +1 -1
  237. package/src/tools/Tree/README.md +4 -8
  238. package/src/tools/Tree/TreeRoot.tsx +22 -10
  239. package/src/tools/Tree/components/TreeContent.tsx +24 -4
  240. package/src/tools/Tree/components/TreeLabel.tsx +8 -2
  241. package/src/tools/Tree/components/TreeRow.tsx +16 -6
  242. package/src/tools/Tree/data/flatten.ts +10 -4
  243. package/src/tools/Tree/types.ts +4 -0
  244. package/src/tools/Uploader/components/UploadAddButton.tsx +29 -6
  245. package/src/tools/Uploader/components/UploadDropzone.tsx +63 -7
  246. package/src/tools/Uploader/components/UploadPageDropOverlay.tsx +19 -5
  247. package/src/tools/Uploader/components/UploadPreviewItem.tsx +47 -17
  248. package/src/tools/Uploader/components/UploadPreviewList.tsx +24 -12
  249. package/src/tools/Uploader/utils/formatters.ts +8 -3
  250. package/src/tools/VideoPlayer/canvas/hls-canvas.tsx +1 -0
  251. package/src/tools/VideoPlayer/canvas/{jsx.d.ts → jsx-augmentation.ts} +12 -19
  252. package/src/tools/VideoPlayer/canvas/vimeo-canvas.tsx +1 -0
  253. package/src/tools/VideoPlayer/canvas/youtube-canvas.tsx +1 -0
  254. package/src/tools/VideoPlayer/parts/fullscreen.tsx +1 -1
  255. package/src/tools/VideoPlayer/parts/pip.tsx +1 -1
  256. package/src/tools/VideoPlayer/parts/playback-rate.tsx +1 -1
  257. package/src/tools/VideoPlayer/parts/seek-bar.tsx +2 -2
  258. package/src/tools/VideoPlayer/parts/volume.tsx +2 -2
  259. package/src/tools/index.ts +2 -1
  260. package/src/tools/Chat/components/AudioToggle.tsx +0 -78
  261. package/src/tools/Chat/components/ChatRoot.tsx +0 -305
  262. package/src/tools/Chat/components/Composer.tsx +0 -216
  263. package/src/tools/Chat/hooks/useChatScroll.ts +0 -145
  264. package/src/tools/Chat/types.ts +0 -9
  265. package/src/tools/JsonTree/components/JsonToolbar.tsx +0 -95
  266. package/src/tools/JsonTree/hooks/useElementCorner.ts +0 -84
  267. package/src/tools/JsonTree/hooks/useNavbarHeight.ts +0 -83
  268. package/src/tools/OpenapiViewer/components/DocsLayout/schemaFields.ts +0 -121
  269. package/src/tools/Tour/README.md +0 -373
  270. package/src/tools/Tour/components/Tour.tsx +0 -12
  271. package/src/tools/Tour/components/TourContent.tsx +0 -171
  272. package/src/tools/Tour/components/TourNavigation.tsx +0 -77
  273. package/src/tools/Tour/components/TourProgress.tsx +0 -88
  274. package/src/tools/Tour/components/TourSpotlight.tsx +0 -199
  275. package/src/tools/Tour/components/index.ts +0 -5
  276. package/src/tools/Tour/context/TourContext.ts +0 -19
  277. package/src/tools/Tour/context/TourProvider.tsx +0 -292
  278. package/src/tools/Tour/context/index.ts +0 -2
  279. package/src/tools/Tour/hooks/index.ts +0 -3
  280. package/src/tools/Tour/hooks/useKeyboardNavigation.ts +0 -59
  281. package/src/tools/Tour/hooks/useStepTarget.ts +0 -121
  282. package/src/tools/Tour/hooks/useTour.ts +0 -42
  283. package/src/tools/Tour/index.ts +0 -38
  284. package/src/tools/Tour/types/index.ts +0 -224
  285. package/src/tools/Tour/utils/dom.ts +0 -98
  286. package/src/tools/Tour/utils/index.ts +0 -3
  287. package/src/tools/Tour/utils/logger.ts +0 -3
  288. package/src/tools/Tour/utils/scrollIntoView.ts +0 -24
  289. /package/src/tools/Chat/{config.ts → constants.ts} +0 -0
  290. /package/src/tools/Chat/launcher/{ChatHeaderAudioToggle.tsx → header/ChatHeaderAudioToggle.tsx} +0 -0
  291. /package/src/tools/Chat/{components → messages}/Attachments.tsx +0 -0
  292. /package/src/tools/Chat/{components → messages}/JumpToLatest.tsx +0 -0
  293. /package/src/tools/Chat/{components → messages}/MessageActions.tsx +0 -0
  294. /package/src/tools/Chat/{components → messages}/Sources.tsx +0 -0
  295. /package/src/tools/Chat/{components → messages}/StreamingIndicator.tsx +0 -0
  296. /package/src/tools/Chat/{components → messages}/ToolCalls.tsx +0 -0
  297. /package/src/tools/Chat/{components → shell}/ErrorBanner.tsx +0 -0
@@ -1,77 +0,0 @@
1
- 'use client';
2
-
3
- import { useCallback, useMemo } from 'react';
4
- import { cn } from '@djangocfg/ui-core/lib';
5
- import { Button } from '@djangocfg/ui-core/components';
6
- import { ChevronLeft, ChevronRight } from 'lucide-react';
7
- import { useT } from '@djangocfg/i18n';
8
- import type { TourNavigationProps } from '../types';
9
-
10
- /**
11
- * Navigation buttons for tour steps.
12
- * For multi-page tours, use nextRoute/previousRoute with your router.
13
- * Skip button is now in TourContent header as × icon.
14
- */
15
- export function TourNavigation({
16
- onNext,
17
- onPrevious,
18
- isFirstStep,
19
- isLastStep,
20
- nextLabel,
21
- previousLabel,
22
- finishLabel,
23
- nextRoute,
24
- previousRoute,
25
- className,
26
- }: TourNavigationProps) {
27
- const t = useT();
28
-
29
- // Prepare labels
30
- const labels = useMemo(() => {
31
- const defaultNext = isLastStep ? t('tools.tour.finish') : t('tools.tour.next');
32
- return {
33
- next: nextLabel ?? finishLabel ?? defaultNext,
34
- previous: previousLabel ?? t('tools.tour.previous'),
35
- };
36
- }, [isLastStep, nextLabel, finishLabel, previousLabel, t]);
37
-
38
- // Prepare visibility flags
39
- const showPrevious = !isFirstStep;
40
- const showNextArrow = !isLastStep;
41
-
42
- // Handlers
43
- const handleNext = useCallback(() => {
44
- if (nextRoute && typeof window !== 'undefined') {
45
- window.location.href = nextRoute;
46
- }
47
- onNext();
48
- }, [nextRoute, onNext]);
49
-
50
- const handlePrevious = useCallback(() => {
51
- if (previousRoute && typeof window !== 'undefined') {
52
- window.location.href = previousRoute;
53
- }
54
- onPrevious();
55
- }, [previousRoute, onPrevious]);
56
-
57
- // Styles
58
- const containerClassName = cn('flex items-center gap-2', className);
59
-
60
- return (
61
- <div className={containerClassName}>
62
- {showPrevious ? (
63
- <Button variant="ghost" size="sm" onClick={handlePrevious} className="flex-1 gap-1">
64
- <ChevronLeft className="h-4 w-4 shrink-0" />
65
- <span className="truncate">{labels.previous}</span>
66
- </Button>
67
- ) : (
68
- <div className="flex-1" />
69
- )}
70
-
71
- <Button size="sm" onClick={handleNext} className="flex-1 gap-1">
72
- <span className="truncate">{labels.next}</span>
73
- {showNextArrow && <ChevronRight className="h-4 w-4 shrink-0" />}
74
- </Button>
75
- </div>
76
- );
77
- }
@@ -1,88 +0,0 @@
1
- 'use client';
2
-
3
- import { useMemo } from 'react';
4
- import { cn } from '@djangocfg/ui-core/lib';
5
- import { Progress } from '@djangocfg/ui-core/components';
6
- import { useT } from '@djangocfg/i18n';
7
- import type { TourProgressProps } from '../types';
8
-
9
- /**
10
- * Progress indicator for tour steps.
11
- * Supports dots, bar, fraction, and none variants.
12
- */
13
- export function TourProgress({
14
- current,
15
- total,
16
- variant = 'dots',
17
- onDotClick,
18
- className,
19
- }: TourProgressProps) {
20
- const t = useT();
21
-
22
- // Early return for hidden states
23
- if (variant === 'none' || total <= 1) return null;
24
-
25
- // Prepare data
26
- const currentStep = current + 1;
27
- const progressPercent = (currentStep / total) * 100;
28
- const stepLabel = t('tools.tour.stepOf', { current: currentStep, total });
29
- const isClickable = !!onDotClick;
30
-
31
- // Prepare dots data
32
- const dots = useMemo(() => {
33
- if (variant !== 'dots') return [];
34
-
35
- return Array.from({ length: total }, (_, index) => ({
36
- index,
37
- isActive: index === current,
38
- label: t('tools.tour.stepOf', { current: index + 1, total }),
39
- className: cn(
40
- 'h-2 w-2 rounded-full transition-all',
41
- index === current ? 'bg-primary scale-110' : 'bg-muted hover:bg-muted-foreground/50',
42
- isClickable && 'cursor-pointer'
43
- ),
44
- }));
45
- }, [variant, total, current, isClickable, t]);
46
-
47
- // Render bar variant
48
- if (variant === 'bar') {
49
- return (
50
- <Progress
51
- value={progressPercent}
52
- className={cn('h-1', className)}
53
- aria-label={stepLabel}
54
- />
55
- );
56
- }
57
-
58
- // Render fraction variant
59
- if (variant === 'fraction') {
60
- const fractionClassName = cn('text-sm text-muted-foreground tabular-nums', className);
61
-
62
- return (
63
- <div className={fractionClassName} aria-label={stepLabel}>
64
- {currentStep} / {total}
65
- </div>
66
- );
67
- }
68
-
69
- // Render dots variant (default)
70
- const dotsContainerClassName = cn('flex items-center justify-center gap-1.5', className);
71
-
72
- return (
73
- <div className={dotsContainerClassName} role="tablist" aria-label={stepLabel}>
74
- {dots.map((dot) => (
75
- <button
76
- key={dot.index}
77
- type="button"
78
- role="tab"
79
- aria-selected={dot.isActive}
80
- aria-label={dot.label}
81
- className={dot.className}
82
- onClick={() => onDotClick?.(dot.index)}
83
- disabled={!isClickable}
84
- />
85
- ))}
86
- </div>
87
- );
88
- }
@@ -1,199 +0,0 @@
1
- 'use client';
2
-
3
- import { useMemo } from 'react';
4
- import { cn } from '@djangocfg/ui-core/lib';
5
- import type { TourSpotlightProps } from '../types';
6
-
7
- // CSS for SVG animations (injected once)
8
- const SPOTLIGHT_STYLES = `
9
- @keyframes tour-spotlight-pulse {
10
- 0%, 100% { opacity: 1; }
11
- 50% { opacity: 0.5; }
12
- }
13
- @keyframes tour-spotlight-ring {
14
- 0% { transform: scale(1); opacity: 0.8; }
15
- 100% { transform: scale(1.15); opacity: 0; }
16
- }
17
- .tour-spotlight-rect {
18
- transition: x 300ms ease-out, y 300ms ease-out, width 300ms ease-out, height 300ms ease-out;
19
- }
20
- .tour-spotlight-border {
21
- transition: x 300ms ease-out, y 300ms ease-out, width 300ms ease-out, height 300ms ease-out;
22
- }
23
- .tour-spotlight-pulse {
24
- animation: tour-spotlight-pulse 2s ease-in-out infinite;
25
- }
26
- `;
27
-
28
- /**
29
- * SVG-based spotlight overlay that highlights target elements.
30
- * Uses mask technique to create cutouts for targets.
31
- * Supports animations, backdrop blur, and pulse ring effects.
32
- */
33
- export function TourSpotlight({
34
- targets,
35
- opacity = 0.5,
36
- blur = 0,
37
- animated = true,
38
- pulseRing = false,
39
- className,
40
- onClick,
41
- allowInteraction = false,
42
- }: TourSpotlightProps) {
43
- // Early return for no targets
44
- if (targets.length === 0) return null;
45
-
46
- // Prepare flags
47
- const hasBlur = blur > 0;
48
-
49
- // Prepare target rects with computed values
50
- const computedTargets = useMemo(() => {
51
- return targets.map((target, index) => {
52
- const x = target.rect.x - target.padding;
53
- const y = target.rect.y - target.padding;
54
- const width = target.rect.width + target.padding * 2;
55
- const height = target.rect.height + target.padding * 2;
56
- const rx = target.radius + 2;
57
- const centerX = target.rect.x + target.rect.width / 2;
58
- const centerY = target.rect.y + target.rect.height / 2;
59
-
60
- return {
61
- key: index,
62
- x,
63
- y,
64
- width,
65
- height,
66
- rx,
67
- ry: rx,
68
- transformOrigin: `${centerX}px ${centerY}px`,
69
- };
70
- });
71
- }, [targets]);
72
-
73
- // Prepare styles
74
- const svgClassName = cn('absolute inset-0 h-full w-full', className);
75
- const svgStyle = { pointerEvents: allowInteraction ? 'none' as const : 'auto' as const };
76
-
77
- const blurStyle = useMemo(() => {
78
- if (!hasBlur) return undefined;
79
- return {
80
- backdropFilter: `blur(${blur}px)`,
81
- WebkitBackdropFilter: `blur(${blur}px)`,
82
- mask: 'url(#tour-spotlight-mask-blur)',
83
- WebkitMask: 'url(#tour-spotlight-mask-blur)',
84
- pointerEvents: 'none' as const,
85
- };
86
- }, [hasBlur, blur]);
87
-
88
- const rectAnimatedClass = animated ? 'tour-spotlight-rect' : undefined;
89
- const overlayClass = animated ? 'transition-opacity duration-300' : undefined;
90
-
91
- return (
92
- <>
93
- {/* Inject styles for animations */}
94
- <style dangerouslySetInnerHTML={{ __html: SPOTLIGHT_STYLES }} />
95
-
96
- {/* Backdrop blur layer (separate from SVG for better performance) */}
97
- {hasBlur && (
98
- <div
99
- className="absolute inset-0 transition-all duration-300"
100
- style={blurStyle}
101
- aria-hidden="true"
102
- />
103
- )}
104
-
105
- <svg
106
- className={svgClassName}
107
- style={svgStyle}
108
- onClick={onClick}
109
- aria-hidden="true"
110
- >
111
- <defs>
112
- <mask id="tour-spotlight-mask">
113
- {/* White background - visible area */}
114
- <rect fill="white" width="100%" height="100%" />
115
- {/* Black cutouts - transparent area for targets */}
116
- {computedTargets.map((target) => (
117
- <rect
118
- key={target.key}
119
- fill="black"
120
- className={rectAnimatedClass}
121
- x={target.x}
122
- y={target.y}
123
- width={target.width}
124
- height={target.height}
125
- rx={target.rx}
126
- ry={target.ry}
127
- />
128
- ))}
129
- </mask>
130
-
131
- {/* Separate mask for blur */}
132
- {hasBlur && (
133
- <mask id="tour-spotlight-mask-blur">
134
- <rect fill="white" width="100%" height="100%" />
135
- {computedTargets.map((target) => (
136
- <rect
137
- key={target.key}
138
- fill="black"
139
- className={rectAnimatedClass}
140
- x={target.x}
141
- y={target.y}
142
- width={target.width}
143
- height={target.height}
144
- rx={target.rx}
145
- ry={target.ry}
146
- />
147
- ))}
148
- </mask>
149
- )}
150
- </defs>
151
-
152
- {/* Overlay with mask */}
153
- <rect
154
- fill="black"
155
- fillOpacity={opacity}
156
- width="100%"
157
- height="100%"
158
- mask="url(#tour-spotlight-mask)"
159
- className={overlayClass}
160
- />
161
-
162
- {/* Pulse ring effect */}
163
- {pulseRing && computedTargets.map((target) => (
164
- <rect
165
- key={`pulse-${target.key}`}
166
- className="fill-none stroke-primary stroke-2"
167
- style={{
168
- transformOrigin: target.transformOrigin,
169
- animation: 'tour-spotlight-ring 1.5s ease-out infinite',
170
- }}
171
- x={target.x}
172
- y={target.y}
173
- width={target.width}
174
- height={target.height}
175
- rx={target.rx}
176
- ry={target.ry}
177
- />
178
- ))}
179
-
180
- {/* Border around targets */}
181
- {computedTargets.map((target) => (
182
- <rect
183
- key={`border-${target.key}`}
184
- className={cn(
185
- 'fill-none stroke-primary stroke-2',
186
- animated && 'tour-spotlight-border'
187
- )}
188
- x={target.x}
189
- y={target.y}
190
- width={target.width}
191
- height={target.height}
192
- rx={target.rx}
193
- ry={target.ry}
194
- />
195
- ))}
196
- </svg>
197
- </>
198
- );
199
- }
@@ -1,5 +0,0 @@
1
- export { TourSpotlight } from './TourSpotlight';
2
- export { TourProgress } from './TourProgress';
3
- export { TourNavigation } from './TourNavigation';
4
- export { TourContent } from './TourContent';
5
- export { Tour } from './Tour';
@@ -1,19 +0,0 @@
1
- 'use client';
2
-
3
- import { createContext, useContext } from 'react';
4
- import type { TourState, TourStep, UseTourReturn } from '../types';
5
-
6
- export interface TourContextValue extends UseTourReturn {
7
- state: TourState;
8
- steps: TourStep[];
9
- }
10
-
11
- export const TourContext = createContext<TourContextValue | null>(null);
12
-
13
- export function useTourContext(): TourContextValue {
14
- const context = useContext(TourContext);
15
- if (!context) {
16
- throw new Error('useTourContext must be used within a TourProvider');
17
- }
18
- return context;
19
- }
@@ -1,292 +0,0 @@
1
- 'use client';
2
-
3
- import { useState, useCallback, useMemo, useEffect } from 'react';
4
- import { createPortal } from 'react-dom';
5
- import { TourContext, type TourContextValue } from './TourContext';
6
- import { TourSpotlight } from '../components/TourSpotlight';
7
- import { TourContent } from '../components/TourContent';
8
- import { useStepTarget } from '../hooks/useStepTarget';
9
- import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation';
10
- import { logger } from '../utils/logger';
11
- import type { TourProviderProps, TourState, TourStep } from '../types';
12
-
13
- /**
14
- * Tour context provider.
15
- * Manages tour state and renders overlay.
16
- */
17
- export function TourProvider({
18
- tours,
19
- children,
20
- spotlight = true,
21
- spotlightOpacity = 0.5,
22
- spotlightBlur = 0,
23
- animated = true,
24
- pulseRing = false,
25
- showArrow = false,
26
- keyboardNavigation = true,
27
- closeOnOverlayClick = false,
28
- closeOnEscape = true,
29
- scrollToTarget = true,
30
- scrollBehavior = 'smooth',
31
- progressVariant = 'dots',
32
- showSkipButton = true,
33
- skipLabel,
34
- finishLabel,
35
- portalContainer,
36
- zIndex = 50,
37
- onStart,
38
- onComplete,
39
- onSkip,
40
- onStepChange,
41
- onBeforeStepChange,
42
- }: TourProviderProps) {
43
- const [state, setState] = useState<TourState>({
44
- isActive: false,
45
- activeTourId: null,
46
- currentStepIndex: 0,
47
- isTransitioning: false,
48
- direction: 'forward',
49
- });
50
-
51
- const [mounted, setMounted] = useState(false);
52
-
53
- useEffect(() => {
54
- setMounted(true);
55
- }, []);
56
-
57
- // Get active tour and current step
58
- const activeTour = useMemo(
59
- () => tours.find((t) => t.id === state.activeTourId),
60
- [tours, state.activeTourId]
61
- );
62
-
63
- const steps = useMemo(() => {
64
- if (!activeTour) return [];
65
- // Apply tour defaults to each step
66
- return activeTour.steps.map((step) => ({
67
- ...activeTour.defaults,
68
- ...step,
69
- }));
70
- }, [activeTour]);
71
-
72
- const currentStep = steps[state.currentStepIndex] as TourStep | undefined;
73
-
74
- // Track target elements
75
- const { targets, isReady } = useStepTarget(currentStep?.target, currentStep?.id, {
76
- scrollToTarget,
77
- scrollBehavior,
78
- spotlightPadding: currentStep?.spotlightPadding ?? 8,
79
- });
80
-
81
- // Start tour
82
- const start = useCallback(
83
- (tourId: string, startIndex = 0) => {
84
- const tour = tours.find((t) => t.id === tourId);
85
- if (!tour) {
86
- logger.warn(`Tour "${tourId}" not found`);
87
- return;
88
- }
89
- if (tour.steps.length === 0) {
90
- logger.warn(`Tour "${tourId}" has no steps`);
91
- return;
92
- }
93
-
94
- logger.info(`Starting tour "${tourId}"`);
95
- setState({
96
- isActive: true,
97
- activeTourId: tourId,
98
- currentStepIndex: Math.min(startIndex, tour.steps.length - 1),
99
- isTransitioning: false,
100
- direction: 'forward',
101
- });
102
- onStart?.(tourId);
103
- },
104
- [tours, onStart]
105
- );
106
-
107
- // Close tour
108
- const close = useCallback(() => {
109
- if (state.activeTourId) {
110
- logger.info(`Tour "${state.activeTourId}" closed at step ${state.currentStepIndex}`);
111
- onSkip?.(state.activeTourId, state.currentStepIndex);
112
- }
113
- setState({
114
- isActive: false,
115
- activeTourId: null,
116
- currentStepIndex: 0,
117
- isTransitioning: false,
118
- direction: 'forward',
119
- });
120
- }, [state.activeTourId, state.currentStepIndex, onSkip]);
121
-
122
- // Next step
123
- const next = useCallback(async () => {
124
- if (!activeTour) return;
125
-
126
- const nextIndex = state.currentStepIndex + 1;
127
-
128
- // Complete tour if at last step
129
- if (nextIndex >= steps.length) {
130
- logger.info(`Tour "${state.activeTourId}" completed`);
131
- onComplete?.(state.activeTourId!);
132
- setState({
133
- isActive: false,
134
- activeTourId: null,
135
- currentStepIndex: 0,
136
- isTransitioning: false,
137
- direction: 'forward',
138
- });
139
- return;
140
- }
141
-
142
- // Check if can proceed
143
- const canProceed = (await onBeforeStepChange?.(state.currentStepIndex, nextIndex)) ?? true;
144
- if (!canProceed) return;
145
-
146
- // Call step onHide callback
147
- currentStep?.onHide?.();
148
-
149
- setState((prev) => ({
150
- ...prev,
151
- currentStepIndex: nextIndex,
152
- direction: 'forward',
153
- }));
154
-
155
- const nextStep = steps[nextIndex];
156
- onStepChange?.(nextIndex, nextStep, 'forward');
157
- nextStep?.onShow?.();
158
- }, [activeTour, state, steps, currentStep, onComplete, onBeforeStepChange, onStepChange]);
159
-
160
- // Previous step
161
- const previous = useCallback(async () => {
162
- if (state.currentStepIndex <= 0) return;
163
-
164
- const prevIndex = state.currentStepIndex - 1;
165
-
166
- const canProceed = (await onBeforeStepChange?.(state.currentStepIndex, prevIndex)) ?? true;
167
- if (!canProceed) return;
168
-
169
- currentStep?.onHide?.();
170
-
171
- setState((prev) => ({
172
- ...prev,
173
- currentStepIndex: prevIndex,
174
- direction: 'backward',
175
- }));
176
-
177
- const prevStep = steps[prevIndex];
178
- onStepChange?.(prevIndex, prevStep, 'backward');
179
- prevStep?.onShow?.();
180
- }, [state.currentStepIndex, steps, currentStep, onBeforeStepChange, onStepChange]);
181
-
182
- // Go to specific step
183
- const goTo = useCallback(
184
- async (index: number) => {
185
- if (!activeTour || index < 0 || index >= steps.length) return;
186
- if (index === state.currentStepIndex) return;
187
-
188
- const canProceed = (await onBeforeStepChange?.(state.currentStepIndex, index)) ?? true;
189
- if (!canProceed) return;
190
-
191
- currentStep?.onHide?.();
192
-
193
- const direction = index > state.currentStepIndex ? 'forward' : 'backward';
194
- setState((prev) => ({
195
- ...prev,
196
- currentStepIndex: index,
197
- direction,
198
- }));
199
-
200
- const targetStep = steps[index];
201
- onStepChange?.(index, targetStep, direction);
202
- targetStep?.onShow?.();
203
- },
204
- [activeTour, steps, state.currentStepIndex, currentStep, onBeforeStepChange, onStepChange]
205
- );
206
-
207
- // Keyboard navigation
208
- useKeyboardNavigation({
209
- enabled: keyboardNavigation && state.isActive,
210
- onNext: next,
211
- onPrevious: previous,
212
- onClose: closeOnEscape ? close : undefined,
213
- });
214
-
215
- // Lock body scroll when tour is active
216
- useEffect(() => {
217
- if (state.isActive) {
218
- const originalOverflow = document.body.style.overflow;
219
- document.body.style.overflow = 'hidden';
220
- return () => {
221
- document.body.style.overflow = originalOverflow;
222
- };
223
- }
224
- }, [state.isActive]);
225
-
226
- // Context value
227
- const contextValue: TourContextValue = useMemo(
228
- () => ({
229
- state,
230
- steps,
231
- currentStep: currentStep ?? null,
232
- start,
233
- next,
234
- previous,
235
- goTo,
236
- close,
237
- isActive: state.isActive,
238
- activeTourId: state.activeTourId,
239
- currentStepIndex: state.currentStepIndex,
240
- totalSteps: steps.length,
241
- isFirstStep: state.currentStepIndex === 0,
242
- isLastStep: state.currentStepIndex === steps.length - 1,
243
- progress: steps.length > 0 ? (state.currentStepIndex + 1) / steps.length : 0,
244
- }),
245
- [state, steps, currentStep, start, next, previous, goTo, close]
246
- );
247
-
248
- // Overlay content
249
- const overlay = state.isActive && currentStep && isReady && targets.length > 0 && (
250
- <div
251
- className="fixed inset-0"
252
- style={{ zIndex }}
253
- role="presentation"
254
- aria-hidden="true"
255
- >
256
- {spotlight && !currentStep.disableSpotlight && (
257
- <TourSpotlight
258
- targets={targets}
259
- opacity={spotlightOpacity}
260
- blur={spotlightBlur}
261
- animated={animated}
262
- pulseRing={pulseRing}
263
- onClick={closeOnOverlayClick ? close : undefined}
264
- allowInteraction={currentStep.allowTargetInteraction}
265
- />
266
- )}
267
-
268
- <TourContent
269
- step={currentStep}
270
- targetRect={targets[0].rect}
271
- currentIndex={state.currentStepIndex}
272
- totalSteps={steps.length}
273
- onNext={next}
274
- onPrevious={previous}
275
- onClose={close}
276
- progressVariant={progressVariant}
277
- showSkipButton={showSkipButton}
278
- showArrow={showArrow}
279
- skipLabel={skipLabel}
280
- finishLabel={finishLabel}
281
- onGoTo={goTo}
282
- />
283
- </div>
284
- );
285
-
286
- return (
287
- <TourContext.Provider value={contextValue}>
288
- {children}
289
- {mounted && createPortal(overlay, portalContainer ?? document.body)}
290
- </TourContext.Provider>
291
- );
292
- }
@@ -1,2 +0,0 @@
1
- export { TourProvider } from './TourProvider';
2
- export { TourContext, useTourContext, type TourContextValue } from './TourContext';
@@ -1,3 +0,0 @@
1
- export { useTour } from './useTour';
2
- export { useStepTarget } from './useStepTarget';
3
- export { useKeyboardNavigation } from './useKeyboardNavigation';