ai-design-system 0.1.0
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 +307 -0
- package/components/ai-elements/actions.tsx +65 -0
- package/components/ai-elements/artifact.tsx +147 -0
- package/components/ai-elements/branch.tsx +212 -0
- package/components/ai-elements/canvas.tsx +24 -0
- package/components/ai-elements/chain-of-thought.tsx +228 -0
- package/components/ai-elements/code-block.tsx +179 -0
- package/components/ai-elements/confirmation.tsx +169 -0
- package/components/ai-elements/connection.tsx +28 -0
- package/components/ai-elements/context.tsx +408 -0
- package/components/ai-elements/controls.tsx +18 -0
- package/components/ai-elements/conversation.tsx +97 -0
- package/components/ai-elements/edge.tsx +140 -0
- package/components/ai-elements/image.tsx +24 -0
- package/components/ai-elements/inline-citation.tsx +287 -0
- package/components/ai-elements/loader.tsx +96 -0
- package/components/ai-elements/message.tsx +80 -0
- package/components/ai-elements/node.tsx +71 -0
- package/components/ai-elements/open-in-chat.tsx +363 -0
- package/components/ai-elements/panel.tsx +15 -0
- package/components/ai-elements/plan.tsx +142 -0
- package/components/ai-elements/prompt-input.tsx +1352 -0
- package/components/ai-elements/queue.tsx +274 -0
- package/components/ai-elements/reasoning.tsx +178 -0
- package/components/ai-elements/response.tsx +22 -0
- package/components/ai-elements/shimmer.tsx +64 -0
- package/components/ai-elements/sources.tsx +77 -0
- package/components/ai-elements/suggestion.tsx +56 -0
- package/components/ai-elements/task.tsx +87 -0
- package/components/ai-elements/tool.tsx +179 -0
- package/components/ai-elements/toolbar.tsx +16 -0
- package/components/ai-elements/web-preview.tsx +263 -0
- package/components/blocks/AIConversation/AIConversation.stories.tsx +164 -0
- package/components/blocks/AIConversation/AIConversation.tsx +186 -0
- package/components/blocks/AIConversation/index.ts +8 -0
- package/components/blocks/AppSidebar/AppSidebar.stories.tsx +63 -0
- package/components/blocks/AppSidebar/AppSidebar.tsx +87 -0
- package/components/blocks/AppSidebar/index.ts +2 -0
- package/components/blocks/DocumentEditorWithComments/DocumentEditorWithComments.stories.tsx +341 -0
- package/components/blocks/DocumentEditorWithComments/DocumentEditorWithComments.tsx +255 -0
- package/components/blocks/DocumentEditorWithComments/index.ts +9 -0
- package/components/blocks/FileChangeQueue/FileChangeQueue.stories.tsx +207 -0
- package/components/blocks/FileChangeQueue/FileChangeQueue.tsx +143 -0
- package/components/blocks/FileChangeQueue/index.ts +7 -0
- package/components/blocks/LayoutProvider/LayoutProvider.tsx +34 -0
- package/components/blocks/LayoutProvider/index.ts +1 -0
- package/components/blocks/index.ts +2 -0
- package/components/composites/AgentIndicator/AgentIndicator.stories.tsx +154 -0
- package/components/composites/AgentIndicator/AgentIndicator.tsx +102 -0
- package/components/composites/AgentIndicator/index.ts +8 -0
- package/components/composites/AppHeader/AppHeader.stories.tsx +46 -0
- package/components/composites/AppHeader/AppHeader.tsx +24 -0
- package/components/composites/AppHeader/index.ts +2 -0
- package/components/composites/CommentBox/CommentBox.stories.tsx +192 -0
- package/components/composites/CommentBox/CommentBox.tsx +364 -0
- package/components/composites/CommentBox/index.ts +8 -0
- package/components/composites/Confirmation/Confirmation.stories.tsx +151 -0
- package/components/composites/Confirmation/Confirmation.tsx +93 -0
- package/components/composites/Confirmation/index.ts +7 -0
- package/components/composites/DataTable/DataTable.stories.tsx +35 -0
- package/components/composites/DataTable/DataTable.tsx +95 -0
- package/components/composites/DataTable/index.ts +2 -0
- package/components/composites/DocumentEditor/DocumentEditor.css +106 -0
- package/components/composites/DocumentEditor/DocumentEditor.stories.tsx +927 -0
- package/components/composites/DocumentEditor/DocumentEditor.tsx +279 -0
- package/components/composites/DocumentEditor/index.ts +8 -0
- package/components/composites/FileQueue/FileQueue.stories.tsx +175 -0
- package/components/composites/FileQueue/FileQueue.tsx +161 -0
- package/components/composites/FileQueue/FileStatusBadge.tsx +74 -0
- package/components/composites/FileQueue/index.ts +24 -0
- package/components/composites/InteractiveChart/InteractiveChart.stories.tsx +49 -0
- package/components/composites/InteractiveChart/InteractiveChart.tsx +69 -0
- package/components/composites/InteractiveChart/index.ts +2 -0
- package/components/composites/ModeToggle/ModeToggle.stories.tsx +212 -0
- package/components/composites/ModeToggle/ModeToggle.tsx +100 -0
- package/components/composites/ModeToggle/index.ts +7 -0
- package/components/composites/NavUser/NavUser.stories.tsx +50 -0
- package/components/composites/NavUser/NavUser.tsx +60 -0
- package/components/composites/NavUser/index.ts +2 -0
- package/components/composites/NavigationList/NavigationList.stories.tsx +46 -0
- package/components/composites/NavigationList/NavigationList.tsx +46 -0
- package/components/composites/NavigationList/index.ts +2 -0
- package/components/composites/OrchestratorMessage/OrchestratorMessage.stories.tsx +188 -0
- package/components/composites/OrchestratorMessage/OrchestratorMessage.tsx +72 -0
- package/components/composites/OrchestratorMessage/index.ts +8 -0
- package/components/composites/PageContainer/PageContainer.stories.tsx +41 -0
- package/components/composites/PageContainer/PageContainer.tsx +24 -0
- package/components/composites/PageContainer/index.ts +2 -0
- package/components/composites/PromptInput/PromptInput.stories.tsx +200 -0
- package/components/composites/PromptInput/PromptInput.tsx +129 -0
- package/components/composites/PromptInput/index.ts +8 -0
- package/components/composites/SpecialistMessage/SpecialistMessage.stories.tsx +286 -0
- package/components/composites/SpecialistMessage/SpecialistMessage.tsx +107 -0
- package/components/composites/SpecialistMessage/index.ts +8 -0
- package/components/composites/StatsCard/StatsCard.stories.tsx +76 -0
- package/components/composites/StatsCard/StatsCard.tsx +81 -0
- package/components/composites/StatsCard/index.ts +2 -0
- package/components/composites/TablePagination/TablePagination.stories.tsx +38 -0
- package/components/composites/TablePagination/TablePagination.tsx +119 -0
- package/components/composites/TablePagination/index.ts +2 -0
- package/components/composites/TableToolbar/TableToolbar.stories.tsx +60 -0
- package/components/composites/TableToolbar/TableToolbar.tsx +66 -0
- package/components/composites/TableToolbar/index.ts +2 -0
- package/components/composites/ThemeSelector/ThemeSelector.stories.tsx +48 -0
- package/components/composites/ThemeSelector/ThemeSelector.tsx +79 -0
- package/components/composites/ThemeSelector/index.ts +2 -0
- package/components/composites/ToolCallDisplay/ToolCallDisplay.stories.tsx +49 -0
- package/components/composites/ToolCallDisplay/ToolCallDisplay.tsx +108 -0
- package/components/composites/ToolCallDisplay/index.ts +8 -0
- package/components/composites/UserMessage/UserMessage.stories.tsx +59 -0
- package/components/composites/UserMessage/UserMessage.tsx +52 -0
- package/components/composites/UserMessage/index.ts +8 -0
- package/components/composites/index.ts +90 -0
- package/components/features/AIDocEditor/AIDocEditor.behaviors.stories.tsx +451 -0
- package/components/features/AIDocEditor/AIDocEditor.mocks.ts +96 -0
- package/components/features/AIDocEditor/AIDocEditor.stories.tsx +140 -0
- package/components/features/AIDocEditor/AIDocEditor.tsx +130 -0
- package/components/features/AIDocEditor/index.ts +8 -0
- package/components/features/AIDocEditor/useAIDocEditor.d.ts +97 -0
- package/components/features/AIDocEditor/useAIDocEditor.mock.ts +83 -0
- package/components/features/PageLayout/PageLayout.behaviors.stories.tsx +119 -0
- package/components/features/PageLayout/PageLayout.mocks.ts +27 -0
- package/components/features/PageLayout/PageLayout.stories.tsx +142 -0
- package/components/features/PageLayout/PageLayout.tsx +94 -0
- package/components/features/PageLayout/index.ts +4 -0
- package/components/features/PageLayout/usePageLayout.d.ts +24 -0
- package/components/features/PageLayout/usePageLayout.mock.ts +19 -0
- package/components/features/RefinementPanel/README.md +189 -0
- package/components/features/RefinementPanel/RefinementPanel.behaviors.stories.tsx +475 -0
- package/components/features/RefinementPanel/RefinementPanel.mocks.ts +131 -0
- package/components/features/RefinementPanel/RefinementPanel.stories.tsx +141 -0
- package/components/features/RefinementPanel/RefinementPanel.tsx +160 -0
- package/components/features/RefinementPanel/index.ts +25 -0
- package/components/features/RefinementPanel/useRefinementPanel.d.ts +74 -0
- package/components/features/RefinementPanel/useRefinementPanel.mock.ts +121 -0
- package/components/features/SpecNavigator/SpecNavigator.behaviors.stories.tsx +379 -0
- package/components/features/SpecNavigator/SpecNavigator.mocks.ts +131 -0
- package/components/features/SpecNavigator/SpecNavigator.stories.tsx +122 -0
- package/components/features/SpecNavigator/SpecNavigator.tsx +43 -0
- package/components/features/SpecNavigator/index.ts +2 -0
- package/components/features/SpecNavigator/useSpecNavigator.d.ts +122 -0
- package/components/features/SpecNavigator/useSpecNavigator.mock.ts +93 -0
- package/components/features/index.ts +18 -0
- package/components/index.ts +14 -0
- package/components/primitives/Accordion/Accordion.stories.tsx +87 -0
- package/components/primitives/Accordion/Accordion.tsx +66 -0
- package/components/primitives/Accordion/index.ts +13 -0
- package/components/primitives/Alert/Alert.stories.tsx +422 -0
- package/components/primitives/Alert/Alert.tsx +61 -0
- package/components/primitives/Alert/index.ts +8 -0
- package/components/primitives/AlertDialog/AlertDialog.stories.tsx +367 -0
- package/components/primitives/AlertDialog/AlertDialog.tsx +182 -0
- package/components/primitives/AlertDialog/index.ts +25 -0
- package/components/primitives/Avatar/Avatar.stories.tsx +321 -0
- package/components/primitives/Avatar/Avatar.tsx +63 -0
- package/components/primitives/Avatar/index.ts +8 -0
- package/components/primitives/Badge/Badge.stories.tsx +74 -0
- package/components/primitives/Badge/Badge.tsx +49 -0
- package/components/primitives/Badge/index.ts +2 -0
- package/components/primitives/Button/Button.stories.tsx +445 -0
- package/components/primitives/Button/Button.tsx +89 -0
- package/components/primitives/Button/index.ts +7 -0
- package/components/primitives/Card/Card.stories.tsx +831 -0
- package/components/primitives/Card/Card.tsx +242 -0
- package/components/primitives/Card/index.ts +30 -0
- package/components/primitives/Carousel/Carousel.stories.tsx +32 -0
- package/components/primitives/Carousel/Carousel.tsx +63 -0
- package/components/primitives/Carousel/index.ts +13 -0
- package/components/primitives/Chart/Chart.stories.tsx +346 -0
- package/components/primitives/Chart/Chart.tsx +117 -0
- package/components/primitives/Chart/index.ts +20 -0
- package/components/primitives/Checkbox/Checkbox.stories.tsx +87 -0
- package/components/primitives/Checkbox/Checkbox.tsx +38 -0
- package/components/primitives/Checkbox/index.ts +2 -0
- package/components/primitives/Collapsible/Collapsible.stories.tsx +38 -0
- package/components/primitives/Collapsible/Collapsible.tsx +39 -0
- package/components/primitives/Collapsible/index.ts +8 -0
- package/components/primitives/Command/Command.stories.tsx +150 -0
- package/components/primitives/Command/Command.tsx +147 -0
- package/components/primitives/Command/index.ts +20 -0
- package/components/primitives/Dialog/Dialog.stories.tsx +390 -0
- package/components/primitives/Dialog/Dialog.tsx +140 -0
- package/components/primitives/Dialog/index.ts +22 -0
- package/components/primitives/Drawer/Drawer.stories.tsx +327 -0
- package/components/primitives/Drawer/Drawer.tsx +208 -0
- package/components/primitives/Drawer/index.ts +27 -0
- package/components/primitives/DropdownMenu/DropdownMenu.stories.tsx +150 -0
- package/components/primitives/DropdownMenu/DropdownMenu.tsx +73 -0
- package/components/primitives/DropdownMenu/index.ts +1 -0
- package/components/primitives/HoverCard/HoverCard.stories.tsx +26 -0
- package/components/primitives/HoverCard/HoverCard.tsx +39 -0
- package/components/primitives/HoverCard/index.ts +8 -0
- package/components/primitives/Icon/Icon.stories.tsx +281 -0
- package/components/primitives/Icon/Icon.tsx +87 -0
- package/components/primitives/Icon/index.ts +8 -0
- package/components/primitives/Input/Input.stories.tsx +370 -0
- package/components/primitives/Input/Input.tsx +88 -0
- package/components/primitives/Input/index.ts +7 -0
- package/components/primitives/InputGroup/InputGroup.stories.tsx +40 -0
- package/components/primitives/InputGroup/InputGroup.tsx +72 -0
- package/components/primitives/InputGroup/index.ts +14 -0
- package/components/primitives/Label/Label.stories.tsx +227 -0
- package/components/primitives/Label/Label.tsx +53 -0
- package/components/primitives/Label/index.ts +7 -0
- package/components/primitives/Popover/Popover.stories.tsx +42 -0
- package/components/primitives/Popover/Popover.tsx +107 -0
- package/components/primitives/Popover/index.ts +2 -0
- package/components/primitives/Progress/Progress.stories.tsx +340 -0
- package/components/primitives/Progress/Progress.tsx +31 -0
- package/components/primitives/Progress/index.ts +1 -0
- package/components/primitives/ScrollArea/ScrollArea.stories.tsx +26 -0
- package/components/primitives/ScrollArea/ScrollArea.tsx +28 -0
- package/components/primitives/ScrollArea/index.ts +6 -0
- package/components/primitives/Select/Select.stories.tsx +288 -0
- package/components/primitives/Select/Select.tsx +162 -0
- package/components/primitives/Select/index.ts +22 -0
- package/components/primitives/Separator/Separator.stories.tsx +264 -0
- package/components/primitives/Separator/Separator.tsx +48 -0
- package/components/primitives/Separator/index.ts +7 -0
- package/components/primitives/Sidebar/Sidebar.stories.tsx +358 -0
- package/components/primitives/Sidebar/Sidebar.tsx +317 -0
- package/components/primitives/Sidebar/index.ts +41 -0
- package/components/primitives/Table/Table.stories.tsx +389 -0
- package/components/primitives/Table/Table.tsx +191 -0
- package/components/primitives/Table/index.ts +26 -0
- package/components/primitives/Tabs/Tabs.stories.tsx +129 -0
- package/components/primitives/Tabs/Tabs.tsx +70 -0
- package/components/primitives/Tabs/index.ts +13 -0
- package/components/primitives/Textarea/Textarea.stories.tsx +358 -0
- package/components/primitives/Textarea/Textarea.tsx +91 -0
- package/components/primitives/Textarea/index.ts +7 -0
- package/components/primitives/ToggleGroup/ToggleGroup.stories.tsx +87 -0
- package/components/primitives/ToggleGroup/ToggleGroup.tsx +52 -0
- package/components/primitives/ToggleGroup/index.ts +6 -0
- package/components/primitives/Tooltip/Tooltip.stories.tsx +336 -0
- package/components/primitives/Tooltip/Tooltip.tsx +78 -0
- package/components/primitives/Tooltip/index.ts +10 -0
- package/components/primitives/index.ts +34 -0
- package/components/ui/accordion.tsx +66 -0
- package/components/ui/alert-dialog.tsx +157 -0
- package/components/ui/alert.tsx +66 -0
- package/components/ui/avatar.tsx +53 -0
- package/components/ui/badge.tsx +46 -0
- package/components/ui/button.tsx +60 -0
- package/components/ui/card.tsx +117 -0
- package/components/ui/carousel.tsx +241 -0
- package/components/ui/chart.tsx +334 -0
- package/components/ui/checkbox.tsx +32 -0
- package/components/ui/collapsible.tsx +33 -0
- package/components/ui/command.tsx +184 -0
- package/components/ui/dialog.tsx +143 -0
- package/components/ui/drawer.tsx +118 -0
- package/components/ui/dropdown-menu.tsx +257 -0
- package/components/ui/hover-card.tsx +44 -0
- package/components/ui/input-group.tsx +170 -0
- package/components/ui/input.tsx +48 -0
- package/components/ui/label.tsx +26 -0
- package/components/ui/popover.tsx +33 -0
- package/components/ui/progress.tsx +31 -0
- package/components/ui/scroll-area.tsx +58 -0
- package/components/ui/select.tsx +187 -0
- package/components/ui/separator.tsx +31 -0
- package/components/ui/sidebar.tsx +577 -0
- package/components/ui/table.tsx +120 -0
- package/components/ui/tabs.tsx +66 -0
- package/components/ui/textarea.tsx +46 -0
- package/components/ui/toggle-group.tsx +83 -0
- package/components/ui/toggle.tsx +47 -0
- package/components/ui/tooltip.tsx +61 -0
- package/dist/index.cjs +7389 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.css +75 -0
- package/dist/index.css.map +1 -0
- package/dist/index.js +7160 -0
- package/dist/index.js.map +1 -0
- package/hooks/useAIDocReviewer.d.ts +0 -0
- package/lib/utils.ts +6 -0
- package/package.json +140 -0
- package/tokens/color/base.json +14 -0
- package/tokens/color/dark.json +40 -0
- package/tokens/color/green.json +21 -0
- package/tokens/color/light.json +52 -0
- package/tokens/color/neutral.json +20 -0
- package/tokens/color/violet.json +21 -0
- package/tokens/spacing.json +22 -0
- package/utils/ai-editor/format-date.ts +41 -0
- package/utils/ai-editor/index.ts +22 -0
- package/utils/ai-editor/type-guards.ts +72 -0
- package/utils/ai-editor/validation.ts +130 -0
- package/utils/editor-annotations.ts +122 -0
|
@@ -0,0 +1,255 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DocumentEditorWithComments Section
|
|
3
|
+
*
|
|
4
|
+
* Combines DocumentEditor and CommentBox blocks to provide inline commenting functionality
|
|
5
|
+
* Section layer: composes blocks and manages UI state
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import React, { useState, useCallback, useMemo, useEffect } from 'react'
|
|
9
|
+
import { DocumentEditor } from '@/components/composites/DocumentEditor'
|
|
10
|
+
import { CommentBox } from '@/components/composites/CommentBox'
|
|
11
|
+
import { Card } from '@/components/primitives/Card'
|
|
12
|
+
import type { JSONContent } from '@tiptap/core'
|
|
13
|
+
import type { Annotation, CommentAnnotation, Range } from '@/types/ai-editor'
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* Props for DocumentEditorWithComments section
|
|
17
|
+
*/
|
|
18
|
+
export interface DocumentEditorWithCommentsProps {
|
|
19
|
+
/**
|
|
20
|
+
* Document content - can be either:
|
|
21
|
+
* - JSONContent: Tiptap's JSON format (default)
|
|
22
|
+
* - string: Markdown string (when format='markdown')
|
|
23
|
+
*/
|
|
24
|
+
content: JSONContent | string
|
|
25
|
+
/**
|
|
26
|
+
* Content format - determines how content prop is interpreted
|
|
27
|
+
* @default 'json'
|
|
28
|
+
*/
|
|
29
|
+
format?: 'json' | 'markdown'
|
|
30
|
+
/** Array of annotations to display */
|
|
31
|
+
annotations: Annotation[]
|
|
32
|
+
/** ID of currently selected annotation */
|
|
33
|
+
selectedAnnotationId?: string
|
|
34
|
+
/** Current user ID for comment ownership */
|
|
35
|
+
currentUserId: string
|
|
36
|
+
/** Current user name for new comments and replies */
|
|
37
|
+
currentUserName: string
|
|
38
|
+
/** Whether editor is in read-only mode */
|
|
39
|
+
readOnly?: boolean
|
|
40
|
+
/** Callback when new annotation is added */
|
|
41
|
+
onAnnotationAdd?: (annotation: Annotation) => void
|
|
42
|
+
/** Callback when annotation is updated (e.g., reply added) */
|
|
43
|
+
onAnnotationUpdate?: (annotation: Annotation) => void
|
|
44
|
+
/** Additional CSS classes */
|
|
45
|
+
className?: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export const DocumentEditorWithComments = React.memo<DocumentEditorWithCommentsProps>(
|
|
49
|
+
({
|
|
50
|
+
content,
|
|
51
|
+
format = 'json',
|
|
52
|
+
annotations,
|
|
53
|
+
selectedAnnotationId,
|
|
54
|
+
currentUserId,
|
|
55
|
+
currentUserName,
|
|
56
|
+
readOnly = false,
|
|
57
|
+
onAnnotationAdd,
|
|
58
|
+
onAnnotationUpdate,
|
|
59
|
+
className,
|
|
60
|
+
}) => {
|
|
61
|
+
// Internal UI state
|
|
62
|
+
const [commentBoxVisible, setCommentBoxVisible] = useState(false)
|
|
63
|
+
const [commentBoxPosition, setCommentBoxPosition] = useState({ x: 0, y: 0 })
|
|
64
|
+
const [activeAnnotation, setActiveAnnotation] = useState<Annotation | undefined>()
|
|
65
|
+
const [selectedRange, setSelectedRange] = useState<Range | undefined>()
|
|
66
|
+
const [hoveredAnnotationId, setHoveredAnnotationId] = useState<string | null>(null)
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Sync activeAnnotation with latest data from annotations array
|
|
70
|
+
* This ensures CommentBox shows updated data when replies are added
|
|
71
|
+
*/
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
if (activeAnnotation && commentBoxVisible) {
|
|
74
|
+
const updatedAnnotation = annotations.find((a) => a.id === activeAnnotation.id)
|
|
75
|
+
if (updatedAnnotation) {
|
|
76
|
+
setActiveAnnotation(updatedAnnotation)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}, [annotations, activeAnnotation, commentBoxVisible])
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Handle annotation click - show CommentBox near the annotation
|
|
83
|
+
*/
|
|
84
|
+
const handleAnnotationClick = useCallback(
|
|
85
|
+
(annotationId: string, position: { x: number; y: number }) => {
|
|
86
|
+
const annotation = annotations.find((a) => a.id === annotationId)
|
|
87
|
+
if (annotation) {
|
|
88
|
+
setActiveAnnotation(annotation)
|
|
89
|
+
setSelectedRange(undefined)
|
|
90
|
+
setCommentBoxPosition(position)
|
|
91
|
+
setCommentBoxVisible(true)
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
[annotations]
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Handle text selection - show CommentBox for new comment
|
|
99
|
+
*/
|
|
100
|
+
const handleTextSelect = useCallback(
|
|
101
|
+
(range: Range, text: string, position?: { x: number; y: number }) => {
|
|
102
|
+
if (readOnly || !text.trim()) return
|
|
103
|
+
|
|
104
|
+
// Show CommentBox for new comment
|
|
105
|
+
setActiveAnnotation(undefined)
|
|
106
|
+
setSelectedRange(range)
|
|
107
|
+
|
|
108
|
+
// Use calculated position from DocumentEditor, or fallback to default
|
|
109
|
+
const finalPosition = position || { x: 600, y: 100 }
|
|
110
|
+
setCommentBoxPosition(finalPosition)
|
|
111
|
+
setCommentBoxVisible(true)
|
|
112
|
+
},
|
|
113
|
+
[readOnly]
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Handle new comment submission
|
|
118
|
+
*/
|
|
119
|
+
const handleCommentAdd = useCallback(
|
|
120
|
+
(content: JSONContent) => {
|
|
121
|
+
if (!selectedRange || !onAnnotationAdd) return
|
|
122
|
+
|
|
123
|
+
// Create new comment annotation
|
|
124
|
+
const newAnnotation: CommentAnnotation = {
|
|
125
|
+
type: 'comment',
|
|
126
|
+
id: `comment-${Date.now()}`,
|
|
127
|
+
range: selectedRange,
|
|
128
|
+
createdAt: Date.now(),
|
|
129
|
+
userId: currentUserId,
|
|
130
|
+
data: {
|
|
131
|
+
thread: [
|
|
132
|
+
{
|
|
133
|
+
id: `comment-msg-${Date.now()}`,
|
|
134
|
+
userId: currentUserId,
|
|
135
|
+
userName: currentUserName,
|
|
136
|
+
contentRich: content,
|
|
137
|
+
timestamp: Date.now(),
|
|
138
|
+
},
|
|
139
|
+
],
|
|
140
|
+
},
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
onAnnotationAdd(newAnnotation)
|
|
144
|
+
setCommentBoxVisible(false)
|
|
145
|
+
setSelectedRange(undefined)
|
|
146
|
+
},
|
|
147
|
+
[selectedRange, currentUserId, currentUserName, onAnnotationAdd]
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Handle reply to existing annotation
|
|
152
|
+
*/
|
|
153
|
+
const handleCommentReply = useCallback(
|
|
154
|
+
(annotationId: string, content: JSONContent) => {
|
|
155
|
+
if (!onAnnotationUpdate) return
|
|
156
|
+
|
|
157
|
+
const annotation = annotations.find((a) => a.id === annotationId)
|
|
158
|
+
if (!annotation) return
|
|
159
|
+
|
|
160
|
+
// Add reply to the annotation's thread
|
|
161
|
+
const newComment = {
|
|
162
|
+
id: `comment-msg-${Date.now()}`,
|
|
163
|
+
userId: currentUserId,
|
|
164
|
+
userName: currentUserName,
|
|
165
|
+
contentRich: content,
|
|
166
|
+
timestamp: Date.now(),
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
let updatedAnnotation: Annotation
|
|
170
|
+
|
|
171
|
+
if (annotation.type === 'comment') {
|
|
172
|
+
updatedAnnotation = {
|
|
173
|
+
...annotation,
|
|
174
|
+
data: {
|
|
175
|
+
...annotation.data,
|
|
176
|
+
thread: [...annotation.data.thread, newComment],
|
|
177
|
+
},
|
|
178
|
+
}
|
|
179
|
+
} else if (annotation.type === 'suggestion') {
|
|
180
|
+
updatedAnnotation = {
|
|
181
|
+
...annotation,
|
|
182
|
+
data: {
|
|
183
|
+
...annotation.data,
|
|
184
|
+
thread: [...annotation.data.thread, newComment],
|
|
185
|
+
},
|
|
186
|
+
}
|
|
187
|
+
} else {
|
|
188
|
+
updatedAnnotation = {
|
|
189
|
+
...annotation,
|
|
190
|
+
data: {
|
|
191
|
+
...annotation.data,
|
|
192
|
+
thread: [...annotation.data.thread, newComment],
|
|
193
|
+
},
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
onAnnotationUpdate(updatedAnnotation)
|
|
198
|
+
},
|
|
199
|
+
[annotations, currentUserId, currentUserName, onAnnotationUpdate]
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Handle annotation hover - update hover state
|
|
204
|
+
*/
|
|
205
|
+
const handleAnnotationHover = useCallback((annotationId: string | null) => {
|
|
206
|
+
setHoveredAnnotationId(annotationId)
|
|
207
|
+
}, [])
|
|
208
|
+
|
|
209
|
+
/**
|
|
210
|
+
* Handle CommentBox close - clear pending comment highlight
|
|
211
|
+
*/
|
|
212
|
+
const handleCommentBoxClose = useCallback(() => {
|
|
213
|
+
setCommentBoxVisible(false)
|
|
214
|
+
setActiveAnnotation(undefined)
|
|
215
|
+
setSelectedRange(undefined) // This clears the yellow highlight
|
|
216
|
+
}, [])
|
|
217
|
+
|
|
218
|
+
// Memoize props to prevent unnecessary re-renders of DocumentEditor
|
|
219
|
+
// This is critical because re-renders trigger setContent() which clears marks
|
|
220
|
+
const documentEditorProps = useMemo(
|
|
221
|
+
() => ({
|
|
222
|
+
content,
|
|
223
|
+
format,
|
|
224
|
+
annotations,
|
|
225
|
+
selectedAnnotationId,
|
|
226
|
+
hoveredAnnotationId,
|
|
227
|
+
pendingCommentRange: selectedRange, // Pass pending range to show yellow highlight
|
|
228
|
+
onTextSelect: handleTextSelect,
|
|
229
|
+
onAnnotationClick: handleAnnotationClick,
|
|
230
|
+
onAnnotationHover: handleAnnotationHover,
|
|
231
|
+
readOnly,
|
|
232
|
+
}),
|
|
233
|
+
[content, format, annotations, selectedAnnotationId, hoveredAnnotationId, selectedRange, handleTextSelect, handleAnnotationClick, handleAnnotationHover, readOnly]
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
return (
|
|
237
|
+
<Card className={className}>
|
|
238
|
+
<DocumentEditor {...documentEditorProps} />
|
|
239
|
+
|
|
240
|
+
<CommentBox
|
|
241
|
+
annotation={activeAnnotation}
|
|
242
|
+
position={commentBoxPosition}
|
|
243
|
+
visible={commentBoxVisible}
|
|
244
|
+
currentUserId={currentUserId}
|
|
245
|
+
onClose={handleCommentBoxClose}
|
|
246
|
+
onCommentAdd={handleCommentAdd}
|
|
247
|
+
onCommentReply={handleCommentReply}
|
|
248
|
+
/>
|
|
249
|
+
</Card>
|
|
250
|
+
)
|
|
251
|
+
}
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
DocumentEditorWithComments.displayName = 'DocumentEditorWithComments'
|
|
255
|
+
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* DocumentEditorWithComments Section
|
|
3
|
+
*
|
|
4
|
+
* Exports the DocumentEditorWithComments section component
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export { DocumentEditorWithComments } from './DocumentEditorWithComments'
|
|
8
|
+
export type { DocumentEditorWithCommentsProps } from './DocumentEditorWithComments'
|
|
9
|
+
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
2
|
+
import { FileChangeQueue } from "./FileChangeQueue";
|
|
3
|
+
import type { FileChangeData } from "@/components/composites/FileQueue";
|
|
4
|
+
|
|
5
|
+
const meta: Meta<typeof FileChangeQueue> = {
|
|
6
|
+
title: "Blocks/FileChangeQueue",
|
|
7
|
+
component: FileChangeQueue,
|
|
8
|
+
parameters: {
|
|
9
|
+
layout: "padded",
|
|
10
|
+
},
|
|
11
|
+
} satisfies Meta<typeof FileChangeQueue>;
|
|
12
|
+
|
|
13
|
+
export default meta;
|
|
14
|
+
type Story = StoryObj<typeof meta>;
|
|
15
|
+
|
|
16
|
+
// Sample file changes for stories
|
|
17
|
+
const mixedChanges: FileChangeData[] = [
|
|
18
|
+
{
|
|
19
|
+
id: "1",
|
|
20
|
+
filename: "Button.tsx",
|
|
21
|
+
status: "modified",
|
|
22
|
+
path: "src/components/primitives/Button/Button.tsx",
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
id: "2",
|
|
26
|
+
filename: "Icon.tsx",
|
|
27
|
+
status: "created",
|
|
28
|
+
path: "src/components/primitives/Icon/Icon.tsx",
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
id: "3",
|
|
32
|
+
filename: "FileQueue.tsx",
|
|
33
|
+
status: "created",
|
|
34
|
+
path: "src/components/composites/FileQueue/FileQueue.tsx",
|
|
35
|
+
},
|
|
36
|
+
{
|
|
37
|
+
id: "4",
|
|
38
|
+
filename: "OldComponent.tsx",
|
|
39
|
+
status: "deleted",
|
|
40
|
+
path: "src/components/deprecated/OldComponent.tsx",
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
id: "5",
|
|
44
|
+
filename: "README.md",
|
|
45
|
+
status: "modified",
|
|
46
|
+
path: "docs/README.md",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
id: "6",
|
|
50
|
+
filename: "package.json",
|
|
51
|
+
status: "pending",
|
|
52
|
+
path: "package.json",
|
|
53
|
+
},
|
|
54
|
+
];
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Default state - approval requested with mixed file statuses
|
|
58
|
+
*/
|
|
59
|
+
export const ApprovalRequested: Story = {
|
|
60
|
+
args: {
|
|
61
|
+
changes: mixedChanges,
|
|
62
|
+
title: "Review and approve these file changes",
|
|
63
|
+
state: "approval-requested",
|
|
64
|
+
approval: {},
|
|
65
|
+
onApprove: () => console.log("Approved all changes"),
|
|
66
|
+
onReject: () => console.log("Rejected all changes"),
|
|
67
|
+
},
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Approved state - shows after approval
|
|
72
|
+
*/
|
|
73
|
+
export const Approved: Story = {
|
|
74
|
+
args: {
|
|
75
|
+
changes: mixedChanges,
|
|
76
|
+
title: "Review and approve these file changes",
|
|
77
|
+
state: "approval-responded",
|
|
78
|
+
approval: { approved: true },
|
|
79
|
+
onApprove: () => console.log("Approved all changes"),
|
|
80
|
+
onReject: () => console.log("Rejected all changes"),
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Rejected state - shows after rejection
|
|
86
|
+
*/
|
|
87
|
+
export const Rejected: Story = {
|
|
88
|
+
args: {
|
|
89
|
+
changes: mixedChanges,
|
|
90
|
+
title: "Review and approve these file changes",
|
|
91
|
+
state: "approval-responded",
|
|
92
|
+
approval: { approved: false },
|
|
93
|
+
onApprove: () => console.log("Approved all changes"),
|
|
94
|
+
onReject: () => console.log("Rejected all changes"),
|
|
95
|
+
},
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* All modified files
|
|
100
|
+
*/
|
|
101
|
+
export const AllModified: Story = {
|
|
102
|
+
args: {
|
|
103
|
+
changes: [
|
|
104
|
+
{
|
|
105
|
+
id: "1",
|
|
106
|
+
filename: "Button.tsx",
|
|
107
|
+
status: "modified",
|
|
108
|
+
path: "src/components/primitives/Button/Button.tsx",
|
|
109
|
+
},
|
|
110
|
+
{
|
|
111
|
+
id: "2",
|
|
112
|
+
filename: "Input.tsx",
|
|
113
|
+
status: "modified",
|
|
114
|
+
path: "src/components/primitives/Input/Input.tsx",
|
|
115
|
+
},
|
|
116
|
+
{
|
|
117
|
+
id: "3",
|
|
118
|
+
filename: "Card.tsx",
|
|
119
|
+
status: "modified",
|
|
120
|
+
path: "src/components/primitives/Card/Card.tsx",
|
|
121
|
+
},
|
|
122
|
+
],
|
|
123
|
+
title: "Review modified files",
|
|
124
|
+
state: "approval-requested",
|
|
125
|
+
approval: {},
|
|
126
|
+
onApprove: () => console.log("Approved"),
|
|
127
|
+
onReject: () => console.log("Rejected"),
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Large change set
|
|
133
|
+
*/
|
|
134
|
+
export const LargeChangeSet: Story = {
|
|
135
|
+
args: {
|
|
136
|
+
changes: [
|
|
137
|
+
...Array.from({ length: 15 }, (_, i) => ({
|
|
138
|
+
id: `modified-${i}`,
|
|
139
|
+
filename: `Component${i + 1}.tsx`,
|
|
140
|
+
status: "modified" as const,
|
|
141
|
+
path: `src/components/composites/Component${i + 1}/Component${i + 1}.tsx`,
|
|
142
|
+
})),
|
|
143
|
+
...Array.from({ length: 10 }, (_, i) => ({
|
|
144
|
+
id: `created-${i}`,
|
|
145
|
+
filename: `NewComponent${i + 1}.tsx`,
|
|
146
|
+
status: "created" as const,
|
|
147
|
+
path: `src/components/new/NewComponent${i + 1}/NewComponent${i + 1}.tsx`,
|
|
148
|
+
})),
|
|
149
|
+
...Array.from({ length: 5 }, (_, i) => ({
|
|
150
|
+
id: `deleted-${i}`,
|
|
151
|
+
filename: `OldComponent${i + 1}.tsx`,
|
|
152
|
+
status: "deleted" as const,
|
|
153
|
+
path: `src/components/deprecated/OldComponent${i + 1}.tsx`,
|
|
154
|
+
})),
|
|
155
|
+
],
|
|
156
|
+
title: "Review 30 file changes",
|
|
157
|
+
state: "approval-requested",
|
|
158
|
+
approval: {},
|
|
159
|
+
onApprove: () => console.log("Approved"),
|
|
160
|
+
onReject: () => console.log("Rejected"),
|
|
161
|
+
},
|
|
162
|
+
};
|
|
163
|
+
|
|
164
|
+
/**
|
|
165
|
+
* Empty state (no changes) - should not render
|
|
166
|
+
*/
|
|
167
|
+
export const EmptyState: Story = {
|
|
168
|
+
args: {
|
|
169
|
+
changes: [],
|
|
170
|
+
title: "No changes to review",
|
|
171
|
+
state: "approval-requested",
|
|
172
|
+
},
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Without title
|
|
177
|
+
*/
|
|
178
|
+
export const WithoutTitle: Story = {
|
|
179
|
+
args: {
|
|
180
|
+
changes: mixedChanges,
|
|
181
|
+
state: "approval-requested",
|
|
182
|
+
approval: {},
|
|
183
|
+
onApprove: () => console.log("Approved"),
|
|
184
|
+
onReject: () => console.log("Rejected"),
|
|
185
|
+
},
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Single file change
|
|
190
|
+
*/
|
|
191
|
+
export const SingleFile: Story = {
|
|
192
|
+
args: {
|
|
193
|
+
changes: [
|
|
194
|
+
{
|
|
195
|
+
id: "1",
|
|
196
|
+
filename: "Button.tsx",
|
|
197
|
+
status: "modified",
|
|
198
|
+
path: "src/components/primitives/Button/Button.tsx",
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
title: "Review single file change",
|
|
202
|
+
state: "approval-requested",
|
|
203
|
+
approval: {},
|
|
204
|
+
onApprove: () => console.log("Approved"),
|
|
205
|
+
onReject: () => console.log("Rejected"),
|
|
206
|
+
},
|
|
207
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Confirmation, type ConfirmationProps } from "@/components/composites/Confirmation";
|
|
3
|
+
import { FileQueue, type FileGroup } from "@/components/composites/FileQueue";
|
|
4
|
+
import type { FileChangeData, FileStatus } from "@/components/composites/FileQueue";
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* FileChangeQueue Section
|
|
8
|
+
*
|
|
9
|
+
* Composes FileQueue block with Confirmation block for file approval workflow.
|
|
10
|
+
* Displays file modifications with approve/reject actions.
|
|
11
|
+
*/
|
|
12
|
+
|
|
13
|
+
export interface FileChangeQueueProps {
|
|
14
|
+
/**
|
|
15
|
+
* Array of file changes to display
|
|
16
|
+
*/
|
|
17
|
+
changes: FileChangeData[];
|
|
18
|
+
/**
|
|
19
|
+
* Title/prompt text for the confirmation
|
|
20
|
+
*/
|
|
21
|
+
title?: string;
|
|
22
|
+
/**
|
|
23
|
+
* Confirmation state
|
|
24
|
+
*/
|
|
25
|
+
state: ConfirmationProps["state"];
|
|
26
|
+
/**
|
|
27
|
+
* Approval data
|
|
28
|
+
*/
|
|
29
|
+
approval?: ConfirmationProps["approval"];
|
|
30
|
+
/**
|
|
31
|
+
* Approve all changes callback
|
|
32
|
+
*/
|
|
33
|
+
onApprove?: () => void;
|
|
34
|
+
/**
|
|
35
|
+
* Reject all changes callback
|
|
36
|
+
*/
|
|
37
|
+
onReject?: () => void;
|
|
38
|
+
/**
|
|
39
|
+
* Additional CSS classes
|
|
40
|
+
*/
|
|
41
|
+
className?: string;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Transform file changes to groups for FileQueue
|
|
46
|
+
* Maintains existing visual appearance with status-based grouping
|
|
47
|
+
*/
|
|
48
|
+
const transformFileChangesToGroups = (
|
|
49
|
+
changes: FileChangeData[]
|
|
50
|
+
): FileGroup[] => {
|
|
51
|
+
const statusConfig = {
|
|
52
|
+
modified: {
|
|
53
|
+
title: "Modified",
|
|
54
|
+
icon: "file-text",
|
|
55
|
+
iconColor: "text-blue-600 dark:text-blue-500",
|
|
56
|
+
},
|
|
57
|
+
created: {
|
|
58
|
+
title: "Created",
|
|
59
|
+
icon: "plus",
|
|
60
|
+
iconColor: "text-green-600 dark:text-green-500",
|
|
61
|
+
},
|
|
62
|
+
deleted: {
|
|
63
|
+
title: "Deleted",
|
|
64
|
+
icon: "x",
|
|
65
|
+
iconColor: "text-red-600 dark:text-red-500",
|
|
66
|
+
},
|
|
67
|
+
pending: {
|
|
68
|
+
title: "Pending",
|
|
69
|
+
icon: "loader-2",
|
|
70
|
+
iconColor: "text-yellow-600 dark:text-yellow-500",
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
const grouped: Record<FileStatus, FileChangeData[]> = {
|
|
75
|
+
modified: [],
|
|
76
|
+
created: [],
|
|
77
|
+
deleted: [],
|
|
78
|
+
pending: [],
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
changes.forEach((change) => {
|
|
82
|
+
grouped[change.status].push(change);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
const statusOrder: FileStatus[] = ["modified", "created", "deleted", "pending"];
|
|
86
|
+
|
|
87
|
+
const result: FileGroup[] = [];
|
|
88
|
+
|
|
89
|
+
statusOrder.forEach((status) => {
|
|
90
|
+
const files = grouped[status];
|
|
91
|
+
if (files.length === 0) return;
|
|
92
|
+
|
|
93
|
+
const config = statusConfig[status];
|
|
94
|
+
|
|
95
|
+
result.push({
|
|
96
|
+
id: status,
|
|
97
|
+
title: config.title,
|
|
98
|
+
icon: config.icon,
|
|
99
|
+
iconColor: config.iconColor,
|
|
100
|
+
files: files.map((file) => ({
|
|
101
|
+
id: file.id,
|
|
102
|
+
name: file.filename,
|
|
103
|
+
path: file.path,
|
|
104
|
+
})),
|
|
105
|
+
defaultOpen: false,
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return result;
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* FileChangeQueue component - section-level component for file approval workflow
|
|
114
|
+
*/
|
|
115
|
+
export const FileChangeQueue = React.memo<FileChangeQueueProps>(
|
|
116
|
+
({ changes, title, state, approval, onApprove, onReject, className }) => {
|
|
117
|
+
// Only display if at least 1 file change exists
|
|
118
|
+
if (changes.length === 0) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
// Transform file changes to groups
|
|
123
|
+
const groups = React.useMemo(
|
|
124
|
+
() => transformFileChangesToGroups(changes),
|
|
125
|
+
[changes]
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
return (
|
|
129
|
+
<Confirmation
|
|
130
|
+
title={title}
|
|
131
|
+
state={state}
|
|
132
|
+
approval={approval}
|
|
133
|
+
onApprove={onApprove}
|
|
134
|
+
onReject={onReject}
|
|
135
|
+
className={className}
|
|
136
|
+
>
|
|
137
|
+
<FileQueue groups={groups} />
|
|
138
|
+
</Confirmation>
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
FileChangeQueue.displayName = "FileChangeQueue";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import * as React from "react"
|
|
2
|
+
import { SidebarProvider } from "@/components/primitives/Sidebar"
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* LayoutProvider Block
|
|
6
|
+
*
|
|
7
|
+
* Wraps SidebarProvider primitive for use in feature layer.
|
|
8
|
+
* Provides sidebar state management and CSS variables.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export interface LayoutProviderProps {
|
|
12
|
+
children: React.ReactNode
|
|
13
|
+
defaultOpen?: boolean
|
|
14
|
+
sidebarWidth?: string
|
|
15
|
+
sidebarWidthIcon?: string
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const LayoutProvider = React.memo<LayoutProviderProps>(
|
|
19
|
+
({ children, defaultOpen = true, sidebarWidth = "var(--spacing-sidebar-width)", sidebarWidthIcon = "var(--spacing-sidebar-width-icon)" }) => {
|
|
20
|
+
return (
|
|
21
|
+
<SidebarProvider
|
|
22
|
+
defaultOpen={defaultOpen}
|
|
23
|
+
style={{
|
|
24
|
+
"--sidebar-width": sidebarWidth,
|
|
25
|
+
"--sidebar-width-icon": sidebarWidthIcon,
|
|
26
|
+
} as React.CSSProperties}
|
|
27
|
+
>
|
|
28
|
+
{children}
|
|
29
|
+
</SidebarProvider>
|
|
30
|
+
)
|
|
31
|
+
}
|
|
32
|
+
)
|
|
33
|
+
|
|
34
|
+
LayoutProvider.displayName = "LayoutProvider"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { LayoutProvider, type LayoutProviderProps } from "./LayoutProvider"
|