@djangocfg/ui-tools 2.1.404 → 2.1.408
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 -11
- 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 +13 -16
- 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/lazy.tsx +13 -27
- package/src/tools/AudioPlayer/parts/Meta/TimeDisplay.tsx +2 -5
- package/src/tools/Chat/README.md +267 -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 +326 -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 +106 -26
- 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/README.md +87 -230
- package/src/tools/VideoPlayer/VideoPlayer.tsx +82 -0
- package/src/tools/VideoPlayer/canvas/canvas-dispatcher.tsx +34 -0
- package/src/tools/VideoPlayer/canvas/hls-canvas.tsx +39 -0
- package/src/tools/VideoPlayer/canvas/iframe-canvas.tsx +33 -0
- package/src/tools/VideoPlayer/canvas/index.ts +12 -0
- package/src/tools/VideoPlayer/canvas/jsx-augmentation.ts +47 -0
- package/src/tools/VideoPlayer/canvas/native-canvas.tsx +38 -0
- package/src/tools/VideoPlayer/canvas/vimeo-canvas.tsx +40 -0
- package/src/tools/VideoPlayer/canvas/youtube-canvas.tsx +78 -0
- package/src/tools/VideoPlayer/index.ts +51 -65
- package/src/tools/VideoPlayer/lazy.tsx +11 -54
- package/src/tools/VideoPlayer/parts/controls-bar.tsx +35 -0
- package/src/tools/VideoPlayer/parts/fullscreen.tsx +19 -0
- package/src/tools/VideoPlayer/parts/index.ts +15 -0
- package/src/tools/VideoPlayer/parts/pip.tsx +19 -0
- package/src/tools/VideoPlayer/parts/play-button.tsx +19 -0
- package/src/tools/VideoPlayer/parts/playback-rate.tsx +31 -0
- package/src/tools/VideoPlayer/parts/poster.tsx +3 -0
- package/src/tools/VideoPlayer/parts/seek-bar.tsx +26 -0
- package/src/tools/VideoPlayer/parts/volume.tsx +32 -0
- package/src/tools/VideoPlayer/styles/video-player.css +141 -0
- package/src/tools/VideoPlayer/types.ts +82 -0
- package/src/tools/VideoPlayer/utils/parse-embed-url.ts +70 -0
- package/src/tools/VideoPlayer/utils/vimeo-id.ts +24 -0
- package/src/tools/VideoPlayer/utils/youtube-id.ts +64 -0
- package/src/tools/index.ts +37 -29
- 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/VideoPlayer/components/VideoControls.tsx +0 -138
- package/src/tools/VideoPlayer/components/VideoErrorFallback.tsx +0 -172
- package/src/tools/VideoPlayer/components/VideoPlayer.tsx +0 -201
- package/src/tools/VideoPlayer/components/index.ts +0 -14
- package/src/tools/VideoPlayer/context/VideoPlayerContext.tsx +0 -52
- package/src/tools/VideoPlayer/context/index.ts +0 -8
- package/src/tools/VideoPlayer/hooks/index.ts +0 -12
- package/src/tools/VideoPlayer/hooks/useVideoPlayerSettings.ts +0 -71
- package/src/tools/VideoPlayer/hooks/useVideoPositionCache.ts +0 -117
- package/src/tools/VideoPlayer/providers/NativeProvider.tsx +0 -284
- package/src/tools/VideoPlayer/providers/StreamProvider.tsx +0 -505
- package/src/tools/VideoPlayer/providers/VidstackProvider.tsx +0 -397
- package/src/tools/VideoPlayer/providers/index.ts +0 -8
- package/src/tools/VideoPlayer/types/index.ts +0 -38
- package/src/tools/VideoPlayer/types/player.ts +0 -116
- package/src/tools/VideoPlayer/types/provider.ts +0 -93
- package/src/tools/VideoPlayer/types/sources.ts +0 -97
- package/src/tools/VideoPlayer/utils/debug.ts +0 -14
- package/src/tools/VideoPlayer/utils/fileSource.ts +0 -78
- package/src/tools/VideoPlayer/utils/index.ts +0 -12
- package/src/tools/VideoPlayer/utils/resolvers.ts +0 -75
- /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,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState, useCallback, useMemo, memo } from 'react'
|
|
3
|
+
import { useState, useCallback, useMemo, useEffect, memo, type KeyboardEvent } from 'react'
|
|
4
4
|
import { useMapLayers } from '../hooks/useMapLayers'
|
|
5
5
|
|
|
6
6
|
import type { LayerSwitcherProps, LayerConfig } from '../types'
|
|
@@ -36,14 +36,29 @@ function LayerSwitcherComponent({
|
|
|
36
36
|
|
|
37
37
|
const [visibility, setVisibility] = useState<Record<string, boolean>>(initialVisibility)
|
|
38
38
|
|
|
39
|
+
// Keep visibility in sync when the `layers` prop changes: add entries for
|
|
40
|
+
// new layers, drop stale ones, preserve user toggles for existing layers.
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
setVisibility((prev) => {
|
|
43
|
+
const next: Record<string, boolean> = {}
|
|
44
|
+
for (const layer of layers) {
|
|
45
|
+
next[layer.id] =
|
|
46
|
+
layer.id in prev ? prev[layer.id] : layer.defaultVisible !== false
|
|
47
|
+
}
|
|
48
|
+
return next
|
|
49
|
+
})
|
|
50
|
+
}, [layers])
|
|
51
|
+
|
|
39
52
|
const handleToggle = useCallback(
|
|
40
53
|
(layerId: string) => {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
54
|
+
setVisibility((prev) => {
|
|
55
|
+
const newVisible = !prev[layerId]
|
|
56
|
+
setLayerVisibility(layerId, newVisible)
|
|
57
|
+
onChange?.(layerId, newVisible)
|
|
58
|
+
return { ...prev, [layerId]: newVisible }
|
|
59
|
+
})
|
|
45
60
|
},
|
|
46
|
-
[
|
|
61
|
+
[setLayerVisibility, onChange]
|
|
47
62
|
)
|
|
48
63
|
|
|
49
64
|
const handleToggleAll = useCallback(
|
|
@@ -66,7 +81,9 @@ function LayerSwitcherComponent({
|
|
|
66
81
|
() => ({
|
|
67
82
|
position: 'absolute',
|
|
68
83
|
...positionStyles[position],
|
|
69
|
-
backgroundColor: '
|
|
84
|
+
backgroundColor: 'var(--color-popover)',
|
|
85
|
+
color: 'var(--color-popover-foreground)',
|
|
86
|
+
border: '1px solid var(--color-border)',
|
|
70
87
|
borderRadius: 8,
|
|
71
88
|
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
|
72
89
|
padding: collapsed ? 8 : 12,
|
|
@@ -86,7 +103,7 @@ function LayerSwitcherComponent({
|
|
|
86
103
|
cursor: collapsible ? 'pointer' : 'default',
|
|
87
104
|
fontWeight: 600,
|
|
88
105
|
fontSize: 12,
|
|
89
|
-
color: '
|
|
106
|
+
color: 'var(--color-foreground)',
|
|
90
107
|
marginBottom: collapsed ? 0 : 8,
|
|
91
108
|
}),
|
|
92
109
|
[collapsible, collapsed]
|
|
@@ -99,7 +116,7 @@ function LayerSwitcherComponent({
|
|
|
99
116
|
gap: 8,
|
|
100
117
|
padding: '4px 0',
|
|
101
118
|
fontSize: 12,
|
|
102
|
-
color: '
|
|
119
|
+
color: 'var(--color-muted-foreground)',
|
|
103
120
|
cursor: 'pointer',
|
|
104
121
|
}),
|
|
105
122
|
[]
|
|
@@ -126,23 +143,40 @@ function LayerSwitcherComponent({
|
|
|
126
143
|
)
|
|
127
144
|
}, [layers])
|
|
128
145
|
|
|
129
|
-
const handleHeaderClick = collapsible ? () => setCollapsed(!
|
|
146
|
+
const handleHeaderClick = collapsible ? () => setCollapsed((c) => !c) : undefined
|
|
130
147
|
|
|
131
148
|
return (
|
|
132
149
|
<div className={className} style={containerStyle}>
|
|
133
|
-
<div
|
|
150
|
+
<div
|
|
151
|
+
style={headerStyle}
|
|
152
|
+
onClick={handleHeaderClick}
|
|
153
|
+
{...(collapsible
|
|
154
|
+
? {
|
|
155
|
+
role: 'button' as const,
|
|
156
|
+
tabIndex: 0,
|
|
157
|
+
'aria-expanded': !collapsed,
|
|
158
|
+
onKeyDown: (e: KeyboardEvent) => {
|
|
159
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
160
|
+
e.preventDefault()
|
|
161
|
+
setCollapsed((c) => !c)
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
}
|
|
165
|
+
: {})}
|
|
166
|
+
>
|
|
134
167
|
<span>{title}</span>
|
|
135
168
|
{collapsible && (
|
|
136
169
|
<svg
|
|
137
170
|
width="12"
|
|
138
171
|
height="12"
|
|
139
172
|
viewBox="0 0 12 12"
|
|
173
|
+
aria-hidden="true"
|
|
140
174
|
style={{
|
|
141
175
|
transform: collapsed ? 'rotate(-90deg)' : 'rotate(0deg)',
|
|
142
176
|
transition: 'transform 0.2s',
|
|
143
177
|
}}
|
|
144
178
|
>
|
|
145
|
-
<path d="M3 4.5L6 7.5L9 4.5" stroke="
|
|
179
|
+
<path d="M3 4.5L6 7.5L9 4.5" stroke="currentColor" fill="none" />
|
|
146
180
|
</svg>
|
|
147
181
|
)}
|
|
148
182
|
</div>
|
|
@@ -152,12 +186,14 @@ function LayerSwitcherComponent({
|
|
|
152
186
|
<div
|
|
153
187
|
style={{
|
|
154
188
|
...itemStyle,
|
|
155
|
-
|
|
189
|
+
cursor: 'default',
|
|
190
|
+
borderBottom: '1px solid var(--color-border)',
|
|
156
191
|
marginBottom: 4,
|
|
157
192
|
paddingBottom: 8,
|
|
158
193
|
}}
|
|
159
194
|
>
|
|
160
195
|
<button
|
|
196
|
+
type="button"
|
|
161
197
|
onClick={() => handleToggleAll(true)}
|
|
162
198
|
disabled={allVisible}
|
|
163
199
|
style={{
|
|
@@ -170,6 +206,7 @@ function LayerSwitcherComponent({
|
|
|
170
206
|
All
|
|
171
207
|
</button>
|
|
172
208
|
<button
|
|
209
|
+
type="button"
|
|
173
210
|
onClick={() => handleToggleAll(false)}
|
|
174
211
|
disabled={noneVisible}
|
|
175
212
|
style={{
|
|
@@ -190,7 +227,7 @@ function LayerSwitcherComponent({
|
|
|
190
227
|
style={{
|
|
191
228
|
fontSize: 10,
|
|
192
229
|
fontWeight: 600,
|
|
193
|
-
color: '
|
|
230
|
+
color: 'var(--color-muted-foreground)',
|
|
194
231
|
marginTop: 8,
|
|
195
232
|
marginBottom: 4,
|
|
196
233
|
textTransform: 'uppercase',
|
|
@@ -200,19 +237,15 @@ function LayerSwitcherComponent({
|
|
|
200
237
|
</div>
|
|
201
238
|
)}
|
|
202
239
|
{groupLayers.map((layer) => (
|
|
203
|
-
<
|
|
204
|
-
key={layer.id}
|
|
205
|
-
style={itemStyle}
|
|
206
|
-
onClick={() => handleToggle(layer.id)}
|
|
207
|
-
>
|
|
240
|
+
<label key={layer.id} style={itemStyle}>
|
|
208
241
|
<input
|
|
209
242
|
type="checkbox"
|
|
210
|
-
checked={visibility[layer.id]}
|
|
243
|
+
checked={visibility[layer.id] ?? false}
|
|
211
244
|
onChange={() => handleToggle(layer.id)}
|
|
212
245
|
style={checkboxStyle}
|
|
213
246
|
/>
|
|
214
247
|
<span>{layer.label}</span>
|
|
215
|
-
</
|
|
248
|
+
</label>
|
|
216
249
|
))}
|
|
217
250
|
</div>
|
|
218
251
|
))}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useCallback, useState, useEffect, memo, type ReactNode } from 'react'
|
|
3
|
+
import { useCallback, useState, useEffect, useMemo, memo, type ReactNode } from 'react'
|
|
4
4
|
import { Source, Layer, useMap, Popup } from 'react-map-gl/maplibre'
|
|
5
5
|
import type { GeoJSONSource, MapMouseEvent } from 'maplibre-gl'
|
|
6
6
|
import { createClusterLayers } from '../layers'
|
|
@@ -78,15 +78,21 @@ export const MapCluster = memo(function MapCluster({
|
|
|
78
78
|
injectPopupStyles()
|
|
79
79
|
}, [])
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
81
|
+
// Memoize layers so layer ids stay referentially stable across renders.
|
|
82
|
+
// Without this, the click/hover effect below re-subscribes on every render.
|
|
83
|
+
const { cluster, clusterCount, unclusteredPoint } = useMemo(() => {
|
|
84
|
+
const layerOptions: ClusterLayerOptions = {
|
|
85
|
+
sourceId,
|
|
86
|
+
colors,
|
|
87
|
+
radii,
|
|
88
|
+
thresholds,
|
|
89
|
+
hoverColor,
|
|
90
|
+
}
|
|
91
|
+
return createClusterLayers(layerOptions)
|
|
92
|
+
}, [sourceId, colors, radii, thresholds, hoverColor])
|
|
88
93
|
|
|
89
|
-
const
|
|
94
|
+
const clusterLayerId = cluster.id as string
|
|
95
|
+
const pointLayerId = unclusteredPoint.id as string
|
|
90
96
|
|
|
91
97
|
const handleClosePopup = useCallback(() => {
|
|
92
98
|
setSelectedFeature(null)
|
|
@@ -98,7 +104,7 @@ export const MapCluster = memo(function MapCluster({
|
|
|
98
104
|
if (!map) return
|
|
99
105
|
|
|
100
106
|
const features = map.queryRenderedFeatures(event.point, {
|
|
101
|
-
layers: [
|
|
107
|
+
layers: [clusterLayerId, pointLayerId],
|
|
102
108
|
})
|
|
103
109
|
|
|
104
110
|
if (!features || features.length === 0) return
|
|
@@ -143,7 +149,7 @@ export const MapCluster = memo(function MapCluster({
|
|
|
143
149
|
}
|
|
144
150
|
}
|
|
145
151
|
},
|
|
146
|
-
[map, sourceId,
|
|
152
|
+
[map, sourceId, clusterLayerId, pointLayerId, onClusterClick, onPointClick, renderPopup, panOffsetX, panOffsetY]
|
|
147
153
|
)
|
|
148
154
|
|
|
149
155
|
// Close popup when clicking empty area
|
|
@@ -152,7 +158,7 @@ export const MapCluster = memo(function MapCluster({
|
|
|
152
158
|
if (!map) return
|
|
153
159
|
|
|
154
160
|
const features = map.queryRenderedFeatures(event.point, {
|
|
155
|
-
layers: [
|
|
161
|
+
layers: [clusterLayerId, pointLayerId],
|
|
156
162
|
})
|
|
157
163
|
|
|
158
164
|
// Close popup if clicked on empty area (no features)
|
|
@@ -160,24 +166,25 @@ export const MapCluster = memo(function MapCluster({
|
|
|
160
166
|
handleClosePopup()
|
|
161
167
|
}
|
|
162
168
|
},
|
|
163
|
-
[map,
|
|
169
|
+
[map, clusterLayerId, pointLayerId, handleClosePopup]
|
|
164
170
|
)
|
|
165
171
|
|
|
166
172
|
// Register click and hover handlers with proper cleanup
|
|
167
173
|
useEffect(() => {
|
|
168
174
|
if (!map) return
|
|
169
175
|
|
|
170
|
-
const clusterLayerId = cluster.id!
|
|
171
|
-
const pointLayerId = unclusteredPoint.id!
|
|
172
176
|
let currentHoveredId: number | string | null = null
|
|
173
177
|
|
|
174
|
-
// Helper to clear hover state
|
|
178
|
+
// Helper to clear hover state.
|
|
179
|
+
// Guarded: on unmount the source may already be gone, which would throw.
|
|
175
180
|
const clearHoverState = () => {
|
|
176
181
|
if (currentHoveredId !== null) {
|
|
177
|
-
map.
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
182
|
+
if (map.getSource(sourceId)) {
|
|
183
|
+
map.setFeatureState(
|
|
184
|
+
{ source: sourceId, id: currentHoveredId },
|
|
185
|
+
{ hover: false }
|
|
186
|
+
)
|
|
187
|
+
}
|
|
181
188
|
currentHoveredId = null
|
|
182
189
|
}
|
|
183
190
|
}
|
|
@@ -231,7 +238,7 @@ export const MapCluster = memo(function MapCluster({
|
|
|
231
238
|
map.off('mouseleave', clusterLayerId, handleMouseLeave)
|
|
232
239
|
map.off('mouseleave', pointLayerId, handleMouseLeave)
|
|
233
240
|
}
|
|
234
|
-
}, [map, sourceId,
|
|
241
|
+
}, [map, sourceId, clusterLayerId, pointLayerId, handleClick, handleMapClick])
|
|
235
242
|
|
|
236
243
|
return (
|
|
237
244
|
<>
|
|
@@ -28,6 +28,8 @@ export interface MapContainerProps {
|
|
|
28
28
|
autoResetDelay?: number
|
|
29
29
|
/** Show reset button */
|
|
30
30
|
showResetButton?: boolean
|
|
31
|
+
/** Accessible label for the map region (screen readers) */
|
|
32
|
+
ariaLabel?: string
|
|
31
33
|
}
|
|
32
34
|
|
|
33
35
|
interface MapInnerProps extends Omit<MapContainerProps, 'initialViewport'> {}
|
|
@@ -44,6 +46,7 @@ function MapInner({
|
|
|
44
46
|
openInMapsLabel = 'Open in Maps',
|
|
45
47
|
autoResetDelay = 0,
|
|
46
48
|
showResetButton = false,
|
|
49
|
+
ariaLabel = 'Interactive map',
|
|
47
50
|
}: MapInnerProps) {
|
|
48
51
|
const { mapRef, viewport, setViewport, setIsLoaded, resetToInitial, initialViewport } = useMapContext()
|
|
49
52
|
const resetTimerRef = useRef<NodeJS.Timeout | null>(null)
|
|
@@ -125,6 +128,7 @@ function MapInner({
|
|
|
125
128
|
...style,
|
|
126
129
|
}}
|
|
127
130
|
cursor={cursor}
|
|
131
|
+
aria-label={ariaLabel}
|
|
128
132
|
>
|
|
129
133
|
{children}
|
|
130
134
|
</Map>
|
|
@@ -136,13 +140,15 @@ function MapInner({
|
|
|
136
140
|
<button
|
|
137
141
|
type="button"
|
|
138
142
|
onClick={resetToInitial}
|
|
143
|
+
aria-label="Reset map view"
|
|
139
144
|
className={cn(
|
|
140
145
|
'inline-flex items-center gap-2 px-3 py-2 rounded-lg',
|
|
141
146
|
'bg-background/95 backdrop-blur-sm text-foreground text-sm font-medium',
|
|
142
|
-
'hover:bg-background transition-colors shadow-sm border border-border'
|
|
147
|
+
'hover:bg-background transition-colors shadow-sm border border-border',
|
|
148
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'
|
|
143
149
|
)}
|
|
144
150
|
>
|
|
145
|
-
<RotateCcw className="w-4 h-4" />
|
|
151
|
+
<RotateCcw className="w-4 h-4" aria-hidden="true" />
|
|
146
152
|
Reset
|
|
147
153
|
</button>
|
|
148
154
|
)}
|
|
@@ -156,10 +162,11 @@ function MapInner({
|
|
|
156
162
|
className={cn(
|
|
157
163
|
'inline-flex items-center gap-2 px-4 py-2 rounded-lg',
|
|
158
164
|
'bg-background/95 backdrop-blur-sm text-foreground text-sm font-medium',
|
|
159
|
-
'hover:bg-background transition-colors shadow-sm border border-border'
|
|
165
|
+
'hover:bg-background transition-colors shadow-sm border border-border',
|
|
166
|
+
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring'
|
|
160
167
|
)}
|
|
161
168
|
>
|
|
162
|
-
<ExternalLink className="w-4 h-4" />
|
|
169
|
+
<ExternalLink className="w-4 h-4" aria-hidden="true" />
|
|
163
170
|
{openInMapsLabel}
|
|
164
171
|
</a>
|
|
165
172
|
)}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useState, useMemo, memo } from 'react'
|
|
3
|
+
import { useState, useMemo, memo, type KeyboardEvent } from 'react'
|
|
4
4
|
|
|
5
5
|
import type { MapLegendProps, LegendItem } from '../types'
|
|
6
6
|
|
|
@@ -23,13 +23,13 @@ function LegendIcon({
|
|
|
23
23
|
switch (type) {
|
|
24
24
|
case 'circle':
|
|
25
25
|
return (
|
|
26
|
-
<svg width="16" height="16" viewBox="0 0 16 16">
|
|
26
|
+
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
|
|
27
27
|
<circle cx="8" cy="8" r="6" fill={fill} />
|
|
28
28
|
</svg>
|
|
29
29
|
)
|
|
30
30
|
case 'line':
|
|
31
31
|
return (
|
|
32
|
-
<svg width="16" height="16" viewBox="0 0 16 16">
|
|
32
|
+
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
|
|
33
33
|
<line
|
|
34
34
|
x1="0"
|
|
35
35
|
y1="8"
|
|
@@ -43,14 +43,14 @@ function LegendIcon({
|
|
|
43
43
|
)
|
|
44
44
|
case 'fill':
|
|
45
45
|
return (
|
|
46
|
-
<svg width="16" height="16" viewBox="0 0 16 16">
|
|
46
|
+
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
|
|
47
47
|
<rect x="1" y="1" width="14" height="14" fill={fill} rx="2" />
|
|
48
48
|
</svg>
|
|
49
49
|
)
|
|
50
50
|
case 'symbol':
|
|
51
51
|
default:
|
|
52
52
|
return (
|
|
53
|
-
<svg width="16" height="16" viewBox="0 0 16 16">
|
|
53
|
+
<svg width="16" height="16" viewBox="0 0 16 16" aria-hidden="true">
|
|
54
54
|
<path
|
|
55
55
|
d="M8 1l2.5 5 5.5.8-4 3.9.9 5.3L8 13.5l-4.9 2.5.9-5.3-4-3.9 5.5-.8z"
|
|
56
56
|
fill={fill}
|
|
@@ -76,7 +76,9 @@ function MapLegendComponent({
|
|
|
76
76
|
() => ({
|
|
77
77
|
position: 'absolute',
|
|
78
78
|
...positionStyles[position],
|
|
79
|
-
backgroundColor: '
|
|
79
|
+
backgroundColor: 'var(--color-popover)',
|
|
80
|
+
color: 'var(--color-popover-foreground)',
|
|
81
|
+
border: '1px solid var(--color-border)',
|
|
80
82
|
borderRadius: 8,
|
|
81
83
|
boxShadow: '0 2px 8px rgba(0,0,0,0.15)',
|
|
82
84
|
padding: collapsed ? 8 : 12,
|
|
@@ -96,7 +98,7 @@ function MapLegendComponent({
|
|
|
96
98
|
cursor: collapsible ? 'pointer' : 'default',
|
|
97
99
|
fontWeight: 600,
|
|
98
100
|
fontSize: 12,
|
|
99
|
-
color: '
|
|
101
|
+
color: 'var(--color-foreground)',
|
|
100
102
|
marginBottom: collapsed ? 0 : 8,
|
|
101
103
|
}),
|
|
102
104
|
[collapsible, collapsed]
|
|
@@ -109,50 +111,79 @@ function MapLegendComponent({
|
|
|
109
111
|
gap: 8,
|
|
110
112
|
padding: '4px 0',
|
|
111
113
|
fontSize: 12,
|
|
112
|
-
color: '
|
|
114
|
+
color: 'var(--color-muted-foreground)',
|
|
113
115
|
cursor: onItemClick ? 'pointer' : 'default',
|
|
114
116
|
}),
|
|
115
117
|
[onItemClick]
|
|
116
118
|
)
|
|
117
119
|
|
|
118
|
-
const handleHeaderClick = collapsible ? () => setCollapsed(!
|
|
120
|
+
const handleHeaderClick = collapsible ? () => setCollapsed((c) => !c) : undefined
|
|
119
121
|
|
|
120
122
|
return (
|
|
121
123
|
<div className={className} style={containerStyle}>
|
|
122
124
|
{(title || collapsible) && (
|
|
123
|
-
<div
|
|
125
|
+
<div
|
|
126
|
+
style={headerStyle}
|
|
127
|
+
onClick={handleHeaderClick}
|
|
128
|
+
{...(collapsible
|
|
129
|
+
? {
|
|
130
|
+
role: 'button' as const,
|
|
131
|
+
tabIndex: 0,
|
|
132
|
+
'aria-expanded': !collapsed,
|
|
133
|
+
onKeyDown: (e: KeyboardEvent) => {
|
|
134
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
135
|
+
e.preventDefault()
|
|
136
|
+
setCollapsed((c) => !c)
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
}
|
|
140
|
+
: {})}
|
|
141
|
+
>
|
|
124
142
|
{title && <span>{title}</span>}
|
|
125
143
|
{collapsible && (
|
|
126
144
|
<svg
|
|
127
145
|
width="12"
|
|
128
146
|
height="12"
|
|
129
147
|
viewBox="0 0 12 12"
|
|
148
|
+
aria-hidden="true"
|
|
130
149
|
style={{
|
|
131
150
|
transform: collapsed ? 'rotate(-90deg)' : 'rotate(0deg)',
|
|
132
151
|
transition: 'transform 0.2s',
|
|
133
152
|
}}
|
|
134
153
|
>
|
|
135
|
-
<path d="M3 4.5L6 7.5L9 4.5" stroke="
|
|
154
|
+
<path d="M3 4.5L6 7.5L9 4.5" stroke="currentColor" fill="none" />
|
|
136
155
|
</svg>
|
|
137
156
|
)}
|
|
138
157
|
</div>
|
|
139
158
|
)}
|
|
140
159
|
{!collapsed && (
|
|
141
|
-
<
|
|
160
|
+
<ul style={{ listStyle: 'none', margin: 0, padding: 0 }}>
|
|
142
161
|
{items.map((item) => (
|
|
143
|
-
<
|
|
162
|
+
<li
|
|
144
163
|
key={item.id}
|
|
145
164
|
style={{
|
|
146
165
|
...itemStyle,
|
|
147
166
|
opacity: item.visible === false ? 0.5 : 1,
|
|
148
167
|
}}
|
|
149
168
|
onClick={onItemClick ? () => onItemClick(item) : undefined}
|
|
169
|
+
{...(onItemClick
|
|
170
|
+
? {
|
|
171
|
+
role: 'button' as const,
|
|
172
|
+
tabIndex: 0,
|
|
173
|
+
onKeyDown: (e: KeyboardEvent) => {
|
|
174
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
175
|
+
e.preventDefault()
|
|
176
|
+
onItemClick(item)
|
|
177
|
+
}
|
|
178
|
+
},
|
|
179
|
+
}
|
|
180
|
+
: {})}
|
|
150
181
|
>
|
|
151
182
|
{item.icon || <LegendIcon type={item.type} color={item.color} />}
|
|
152
183
|
<span>{item.label}</span>
|
|
153
|
-
</
|
|
184
|
+
</li>
|
|
154
185
|
))}
|
|
155
|
-
</
|
|
186
|
+
</ul>
|
|
156
187
|
)}
|
|
157
188
|
</div>
|
|
158
189
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useCallback, memo, type ReactNode } from 'react'
|
|
3
|
+
import { useCallback, memo, type ReactNode, type KeyboardEvent } from 'react'
|
|
4
4
|
import { Marker, type MarkerEvent, type MarkerDragEvent } from 'react-map-gl/maplibre'
|
|
5
5
|
import type { LngLat } from 'maplibre-gl'
|
|
6
6
|
import type { MarkerData } from '../types'
|
|
@@ -16,6 +16,8 @@ export interface MapMarkerProps {
|
|
|
16
16
|
onDragEnd?: (marker: MarkerData, lngLat: LngLat) => void
|
|
17
17
|
color?: string
|
|
18
18
|
size?: number
|
|
19
|
+
/** Accessible label for the marker (used when onClick is set) */
|
|
20
|
+
ariaLabel?: string
|
|
19
21
|
}
|
|
20
22
|
|
|
21
23
|
const DefaultPin = memo(function DefaultPin({
|
|
@@ -31,6 +33,7 @@ const DefaultPin = memo(function DefaultPin({
|
|
|
31
33
|
height={size}
|
|
32
34
|
viewBox="0 0 24 24"
|
|
33
35
|
fill="none"
|
|
36
|
+
aria-hidden="true"
|
|
34
37
|
style={{ cursor: 'pointer' }}
|
|
35
38
|
>
|
|
36
39
|
<path
|
|
@@ -55,6 +58,7 @@ export const MapMarker = memo(function MapMarker({
|
|
|
55
58
|
onDragEnd,
|
|
56
59
|
color,
|
|
57
60
|
size,
|
|
61
|
+
ariaLabel,
|
|
58
62
|
}: MapMarkerProps) {
|
|
59
63
|
const handleClick = useCallback(
|
|
60
64
|
(e: MarkerEvent<MouseEvent>) => {
|
|
@@ -85,6 +89,19 @@ export const MapMarker = memo(function MapMarker({
|
|
|
85
89
|
[onDragEnd, marker]
|
|
86
90
|
)
|
|
87
91
|
|
|
92
|
+
const handleKeyDown = useCallback(
|
|
93
|
+
(e: KeyboardEvent) => {
|
|
94
|
+
if (!onClick) return
|
|
95
|
+
if (e.key === 'Enter' || e.key === ' ') {
|
|
96
|
+
e.preventDefault()
|
|
97
|
+
onClick(marker)
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
[onClick, marker]
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
const content = children ?? <DefaultPin color={color} size={size} />
|
|
104
|
+
|
|
88
105
|
return (
|
|
89
106
|
<Marker
|
|
90
107
|
longitude={marker.longitude}
|
|
@@ -96,7 +113,19 @@ export const MapMarker = memo(function MapMarker({
|
|
|
96
113
|
onDrag={handleDrag}
|
|
97
114
|
onDragEnd={handleDragEnd}
|
|
98
115
|
>
|
|
99
|
-
{
|
|
116
|
+
{onClick ? (
|
|
117
|
+
<div
|
|
118
|
+
role="button"
|
|
119
|
+
tabIndex={0}
|
|
120
|
+
aria-label={ariaLabel ?? `Map marker ${marker.id}`}
|
|
121
|
+
onKeyDown={handleKeyDown}
|
|
122
|
+
style={{ cursor: 'pointer', display: 'inline-flex', outline: 'none' }}
|
|
123
|
+
>
|
|
124
|
+
{content}
|
|
125
|
+
</div>
|
|
126
|
+
) : (
|
|
127
|
+
content
|
|
128
|
+
)}
|
|
100
129
|
</Marker>
|
|
101
130
|
)
|
|
102
131
|
})
|