dishui 0.0.108 → 0.0.110
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.release-verified.json +3 -2
- package/dist/components/Accordion/index.js.map +1 -1
- package/dist/components/Avatar/index.js.map +1 -1
- package/dist/components/Badge/index.js.map +1 -1
- package/dist/components/Button/index.js.map +1 -1
- package/dist/components/ButtonMenu/index.js.map +1 -1
- package/dist/components/Card/Metric2.js.map +1 -1
- package/dist/components/Card/Ticket2.js.map +1 -1
- package/dist/components/Card/index.js.map +1 -1
- package/dist/components/Chart/Chart.js.map +1 -1
- package/dist/components/Checkbox/index.js.map +1 -1
- package/dist/components/ChoicePicker/index.js.map +1 -1
- package/dist/components/Column/index.js.map +1 -1
- package/dist/components/ConfirmDialog/index.js.map +1 -1
- package/dist/components/DateTimeInput/index.js.map +1 -1
- package/dist/components/Demo/index.js.map +1 -1
- package/dist/components/Demo/source.js.map +1 -1
- package/dist/components/Divider/index.js.map +1 -1
- package/dist/components/EmptyState/index.js.map +1 -1
- package/dist/components/Field/index.js.map +1 -1
- package/dist/components/FileUploader/DropZone.js.map +1 -1
- package/dist/components/FileUploader/FileItem.js.map +1 -1
- package/dist/components/FileUploader/FileUploader.js.map +1 -1
- package/dist/components/FileUploader/UploadQueue.js.map +1 -1
- package/dist/components/GroupButton/index.js.map +1 -1
- package/dist/components/GroupedTable/index.js.map +1 -1
- package/dist/components/Icon/index.js.map +1 -1
- package/dist/components/Image/index.js.map +1 -1
- package/dist/components/Input/index.js.map +1 -1
- package/dist/components/Label/index.js.map +1 -1
- package/dist/components/Layout/index.js.map +1 -1
- package/dist/components/LayoutSuite/DashboardSuite.js.map +1 -1
- package/dist/components/LayoutSuite/index.js.map +1 -1
- package/dist/components/List/index.js.map +1 -1
- package/dist/components/Markdown/Markdown.js.map +1 -1
- package/dist/components/Markdown/codeThemes.js.map +1 -1
- package/dist/components/Markdown/markdownComponents.js.map +1 -1
- package/dist/components/Markdown/parseCodeBlockMeta.js.map +1 -1
- package/dist/components/Markdown/useMarkdownSource.js.map +1 -1
- package/dist/components/Markdown/useMarkmapRenderer.js.map +1 -1
- package/dist/components/Markdown/useMermaidRenderer.js.map +1 -1
- package/dist/components/Modal/focusScope.js.map +1 -1
- package/dist/components/Modal/index.js.map +1 -1
- package/dist/components/MultipleChoice/index.js.map +1 -1
- package/dist/components/NavigationList/index.js.map +1 -1
- package/dist/components/Page/Float2.js.map +1 -1
- package/dist/components/Page/index.js.map +1 -1
- package/dist/components/Page/resize2.js.map +1 -1
- package/dist/components/PageHeader/index.js.map +1 -1
- package/dist/components/PagePanel/index.js.map +1 -1
- package/dist/components/ProtectedRoute/index.js.map +1 -1
- package/dist/components/Resizable/ResizeGrip.js.map +1 -1
- package/dist/components/Resizable/index.js.map +1 -1
- package/dist/components/Resizable/useResize.js.map +1 -1
- package/dist/components/Row/index.js.map +1 -1
- package/dist/components/Section/index.js.map +1 -1
- package/dist/components/Select/index.js.map +1 -1
- package/dist/components/SidebarToggleButton/index.js.map +1 -1
- package/dist/components/Skeleton/index.js.map +1 -1
- package/dist/components/Slider/index.js.map +1 -1
- package/dist/components/Switch/index.js.map +1 -1
- package/dist/components/Tab/index.js.map +1 -1
- package/dist/components/Table/index.js.map +1 -1
- package/dist/components/Tabs/index.js.map +1 -1
- package/dist/components/Tabs/tabNavigation.js.map +1 -1
- package/dist/components/Text/index.js.map +1 -1
- package/dist/components/Textarea/index.js.map +1 -1
- package/dist/components/Tooltip/index.js.map +1 -1
- package/dist/components/Tree/Tree.js.map +1 -1
- package/dist/components/ai/AgentChat/index.js.map +1 -1
- package/dist/components/ai/AgentRunCard/index.js.map +1 -1
- package/dist/components/ai/AgentScreen/index.js.map +1 -1
- package/dist/components/ai/ApprovalCard/index.js.map +1 -1
- package/dist/components/ai/CodeView/index.js.map +1 -1
- package/dist/components/ai/CommandSearch/index.js.map +1 -1
- package/dist/components/ai/ContextCards/index.js.map +1 -1
- package/dist/components/ai/DiffTable/index.js.map +1 -1
- package/dist/components/ai/FilterTable/index.js.map +1 -1
- package/dist/components/ai/FineTuneCard/index.js.map +1 -1
- package/dist/components/ai/InsightCards/index.js.map +1 -1
- package/dist/components/ai/IssueCard/index.js.map +1 -1
- package/dist/components/ai/LoadingState/index.js.map +1 -1
- package/dist/components/ai/PromptBar/index.js.map +1 -1
- package/dist/components/ai/RecommendationCard/index.js.map +1 -1
- package/dist/components/ai/RecordsTable/index.js.map +1 -1
- package/dist/components/ai/SelectionActions/index.js.map +1 -1
- package/dist/components/ai/StateFlow/index.js.map +1 -1
- package/dist/components/ai/StreamingText/index.js.map +1 -1
- package/dist/components/ai/TaskRows/index.js.map +1 -1
- package/dist/components/ai/Thinking/index.js.map +1 -1
- package/dist/components/ai/ToolChips/index.js.map +1 -1
- package/dist/components/ai/shared.js.map +1 -1
- package/dist/components/pro/ArticleContextMenu/index.js.map +1 -1
- package/dist/components/pro/ArticleControl/ArticleControl2.js.map +1 -1
- package/dist/components/pro/ArticleControl/buildHeadingTree.js.map +1 -1
- package/dist/components/pro/ArticleControl/useArticleToc.js.map +1 -1
- package/dist/components/pro/Calendar/Calendar.js +165 -160
- package/dist/components/pro/Calendar/Calendar.js.map +1 -1
- package/dist/components/pro/Calendar/hooks/useCalendarDrag.js.map +1 -1
- package/dist/components/pro/Calendar/hooks/useCalendarState.js +31 -31
- package/dist/components/pro/Calendar/hooks/useCalendarState.js.map +1 -1
- package/dist/components/pro/Calendar/utils.js.map +1 -1
- package/dist/components/pro/CodeEditor/editors/CodeEditor.js.map +1 -1
- package/dist/components/pro/CodeEditor/editors/MarkdownEditor.js.map +1 -1
- package/dist/components/pro/CodeEditor/editors/markdownExtensions.js.map +1 -1
- package/dist/components/pro/CodeEditor/extensions/contextMenu.js.map +1 -1
- package/dist/components/pro/CodeEditor/extensions/index.js.map +1 -1
- package/dist/components/pro/CodeEditor/langs/index.js.map +1 -1
- package/dist/components/pro/CodeEditor/langs/javascript.js.map +1 -1
- package/dist/components/pro/CodeEditor/langs/python.js.map +1 -1
- package/dist/components/pro/CodeEditor/langs/snippets.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/FootnotePanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/HistoryPanel.js +65 -59
- package/dist/components/pro/CodeEditor/panels/HistoryPanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/OutlinePanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/TableOfContentsPanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/panels/VariablePanel.js.map +1 -1
- package/dist/components/pro/CodeEditor/themes.js.map +1 -1
- package/dist/components/pro/CodeEditor/utils.js.map +1 -1
- package/dist/components/pro/DataTable/DataTable.js.map +1 -1
- package/dist/components/pro/DataTable/Filters.js.map +1 -1
- package/dist/components/pro/DataTable/PivotPanel.js.map +1 -1
- package/dist/components/pro/DataTable/PivotView.js.map +1 -1
- package/dist/components/pro/DataTable/components/BulkActions.js.map +1 -1
- package/dist/components/pro/DataTable/components/ColumnSelector.js.map +1 -1
- package/dist/components/pro/DataTable/components/FilterBar.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableBody.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableHeader.js.map +1 -1
- package/dist/components/pro/DataTable/components/TablePagination.js.map +1 -1
- package/dist/components/pro/DataTable/components/TableToolbar.js.map +1 -1
- package/dist/components/pro/DataTable/extension.js.map +1 -1
- package/dist/components/pro/DataTable/pivotEngine.js.map +1 -1
- package/dist/components/pro/DataTable/pivotExtension.js.map +1 -1
- package/dist/components/pro/DataTable/rowDetailExtension.js.map +1 -1
- package/dist/components/pro/DataTable/tanstack-table.js.map +1 -1
- package/dist/components/pro/DataTable/treeDataUtils.js.map +1 -1
- package/dist/components/pro/DataTable/useColumnDragReorder.js.map +1 -1
- package/dist/components/pro/DataTable/useDataTableModel.js +119 -104
- package/dist/components/pro/DataTable/useDataTableModel.js.map +1 -1
- package/dist/components/pro/DataTable/utils.js.map +1 -1
- package/dist/components/pro/FileExplorer/Breadcrumb.js.map +1 -1
- package/dist/components/pro/FileExplorer/CreateDialog.js.map +1 -1
- package/dist/components/pro/FileExplorer/FileExplorer.js.map +1 -1
- package/dist/components/pro/FileExplorer/FileIcon.js.map +1 -1
- package/dist/components/pro/FileExplorer/GridView.js.map +1 -1
- package/dist/components/pro/FileExplorer/ListView.js.map +1 -1
- package/dist/components/pro/FileExplorer/Toolbar.js.map +1 -1
- package/dist/components/pro/Flow/Flow.js.map +1 -1
- package/dist/components/pro/Flow/FlowCanvas.js.map +1 -1
- package/dist/components/pro/Flow/archify/build.js.map +1 -1
- package/dist/components/pro/Flow/archify/convert.js.map +1 -1
- package/dist/components/pro/Flow/archify/palette.js.map +1 -1
- package/dist/components/pro/Flow/archify/presets.js.map +1 -1
- package/dist/components/pro/Flow/archify/spec.js.map +1 -1
- package/dist/components/pro/Flow/canvas/AlignmentGuides.js.map +1 -1
- package/dist/components/pro/Flow/canvas/GridRenderer.js.map +1 -1
- package/dist/components/pro/Flow/canvas/MiniMapRenderer.js.map +1 -1
- package/dist/components/pro/Flow/canvas/PixiApp.js.map +1 -1
- package/dist/components/pro/Flow/canvas/SelectionOverlay.js.map +1 -1
- package/dist/components/pro/Flow/canvas/ViewportContainer.js.map +1 -1
- package/dist/components/pro/Flow/constants.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowAIContext.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowContext.js.map +1 -1
- package/dist/components/pro/Flow/context/FlowUIContext.js.map +1 -1
- package/dist/components/pro/Flow/edges/EdgeRenderer.js.map +1 -1
- package/dist/components/pro/Flow/edges/edgeMarkers.js.map +1 -1
- package/dist/components/pro/Flow/edges/edgePaths.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useAlignmentGuides.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useClipboard.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useCompactMode.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useDeleteSelection.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useDraggablePanel.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useEdgeMutations.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useFlowImportExport.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useKeyboardShortcuts.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useLayoutActions.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useLineMutations.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useMindMap.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useNodeMutations.js.map +1 -1
- package/dist/components/pro/Flow/hooks/usePdfExport.js.map +1 -1
- package/dist/components/pro/Flow/hooks/usePixiApp.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useResizablePanel.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useSelection.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useUndoRedo.js.map +1 -1
- package/dist/components/pro/Flow/hooks/useViewport.js.map +1 -1
- package/dist/components/pro/Flow/lines/FreehandTool.js.map +1 -1
- package/dist/components/pro/Flow/lines/GeometricLineTool.js.map +1 -1
- package/dist/components/pro/Flow/lines/LineRenderer.js.map +1 -1
- package/dist/components/pro/Flow/lines/lineSmoothing.js.map +1 -1
- package/dist/components/pro/Flow/nodes/DataNodeView.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaContent.js.map +1 -1
- package/dist/components/pro/Flow/nodes/MediaOverlay.js.map +1 -1
- package/dist/components/pro/Flow/nodes/Model3DView.js.map +1 -1
- package/dist/components/pro/Flow/nodes/NodeHandles.js.map +1 -1
- package/dist/components/pro/Flow/nodes/NodeRenderer.js.map +1 -1
- package/dist/components/pro/Flow/nodes/NodeText.js.map +1 -1
- package/dist/components/pro/Flow/nodes/aiClient.js.map +1 -1
- package/dist/components/pro/Flow/nodes/animAnchors.js.map +1 -1
- package/dist/components/pro/Flow/nodes/chartGeometryBudget.js.map +1 -1
- package/dist/components/pro/Flow/nodes/dataUtils.js.map +1 -1
- package/dist/components/pro/Flow/nodes/demandRender.js.map +1 -1
- package/dist/components/pro/Flow/nodes/nodeUtils.js.map +1 -1
- package/dist/components/pro/Flow/nodes/shapes.js.map +1 -1
- package/dist/components/pro/Flow/nodes/svgIconTexture.js.map +1 -1
- package/dist/components/pro/Flow/nodes/svgIcons.js.map +1 -1
- package/dist/components/pro/Flow/panels/AIPanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/Breadcrumb.js.map +1 -1
- package/dist/components/pro/Flow/panels/ContextMenu.js.map +1 -1
- package/dist/components/pro/Flow/panels/DataInspectorSection.js.map +1 -1
- package/dist/components/pro/Flow/panels/ExpandableTextarea.js.map +1 -1
- package/dist/components/pro/Flow/panels/FlowPanelGroup.js.map +1 -1
- package/dist/components/pro/Flow/panels/FlowToolbar.js.map +1 -1
- package/dist/components/pro/Flow/panels/InspectorPanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/OutlinePanel.js.map +1 -1
- package/dist/components/pro/Flow/panels/PresentationMode.js.map +1 -1
- package/dist/components/pro/Flow/panels/ProjectTitle.js.map +1 -1
- package/dist/components/pro/Flow/panels/ShapePalette.js.map +1 -1
- package/dist/components/pro/Flow/panels/toolbarIcons.js.map +1 -1
- package/dist/components/pro/Flow/types.js.map +1 -1
- package/dist/components/pro/Flow/utils/dashedLine.js.map +1 -1
- package/dist/components/pro/Flow/utils/gradient.js.map +1 -1
- package/dist/components/pro/Flow/utils/pdfExport.js.map +1 -1
- package/dist/components/pro/Flow/utils/themeColor.js.map +1 -1
- package/dist/components/pro/Gantt/Gantt.js.map +1 -1
- package/dist/components/pro/Gantt/hooks/useGanttDrag.js.map +1 -1
- package/dist/components/pro/Gantt/hooks/useGanttState.js +79 -71
- package/dist/components/pro/Gantt/hooks/useGanttState.js.map +1 -1
- package/dist/components/pro/Gantt/utils.js.map +1 -1
- package/dist/components/pro/Kanban/Kanban.js.map +1 -1
- package/dist/components/pro/Kanban/Swimlanes.js.map +1 -1
- package/dist/components/pro/Kanban/useKanbanDrag.js.map +1 -1
- package/dist/components/pro/Kanban/utils.js.map +1 -1
- package/dist/components/pro/RichTextEditor/AIRewrite.js.map +1 -1
- package/dist/components/pro/RichTextEditor/BlockHandle.js.map +1 -1
- package/dist/components/pro/RichTextEditor/BubbleToolbar.js.map +1 -1
- package/dist/components/pro/RichTextEditor/CommentDialog.js.map +1 -1
- package/dist/components/pro/RichTextEditor/CommentPanel.js.map +1 -1
- package/dist/components/pro/RichTextEditor/CommentPopover.js.map +1 -1
- package/dist/components/pro/RichTextEditor/ContextMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/RichTextEditor.js.map +1 -1
- package/dist/components/pro/RichTextEditor/SlashMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/ColorPicker.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/FloatingDialogMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/MarkdownTemplates.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/SelectDropdown.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/TableOpsMenu.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/Toolbar.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/ToolbarButton.js.map +1 -1
- package/dist/components/pro/RichTextEditor/Toolbar/useAnchoredDropdown.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/CodeBlockView.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/CommentMark.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/FloatingDialog.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/FontSize.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/MarkdownBlock.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/ResizableImage.js.map +1 -1
- package/dist/components/pro/RichTextEditor/extensions/legacyMermaid.js.map +1 -1
- package/dist/components/pro/RichTextEditor/types.js.map +1 -1
- package/dist/components/pro/Sprite/Molly/animations.d.ts +6 -0
- package/dist/components/pro/Sprite/Molly/animations.js +52 -0
- package/dist/components/pro/Sprite/Molly/animations.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/events.d.ts +2 -0
- package/dist/components/pro/Sprite/Molly/events.js +46 -0
- package/dist/components/pro/Sprite/Molly/events.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/index.d.ts +1 -0
- package/dist/components/pro/Sprite/Molly/index.js +12 -0
- package/dist/components/pro/Sprite/Molly/index.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/legFlexion.d.ts +3 -0
- package/dist/components/pro/Sprite/Molly/legFlexion.js +48 -0
- package/dist/components/pro/Sprite/Molly/legFlexion.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/locomotion.d.ts +4 -0
- package/dist/components/pro/Sprite/Molly/locomotion.js +51 -0
- package/dist/components/pro/Sprite/Molly/locomotion.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/model.d.ts +23 -0
- package/dist/components/pro/Sprite/Molly/model.js +614 -0
- package/dist/components/pro/Sprite/Molly/model.js.map +1 -0
- package/dist/components/pro/Sprite/Molly/rig.d.ts +25 -0
- package/dist/components/pro/Sprite/Molly/rig.js +102 -0
- package/dist/components/pro/Sprite/Molly/rig.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/animations.d.ts +6 -0
- package/dist/components/pro/Sprite/Ruby/animations.js +58 -0
- package/dist/components/pro/Sprite/Ruby/animations.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/events.d.ts +2 -0
- package/dist/components/pro/Sprite/Ruby/events.js +46 -0
- package/dist/components/pro/Sprite/Ruby/events.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/index.d.ts +1 -0
- package/dist/components/pro/Sprite/Ruby/index.js +12 -0
- package/dist/components/pro/Sprite/Ruby/index.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/locomotion.d.ts +4 -0
- package/dist/components/pro/Sprite/Ruby/locomotion.js +41 -0
- package/dist/components/pro/Sprite/Ruby/locomotion.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/model.d.ts +23 -0
- package/dist/components/pro/Sprite/Ruby/model.js +605 -0
- package/dist/components/pro/Sprite/Ruby/model.js.map +1 -0
- package/dist/components/pro/Sprite/Ruby/rig.d.ts +26 -0
- package/dist/components/pro/Sprite/Ruby/rig.js +112 -0
- package/dist/components/pro/Sprite/Ruby/rig.js.map +1 -0
- package/dist/components/pro/Sprite/Sprite.d.ts +23 -0
- package/dist/components/pro/Sprite/Sprite2.js +224 -0
- package/dist/components/pro/Sprite/Sprite2.js.map +1 -0
- package/dist/components/pro/Sprite/actions.d.ts +3 -0
- package/dist/components/pro/Sprite/actions.js +16 -0
- package/dist/components/pro/Sprite/actions.js.map +1 -0
- package/dist/components/pro/Sprite/createSpriteScene.d.ts +4 -0
- package/dist/components/pro/Sprite/createSpriteScene.js +14 -0
- package/dist/components/pro/Sprite/createSpriteScene.js.map +1 -0
- package/dist/components/pro/Sprite/index.d.ts +4 -0
- package/dist/components/pro/Sprite/shared/animation.d.ts +33 -0
- package/dist/components/pro/Sprite/shared/animation.js +39 -0
- package/dist/components/pro/Sprite/shared/animation.js.map +1 -0
- package/dist/components/pro/Sprite/shared/createSpriteStage.d.ts +14 -0
- package/dist/components/pro/Sprite/shared/createSpriteStage.js +127 -0
- package/dist/components/pro/Sprite/shared/createSpriteStage.js.map +1 -0
- package/dist/components/pro/Sprite/shared/events.d.ts +5 -0
- package/dist/components/pro/Sprite/shared/events.js +21 -0
- package/dist/components/pro/Sprite/shared/events.js.map +1 -0
- package/dist/components/pro/Sprite/shared/face.d.ts +15 -0
- package/dist/components/pro/Sprite/shared/face.js +33 -0
- package/dist/components/pro/Sprite/shared/face.js.map +1 -0
- package/dist/components/pro/Sprite/shared/faceSurface.d.ts +6 -0
- package/dist/components/pro/Sprite/shared/faceSurface.js +66 -0
- package/dist/components/pro/Sprite/shared/faceSurface.js.map +1 -0
- package/dist/components/pro/Sprite/shared/gait.d.ts +5 -0
- package/dist/components/pro/Sprite/shared/gait.js +19 -0
- package/dist/components/pro/Sprite/shared/gait.js.map +1 -0
- package/dist/components/pro/Sprite/shared/geometry.d.ts +8 -0
- package/dist/components/pro/Sprite/shared/geometry.js +59 -0
- package/dist/components/pro/Sprite/shared/geometry.js.map +1 -0
- package/dist/components/pro/Sprite/shared/magicEffect.d.ts +27 -0
- package/dist/components/pro/Sprite/shared/magicEffect.js +61 -0
- package/dist/components/pro/Sprite/shared/magicEffect.js.map +1 -0
- package/dist/components/pro/Sprite/shared/playback.d.ts +8 -0
- package/dist/components/pro/Sprite/shared/playback.js +74 -0
- package/dist/components/pro/Sprite/shared/playback.js.map +1 -0
- package/dist/components/pro/Sprite/shared/rig.d.ts +6 -0
- package/dist/components/pro/Sprite/shared/rig.js +48 -0
- package/dist/components/pro/Sprite/shared/rig.js.map +1 -0
- package/dist/components/pro/Sprite/shared/scene.d.ts +9 -0
- package/dist/components/pro/Sprite/shared/scene.js +21 -0
- package/dist/components/pro/Sprite/shared/scene.js.map +1 -0
- package/dist/components/pro/Sprite/shared/sculptor.d.ts +14 -0
- package/dist/components/pro/Sprite/shared/sculptor.js +78 -0
- package/dist/components/pro/Sprite/shared/sculptor.js.map +1 -0
- package/dist/components/pro/Sprite/shared/speechBubble.d.ts +15 -0
- package/dist/components/pro/Sprite/shared/speechBubble.js +129 -0
- package/dist/components/pro/Sprite/shared/speechBubble.js.map +1 -0
- package/dist/components/pro/Sprite/types.d.ts +40 -0
- package/dist/contexts/AppContext.js.map +1 -1
- package/dist/contexts/authentication.js.map +1 -1
- package/dist/data-table.js +3 -3
- package/dist/data-table.js.map +1 -1
- package/dist/dishui.css +1 -1
- package/dist/hooks/useClickOutside.js.map +1 -1
- package/dist/hooks/useContainerElement.js.map +1 -1
- package/dist/hooks/useControllableState.js.map +1 -1
- package/dist/hooks/useCopyToClipboard.js.map +1 -1
- package/dist/hooks/useDebounce.js.map +1 -1
- package/dist/hooks/useDraggable.js.map +1 -1
- package/dist/hooks/useTableColumns.js.map +1 -1
- package/dist/hooks/useTableData.js.map +1 -1
- package/dist/index-nostyled.d.ts +2 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +116 -115
- package/dist/layouts/MainLayout.js.map +1 -1
- package/dist/layouts/Navbar.js.map +1 -1
- package/dist/layouts/Sidebar.js.map +1 -1
- package/dist/layouts/headbar/Headbar.js.map +1 -1
- package/dist/layouts/headbar/HeadbarLangSwitcher.js.map +1 -1
- package/dist/layouts/headbar/HeadbarSearch.js.map +1 -1
- package/dist/layouts/headbar/HeadbarThemeSwitcher.js.map +1 -1
- package/dist/layouts/headbar/HeadbarTitle.js.map +1 -1
- package/dist/sprite.js +2 -0
- package/dist/styles/themeStyles.js.map +1 -1
- package/dist/utils/cn.js.map +1 -1
- package/dist/utils/flexLayout.js.map +1 -1
- package/dist/utils/i18n.js.map +1 -1
- package/dist/utils/menu.js.map +1 -1
- package/dist/utils/rehype-code-meta.js.map +1 -1
- package/dist/utils/remark-code-meta.js.map +1 -1
- package/dist/utils/routerBuilder.js.map +1 -1
- package/dist/utils/safeStorage.js.map +1 -1
- package/dist/utils/selection.js +29 -20
- package/dist/utils/selection.js.map +1 -1
- package/dist/utils/themes.js.map +1 -1
- package/package.json +15 -11
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Calendar.js","names":[],"sources":["../../../../src/components/pro/Calendar/Calendar.tsx"],"sourcesContent":["/**\n * DishUI Calendar Component\n *\n * A full-featured calendar with month/week/day views, drag-and-drop,\n * holiday support, and entry management.\n */\n\nimport React, {\n forwardRef,\n useImperativeHandle,\n useRef,\n useState,\n useEffect,\n} from \"react\";\nimport type {\n CalendarProps,\n CalendarHandle,\n CalendarEntry,\n CalendarViewMode,\n} from \"./types\";\nimport { useCalendarState } from \"./hooks/useCalendarState\";\nimport { useCalendarDrag } from \"./hooks/useCalendarDrag\";\nimport {\n formatDate,\n isToday,\n isWeekend,\n isHoliday,\n getHolidayEntry,\n timeToMinutes,\n TIME_SLOTS,\n SLOT_HEIGHT,\n TIME_GRID_HEIGHT,\n DEFAULT_COLOR_OPTIONS,\n} from \"./utils\";\nimport \"./styles.css\";\n\n// ─── Component ────────────────────────────────────────────────────\n\nconst Calendar = forwardRef<CalendarHandle, CalendarProps>((props, ref) => {\n const {\n entries: externalEntries,\n holidays,\n defaultView = \"month\",\n defaultDate,\n readOnly = false,\n locale = \"en\",\n labels: userLabels,\n height,\n width,\n onEntryAdd,\n onEntryUpdate,\n onEntryDelete,\n onEntryDrop,\n onEntryClick,\n onDateClick,\n onViewChange,\n holidayColor,\n todayColor,\n weekendColor,\n className,\n style,\n colorOptions = DEFAULT_COLOR_OPTIONS,\n } = props;\n\n // ─── State Hook ─────────────────────────────────────────────────\n\n const state = useCalendarState({\n entries: externalEntries,\n holidays,\n defaultView,\n defaultDate,\n locale,\n labels: userLabels,\n colorOptions,\n readOnly,\n onEntryAdd,\n onEntryUpdate,\n onEntryDelete,\n onViewChange,\n });\n\n // ─── Drag Hook ──────────────────────────────────────────────────\n\n const drag = useCalendarDrag({\n readOnly,\n onEntryDrop,\n onEntryUpdate: (entry) => {\n state.updateEntry(entry);\n onEntryUpdate?.(entry);\n },\n });\n\n // ─── Imperative Handle ──────────────────────────────────────────\n\n useImperativeHandle(ref, () => ({\n goToToday: state.goToToday,\n goToDate: state.goToDate,\n setView: state.setViewMode,\n getEntries: () => state.entries,\n }));\n\n // ─── Modal Refs ─────────────────────────────────────────────────\n\n const modalRef = useRef<HTMLDialogElement>(null);\n\n useEffect(() => {\n if (state.modalState.open) {\n modalRef.current?.showModal();\n } else {\n modalRef.current?.close();\n }\n }, [state.modalState.open]);\n\n // ─── Render Header ──────────────────────────────────────────────\n\n const renderHeader = () => (\n <div className=\"flex items-center gap-2 p-2 border-b border-base-300 flex-wrap\">\n {/* Navigation */}\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n className=\"btn btn-ghost btn-xs btn-square\"\n onClick={state.goPrev}\n aria-label=\"Previous\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n className=\"w-4 h-4\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n className=\"btn btn-ghost btn-xs btn-square\"\n onClick={state.goNext}\n aria-label=\"Next\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n className=\"w-4 h-4\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n className=\"btn btn-ghost btn-xs\"\n onClick={state.goToToday}\n >\n {state.labels.today}\n </button>\n </div>\n\n {/* Title */}\n <h2 className=\"text-sm font-semibold flex-1 text-center min-w-0 truncate\">\n {state.title}\n </h2>\n\n {/* View Toggle */}\n <div className=\"join\">\n {([\"month\", \"week\", \"day\"] as CalendarViewMode[]).map((v) => (\n <button\n key={v}\n type=\"button\"\n className={`btn btn-xs join-item ${state.viewMode === v ? \"btn-active\" : \"\"}`}\n onClick={() => state.setViewMode(v)}\n >\n {v === \"month\"\n ? state.labels.month\n : v === \"week\"\n ? state.labels.week\n : state.labels.day}\n </button>\n ))}\n </div>\n\n {/* Add Button */}\n {!readOnly && (\n <button\n type=\"button\"\n className=\"btn btn-primary btn-xs\"\n onClick={() => state.openAddModal()}\n >\n + {state.labels.addEntry}\n </button>\n )}\n </div>\n );\n\n // ─── Render Month View ──────────────────────────────────────────\n\n const renderMonthView = () => {\n const currentMonth = state.currentDateObj.getMonth();\n\n return (\n <div className=\"flex flex-col flex-1 min-h-0 overflow-hidden\">\n {/* Weekday headers */}\n <div className=\"cal-weekday-header\">\n {state.labels.weekDays!.map((day, i) => (\n <div key={i} className=\"cal-weekday-header-cell\">\n {day}\n </div>\n ))}\n </div>\n\n {/* Month grid */}\n <div className=\"cal-month-grid overflow-auto cal-scroll\">\n {state.monthDates.map((date, idx) => {\n const dateStr = formatDate(date);\n const isCurrentMonth = date.getMonth() === currentMonth;\n const isTodayCell = isToday(date);\n const isWknd = isWeekend(date);\n const isHol = isHoliday(dateStr, holidays);\n const holEntry = getHolidayEntry(dateStr, holidays);\n const dayEntries = state.getEntriesForDate(dateStr);\n const isTarget = drag.isDropTarget(dateStr);\n\n const cellClasses = [\n \"cal-month-cell\",\n !isCurrentMonth && \"cal-month-cell--outside\",\n isTodayCell && \"cal-month-cell--today\",\n isWknd && !isHol && \"cal-month-cell--weekend\",\n isHol && \"cal-month-cell--holiday\",\n isTarget && \"cal-month-cell--drop-target\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n key={idx}\n className={cellClasses}\n style={{\n ...(isTodayCell && todayColor\n ? { backgroundColor: todayColor + \"33\" }\n : {}),\n ...(isHol && holidayColor\n ? { backgroundColor: holidayColor + \"33\" }\n : {}),\n ...(isWknd && !isHol && weekendColor\n ? { backgroundColor: weekendColor + \"22\" }\n : {}),\n }}\n onClick={(e) => {\n // Only fire if not clicking on an entry\n if ((e.target as HTMLElement).closest(\".cal-entry-pill\"))\n return;\n onDateClick?.(dateStr);\n if (!readOnly) {\n state.openAddModal(dateStr);\n }\n }}\n onDragOver={(e) => drag.handleDragOver(e, dateStr)}\n onDragLeave={drag.handleDragLeave}\n onDrop={(e) => drag.handleDrop(e, dateStr)}\n >\n {/* Date number */}\n <div className=\"flex items-center gap-1 mb-0.5\">\n <span\n className={`text-xs font-medium leading-none ${\n isTodayCell\n ? \"bg-primary text-primary-content rounded-full w-5 h-5 flex items-center justify-center\"\n : \"\"\n }`}\n >\n {date.getDate()}\n </span>\n {holEntry && !holEntry.isWorkday && (\n <span className=\"cal-holiday-tag\">{holEntry.name}</span>\n )}\n </div>\n\n {/* Entry pills */}\n {dayEntries.slice(0, 3).map((entry) => (\n <div\n key={entry.id}\n className={`cal-entry-pill ${drag.draggingEntryId === entry.id ? \"cal-entry-pill--dragging\" : \"\"}`}\n style={{\n backgroundColor: (entry.color || \"#3b82f6\") + \"22\",\n color: entry.color || \"#3b82f6\",\n }}\n draggable={!readOnly}\n onDragStart={(e) => drag.handleDragStart(e, entry)}\n onDragEnd={drag.handleDragEnd}\n onClick={(e) => {\n e.stopPropagation();\n onEntryClick?.(entry);\n if (!readOnly) {\n state.openEditModal(entry);\n }\n }}\n title={entry.title}\n >\n <span\n className=\"cal-entry-dot\"\n style={{ backgroundColor: entry.color || \"#3b82f6\" }}\n />\n <span className=\"truncate\">{entry.title}</span>\n </div>\n ))}\n {dayEntries.length > 3 && (\n <div className=\"text-[0.6rem] opacity-50 pl-1\">\n +{dayEntries.length - 3} more\n </div>\n )}\n </div>\n );\n })}\n </div>\n </div>\n );\n };\n\n // ─── Render Week/Day View ───────────────────────────────────────\n\n const renderTimeView = () => {\n const dates =\n state.viewMode === \"week\" ? state.weekDates : [state.currentDateObj];\n\n // Separate all-day entries from timed entries\n const getTimedEntries = (dateStr: string) =>\n state.getEntriesForDate(dateStr).filter((e) => !e.allDay && e.startTime);\n const getAllDayEntries = (dateStr: string) =>\n state.getEntriesForDate(dateStr).filter((e) => e.allDay || !e.startTime);\n\n // Now indicator position\n const now = new Date();\n const nowMinutes = now.getHours() * 60 + now.getMinutes();\n const nowTop = (nowMinutes / 30) * SLOT_HEIGHT;\n\n return (\n <div className=\"flex flex-col flex-1 min-h-0 overflow-hidden\">\n {/* Column headers */}\n <div className=\"cal-column-header\">\n <div className=\"cal-column-header-gutter\" />\n <div className=\"cal-column-header-cells\">\n {dates.map((date, i) => {\n const dateStr = formatDate(date);\n const isTodayCol = isToday(date);\n const isWknd = isWeekend(date);\n const holEntry = getHolidayEntry(dateStr, holidays);\n\n return (\n <div\n key={i}\n className={`cal-column-header-cell ${isTodayCol ? \"cal-column-header-cell--today\" : \"\"} ${isWknd ? \"cal-column-header-cell--weekend\" : \"\"}`}\n >\n <div className=\"font-medium\">\n {state.labels.weekDays![date.getDay()]}\n </div>\n <div\n className={`text-lg font-bold ${isTodayCol ? \"bg-primary text-primary-content rounded-full w-7 h-7 flex items-center justify-center mx-auto\" : \"\"}`}\n >\n {date.getDate()}\n </div>\n {holEntry && !holEntry.isWorkday && (\n <span className=\"cal-holiday-tag\">{holEntry.name}</span>\n )}\n </div>\n );\n })}\n </div>\n </div>\n\n {/* All-day section */}\n <div className=\"cal-allday-section\">\n <div className=\"cal-allday-gutter\">{state.labels.allDay}</div>\n <div className=\"cal-allday-cells\">\n {dates.map((date, i) => {\n const dateStr = formatDate(date);\n const allDayEntries = getAllDayEntries(dateStr);\n const isTarget =\n drag.isDropTarget(dateStr) && !drag.dropTarget?.time;\n const isWkndAllDay = isWeekend(date);\n const isHolAllDay = isHoliday(dateStr, holidays);\n\n const allDayClasses = [\n \"cal-allday-cell\",\n isWkndAllDay && !isHolAllDay && \"cal-allday-cell--weekend\",\n isHolAllDay && \"cal-allday-cell--holiday\",\n isTarget && \"cal-allday-cell--drop-target\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n key={i}\n className={allDayClasses}\n onDragOver={(e) => drag.handleDragOver(e, dateStr)}\n onDragLeave={drag.handleDragLeave}\n onDrop={(e) => drag.handleDrop(e, dateStr)}\n >\n {allDayEntries.map((entry) => (\n <div\n key={entry.id}\n className={`cal-entry-pill ${drag.draggingEntryId === entry.id ? \"cal-entry-pill--dragging\" : \"\"}`}\n style={{\n backgroundColor: (entry.color || \"#3b82f6\") + \"22\",\n color: entry.color || \"#3b82f6\",\n }}\n draggable={!readOnly}\n onDragStart={(e) => drag.handleDragStart(e, entry)}\n onDragEnd={drag.handleDragEnd}\n onClick={(e) => {\n e.stopPropagation();\n onEntryClick?.(entry);\n if (!readOnly) state.openEditModal(entry);\n }}\n >\n <span\n className=\"cal-entry-dot\"\n style={{ backgroundColor: entry.color || \"#3b82f6\" }}\n />\n <span className=\"truncate\">{entry.title}</span>\n </div>\n ))}\n </div>\n );\n })}\n </div>\n </div>\n\n {/* Time grid */}\n <div className=\"cal-time-grid\">\n {/* Time gutter */}\n <div className=\"cal-time-gutter cal-scroll overflow-y-auto\">\n <div style={{ height: TIME_GRID_HEIGHT }}>\n {TIME_SLOTS.map((slot, i) => (\n <div key={i} className=\"cal-time-gutter-label\">\n {i % 2 === 0 ? slot : \"\"}\n </div>\n ))}\n </div>\n </div>\n\n {/* Time columns */}\n <div className=\"cal-time-columns cal-scroll\">\n {dates.map((date, colIdx) => {\n const dateStr = formatDate(date);\n const timedEntries = getTimedEntries(dateStr);\n const isTodayCol = isToday(date);\n const isWkndCol = isWeekend(date);\n const isHolCol = isHoliday(dateStr, holidays);\n\n const columnClasses = [\n \"cal-time-column\",\n isTodayCol && \"cal-time-column--today\",\n isWkndCol && !isHolCol && \"cal-time-column--weekend\",\n isHolCol && \"cal-time-column--holiday\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n key={colIdx}\n className={columnClasses}\n style={{ height: TIME_GRID_HEIGHT }}\n >\n {/* Time slots (drop targets) */}\n {TIME_SLOTS.map((slot, slotIdx) => {\n const isTarget = drag.isDropTarget(dateStr, slot);\n return (\n <div\n key={slotIdx}\n className={`cal-time-slot ${isTarget ? \"cal-time-slot--drop-target\" : \"\"}`}\n onDragOver={(e) =>\n drag.handleDragOver(e, dateStr, slot)\n }\n onDragLeave={drag.handleDragLeave}\n onDrop={(e) => drag.handleDrop(e, dateStr, slot)}\n onClick={() => {\n onDateClick?.(dateStr);\n if (!readOnly) state.openAddModal(dateStr, slot);\n }}\n />\n );\n })}\n\n {/* Timed entries */}\n {timedEntries.map((entry) => {\n const startMin = timeToMinutes(entry.startTime);\n const endMin = entry.endTime\n ? timeToMinutes(entry.endTime)\n : startMin + 60;\n const top = (startMin / 30) * SLOT_HEIGHT;\n const height = Math.max(\n ((endMin - startMin) / 30) * SLOT_HEIGHT,\n SLOT_HEIGHT / 2,\n );\n const isDraggingThis = drag.draggingEntryId === entry.id;\n\n return (\n <div\n key={entry.id}\n className={`cal-time-entry ${isDraggingThis ? \"cal-time-entry--dragging\" : \"\"}`}\n style={{\n top,\n height,\n backgroundColor: (entry.color || \"#3b82f6\") + \"33\",\n borderLeft: `3px solid ${entry.color || \"#3b82f6\"}`,\n color: entry.color || \"#3b82f6\",\n }}\n draggable={!readOnly}\n onDragStart={(e) => drag.handleDragStart(e, entry)}\n onDragEnd={drag.handleDragEnd}\n onClick={(e) => {\n e.stopPropagation();\n onEntryClick?.(entry);\n if (!readOnly) state.openEditModal(entry);\n }}\n >\n <span className=\"cal-time-entry-title\">\n {entry.title}\n </span>\n <span className=\"cal-time-entry-time\">\n {entry.startTime}\n {entry.endTime ? ` - ${entry.endTime}` : \"\"}\n </span>\n\n {/* Resize handle */}\n {!readOnly && (\n <div\n className=\"cal-time-entry-resize\"\n draggable\n onDragStart={(e) =>\n drag.handleResizeStart(e, entry)\n }\n onDragEnd={drag.handleDragEnd}\n />\n )}\n </div>\n );\n })}\n\n {/* Now indicator */}\n {isTodayCol && (\n <div className=\"cal-now-line\" style={{ top: nowTop }} />\n )}\n </div>\n );\n })}\n </div>\n </div>\n </div>\n );\n };\n\n // ─── Entry Form Modal ───────────────────────────────────────────\n\n const renderModal = () => {\n return (\n <dialog\n ref={modalRef}\n className=\"modal cal-modal\"\n onClose={state.closeModal}\n >\n <div className=\"modal-box\">\n <EntryForm\n key={`${state.modalState.mode}-${state.modalState.entry.id || \"\"}-${state.modalState.entry.date || \"\"}-${state.modalState.entry.startTime || \"\"}-${state.modalState.open}`}\n mode={state.modalState.mode}\n entry={state.modalState.entry}\n labels={state.labels}\n colorOptions={colorOptions}\n onSave={state.saveModal}\n onDelete={\n state.modalState.mode === \"edit\"\n ? state.deleteModalEntry\n : undefined\n }\n onCancel={state.closeModal}\n />\n </div>\n <form method=\"dialog\" className=\"modal-backdrop\">\n <button type=\"button\" onClick={state.closeModal}>\n close\n </button>\n </form>\n </dialog>\n );\n };\n\n // ─── Main Render ────────────────────────────────────────────────\n\n const containerStyle: React.CSSProperties = {\n height: height || \"100%\",\n width: width || \"100%\",\n ...style,\n };\n\n return (\n <div\n className={`cal-root flex flex-col bg-base-100 border border-base-300 rounded-lg overflow-hidden ${className || \"\"}`}\n style={containerStyle}\n >\n {renderHeader()}\n {state.viewMode === \"month\" && renderMonthView()}\n {(state.viewMode === \"week\" || state.viewMode === \"day\") &&\n renderTimeView()}\n {renderModal()}\n </div>\n );\n});\n\nCalendar.displayName = \"Calendar\";\nexport default Calendar;\n\n// ─── Entry Form Sub-component ─────────────────────────────────────\n\ninterface EntryFormProps {\n mode: \"add\" | \"edit\";\n entry: Partial<CalendarEntry>;\n labels: Required<import(\"./types\").CalendarLabels>;\n colorOptions: string[];\n onSave: (entry: Partial<CalendarEntry>) => void;\n onDelete?: () => void;\n onCancel: () => void;\n}\n\nfunction EntryForm({\n mode,\n entry,\n labels,\n colorOptions,\n onSave,\n onDelete,\n onCancel,\n}: EntryFormProps) {\n const [formData, setFormData] = useState<Partial<CalendarEntry>>(() => ({\n title: entry.title || \"\",\n date: entry.date || \"\",\n startTime: entry.startTime || \"\",\n endTime: entry.endTime || \"\",\n description: entry.description || \"\",\n color: entry.color || colorOptions[0] || \"#3b82f6\",\n allDay: entry.allDay ?? (entry.startTime ? false : true),\n }));\n\n const [confirmingDelete, setConfirmingDelete] = useState(false);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!formData.title?.trim()) return;\n onSave(formData);\n };\n\n const handleFieldChange = (\n field: keyof CalendarEntry,\n value: string | boolean,\n ) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"flex flex-col gap-3\">\n <h3 className=\"font-bold text-lg\">\n {mode === \"add\" ? labels.addEntry : labels.editEntry}\n </h3>\n\n {/* Title */}\n <div className=\"form-control\">\n <label className=\"label py-1\">\n <span className=\"label-text text-xs\">{labels.title}</span>\n </label>\n <input\n type=\"text\"\n className=\"input input-bordered input-sm w-full\"\n placeholder={labels.title}\n value={formData.title || \"\"}\n onChange={(e) => handleFieldChange(\"title\", e.target.value)}\n autoFocus\n required\n />\n </div>\n\n {/* Date */}\n <div className=\"form-control\">\n <label className=\"label py-1\">\n <span className=\"label-text text-xs\">{labels.date}</span>\n </label>\n <input\n type=\"date\"\n className=\"input input-bordered input-sm w-full\"\n value={formData.date || \"\"}\n onChange={(e) => handleFieldChange(\"date\", e.target.value)}\n required\n />\n </div>\n\n {/* All Day Toggle */}\n <div className=\"form-control\">\n <label className=\"label cursor-pointer justify-start gap-2 py-1\">\n <input\n type=\"checkbox\"\n className=\"checkbox checkbox-sm checkbox-primary\"\n checked={formData.allDay ?? true}\n onChange={(e) => handleFieldChange(\"allDay\", e.target.checked)}\n />\n <span className=\"label-text text-xs\">{labels.allDay}</span>\n </label>\n </div>\n\n {/* Time fields (only when not all-day) */}\n {!formData.allDay && (\n <div className=\"flex gap-2\">\n <div className=\"form-control flex-1\">\n <label className=\"label py-1\">\n <span className=\"label-text text-xs\">{labels.startTime}</span>\n </label>\n <input\n type=\"time\"\n className=\"input input-bordered input-sm w-full\"\n value={formData.startTime || \"\"}\n onChange={(e) => handleFieldChange(\"startTime\", e.target.value)}\n />\n </div>\n <div className=\"form-control flex-1\">\n <label className=\"label py-1\">\n <span className=\"label-text text-xs\">{labels.endTime}</span>\n </label>\n <input\n type=\"time\"\n className=\"input input-bordered input-sm w-full\"\n value={formData.endTime || \"\"}\n onChange={(e) => handleFieldChange(\"endTime\", e.target.value)}\n />\n </div>\n </div>\n )}\n\n {/* Description */}\n <div className=\"form-control\">\n <label className=\"label py-1\">\n <span className=\"label-text text-xs\">{labels.description}</span>\n </label>\n <textarea\n className=\"textarea textarea-bordered textarea-sm w-full\"\n rows={2}\n placeholder={labels.description}\n value={formData.description || \"\"}\n onChange={(e) => handleFieldChange(\"description\", e.target.value)}\n />\n </div>\n\n {/* Color picker */}\n <div className=\"form-control\">\n <label className=\"label py-1\">\n <span className=\"label-text text-xs\">{labels.color}</span>\n </label>\n <div className=\"flex gap-2 flex-wrap\">\n {colorOptions.map((color) => (\n <button\n key={color}\n type=\"button\"\n className={`cal-color-swatch ${formData.color === color ? \"cal-color-swatch--selected\" : \"\"}`}\n style={{ backgroundColor: color }}\n onClick={() => handleFieldChange(\"color\", color)}\n aria-label={color}\n />\n ))}\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex justify-between items-center pt-2\">\n <div>\n {onDelete && !confirmingDelete && (\n <button\n type=\"button\"\n className=\"btn btn-error btn-sm btn-outline\"\n onClick={() => setConfirmingDelete(true)}\n >\n {labels.deleteEntry}\n </button>\n )}\n {onDelete && confirmingDelete && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-error\">{labels.confirmDelete}</span>\n <button\n type=\"button\"\n className=\"btn btn-error btn-xs\"\n onClick={onDelete}\n >\n {labels.deleteEntry}\n </button>\n <button\n type=\"button\"\n className=\"btn btn-ghost btn-xs\"\n onClick={() => setConfirmingDelete(false)}\n >\n {labels.cancel}\n </button>\n </div>\n )}\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n className=\"btn btn-ghost btn-sm\"\n onClick={onCancel}\n >\n {labels.cancel}\n </button>\n <button\n type=\"submit\"\n className=\"btn btn-primary btn-sm\"\n disabled={!formData.title?.trim()}\n >\n {labels.save}\n </button>\n </div>\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Calendar.js","names":["Calendar","forwardRef","externalEntries","userLabels","DEFAULT_COLOR_OPTIONS","props","state","useCalendarState","drag","useCalendarDrag","entry","onEntryUpdate","ref","now","setNow","useState","timer","today","formatDate","modalRef","useRef","renderHeader","v","readOnly","renderMonthView","currentMonth","day","i","dateStr","date","isCurrentMonth","isTodayCell","isWknd","isWeekend","isHol","isHoliday","holidays","holEntry","getHolidayEntry","dayEntries","isTarget","cellClasses","todayColor","holidayColor","weekendColor","onDateClick","e","onEntryClick","idx","renderTimeView","dates","getTimedEntries","getAllDayEntries","nowTop","isTodayCol","allDayEntries","isWkndAllDay","isHolAllDay","allDayClasses","TIME_GRID_HEIGHT","TIME_SLOTS","slot","timedEntries","isWkndCol","isHolCol","columnClasses","slotIdx","startMin","timeToMinutes","endMin","top","height","isDraggingThis","colIdx","renderModal","EntryForm","containerStyle","width","style","className","formData","setFormData","colorOptions","confirmingDelete","setConfirmingDelete","handleSubmit","onSave","handleFieldChange","prev","field","value","mode","labels","color","onDelete","onCancel"],"sources":["../../../../src/components/pro/Calendar/Calendar.tsx"],"sourcesContent":["/**\n * DishUI Calendar Component\n *\n * A full-featured calendar with month/week/day views, drag-and-drop,\n * holiday support, and entry management.\n */\n\nimport React, {\n forwardRef,\n useImperativeHandle,\n useRef,\n useState,\n useEffect,\n} from \"react\";\nimport type {\n CalendarProps,\n CalendarHandle,\n CalendarEntry,\n CalendarViewMode,\n} from \"./types\";\nimport { useCalendarState } from \"./hooks/useCalendarState\";\nimport { useCalendarDrag } from \"./hooks/useCalendarDrag\";\nimport {\n formatDate,\n isWeekend,\n isHoliday,\n getHolidayEntry,\n timeToMinutes,\n TIME_SLOTS,\n SLOT_HEIGHT,\n TIME_GRID_HEIGHT,\n DEFAULT_COLOR_OPTIONS,\n} from \"./utils\";\nimport \"./styles.css\";\n\n// ─── Component ────────────────────────────────────────────────────\n\nconst Calendar = forwardRef<CalendarHandle, CalendarProps>((props, ref) => {\n const {\n entries: externalEntries,\n holidays,\n defaultView = \"month\",\n defaultDate,\n readOnly = false,\n locale = \"en\",\n labels: userLabels,\n height,\n width,\n onEntryAdd,\n onEntryUpdate,\n onEntryDelete,\n onEntryDrop,\n onEntryClick,\n onDateClick,\n onViewChange,\n holidayColor,\n todayColor,\n weekendColor,\n className,\n style,\n colorOptions = DEFAULT_COLOR_OPTIONS,\n } = props;\n\n // ─── State Hook ─────────────────────────────────────────────────\n\n const state = useCalendarState({\n entries: externalEntries,\n holidays,\n defaultView,\n defaultDate,\n locale,\n labels: userLabels,\n colorOptions,\n readOnly,\n onEntryAdd,\n onEntryUpdate,\n onEntryDelete,\n onViewChange,\n });\n\n // ─── Drag Hook ──────────────────────────────────────────────────\n\n const drag = useCalendarDrag({\n readOnly,\n onEntryDrop,\n onEntryUpdate: (entry) => {\n state.updateEntry(entry);\n onEntryUpdate?.(entry);\n },\n });\n\n // ─── Imperative Handle ──────────────────────────────────────────\n\n useImperativeHandle(ref, () => ({\n goToToday: state.goToToday,\n goToDate: state.goToDate,\n setView: state.setViewMode,\n getEntries: () => state.entries,\n }));\n\n // Keep today's highlights and the time indicator in sync with the clock.\n const [now, setNow] = useState(() => new Date());\n useEffect(() => {\n const timer = setInterval(() => setNow(new Date()), 60_000);\n return () => clearInterval(timer);\n }, []);\n const today = formatDate(now);\n\n // ─── Modal Refs ─────────────────────────────────────────────────\n\n const modalRef = useRef<HTMLDialogElement>(null);\n\n useEffect(() => {\n if (state.modalState.open) {\n modalRef.current?.showModal();\n } else {\n modalRef.current?.close();\n }\n }, [state.modalState.open]);\n\n // ─── Render Header ──────────────────────────────────────────────\n\n const renderHeader = () => (\n <div className=\"flex items-center gap-2 p-2 border-b border-base-300 flex-wrap\">\n {/* Navigation */}\n <div className=\"flex items-center gap-1\">\n <button\n type=\"button\"\n className=\"btn btn-ghost btn-xs btn-square\"\n onClick={state.goPrev}\n aria-label=\"Previous\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n className=\"w-4 h-4\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M12.79 5.23a.75.75 0 01-.02 1.06L8.832 10l3.938 3.71a.75.75 0 11-1.04 1.08l-4.5-4.25a.75.75 0 010-1.08l4.5-4.25a.75.75 0 011.06.02z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n className=\"btn btn-ghost btn-xs btn-square\"\n onClick={state.goNext}\n aria-label=\"Next\"\n >\n <svg\n xmlns=\"http://www.w3.org/2000/svg\"\n viewBox=\"0 0 20 20\"\n fill=\"currentColor\"\n className=\"w-4 h-4\"\n >\n <path\n fillRule=\"evenodd\"\n d=\"M7.21 14.77a.75.75 0 01.02-1.06L11.168 10 7.23 6.29a.75.75 0 111.04-1.08l4.5 4.25a.75.75 0 010 1.08l-4.5 4.25a.75.75 0 01-1.06-.02z\"\n clipRule=\"evenodd\"\n />\n </svg>\n </button>\n <button\n type=\"button\"\n className=\"btn btn-ghost btn-xs\"\n onClick={state.goToToday}\n >\n {state.labels.today}\n </button>\n </div>\n\n {/* Title */}\n <h2 className=\"text-sm font-semibold flex-1 text-center min-w-0 truncate\">\n {state.title}\n </h2>\n\n {/* View Toggle */}\n <div className=\"join\">\n {([\"month\", \"week\", \"day\"] as CalendarViewMode[]).map((v) => (\n <button\n key={v}\n type=\"button\"\n className={`btn btn-xs join-item ${state.viewMode === v ? \"btn-active\" : \"\"}`}\n onClick={() => state.setViewMode(v)}\n >\n {v === \"month\"\n ? state.labels.month\n : v === \"week\"\n ? state.labels.week\n : state.labels.day}\n </button>\n ))}\n </div>\n\n {/* Add Button */}\n {!readOnly && (\n <button\n type=\"button\"\n className=\"btn btn-primary btn-xs\"\n onClick={() => state.openAddModal()}\n >\n + {state.labels.addEntry}\n </button>\n )}\n </div>\n );\n\n // ─── Render Month View ──────────────────────────────────────────\n\n const renderMonthView = () => {\n const currentMonth = state.currentDateObj.getMonth();\n\n return (\n <div className=\"flex flex-col flex-1 min-h-0 overflow-hidden\">\n {/* Weekday headers */}\n <div className=\"cal-weekday-header\">\n {state.labels.weekDays!.map((day, i) => (\n <div key={i} className=\"cal-weekday-header-cell\">\n {day}\n </div>\n ))}\n </div>\n\n {/* Month grid */}\n <div className=\"cal-month-grid overflow-auto cal-scroll\">\n {state.monthDates.map((date, idx) => {\n const dateStr = formatDate(date);\n const isCurrentMonth = date.getMonth() === currentMonth;\n const isTodayCell = dateStr === today;\n const isWknd = isWeekend(date);\n const isHol = isHoliday(dateStr, holidays);\n const holEntry = getHolidayEntry(dateStr, holidays);\n const dayEntries = state.getEntriesForDate(dateStr);\n const isTarget = drag.isDropTarget(dateStr);\n\n const cellClasses = [\n \"cal-month-cell\",\n !isCurrentMonth && \"cal-month-cell--outside\",\n isTodayCell && \"cal-month-cell--today\",\n isWknd && !isHol && \"cal-month-cell--weekend\",\n isHol && \"cal-month-cell--holiday\",\n isTarget && \"cal-month-cell--drop-target\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n key={idx}\n className={cellClasses}\n style={{\n ...(isTodayCell && todayColor\n ? { backgroundColor: todayColor + \"33\" }\n : {}),\n ...(isHol && holidayColor\n ? { backgroundColor: holidayColor + \"33\" }\n : {}),\n ...(isWknd && !isHol && weekendColor\n ? { backgroundColor: weekendColor + \"22\" }\n : {}),\n }}\n onClick={(e) => {\n // Only fire if not clicking on an entry\n if ((e.target as HTMLElement).closest(\".cal-entry-pill\"))\n return;\n onDateClick?.(dateStr);\n if (!readOnly) {\n state.openAddModal(dateStr);\n }\n }}\n onDragOver={(e) => drag.handleDragOver(e, dateStr)}\n onDragLeave={drag.handleDragLeave}\n onDrop={(e) => drag.handleDrop(e, dateStr)}\n >\n {/* Date number */}\n <div className=\"flex items-center gap-1 mb-0.5\">\n <span\n className={`text-xs font-medium leading-none ${\n isTodayCell\n ? \"bg-primary text-primary-content rounded-full w-5 h-5 flex items-center justify-center\"\n : \"\"\n }`}\n >\n {date.getDate()}\n </span>\n {holEntry && !holEntry.isWorkday && (\n <span className=\"cal-holiday-tag\">{holEntry.name}</span>\n )}\n </div>\n\n {/* Entry pills */}\n {dayEntries.slice(0, 3).map((entry) => (\n <div\n key={entry.id}\n className={`cal-entry-pill ${drag.draggingEntryId === entry.id ? \"cal-entry-pill--dragging\" : \"\"}`}\n style={{\n backgroundColor: (entry.color || \"#3b82f6\") + \"22\",\n color: entry.color || \"#3b82f6\",\n }}\n draggable={!readOnly}\n onDragStart={(e) => drag.handleDragStart(e, entry)}\n onDragEnd={drag.handleDragEnd}\n onClick={(e) => {\n e.stopPropagation();\n onEntryClick?.(entry);\n if (!readOnly) {\n state.openEditModal(entry);\n }\n }}\n title={entry.title}\n >\n <span\n className=\"cal-entry-dot\"\n style={{ backgroundColor: entry.color || \"#3b82f6\" }}\n />\n <span className=\"truncate\">{entry.title}</span>\n </div>\n ))}\n {dayEntries.length > 3 && (\n <div className=\"text-[0.6rem] opacity-50 pl-1\">\n +{dayEntries.length - 3} more\n </div>\n )}\n </div>\n );\n })}\n </div>\n </div>\n );\n };\n\n // ─── Render Week/Day View ───────────────────────────────────────\n\n const renderTimeView = () => {\n const dates =\n state.viewMode === \"week\" ? state.weekDates : [state.currentDateObj];\n\n // Separate all-day entries from timed entries\n const getTimedEntries = (dateStr: string) =>\n state.getEntriesForDate(dateStr).filter((e) => !e.allDay && e.startTime);\n const getAllDayEntries = (dateStr: string) =>\n state.getEntriesForDate(dateStr).filter((e) => e.allDay || !e.startTime);\n\n // Now indicator position\n const nowMinutes = now.getHours() * 60 + now.getMinutes();\n const nowTop = (nowMinutes / 30) * SLOT_HEIGHT;\n\n return (\n <div className=\"flex flex-col flex-1 min-h-0 overflow-hidden\">\n {/* Column headers */}\n <div className=\"cal-column-header\">\n <div className=\"cal-column-header-gutter\" />\n <div className=\"cal-column-header-cells\">\n {dates.map((date, i) => {\n const dateStr = formatDate(date);\n const isTodayCol = dateStr === today;\n const isWknd = isWeekend(date);\n const holEntry = getHolidayEntry(dateStr, holidays);\n\n return (\n <div\n key={i}\n className={`cal-column-header-cell ${isTodayCol ? \"cal-column-header-cell--today\" : \"\"} ${isWknd ? \"cal-column-header-cell--weekend\" : \"\"}`}\n >\n <div className=\"font-medium\">\n {state.labels.weekDays![date.getDay()]}\n </div>\n <div\n className={`text-lg font-bold ${isTodayCol ? \"bg-primary text-primary-content rounded-full w-7 h-7 flex items-center justify-center mx-auto\" : \"\"}`}\n >\n {date.getDate()}\n </div>\n {holEntry && !holEntry.isWorkday && (\n <span className=\"cal-holiday-tag\">{holEntry.name}</span>\n )}\n </div>\n );\n })}\n </div>\n </div>\n\n {/* All-day section */}\n <div className=\"cal-allday-section\">\n <div className=\"cal-allday-gutter\">{state.labels.allDay}</div>\n <div className=\"cal-allday-cells\">\n {dates.map((date, i) => {\n const dateStr = formatDate(date);\n const allDayEntries = getAllDayEntries(dateStr);\n const isTarget =\n drag.isDropTarget(dateStr) && !drag.dropTarget?.time;\n const isWkndAllDay = isWeekend(date);\n const isHolAllDay = isHoliday(dateStr, holidays);\n\n const allDayClasses = [\n \"cal-allday-cell\",\n isWkndAllDay && !isHolAllDay && \"cal-allday-cell--weekend\",\n isHolAllDay && \"cal-allday-cell--holiday\",\n isTarget && \"cal-allday-cell--drop-target\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n key={i}\n className={allDayClasses}\n onDragOver={(e) => drag.handleDragOver(e, dateStr)}\n onDragLeave={drag.handleDragLeave}\n onDrop={(e) => drag.handleDrop(e, dateStr)}\n >\n {allDayEntries.map((entry) => (\n <div\n key={entry.id}\n className={`cal-entry-pill ${drag.draggingEntryId === entry.id ? \"cal-entry-pill--dragging\" : \"\"}`}\n style={{\n backgroundColor: (entry.color || \"#3b82f6\") + \"22\",\n color: entry.color || \"#3b82f6\",\n }}\n draggable={!readOnly}\n onDragStart={(e) => drag.handleDragStart(e, entry)}\n onDragEnd={drag.handleDragEnd}\n onClick={(e) => {\n e.stopPropagation();\n onEntryClick?.(entry);\n if (!readOnly) state.openEditModal(entry);\n }}\n >\n <span\n className=\"cal-entry-dot\"\n style={{ backgroundColor: entry.color || \"#3b82f6\" }}\n />\n <span className=\"truncate\">{entry.title}</span>\n </div>\n ))}\n </div>\n );\n })}\n </div>\n </div>\n\n {/* Time grid */}\n <div className=\"cal-time-grid\">\n {/* Time gutter */}\n <div className=\"cal-time-gutter cal-scroll overflow-y-auto\">\n <div style={{ height: TIME_GRID_HEIGHT }}>\n {TIME_SLOTS.map((slot, i) => (\n <div key={i} className=\"cal-time-gutter-label\">\n {i % 2 === 0 ? slot : \"\"}\n </div>\n ))}\n </div>\n </div>\n\n {/* Time columns */}\n <div className=\"cal-time-columns cal-scroll\">\n {dates.map((date, colIdx) => {\n const dateStr = formatDate(date);\n const timedEntries = getTimedEntries(dateStr);\n const isTodayCol = dateStr === today;\n const isWkndCol = isWeekend(date);\n const isHolCol = isHoliday(dateStr, holidays);\n\n const columnClasses = [\n \"cal-time-column\",\n isTodayCol && \"cal-time-column--today\",\n isWkndCol && !isHolCol && \"cal-time-column--weekend\",\n isHolCol && \"cal-time-column--holiday\",\n ]\n .filter(Boolean)\n .join(\" \");\n\n return (\n <div\n key={colIdx}\n className={columnClasses}\n style={{ height: TIME_GRID_HEIGHT }}\n >\n {/* Time slots (drop targets) */}\n {TIME_SLOTS.map((slot, slotIdx) => {\n const isTarget = drag.isDropTarget(dateStr, slot);\n return (\n <div\n key={slotIdx}\n className={`cal-time-slot ${isTarget ? \"cal-time-slot--drop-target\" : \"\"}`}\n onDragOver={(e) =>\n drag.handleDragOver(e, dateStr, slot)\n }\n onDragLeave={drag.handleDragLeave}\n onDrop={(e) => drag.handleDrop(e, dateStr, slot)}\n onClick={() => {\n onDateClick?.(dateStr);\n if (!readOnly) state.openAddModal(dateStr, slot);\n }}\n />\n );\n })}\n\n {/* Timed entries */}\n {timedEntries.map((entry) => {\n const startMin = timeToMinutes(entry.startTime);\n const endMin = entry.endTime\n ? timeToMinutes(entry.endTime)\n : startMin + 60;\n const top = (startMin / 30) * SLOT_HEIGHT;\n const height = Math.max(\n ((endMin - startMin) / 30) * SLOT_HEIGHT,\n SLOT_HEIGHT / 2,\n );\n const isDraggingThis = drag.draggingEntryId === entry.id;\n\n return (\n <div\n key={entry.id}\n className={`cal-time-entry ${isDraggingThis ? \"cal-time-entry--dragging\" : \"\"}`}\n style={{\n top,\n height,\n backgroundColor: (entry.color || \"#3b82f6\") + \"33\",\n borderLeft: `3px solid ${entry.color || \"#3b82f6\"}`,\n color: entry.color || \"#3b82f6\",\n }}\n draggable={!readOnly}\n onDragStart={(e) => drag.handleDragStart(e, entry)}\n onDragEnd={drag.handleDragEnd}\n onClick={(e) => {\n e.stopPropagation();\n onEntryClick?.(entry);\n if (!readOnly) state.openEditModal(entry);\n }}\n >\n <span className=\"cal-time-entry-title\">\n {entry.title}\n </span>\n <span className=\"cal-time-entry-time\">\n {entry.startTime}\n {entry.endTime ? ` - ${entry.endTime}` : \"\"}\n </span>\n\n {/* Resize handle */}\n {!readOnly && (\n <div\n className=\"cal-time-entry-resize\"\n draggable\n onDragStart={(e) =>\n drag.handleResizeStart(e, entry)\n }\n onDragEnd={drag.handleDragEnd}\n />\n )}\n </div>\n );\n })}\n\n {/* Now indicator */}\n {isTodayCol && (\n <div className=\"cal-now-line\" style={{ top: nowTop }} />\n )}\n </div>\n );\n })}\n </div>\n </div>\n </div>\n );\n };\n\n // ─── Entry Form Modal ───────────────────────────────────────────\n\n const renderModal = () => {\n return (\n <dialog\n ref={modalRef}\n className=\"modal cal-modal\"\n onClose={state.closeModal}\n >\n <div className=\"modal-box\">\n <EntryForm\n key={`${state.modalState.mode}-${state.modalState.entry.id || \"\"}-${state.modalState.entry.date || \"\"}-${state.modalState.entry.startTime || \"\"}-${state.modalState.open}`}\n mode={state.modalState.mode}\n entry={state.modalState.entry}\n labels={state.labels}\n colorOptions={colorOptions}\n onSave={state.saveModal}\n onDelete={\n state.modalState.mode === \"edit\"\n ? state.deleteModalEntry\n : undefined\n }\n onCancel={state.closeModal}\n />\n </div>\n <form method=\"dialog\" className=\"modal-backdrop\">\n <button type=\"button\" onClick={state.closeModal}>\n close\n </button>\n </form>\n </dialog>\n );\n };\n\n // ─── Main Render ────────────────────────────────────────────────\n\n const containerStyle: React.CSSProperties = {\n height: height || \"100%\",\n width: width || \"100%\",\n ...style,\n };\n\n return (\n <div\n className={`cal-root flex flex-col bg-base-100 border border-base-300 rounded-lg overflow-hidden ${className || \"\"}`}\n style={containerStyle}\n >\n {renderHeader()}\n {state.viewMode === \"month\" && renderMonthView()}\n {(state.viewMode === \"week\" || state.viewMode === \"day\") &&\n renderTimeView()}\n {renderModal()}\n </div>\n );\n});\n\nCalendar.displayName = \"Calendar\";\nexport default Calendar;\n\n// ─── Entry Form Sub-component ─────────────────────────────────────\n\ninterface EntryFormProps {\n mode: \"add\" | \"edit\";\n entry: Partial<CalendarEntry>;\n labels: Required<import(\"./types\").CalendarLabels>;\n colorOptions: string[];\n onSave: (entry: Partial<CalendarEntry>) => void;\n onDelete?: () => void;\n onCancel: () => void;\n}\n\nfunction EntryForm({\n mode,\n entry,\n labels,\n colorOptions,\n onSave,\n onDelete,\n onCancel,\n}: EntryFormProps) {\n const [formData, setFormData] = useState<Partial<CalendarEntry>>(() => ({\n title: entry.title || \"\",\n date: entry.date || \"\",\n startTime: entry.startTime || \"\",\n endTime: entry.endTime || \"\",\n description: entry.description || \"\",\n color: entry.color || colorOptions[0] || \"#3b82f6\",\n allDay: entry.allDay ?? (entry.startTime ? false : true),\n }));\n\n const [confirmingDelete, setConfirmingDelete] = useState(false);\n\n const handleSubmit = (e: React.FormEvent) => {\n e.preventDefault();\n if (!formData.title?.trim()) return;\n onSave(formData);\n };\n\n const handleFieldChange = (\n field: keyof CalendarEntry,\n value: string | boolean,\n ) => {\n setFormData((prev) => ({ ...prev, [field]: value }));\n };\n\n return (\n <form onSubmit={handleSubmit} className=\"flex flex-col gap-3\">\n <h3 className=\"font-bold text-lg\">\n {mode === \"add\" ? labels.addEntry : labels.editEntry}\n </h3>\n\n {/* Title */}\n <div className=\"form-control\">\n <label className=\"label py-1\">\n <span className=\"label-text text-xs\">{labels.title}</span>\n </label>\n <input\n type=\"text\"\n className=\"input input-bordered input-sm w-full\"\n placeholder={labels.title}\n value={formData.title || \"\"}\n onChange={(e) => handleFieldChange(\"title\", e.target.value)}\n autoFocus\n required\n />\n </div>\n\n {/* Date */}\n <div className=\"form-control\">\n <label className=\"label py-1\">\n <span className=\"label-text text-xs\">{labels.date}</span>\n </label>\n <input\n type=\"date\"\n className=\"input input-bordered input-sm w-full\"\n value={formData.date || \"\"}\n onChange={(e) => handleFieldChange(\"date\", e.target.value)}\n required\n />\n </div>\n\n {/* All Day Toggle */}\n <div className=\"form-control\">\n <label className=\"label cursor-pointer justify-start gap-2 py-1\">\n <input\n type=\"checkbox\"\n className=\"checkbox checkbox-sm checkbox-primary\"\n checked={formData.allDay ?? true}\n onChange={(e) => handleFieldChange(\"allDay\", e.target.checked)}\n />\n <span className=\"label-text text-xs\">{labels.allDay}</span>\n </label>\n </div>\n\n {/* Time fields (only when not all-day) */}\n {!formData.allDay && (\n <div className=\"flex gap-2\">\n <div className=\"form-control flex-1\">\n <label className=\"label py-1\">\n <span className=\"label-text text-xs\">{labels.startTime}</span>\n </label>\n <input\n type=\"time\"\n className=\"input input-bordered input-sm w-full\"\n value={formData.startTime || \"\"}\n onChange={(e) => handleFieldChange(\"startTime\", e.target.value)}\n />\n </div>\n <div className=\"form-control flex-1\">\n <label className=\"label py-1\">\n <span className=\"label-text text-xs\">{labels.endTime}</span>\n </label>\n <input\n type=\"time\"\n className=\"input input-bordered input-sm w-full\"\n value={formData.endTime || \"\"}\n onChange={(e) => handleFieldChange(\"endTime\", e.target.value)}\n />\n </div>\n </div>\n )}\n\n {/* Description */}\n <div className=\"form-control\">\n <label className=\"label py-1\">\n <span className=\"label-text text-xs\">{labels.description}</span>\n </label>\n <textarea\n className=\"textarea textarea-bordered textarea-sm w-full\"\n rows={2}\n placeholder={labels.description}\n value={formData.description || \"\"}\n onChange={(e) => handleFieldChange(\"description\", e.target.value)}\n />\n </div>\n\n {/* Color picker */}\n <div className=\"form-control\">\n <label className=\"label py-1\">\n <span className=\"label-text text-xs\">{labels.color}</span>\n </label>\n <div className=\"flex gap-2 flex-wrap\">\n {colorOptions.map((color) => (\n <button\n key={color}\n type=\"button\"\n className={`cal-color-swatch ${formData.color === color ? \"cal-color-swatch--selected\" : \"\"}`}\n style={{ backgroundColor: color }}\n onClick={() => handleFieldChange(\"color\", color)}\n aria-label={color}\n />\n ))}\n </div>\n </div>\n\n {/* Actions */}\n <div className=\"flex justify-between items-center pt-2\">\n <div>\n {onDelete && !confirmingDelete && (\n <button\n type=\"button\"\n className=\"btn btn-error btn-sm btn-outline\"\n onClick={() => setConfirmingDelete(true)}\n >\n {labels.deleteEntry}\n </button>\n )}\n {onDelete && confirmingDelete && (\n <div className=\"flex items-center gap-2\">\n <span className=\"text-xs text-error\">{labels.confirmDelete}</span>\n <button\n type=\"button\"\n className=\"btn btn-error btn-xs\"\n onClick={onDelete}\n >\n {labels.deleteEntry}\n </button>\n <button\n type=\"button\"\n className=\"btn btn-ghost btn-xs\"\n onClick={() => setConfirmingDelete(false)}\n >\n {labels.cancel}\n </button>\n </div>\n )}\n </div>\n <div className=\"flex gap-2\">\n <button\n type=\"button\"\n className=\"btn btn-ghost btn-sm\"\n onClick={onCancel}\n >\n {labels.cancel}\n </button>\n <button\n type=\"submit\"\n className=\"btn btn-primary btn-sm\"\n disabled={!formData.title?.trim()}\n >\n {labels.save}\n </button>\n </div>\n </div>\n </form>\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCalendarDrag.js","names":[],"sources":["../../../../../src/components/pro/Calendar/hooks/useCalendarDrag.ts"],"sourcesContent":["/**\n * useCalendarDrag - Drag-and-drop interaction hook for Calendar entries.\n *\n * Uses the native HTML5 Drag and Drop API to support:\n * - Moving entries between date cells (month view)\n * - Moving entries between time slots (week/day view)\n * - Resizing entry duration in time-based views\n */\n\nimport { useState, useCallback, useRef } from \"react\";\nimport type { CalendarEntry, CalendarDragEvent } from \"../types\";\nimport { timeToMinutes, minutesToTime } from \"../utils\";\n\n// ─── Types ────────────────────────────────────────────────────────\n\nexport interface DragState {\n /** The entry being dragged */\n entry: CalendarEntry;\n /** The original date of the entry */\n fromDate: string;\n /** The original start time (if time-based) */\n fromTime?: string;\n /** Whether we're resizing (bottom edge) vs moving */\n isResize: boolean;\n /** Initial mouse Y when drag started (for resize calculations) */\n startY: number;\n /** Original entry duration in minutes (for resize) */\n originalDuration: number;\n}\n\nexport interface DropTarget {\n /** Target date (YYYY-MM-DD) */\n date: string;\n /** Target time slot (HH:mm) — only in week/day view */\n time?: string;\n}\n\nexport interface UseCalendarDragOptions {\n readOnly?: boolean;\n onEntryDrop?: (event: CalendarDragEvent) => void;\n onEntryUpdate?: (entry: CalendarEntry) => void;\n}\n\n// ─── Hook ─────────────────────────────────────────────────────────\n\nexport function useCalendarDrag(options: UseCalendarDragOptions) {\n const { readOnly = false, onEntryDrop, onEntryUpdate } = options;\n\n const [dragState, setDragState] = useState<DragState | null>(null);\n const [dropTarget, setDropTarget] = useState<DropTarget | null>(null);\n const dragRef = useRef<DragState | null>(null);\n\n // ─── Drag Start ─────────────────────────────────────────────────\n\n const handleDragStart = useCallback(\n (e: React.DragEvent, entry: CalendarEntry) => {\n if (readOnly) {\n e.preventDefault();\n return;\n }\n\n const state: DragState = {\n entry,\n fromDate: entry.date,\n fromTime: entry.startTime,\n isResize: false,\n startY: e.clientY,\n originalDuration:\n entry.startTime && entry.endTime\n ? timeToMinutes(entry.endTime) - timeToMinutes(entry.startTime)\n : 60,\n };\n\n setDragState(state);\n dragRef.current = state;\n\n // Set drag data for the native API\n e.dataTransfer.setData(\"text/plain\", entry.id);\n e.dataTransfer.effectAllowed = \"move\";\n\n // Set a custom drag image (optional: use the element itself)\n if (e.currentTarget instanceof HTMLElement) {\n const rect = e.currentTarget.getBoundingClientRect();\n e.dataTransfer.setDragImage(\n e.currentTarget,\n e.clientX - rect.left,\n e.clientY - rect.top,\n );\n }\n },\n [readOnly],\n );\n\n // ─── Resize Start ───────────────────────────────────────────────\n\n const handleResizeStart = useCallback(\n (e: React.DragEvent, entry: CalendarEntry) => {\n if (readOnly) {\n e.preventDefault();\n return;\n }\n\n e.stopPropagation();\n\n const state: DragState = {\n entry,\n fromDate: entry.date,\n fromTime: entry.startTime,\n isResize: true,\n startY: e.clientY,\n originalDuration:\n entry.startTime && entry.endTime\n ? timeToMinutes(entry.endTime) - timeToMinutes(entry.startTime)\n : 60,\n };\n\n setDragState(state);\n dragRef.current = state;\n\n e.dataTransfer.setData(\"text/plain\", entry.id);\n e.dataTransfer.effectAllowed = \"move\";\n\n // Use a tiny transparent image for resize to show native cursor\n const img = new Image();\n img.src =\n \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\";\n e.dataTransfer.setDragImage(img, 0, 0);\n },\n [readOnly],\n );\n\n // ─── Drag Over (drop target) ────────────────────────────────────\n\n const handleDragOver = useCallback(\n (e: React.DragEvent, date: string, time?: string) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n setDropTarget({ date, time });\n },\n [],\n );\n\n // ─── Drag Leave ─────────────────────────────────────────────────\n\n const handleDragLeave = useCallback((e: React.DragEvent) => {\n // Only clear if leaving the actual target (not entering a child)\n const relatedTarget = e.relatedTarget as HTMLElement | null;\n if (!relatedTarget || !e.currentTarget.contains(relatedTarget)) {\n setDropTarget(null);\n }\n }, []);\n\n // ─── Drop ───────────────────────────────────────────────────────\n\n const handleDrop = useCallback(\n (e: React.DragEvent, date: string, time?: string) => {\n e.preventDefault();\n const state = dragRef.current;\n if (!state) return;\n\n if (state.isResize) {\n // Handle resize: compute new end time based on drop position\n // The drop time IS the new end time (snapped to slot)\n if (time && state.entry.startTime) {\n const startMinutes = timeToMinutes(state.entry.startTime);\n const dropMinutes = timeToMinutes(time);\n // Ensure minimum 30-minute duration\n const endMinutes = Math.max(dropMinutes + 30, startMinutes + 30);\n const clampedEnd = Math.min(endMinutes, 24 * 60);\n\n const updatedEntry: CalendarEntry = {\n ...state.entry,\n endTime: minutesToTime(clampedEnd),\n };\n\n onEntryUpdate?.(updatedEntry);\n onEntryDrop?.({\n entry: state.entry,\n fromDate: state.fromDate,\n toDate: date,\n fromTime: state.entry.endTime,\n toTime: minutesToTime(clampedEnd),\n });\n }\n } else {\n // Handle move\n const dragEvent: CalendarDragEvent = {\n entry: state.entry,\n fromDate: state.fromDate,\n toDate: date,\n fromTime: state.fromTime,\n toTime: time,\n };\n\n // Compute the updated entry\n const updatedEntry: CalendarEntry = {\n ...state.entry,\n date,\n };\n\n // If we have time info, update start/end times\n if (time && state.entry.startTime) {\n const duration = state.originalDuration;\n const newStartMinutes = timeToMinutes(time);\n const newEndMinutes = Math.min(newStartMinutes + duration, 24 * 60);\n updatedEntry.startTime = time;\n updatedEntry.endTime = minutesToTime(newEndMinutes);\n } else if (time && state.entry.allDay) {\n // Moving an all-day event into a time slot\n updatedEntry.startTime = time;\n updatedEntry.endTime = minutesToTime(\n Math.min(timeToMinutes(time) + 60, 24 * 60),\n );\n updatedEntry.allDay = false;\n }\n\n onEntryUpdate?.(updatedEntry);\n onEntryDrop?.(dragEvent);\n }\n\n // Clear state\n setDragState(null);\n setDropTarget(null);\n dragRef.current = null;\n },\n [onEntryDrop, onEntryUpdate],\n );\n\n // ─── Drag End (cleanup) ─────────────────────────────────────────\n\n const handleDragEnd = useCallback(() => {\n setDragState(null);\n setDropTarget(null);\n dragRef.current = null;\n }, []);\n\n // ─── Computed State ─────────────────────────────────────────────\n\n const isDragging = dragState !== null;\n const draggingEntryId = dragState?.entry.id ?? null;\n\n const isDropTarget = useCallback(\n (date: string, time?: string): boolean => {\n if (!dropTarget) return false;\n if (dropTarget.date !== date) return false;\n if (time !== undefined && dropTarget.time !== time) return false;\n return true;\n },\n [dropTarget],\n );\n\n return {\n isDragging,\n draggingEntryId,\n dragState,\n dropTarget,\n isDropTarget,\n handleDragStart,\n handleResizeStart,\n handleDragOver,\n handleDragLeave,\n handleDrop,\n handleDragEnd,\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useCalendarDrag.js","names":["useCalendarDrag","options","dragState","setDragState","useState","dropTarget","setDropTarget","dragRef","useRef","handleDragStart","useCallback","readOnly","state","entry","timeToMinutes","rect","handleResizeStart","img","handleDragOver","handleDragLeave","relatedTarget","handleDrop","time","startMinutes","dropMinutes","endMinutes","clampedEnd","updatedEntry","minutesToTime","onEntryUpdate","onEntryDrop","date","dragEvent","duration","newStartMinutes","newEndMinutes","handleDragEnd"],"sources":["../../../../../src/components/pro/Calendar/hooks/useCalendarDrag.ts"],"sourcesContent":["/**\n * useCalendarDrag - Drag-and-drop interaction hook for Calendar entries.\n *\n * Uses the native HTML5 Drag and Drop API to support:\n * - Moving entries between date cells (month view)\n * - Moving entries between time slots (week/day view)\n * - Resizing entry duration in time-based views\n */\n\nimport { useState, useCallback, useRef } from \"react\";\nimport type { CalendarEntry, CalendarDragEvent } from \"../types\";\nimport { timeToMinutes, minutesToTime } from \"../utils\";\n\n// ─── Types ────────────────────────────────────────────────────────\n\nexport interface DragState {\n /** The entry being dragged */\n entry: CalendarEntry;\n /** The original date of the entry */\n fromDate: string;\n /** The original start time (if time-based) */\n fromTime?: string;\n /** Whether we're resizing (bottom edge) vs moving */\n isResize: boolean;\n /** Initial mouse Y when drag started (for resize calculations) */\n startY: number;\n /** Original entry duration in minutes (for resize) */\n originalDuration: number;\n}\n\nexport interface DropTarget {\n /** Target date (YYYY-MM-DD) */\n date: string;\n /** Target time slot (HH:mm) — only in week/day view */\n time?: string;\n}\n\nexport interface UseCalendarDragOptions {\n readOnly?: boolean;\n onEntryDrop?: (event: CalendarDragEvent) => void;\n onEntryUpdate?: (entry: CalendarEntry) => void;\n}\n\n// ─── Hook ─────────────────────────────────────────────────────────\n\nexport function useCalendarDrag(options: UseCalendarDragOptions) {\n const { readOnly = false, onEntryDrop, onEntryUpdate } = options;\n\n const [dragState, setDragState] = useState<DragState | null>(null);\n const [dropTarget, setDropTarget] = useState<DropTarget | null>(null);\n const dragRef = useRef<DragState | null>(null);\n\n // ─── Drag Start ─────────────────────────────────────────────────\n\n const handleDragStart = useCallback(\n (e: React.DragEvent, entry: CalendarEntry) => {\n if (readOnly) {\n e.preventDefault();\n return;\n }\n\n const state: DragState = {\n entry,\n fromDate: entry.date,\n fromTime: entry.startTime,\n isResize: false,\n startY: e.clientY,\n originalDuration:\n entry.startTime && entry.endTime\n ? timeToMinutes(entry.endTime) - timeToMinutes(entry.startTime)\n : 60,\n };\n\n setDragState(state);\n dragRef.current = state;\n\n // Set drag data for the native API\n e.dataTransfer.setData(\"text/plain\", entry.id);\n e.dataTransfer.effectAllowed = \"move\";\n\n // Set a custom drag image (optional: use the element itself)\n if (e.currentTarget instanceof HTMLElement) {\n const rect = e.currentTarget.getBoundingClientRect();\n e.dataTransfer.setDragImage(\n e.currentTarget,\n e.clientX - rect.left,\n e.clientY - rect.top,\n );\n }\n },\n [readOnly],\n );\n\n // ─── Resize Start ───────────────────────────────────────────────\n\n const handleResizeStart = useCallback(\n (e: React.DragEvent, entry: CalendarEntry) => {\n if (readOnly) {\n e.preventDefault();\n return;\n }\n\n e.stopPropagation();\n\n const state: DragState = {\n entry,\n fromDate: entry.date,\n fromTime: entry.startTime,\n isResize: true,\n startY: e.clientY,\n originalDuration:\n entry.startTime && entry.endTime\n ? timeToMinutes(entry.endTime) - timeToMinutes(entry.startTime)\n : 60,\n };\n\n setDragState(state);\n dragRef.current = state;\n\n e.dataTransfer.setData(\"text/plain\", entry.id);\n e.dataTransfer.effectAllowed = \"move\";\n\n // Use a tiny transparent image for resize to show native cursor\n const img = new Image();\n img.src =\n \"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7\";\n e.dataTransfer.setDragImage(img, 0, 0);\n },\n [readOnly],\n );\n\n // ─── Drag Over (drop target) ────────────────────────────────────\n\n const handleDragOver = useCallback(\n (e: React.DragEvent, date: string, time?: string) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = \"move\";\n setDropTarget({ date, time });\n },\n [],\n );\n\n // ─── Drag Leave ─────────────────────────────────────────────────\n\n const handleDragLeave = useCallback((e: React.DragEvent) => {\n // Only clear if leaving the actual target (not entering a child)\n const relatedTarget = e.relatedTarget as HTMLElement | null;\n if (!relatedTarget || !e.currentTarget.contains(relatedTarget)) {\n setDropTarget(null);\n }\n }, []);\n\n // ─── Drop ───────────────────────────────────────────────────────\n\n const handleDrop = useCallback(\n (e: React.DragEvent, date: string, time?: string) => {\n e.preventDefault();\n const state = dragRef.current;\n if (!state) return;\n\n if (state.isResize) {\n // Handle resize: compute new end time based on drop position\n // The drop time IS the new end time (snapped to slot)\n if (time && state.entry.startTime) {\n const startMinutes = timeToMinutes(state.entry.startTime);\n const dropMinutes = timeToMinutes(time);\n // Ensure minimum 30-minute duration\n const endMinutes = Math.max(dropMinutes + 30, startMinutes + 30);\n const clampedEnd = Math.min(endMinutes, 24 * 60);\n\n const updatedEntry: CalendarEntry = {\n ...state.entry,\n endTime: minutesToTime(clampedEnd),\n };\n\n onEntryUpdate?.(updatedEntry);\n onEntryDrop?.({\n entry: state.entry,\n fromDate: state.fromDate,\n toDate: date,\n fromTime: state.entry.endTime,\n toTime: minutesToTime(clampedEnd),\n });\n }\n } else {\n // Handle move\n const dragEvent: CalendarDragEvent = {\n entry: state.entry,\n fromDate: state.fromDate,\n toDate: date,\n fromTime: state.fromTime,\n toTime: time,\n };\n\n // Compute the updated entry\n const updatedEntry: CalendarEntry = {\n ...state.entry,\n date,\n };\n\n // If we have time info, update start/end times\n if (time && state.entry.startTime) {\n const duration = state.originalDuration;\n const newStartMinutes = timeToMinutes(time);\n const newEndMinutes = Math.min(newStartMinutes + duration, 24 * 60);\n updatedEntry.startTime = time;\n updatedEntry.endTime = minutesToTime(newEndMinutes);\n } else if (time && state.entry.allDay) {\n // Moving an all-day event into a time slot\n updatedEntry.startTime = time;\n updatedEntry.endTime = minutesToTime(\n Math.min(timeToMinutes(time) + 60, 24 * 60),\n );\n updatedEntry.allDay = false;\n }\n\n onEntryUpdate?.(updatedEntry);\n onEntryDrop?.(dragEvent);\n }\n\n // Clear state\n setDragState(null);\n setDropTarget(null);\n dragRef.current = null;\n },\n [onEntryDrop, onEntryUpdate],\n );\n\n // ─── Drag End (cleanup) ─────────────────────────────────────────\n\n const handleDragEnd = useCallback(() => {\n setDragState(null);\n setDropTarget(null);\n dragRef.current = null;\n }, []);\n\n // ─── Computed State ─────────────────────────────────────────────\n\n const isDragging = dragState !== null;\n const draggingEntryId = dragState?.entry.id ?? null;\n\n const isDropTarget = useCallback(\n (date: string, time?: string): boolean => {\n if (!dropTarget) return false;\n if (dropTarget.date !== date) return false;\n if (time !== undefined && dropTarget.time !== time) return false;\n return true;\n },\n [dropTarget],\n );\n\n return {\n isDragging,\n draggingEntryId,\n dragState,\n dropTarget,\n isDropTarget,\n handleDragStart,\n handleResizeStart,\n handleDragOver,\n handleDragLeave,\n handleDrop,\n handleDragEnd,\n };\n}\n"],"mappings":"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"}
|
|
@@ -2,12 +2,12 @@ import { DEFAULT_COLOR_OPTIONS as e, LABELS_EN as t, LABELS_ZH as n, addDays as
|
|
|
2
2
|
import { useCallback as u, useMemo as d, useRef as f, useState as p } from "react";
|
|
3
3
|
//#region src/components/pro/Calendar/hooks/useCalendarState.ts
|
|
4
4
|
function m(m) {
|
|
5
|
-
let { entries: h, holidays:
|
|
6
|
-
...
|
|
7
|
-
...
|
|
8
|
-
}), [y,
|
|
9
|
-
M(e),
|
|
10
|
-
}, [
|
|
5
|
+
let { entries: h, holidays: ee, defaultView: g = "month", defaultDate: _, locale: v = "en", labels: y, colorOptions: b = e, readOnly: x = !1, onEntryAdd: S, onEntryUpdate: C, onEntryDelete: w, onViewChange: T } = m, E = d(() => ({
|
|
6
|
+
...v === "zh" ? n : t,
|
|
7
|
+
...y
|
|
8
|
+
}), [v, y]), [D] = p(l), [O, k] = p(() => _ || D), A = d(() => c(O) ?? c(D), [O, D]), [j, M] = p(g), N = u((e) => {
|
|
9
|
+
M(e), T?.(e);
|
|
10
|
+
}, [T]), P = h !== void 0 && (S !== void 0 || C !== void 0 || w !== void 0), [F, I] = p(() => h || []), L = f(h);
|
|
11
11
|
P && h !== L.current && (L.current = h);
|
|
12
12
|
let R = P ? h : F, z = u(() => {
|
|
13
13
|
k(l());
|
|
@@ -52,19 +52,19 @@ function m(m) {
|
|
|
52
52
|
}
|
|
53
53
|
}
|
|
54
54
|
}, [O, j]), U = d(() => {
|
|
55
|
-
let e = A, t =
|
|
55
|
+
let e = A, t = E.monthNames[e.getMonth()], n = e.getFullYear();
|
|
56
56
|
switch (j) {
|
|
57
57
|
case "month": return `${t} ${n}`;
|
|
58
58
|
case "week": {
|
|
59
59
|
let t = s(e), r = t[0], i = t[6];
|
|
60
|
-
return r.getMonth() === i.getMonth() ? `${
|
|
60
|
+
return r.getMonth() === i.getMonth() ? `${E.monthNames[r.getMonth()]} ${r.getDate()} - ${i.getDate()}, ${n}` : `${E.monthNames[r.getMonth()]} ${r.getDate()} - ${E.monthNames[i.getMonth()]} ${i.getDate()}, ${n}`;
|
|
61
61
|
}
|
|
62
62
|
case "day": return `${t} ${e.getDate()}, ${n}`;
|
|
63
63
|
}
|
|
64
64
|
}, [
|
|
65
65
|
A,
|
|
66
66
|
j,
|
|
67
|
-
|
|
67
|
+
E
|
|
68
68
|
]), W = d(() => {
|
|
69
69
|
let e = A;
|
|
70
70
|
return o(e.getFullYear(), e.getMonth() + 1);
|
|
@@ -73,17 +73,17 @@ function m(m) {
|
|
|
73
73
|
...e,
|
|
74
74
|
id: e.id || a()
|
|
75
75
|
};
|
|
76
|
-
return P || I((e) => [...e, t]),
|
|
77
|
-
}, [P,
|
|
78
|
-
P || I((t) => t.map((t) => t.id === e.id ? e : t)),
|
|
79
|
-
}, [P,
|
|
80
|
-
P || I((t) => t.filter((t) => t.id !== e)),
|
|
81
|
-
}, [P,
|
|
76
|
+
return P || I((e) => [...e, t]), S?.(t), t;
|
|
77
|
+
}, [P, S]), q = u((e) => {
|
|
78
|
+
P || I((t) => t.map((t) => t.id === e.id ? e : t)), C?.(e);
|
|
79
|
+
}, [P, C]), J = u((e) => {
|
|
80
|
+
P || I((t) => t.filter((t) => t.id !== e)), w?.(e);
|
|
81
|
+
}, [P, w]), [Y, X] = p({
|
|
82
82
|
open: !1,
|
|
83
83
|
mode: "add",
|
|
84
84
|
entry: {}
|
|
85
85
|
}), Z = u((e, t) => {
|
|
86
|
-
|
|
86
|
+
x || X({
|
|
87
87
|
open: !0,
|
|
88
88
|
mode: "add",
|
|
89
89
|
entry: {
|
|
@@ -94,32 +94,32 @@ function m(m) {
|
|
|
94
94
|
return `${String(i).padStart(2, "0")}:${String(a).padStart(2, "0")}`;
|
|
95
95
|
})() : void 0,
|
|
96
96
|
allDay: !t,
|
|
97
|
-
color:
|
|
97
|
+
color: b[0]
|
|
98
98
|
}
|
|
99
99
|
});
|
|
100
100
|
}, [
|
|
101
|
-
|
|
101
|
+
x,
|
|
102
102
|
O,
|
|
103
|
-
|
|
103
|
+
b
|
|
104
104
|
]), Q = u((e) => {
|
|
105
|
-
|
|
105
|
+
x || X({
|
|
106
106
|
open: !0,
|
|
107
107
|
mode: "edit",
|
|
108
108
|
entry: { ...e }
|
|
109
109
|
});
|
|
110
|
-
}, [
|
|
110
|
+
}, [x]), $ = u(() => {
|
|
111
111
|
X((e) => ({
|
|
112
112
|
...e,
|
|
113
113
|
open: !1
|
|
114
114
|
}));
|
|
115
|
-
}, []),
|
|
115
|
+
}, []), te = u((e) => {
|
|
116
116
|
if (Y.mode === "add") {
|
|
117
117
|
let t = K({
|
|
118
118
|
title: e.title || "",
|
|
119
119
|
date: e.date || O,
|
|
120
120
|
startTime: e.allDay ? void 0 : e.startTime,
|
|
121
121
|
endTime: e.allDay ? void 0 : e.endTime,
|
|
122
|
-
color: e.color ||
|
|
122
|
+
color: e.color || b[0],
|
|
123
123
|
description: e.description,
|
|
124
124
|
allDay: e.allDay ?? !0
|
|
125
125
|
});
|
|
@@ -141,8 +141,8 @@ function m(m) {
|
|
|
141
141
|
q,
|
|
142
142
|
$,
|
|
143
143
|
O,
|
|
144
|
-
|
|
145
|
-
]),
|
|
144
|
+
b
|
|
145
|
+
]), ne = u(() => {
|
|
146
146
|
Y.entry.id && J(Y.entry.id), $();
|
|
147
147
|
}, [
|
|
148
148
|
Y.entry.id,
|
|
@@ -154,10 +154,10 @@ function m(m) {
|
|
|
154
154
|
currentDateObj: A,
|
|
155
155
|
viewMode: j,
|
|
156
156
|
entries: R,
|
|
157
|
-
labels:
|
|
158
|
-
colorOptions:
|
|
159
|
-
holidays:
|
|
160
|
-
readOnly:
|
|
157
|
+
labels: E,
|
|
158
|
+
colorOptions: b,
|
|
159
|
+
holidays: ee,
|
|
160
|
+
readOnly: x,
|
|
161
161
|
goToToday: z,
|
|
162
162
|
goToDate: B,
|
|
163
163
|
goNext: V,
|
|
@@ -177,8 +177,8 @@ function m(m) {
|
|
|
177
177
|
openAddModal: Z,
|
|
178
178
|
openEditModal: Q,
|
|
179
179
|
closeModal: $,
|
|
180
|
-
saveModal:
|
|
181
|
-
deleteModalEntry:
|
|
180
|
+
saveModal: te,
|
|
181
|
+
deleteModalEntry: ne
|
|
182
182
|
};
|
|
183
183
|
}
|
|
184
184
|
//#endregion
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useCalendarState.js","names":[],"sources":["../../../../../src/components/pro/Calendar/hooks/useCalendarState.ts"],"sourcesContent":["/**\n * useCalendarState - Core state management hook for the Calendar component.\n *\n * Manages current date, view mode, entries CRUD, navigation,\n * and modal state for add/edit entry forms.\n */\n\nimport { useState, useCallback, useMemo, useRef } from \"react\";\nimport type {\n CalendarViewMode,\n CalendarEntry,\n CalendarLabels,\n HolidayCalendar,\n} from \"../types\";\nimport {\n formatDate,\n todayStr,\n parseDate,\n addDays,\n generateId,\n getMonthDates,\n getWeekDates,\n LABELS_EN,\n LABELS_ZH,\n DEFAULT_COLOR_OPTIONS,\n} from \"../utils\";\n\n// ─── Options ──────────────────────────────────────────────────────\n\nexport interface UseCalendarStateOptions {\n entries?: CalendarEntry[];\n holidays?: HolidayCalendar;\n defaultView?: CalendarViewMode;\n defaultDate?: string;\n locale?: \"zh\" | \"en\";\n labels?: CalendarLabels;\n colorOptions?: string[];\n readOnly?: boolean;\n onEntryAdd?: (entry: CalendarEntry) => void;\n onEntryUpdate?: (entry: CalendarEntry) => void;\n onEntryDelete?: (entryId: string) => void;\n onViewChange?: (view: CalendarViewMode) => void;\n}\n\n// ─── Modal State ──────────────────────────────────────────────────\n\nexport interface EntryModalState {\n open: boolean;\n mode: \"add\" | \"edit\";\n entry: Partial<CalendarEntry>;\n}\n\n// ─── Hook ─────────────────────────────────────────────────────────\n\nexport function useCalendarState(options: UseCalendarStateOptions) {\n const {\n entries: externalEntries,\n holidays,\n defaultView = \"month\",\n defaultDate,\n locale = \"en\",\n labels: userLabels,\n colorOptions = DEFAULT_COLOR_OPTIONS,\n readOnly = false,\n onEntryAdd,\n onEntryUpdate,\n onEntryDelete,\n onViewChange,\n } = options;\n\n // ─── Labels ───────────────────────────────────────────────────\n\n const labels: Required<CalendarLabels> = useMemo(() => {\n const base = locale === \"zh\" ? LABELS_ZH : LABELS_EN;\n return { ...base, ...userLabels } as Required<CalendarLabels>;\n }, [locale, userLabels]);\n\n // ─── Current Date ─────────────────────────────────────────────\n\n const [currentDate, setCurrentDate] = useState<string>(() => {\n if (defaultDate) return defaultDate;\n return todayStr();\n });\n\n const currentDateObj = useMemo(() => {\n const d = parseDate(currentDate);\n return d || new Date();\n }, [currentDate]);\n\n // ─── View Mode ────────────────────────────────────────────────\n\n const [viewMode, setViewModeRaw] = useState<CalendarViewMode>(defaultView);\n\n const setViewMode = useCallback(\n (view: CalendarViewMode) => {\n setViewModeRaw(view);\n onViewChange?.(view);\n },\n [onViewChange],\n );\n\n // ─── Internal Entries ─────────────────────────────────────────\n\n // Controlled mode: external entries + at least one CRUD callback provided.\n // Otherwise entries prop is treated as initial value, managed internally.\n const isControlled =\n externalEntries !== undefined &&\n (onEntryAdd !== undefined ||\n onEntryUpdate !== undefined ||\n onEntryDelete !== undefined);\n\n const [internalEntries, setInternalEntries] = useState<CalendarEntry[]>(\n () => externalEntries || [],\n );\n\n // Sync internal state when external entries change in controlled mode\n // so the component always reflects the parent's truth.\n const prevExternalRef = useRef(externalEntries);\n if (isControlled && externalEntries !== prevExternalRef.current) {\n prevExternalRef.current = externalEntries;\n // will be picked up on next render via `entries` below\n }\n\n const entries = isControlled ? externalEntries! : internalEntries;\n\n // ─── Navigation ───────────────────────────────────────────────\n\n const goToToday = useCallback(() => {\n setCurrentDate(todayStr());\n }, []);\n\n const goToDate = useCallback((date: string) => {\n setCurrentDate(date);\n }, []);\n\n const goNext = useCallback(() => {\n const d = parseDate(currentDate);\n if (!d) return;\n\n switch (viewMode) {\n case \"month\": {\n const next = new Date(d.getFullYear(), d.getMonth() + 1, 1);\n setCurrentDate(formatDate(next));\n break;\n }\n case \"week\": {\n const next = addDays(d, 7);\n setCurrentDate(formatDate(next));\n break;\n }\n case \"day\": {\n const next = addDays(d, 1);\n setCurrentDate(formatDate(next));\n break;\n }\n }\n }, [currentDate, viewMode]);\n\n const goPrev = useCallback(() => {\n const d = parseDate(currentDate);\n if (!d) return;\n\n switch (viewMode) {\n case \"month\": {\n const prev = new Date(d.getFullYear(), d.getMonth() - 1, 1);\n setCurrentDate(formatDate(prev));\n break;\n }\n case \"week\": {\n const prev = addDays(d, -7);\n setCurrentDate(formatDate(prev));\n break;\n }\n case \"day\": {\n const prev = addDays(d, -1);\n setCurrentDate(formatDate(prev));\n break;\n }\n }\n }, [currentDate, viewMode]);\n\n // ─── Title Display ────────────────────────────────────────────\n\n const title = useMemo(() => {\n const d = currentDateObj;\n const monthName = labels.monthNames![d.getMonth()];\n const year = d.getFullYear();\n\n switch (viewMode) {\n case \"month\":\n return `${monthName} ${year}`;\n case \"week\": {\n const weekDates = getWeekDates(d);\n const start = weekDates[0];\n const end = weekDates[6];\n if (start.getMonth() === end.getMonth()) {\n return `${labels.monthNames![start.getMonth()]} ${start.getDate()} - ${end.getDate()}, ${year}`;\n }\n return `${labels.monthNames![start.getMonth()]} ${start.getDate()} - ${labels.monthNames![end.getMonth()]} ${end.getDate()}, ${year}`;\n }\n case \"day\":\n return `${monthName} ${d.getDate()}, ${year}`;\n }\n }, [currentDateObj, viewMode, labels]);\n\n // ─── Grid Dates ───────────────────────────────────────────────\n\n const monthDates = useMemo(() => {\n const d = currentDateObj;\n return getMonthDates(d.getFullYear(), d.getMonth() + 1);\n }, [currentDateObj]);\n\n const weekDates = useMemo(() => {\n return getWeekDates(currentDateObj);\n }, [currentDateObj]);\n\n // ─── Entry CRUD ───────────────────────────────────────────────\n\n const addEntry = useCallback(\n (entry: Omit<CalendarEntry, \"id\"> & { id?: string }) => {\n const newEntry = {\n ...entry,\n id: entry.id || generateId(),\n } as CalendarEntry;\n\n if (!isControlled) {\n setInternalEntries((prev) => [...prev, newEntry]);\n }\n onEntryAdd?.(newEntry);\n return newEntry;\n },\n [isControlled, onEntryAdd],\n );\n\n const updateEntry = useCallback(\n (entry: CalendarEntry) => {\n if (!isControlled) {\n setInternalEntries((prev) =>\n prev.map((e) => (e.id === entry.id ? entry : e)),\n );\n }\n onEntryUpdate?.(entry);\n },\n [isControlled, onEntryUpdate],\n );\n\n const deleteEntry = useCallback(\n (entryId: string) => {\n if (!isControlled) {\n setInternalEntries((prev) => prev.filter((e) => e.id !== entryId));\n }\n onEntryDelete?.(entryId);\n },\n [isControlled, onEntryDelete],\n );\n\n // ─── Entry Modal State ────────────────────────────────────────\n\n const [modalState, setModalState] = useState<EntryModalState>({\n open: false,\n mode: \"add\",\n entry: {},\n });\n\n const openAddModal = useCallback(\n (date?: string, startTime?: string) => {\n if (readOnly) return;\n setModalState({\n open: true,\n mode: \"add\",\n entry: {\n date: date || currentDate,\n startTime: startTime || undefined,\n endTime: startTime\n ? (() => {\n const [h, m] = startTime.split(\":\").map(Number);\n const endM = h * 60 + m + 60; // 1 hour later\n const eh = Math.min(Math.floor(endM / 60), 23);\n const em = endM % 60;\n return `${String(eh).padStart(2, \"0\")}:${String(em).padStart(2, \"0\")}`;\n })()\n : undefined,\n allDay: !startTime,\n color: colorOptions[0],\n },\n });\n },\n [readOnly, currentDate, colorOptions],\n );\n\n const openEditModal = useCallback(\n (entry: CalendarEntry) => {\n if (readOnly) return;\n setModalState({\n open: true,\n mode: \"edit\",\n entry: { ...entry },\n });\n },\n [readOnly],\n );\n\n const closeModal = useCallback(() => {\n setModalState((prev) => ({ ...prev, open: false }));\n }, []);\n\n const saveModal = useCallback(\n (entryData: Partial<CalendarEntry>) => {\n if (modalState.mode === \"add\") {\n const newEntry = addEntry({\n title: entryData.title || \"\",\n date: entryData.date || currentDate,\n startTime: entryData.allDay ? undefined : entryData.startTime,\n endTime: entryData.allDay ? undefined : entryData.endTime,\n color: entryData.color || colorOptions[0],\n description: entryData.description,\n allDay: entryData.allDay ?? true,\n });\n closeModal();\n return newEntry;\n } else {\n const updated: CalendarEntry = {\n ...(modalState.entry as CalendarEntry),\n ...entryData,\n id: modalState.entry.id!,\n startTime: entryData.allDay ? undefined : entryData.startTime,\n endTime: entryData.allDay ? undefined : entryData.endTime,\n };\n updateEntry(updated);\n closeModal();\n return updated;\n }\n },\n [modalState, addEntry, updateEntry, closeModal, currentDate, colorOptions],\n );\n\n const deleteModalEntry = useCallback(() => {\n if (modalState.entry.id) {\n deleteEntry(modalState.entry.id);\n }\n closeModal();\n }, [modalState.entry.id, deleteEntry, closeModal]);\n\n // ─── Helpers ──────────────────────────────────────────────────\n\n /** Get entries for a specific date (YYYY-MM-DD), sorted by time */\n const getEntriesForDate = useCallback(\n (date: string): CalendarEntry[] => {\n return entries\n .filter((e) => e.date === date)\n .sort((a, b) => {\n // All-day events come first\n const aAllDay = a.allDay || !a.startTime;\n const bAllDay = b.allDay || !b.startTime;\n if (aAllDay && !bAllDay) return -1;\n if (!aAllDay && bAllDay) return 1;\n // Both all-day: keep original order\n if (aAllDay && bAllDay) return 0;\n // Both timed: sort by startTime ascending\n return (a.startTime || \"\").localeCompare(b.startTime || \"\");\n });\n },\n [entries],\n );\n\n return {\n // State\n currentDate,\n currentDateObj,\n viewMode,\n entries,\n labels,\n colorOptions,\n holidays,\n readOnly,\n\n // Navigation\n goToToday,\n goToDate,\n goNext,\n goPrev,\n title,\n\n // View mode\n setViewMode,\n\n // Grid data\n monthDates,\n weekDates,\n\n // Entry CRUD\n addEntry,\n updateEntry,\n deleteEntry,\n getEntriesForDate,\n\n // Modal\n modalState,\n openAddModal,\n openEditModal,\n closeModal,\n saveModal,\n deleteModalEntry,\n };\n}\n"],"mappings":";;;AAsDA,SAAgB,EAAiB,GAAkC;CACjE,IAAM,EACJ,SAAS,GACT,aACA,iBAAc,SACd,gBACA,YAAS,MACT,QAAQ,GACR,kBAAe,GACf,cAAW,IACX,eACA,kBACA,kBACA,oBACE,GAIE,IAAmC,SAEhC;EAAE,GADI,MAAW,OAAO,IAAY;EACzB,GAAG;CAAW,IAC/B,CAAC,GAAQ,CAAU,CAAC,GAIjB,CAAC,GAAa,KAAkB,QAChC,KACG,EAAS,CACjB,GAEK,IAAiB,QACX,EAAU,CACb,qBAAK,IAAI,KAAK,GACpB,CAAC,CAAW,CAAC,GAIV,CAAC,GAAU,KAAkB,EAA2B,CAAW,GAEnE,IAAc,GACjB,MAA2B;EAE1B,AADA,EAAe,CAAI,GACnB,IAAe,CAAI;CACrB,GACA,CAAC,CAAY,CACf,GAMM,IACJ,MAAoB,KAAA,MACnB,MAAe,KAAA,KACd,MAAkB,KAAA,KAClB,MAAkB,KAAA,IAEhB,CAAC,GAAiB,KAAsB,QACtC,KAAmB,CAAC,CAC5B,GAIM,IAAkB,EAAO,CAAe;CAC9C,AAAI,KAAgB,MAAoB,EAAgB,YACtD,EAAgB,UAAU;CAI5B,IAAM,IAAU,IAAe,IAAmB,GAI5C,IAAY,QAAkB;EAClC,EAAe,EAAS,CAAC;CAC3B,GAAG,CAAC,CAAC,GAEC,IAAW,GAAa,MAAiB;EAC7C,EAAe,CAAI;CACrB,GAAG,CAAC,CAAC,GAEC,IAAS,QAAkB;EAC/B,IAAM,IAAI,EAAU,CAAW;EAC1B,OAEL,QAAQ,GAAR;GACE,KAAK,SAAS;IACZ,IAAM,IAAO,IAAI,KAAK,EAAE,YAAY,GAAG,EAAE,SAAS,IAAI,GAAG,CAAC;IAC1D,EAAe,EAAW,CAAI,CAAC;IAC/B;GACF;GACA,KAAK,QAAQ;IACX,IAAM,IAAO,EAAQ,GAAG,CAAC;IACzB,EAAe,EAAW,CAAI,CAAC;IAC/B;GACF;GACA,KAAK,OAAO;IACV,IAAM,IAAO,EAAQ,GAAG,CAAC;IACzB,EAAe,EAAW,CAAI,CAAC;IAC/B;GACF;EACF;CACF,GAAG,CAAC,GAAa,CAAQ,CAAC,GAEpB,IAAS,QAAkB;EAC/B,IAAM,IAAI,EAAU,CAAW;EAC1B,OAEL,QAAQ,GAAR;GACE,KAAK,SAAS;IACZ,IAAM,IAAO,IAAI,KAAK,EAAE,YAAY,GAAG,EAAE,SAAS,IAAI,GAAG,CAAC;IAC1D,EAAe,EAAW,CAAI,CAAC;IAC/B;GACF;GACA,KAAK,QAAQ;IACX,IAAM,IAAO,EAAQ,GAAG,EAAE;IAC1B,EAAe,EAAW,CAAI,CAAC;IAC/B;GACF;GACA,KAAK,OAAO;IACV,IAAM,IAAO,EAAQ,GAAG,EAAE;IAC1B,EAAe,EAAW,CAAI,CAAC;IAC/B;GACF;EACF;CACF,GAAG,CAAC,GAAa,CAAQ,CAAC,GAIpB,IAAQ,QAAc;EAC1B,IAAM,IAAI,GACJ,IAAY,EAAO,WAAY,EAAE,SAAS,IAC1C,IAAO,EAAE,YAAY;EAE3B,QAAQ,GAAR;GACE,KAAK,SACH,OAAO,GAAG,EAAU,GAAG;GACzB,KAAK,QAAQ;IACX,IAAM,IAAY,EAAa,CAAC,GAC1B,IAAQ,EAAU,IAClB,IAAM,EAAU;IAItB,OAHI,EAAM,SAAS,MAAM,EAAI,SAAS,IAC7B,GAAG,EAAO,WAAY,EAAM,SAAS,GAAG,GAAG,EAAM,QAAQ,EAAE,KAAK,EAAI,QAAQ,EAAE,IAAI,MAEpF,GAAG,EAAO,WAAY,EAAM,SAAS,GAAG,GAAG,EAAM,QAAQ,EAAE,KAAK,EAAO,WAAY,EAAI,SAAS,GAAG,GAAG,EAAI,QAAQ,EAAE,IAAI;GACjI;GACA,KAAK,OACH,OAAO,GAAG,EAAU,GAAG,EAAE,QAAQ,EAAE,IAAI;EAC3C;CACF,GAAG;EAAC;EAAgB;EAAU;CAAM,CAAC,GAI/B,IAAa,QAAc;EAC/B,IAAM,IAAI;EACV,OAAO,EAAc,EAAE,YAAY,GAAG,EAAE,SAAS,IAAI,CAAC;CACxD,GAAG,CAAC,CAAc,CAAC,GAEb,IAAY,QACT,EAAa,CAAc,GACjC,CAAC,CAAc,CAAC,GAIb,IAAW,GACd,MAAuD;EACtD,IAAM,IAAW;GACf,GAAG;GACH,IAAI,EAAM,MAAM,EAAW;EAC7B;EAMA,OAJK,KACH,GAAoB,MAAS,CAAC,GAAG,GAAM,CAAQ,CAAC,GAElD,IAAa,CAAQ,GACd;CACT,GACA,CAAC,GAAc,CAAU,CAC3B,GAEM,IAAc,GACjB,MAAyB;EAMxB,AALK,KACH,GAAoB,MAClB,EAAK,KAAK,MAAO,EAAE,OAAO,EAAM,KAAK,IAAQ,CAAE,CACjD,GAEF,IAAgB,CAAK;CACvB,GACA,CAAC,GAAc,CAAa,CAC9B,GAEM,IAAc,GACjB,MAAoB;EAInB,AAHK,KACH,GAAoB,MAAS,EAAK,QAAQ,MAAM,EAAE,OAAO,CAAO,CAAC,GAEnE,IAAgB,CAAO;CACzB,GACA,CAAC,GAAc,CAAa,CAC9B,GAIM,CAAC,GAAY,KAAiB,EAA0B;EAC5D,MAAM;EACN,MAAM;EACN,OAAO,CAAC;CACV,CAAC,GAEK,IAAe,GAClB,GAAe,MAAuB;EACjC,KACJ,EAAc;GACZ,MAAM;GACN,MAAM;GACN,OAAO;IACL,MAAM,KAAQ;IACd,WAAW,KAAa,KAAA;IACxB,SAAS,WACE;KACL,IAAM,CAAC,GAAG,KAAK,EAAU,MAAM,GAAG,CAAC,CAAC,IAAI,MAAM,GACxC,IAAO,IAAI,KAAK,IAAI,IACpB,IAAK,KAAK,IAAI,KAAK,MAAM,IAAO,EAAE,GAAG,EAAE,GACvC,IAAK,IAAO;KAClB,OAAO,GAAG,OAAO,CAAE,CAAC,CAAC,SAAS,GAAG,GAAG,EAAE,GAAG,OAAO,CAAE,CAAC,CAAC,SAAS,GAAG,GAAG;IACrE,EAAA,CAAG,IACH,KAAA;IACJ,QAAQ,CAAC;IACT,OAAO,EAAa;GACtB;EACF,CAAC;CACH,GACA;EAAC;EAAU;EAAa;CAAY,CACtC,GAEM,IAAgB,GACnB,MAAyB;EACpB,KACJ,EAAc;GACZ,MAAM;GACN,MAAM;GACN,OAAO,EAAE,GAAG,EAAM;EACpB,CAAC;CACH,GACA,CAAC,CAAQ,CACX,GAEM,IAAa,QAAkB;EACnC,GAAe,OAAU;GAAE,GAAG;GAAM,MAAM;EAAM,EAAE;CACpD,GAAG,CAAC,CAAC,GAEC,KAAY,GACf,MAAsC;EACrC,IAAI,EAAW,SAAS,OAAO;GAC7B,IAAM,IAAW,EAAS;IACxB,OAAO,EAAU,SAAS;IAC1B,MAAM,EAAU,QAAQ;IACxB,WAAW,EAAU,SAAS,KAAA,IAAY,EAAU;IACpD,SAAS,EAAU,SAAS,KAAA,IAAY,EAAU;IAClD,OAAO,EAAU,SAAS,EAAa;IACvC,aAAa,EAAU;IACvB,QAAQ,EAAU,UAAU;GAC9B,CAAC;GAED,OADA,EAAW,GACJ;EACT;EAAO;GACL,IAAM,IAAyB;IAC7B,GAAI,EAAW;IACf,GAAG;IACH,IAAI,EAAW,MAAM;IACrB,WAAW,EAAU,SAAS,KAAA,IAAY,EAAU;IACpD,SAAS,EAAU,SAAS,KAAA,IAAY,EAAU;GACpD;GAGA,OAFA,EAAY,CAAO,GACnB,EAAW,GACJ;EACT;CACF,GACA;EAAC;EAAY;EAAU;EAAa;EAAY;EAAa;CAAY,CAC3E,GAEM,KAAmB,QAAkB;EAIzC,AAHI,EAAW,MAAM,MACnB,EAAY,EAAW,MAAM,EAAE,GAEjC,EAAW;CACb,GAAG;EAAC,EAAW,MAAM;EAAI;EAAa;CAAU,CAAC;CAwBjD,OAAO;EAEL;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EAGA;EACA;EACA;EACA;EACA;EAGA;EAGA;EACA;EAGA;EACA;EACA;EACA,mBAhDwB,GACvB,MACQ,EACJ,QAAQ,MAAM,EAAE,SAAS,CAAI,CAAC,CAC9B,MAAM,GAAG,MAAM;GAEd,IAAM,IAAU,EAAE,UAAU,CAAC,EAAE,WACzB,IAAU,EAAE,UAAU,CAAC,EAAE;GAM/B,OALI,KAAW,CAAC,IAAgB,KAC5B,CAAC,KAAW,IAAgB,IAE5B,KAAW,IAAgB,KAEvB,EAAE,aAAa,GAAA,CAAI,cAAc,EAAE,aAAa,EAAE;EAC5D,CAAC,GAEL,CAAC,CAAO,CAgCR;EAGA;EACA;EACA;EACA;EACA;EACA;CACF;AACF"}
|
|
1
|
+
{"version":3,"file":"useCalendarState.js","names":["useCalendarState","externalEntries","userLabels","DEFAULT_COLOR_OPTIONS","options","labels","useMemo","locale","LABELS_ZH","LABELS_EN","initialDate","useState","todayStr","currentDate","setCurrentDate","defaultDate","currentDateObj","parseDate","viewMode","setViewModeRaw","defaultView","setViewMode","useCallback","view","onViewChange","isControlled","onEntryAdd","onEntryUpdate","onEntryDelete","internalEntries","setInternalEntries","prevExternalRef","useRef","entries","goToToday","goToDate","date","goNext","d","next","formatDate","addDays","goPrev","prev","title","monthName","year","weekDates","getWeekDates","start","end","monthDates","getMonthDates","addEntry","newEntry","entry","generateId","updateEntry","e","deleteEntry","entryId","modalState","setModalState","openAddModal","readOnly","startTime","h","m","endM","eh","em","colorOptions","openEditModal","closeModal","saveModal","entryData","updated","deleteModalEntry","aAllDay","a","bAllDay","b"],"sources":["../../../../../src/components/pro/Calendar/hooks/useCalendarState.ts"],"sourcesContent":["/**\n * useCalendarState - Core state management hook for the Calendar component.\n *\n * Manages current date, view mode, entries CRUD, navigation,\n * and modal state for add/edit entry forms.\n */\n\nimport { useState, useCallback, useMemo, useRef } from \"react\";\nimport type {\n CalendarViewMode,\n CalendarEntry,\n CalendarLabels,\n HolidayCalendar,\n} from \"../types\";\nimport {\n formatDate,\n todayStr,\n parseDate,\n addDays,\n generateId,\n getMonthDates,\n getWeekDates,\n LABELS_EN,\n LABELS_ZH,\n DEFAULT_COLOR_OPTIONS,\n} from \"../utils\";\n\n// ─── Options ──────────────────────────────────────────────────────\n\nexport interface UseCalendarStateOptions {\n entries?: CalendarEntry[];\n holidays?: HolidayCalendar;\n defaultView?: CalendarViewMode;\n defaultDate?: string;\n locale?: \"zh\" | \"en\";\n labels?: CalendarLabels;\n colorOptions?: string[];\n readOnly?: boolean;\n onEntryAdd?: (entry: CalendarEntry) => void;\n onEntryUpdate?: (entry: CalendarEntry) => void;\n onEntryDelete?: (entryId: string) => void;\n onViewChange?: (view: CalendarViewMode) => void;\n}\n\n// ─── Modal State ──────────────────────────────────────────────────\n\nexport interface EntryModalState {\n open: boolean;\n mode: \"add\" | \"edit\";\n entry: Partial<CalendarEntry>;\n}\n\n// ─── Hook ─────────────────────────────────────────────────────────\n\nexport function useCalendarState(options: UseCalendarStateOptions) {\n const {\n entries: externalEntries,\n holidays,\n defaultView = \"month\",\n defaultDate,\n locale = \"en\",\n labels: userLabels,\n colorOptions = DEFAULT_COLOR_OPTIONS,\n readOnly = false,\n onEntryAdd,\n onEntryUpdate,\n onEntryDelete,\n onViewChange,\n } = options;\n\n // ─── Labels ───────────────────────────────────────────────────\n\n const labels: Required<CalendarLabels> = useMemo(() => {\n const base = locale === \"zh\" ? LABELS_ZH : LABELS_EN;\n return { ...base, ...userLabels } as Required<CalendarLabels>;\n }, [locale, userLabels]);\n\n // ─── Current Date ─────────────────────────────────────────────\n\n const [initialDate] = useState(todayStr);\n const [currentDate, setCurrentDate] = useState<string>(\n () => defaultDate || initialDate,\n );\n\n const currentDateObj = useMemo(\n () => parseDate(currentDate) ?? parseDate(initialDate)!,\n [currentDate, initialDate],\n );\n\n // ─── View Mode ────────────────────────────────────────────────\n\n const [viewMode, setViewModeRaw] = useState<CalendarViewMode>(defaultView);\n\n const setViewMode = useCallback(\n (view: CalendarViewMode) => {\n setViewModeRaw(view);\n onViewChange?.(view);\n },\n [onViewChange],\n );\n\n // ─── Internal Entries ─────────────────────────────────────────\n\n // Controlled mode: external entries + at least one CRUD callback provided.\n // Otherwise entries prop is treated as initial value, managed internally.\n const isControlled =\n externalEntries !== undefined &&\n (onEntryAdd !== undefined ||\n onEntryUpdate !== undefined ||\n onEntryDelete !== undefined);\n\n const [internalEntries, setInternalEntries] = useState<CalendarEntry[]>(\n () => externalEntries || [],\n );\n\n // Sync internal state when external entries change in controlled mode\n // so the component always reflects the parent's truth.\n const prevExternalRef = useRef(externalEntries);\n if (isControlled && externalEntries !== prevExternalRef.current) {\n prevExternalRef.current = externalEntries;\n // will be picked up on next render via `entries` below\n }\n\n const entries = isControlled ? externalEntries! : internalEntries;\n\n // ─── Navigation ───────────────────────────────────────────────\n\n const goToToday = useCallback(() => {\n setCurrentDate(todayStr());\n }, []);\n\n const goToDate = useCallback((date: string) => {\n setCurrentDate(date);\n }, []);\n\n const goNext = useCallback(() => {\n const d = parseDate(currentDate);\n if (!d) return;\n\n switch (viewMode) {\n case \"month\": {\n const next = new Date(d.getFullYear(), d.getMonth() + 1, 1);\n setCurrentDate(formatDate(next));\n break;\n }\n case \"week\": {\n const next = addDays(d, 7);\n setCurrentDate(formatDate(next));\n break;\n }\n case \"day\": {\n const next = addDays(d, 1);\n setCurrentDate(formatDate(next));\n break;\n }\n }\n }, [currentDate, viewMode]);\n\n const goPrev = useCallback(() => {\n const d = parseDate(currentDate);\n if (!d) return;\n\n switch (viewMode) {\n case \"month\": {\n const prev = new Date(d.getFullYear(), d.getMonth() - 1, 1);\n setCurrentDate(formatDate(prev));\n break;\n }\n case \"week\": {\n const prev = addDays(d, -7);\n setCurrentDate(formatDate(prev));\n break;\n }\n case \"day\": {\n const prev = addDays(d, -1);\n setCurrentDate(formatDate(prev));\n break;\n }\n }\n }, [currentDate, viewMode]);\n\n // ─── Title Display ────────────────────────────────────────────\n\n const title = useMemo(() => {\n const d = currentDateObj;\n const monthName = labels.monthNames![d.getMonth()];\n const year = d.getFullYear();\n\n switch (viewMode) {\n case \"month\":\n return `${monthName} ${year}`;\n case \"week\": {\n const weekDates = getWeekDates(d);\n const start = weekDates[0];\n const end = weekDates[6];\n if (start.getMonth() === end.getMonth()) {\n return `${labels.monthNames![start.getMonth()]} ${start.getDate()} - ${end.getDate()}, ${year}`;\n }\n return `${labels.monthNames![start.getMonth()]} ${start.getDate()} - ${labels.monthNames![end.getMonth()]} ${end.getDate()}, ${year}`;\n }\n case \"day\":\n return `${monthName} ${d.getDate()}, ${year}`;\n }\n }, [currentDateObj, viewMode, labels]);\n\n // ─── Grid Dates ───────────────────────────────────────────────\n\n const monthDates = useMemo(() => {\n const d = currentDateObj;\n return getMonthDates(d.getFullYear(), d.getMonth() + 1);\n }, [currentDateObj]);\n\n const weekDates = useMemo(() => {\n return getWeekDates(currentDateObj);\n }, [currentDateObj]);\n\n // ─── Entry CRUD ───────────────────────────────────────────────\n\n const addEntry = useCallback(\n (entry: Omit<CalendarEntry, \"id\"> & { id?: string }) => {\n const newEntry = {\n ...entry,\n id: entry.id || generateId(),\n } as CalendarEntry;\n\n if (!isControlled) {\n setInternalEntries((prev) => [...prev, newEntry]);\n }\n onEntryAdd?.(newEntry);\n return newEntry;\n },\n [isControlled, onEntryAdd],\n );\n\n const updateEntry = useCallback(\n (entry: CalendarEntry) => {\n if (!isControlled) {\n setInternalEntries((prev) =>\n prev.map((e) => (e.id === entry.id ? entry : e)),\n );\n }\n onEntryUpdate?.(entry);\n },\n [isControlled, onEntryUpdate],\n );\n\n const deleteEntry = useCallback(\n (entryId: string) => {\n if (!isControlled) {\n setInternalEntries((prev) => prev.filter((e) => e.id !== entryId));\n }\n onEntryDelete?.(entryId);\n },\n [isControlled, onEntryDelete],\n );\n\n // ─── Entry Modal State ────────────────────────────────────────\n\n const [modalState, setModalState] = useState<EntryModalState>({\n open: false,\n mode: \"add\",\n entry: {},\n });\n\n const openAddModal = useCallback(\n (date?: string, startTime?: string) => {\n if (readOnly) return;\n setModalState({\n open: true,\n mode: \"add\",\n entry: {\n date: date || currentDate,\n startTime: startTime || undefined,\n endTime: startTime\n ? (() => {\n const [h, m] = startTime.split(\":\").map(Number);\n const endM = h * 60 + m + 60; // 1 hour later\n const eh = Math.min(Math.floor(endM / 60), 23);\n const em = endM % 60;\n return `${String(eh).padStart(2, \"0\")}:${String(em).padStart(2, \"0\")}`;\n })()\n : undefined,\n allDay: !startTime,\n color: colorOptions[0],\n },\n });\n },\n [readOnly, currentDate, colorOptions],\n );\n\n const openEditModal = useCallback(\n (entry: CalendarEntry) => {\n if (readOnly) return;\n setModalState({\n open: true,\n mode: \"edit\",\n entry: { ...entry },\n });\n },\n [readOnly],\n );\n\n const closeModal = useCallback(() => {\n setModalState((prev) => ({ ...prev, open: false }));\n }, []);\n\n const saveModal = useCallback(\n (entryData: Partial<CalendarEntry>) => {\n if (modalState.mode === \"add\") {\n const newEntry = addEntry({\n title: entryData.title || \"\",\n date: entryData.date || currentDate,\n startTime: entryData.allDay ? undefined : entryData.startTime,\n endTime: entryData.allDay ? undefined : entryData.endTime,\n color: entryData.color || colorOptions[0],\n description: entryData.description,\n allDay: entryData.allDay ?? true,\n });\n closeModal();\n return newEntry;\n } else {\n const updated: CalendarEntry = {\n ...(modalState.entry as CalendarEntry),\n ...entryData,\n id: modalState.entry.id!,\n startTime: entryData.allDay ? undefined : entryData.startTime,\n endTime: entryData.allDay ? undefined : entryData.endTime,\n };\n updateEntry(updated);\n closeModal();\n return updated;\n }\n },\n [modalState, addEntry, updateEntry, closeModal, currentDate, colorOptions],\n );\n\n const deleteModalEntry = useCallback(() => {\n if (modalState.entry.id) {\n deleteEntry(modalState.entry.id);\n }\n closeModal();\n }, [modalState.entry.id, deleteEntry, closeModal]);\n\n // ─── Helpers ──────────────────────────────────────────────────\n\n /** Get entries for a specific date (YYYY-MM-DD), sorted by time */\n const getEntriesForDate = useCallback(\n (date: string): CalendarEntry[] => {\n return entries\n .filter((e) => e.date === date)\n .sort((a, b) => {\n // All-day events come first\n const aAllDay = a.allDay || !a.startTime;\n const bAllDay = b.allDay || !b.startTime;\n if (aAllDay && !bAllDay) return -1;\n if (!aAllDay && bAllDay) return 1;\n // Both all-day: keep original order\n if (aAllDay && bAllDay) return 0;\n // Both timed: sort by startTime ascending\n return (a.startTime || \"\").localeCompare(b.startTime || \"\");\n });\n },\n [entries],\n );\n\n return {\n // State\n currentDate,\n currentDateObj,\n viewMode,\n entries,\n labels,\n colorOptions,\n holidays,\n readOnly,\n\n // Navigation\n goToToday,\n goToDate,\n goNext,\n goPrev,\n title,\n\n // View mode\n setViewMode,\n\n // Grid data\n monthDates,\n weekDates,\n\n // Entry CRUD\n addEntry,\n updateEntry,\n deleteEntry,\n getEntriesForDate,\n\n // Modal\n modalState,\n openAddModal,\n openEditModal,\n closeModal,\n saveModal,\n deleteModalEntry,\n };\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"utils.js","names":[],"sources":["../../../../src/components/pro/Calendar/utils.ts"],"sourcesContent":["/**\n * DishUI Calendar Component - Utility Functions\n *\n * Core date/time helpers, grid generation, and holiday resolution.\n */\n\nimport type { HolidayCalendar, HolidayEntry, CalendarLabels } from \"./types\";\n\n// ─── Default Labels ───────────────────────────────────────────────\n\nexport const LABELS_EN: Required<CalendarLabels> = {\n today: \"Today\",\n month: \"Month\",\n week: \"Week\",\n day: \"Day\",\n addEntry: \"Add\",\n deleteEntry: \"Delete\",\n editEntry: \"Edit\",\n allDay: \"All day\",\n noEntries: \"No entries\",\n confirmDelete: \"Are you sure you want to delete this entry?\",\n cancel: \"Cancel\",\n save: \"Save\",\n title: \"Title\",\n date: \"Date\",\n startTime: \"Start time\",\n endTime: \"End time\",\n description: \"Description\",\n color: \"Color\",\n weekDays: [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n monthNames: [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n ],\n};\n\nexport const LABELS_ZH: Required<CalendarLabels> = {\n today: \"今天\",\n month: \"月\",\n week: \"周\",\n day: \"日\",\n addEntry: \"添加\",\n deleteEntry: \"删除\",\n editEntry: \"编辑\",\n allDay: \"全天\",\n noEntries: \"暂无日程\",\n confirmDelete: \"确定要删除此日程吗?\",\n cancel: \"取消\",\n save: \"保存\",\n title: \"标题\",\n date: \"日期\",\n startTime: \"开始时间\",\n endTime: \"结束时间\",\n description: \"描述\",\n color: \"颜色\",\n weekDays: [\"日\", \"一\", \"二\", \"三\", \"四\", \"五\", \"六\"],\n monthNames: [\n \"一月\",\n \"二月\",\n \"三月\",\n \"四月\",\n \"五月\",\n \"六月\",\n \"七月\",\n \"八月\",\n \"九月\",\n \"十月\",\n \"十一月\",\n \"十二月\",\n ],\n};\n\n/** Default entry color palette */\nexport const DEFAULT_COLOR_OPTIONS = [\n \"#3b82f6\", // blue\n \"#ef4444\", // red\n \"#22c55e\", // green\n \"#f59e0b\", // amber\n \"#8b5cf6\", // violet\n \"#ec4899\", // pink\n \"#06b6d4\", // cyan\n \"#f97316\", // orange\n];\n\n// ─── Date Parsing & Formatting ────────────────────────────────────\n\n/**\n * Parse a YYYY-MM-DD string into a local Date at midnight.\n * Returns null for invalid strings.\n */\nexport function parseDate(value: string | undefined | null): Date | null {\n if (!value) return null;\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n const [y, m, d] = value.split(\"-\").map(Number);\n const date = new Date(y, m - 1, d);\n return isNaN(date.getTime()) ? null : date;\n }\n return null;\n}\n\n/**\n * Format a Date to YYYY-MM-DD string (local timezone).\n */\nexport function formatDate(date: Date): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, \"0\");\n const d = String(date.getDate()).padStart(2, \"0\");\n return `${y}-${m}-${d}`;\n}\n\n/**\n * Get today's date as a YYYY-MM-DD string.\n */\nexport function todayStr(): string {\n return formatDate(new Date());\n}\n\n// ─── Date Query Helpers ───────────────────────────────────────────\n\n/** Check if a Date is today */\nexport function isToday(date: Date): boolean {\n const now = new Date();\n return (\n date.getFullYear() === now.getFullYear() &&\n date.getMonth() === now.getMonth() &&\n date.getDate() === now.getDate()\n );\n}\n\n/** Check if two Dates represent the same calendar day */\nexport function isSameDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\n/** Check if a Date falls on Saturday or Sunday */\nexport function isWeekend(date: Date): boolean {\n const d = date.getDay();\n return d === 0 || d === 6;\n}\n\n/** Get the number of days in a given month (1-indexed month) */\nexport function getDaysInMonth(year: number, month: number): number {\n return new Date(year, month, 0).getDate();\n}\n\n/** Get the day-of-week (0 = Sun) of the 1st of a given month */\nexport function getFirstDayOfMonth(year: number, month: number): number {\n return new Date(year, month - 1, 1).getDay();\n}\n\n// ─── Holiday Helpers ──────────────────────────────────────────────\n\n/**\n * Find a holiday entry for a given date string.\n */\nexport function getHolidayEntry(\n dateStr: string,\n holidays?: HolidayCalendar,\n): HolidayEntry | undefined {\n if (!holidays) return undefined;\n return holidays.entries.find((e) => e.date === dateStr);\n}\n\n/**\n * Check if a date is a holiday (non-working day from the holiday calendar).\n * Returns true for declared holidays (not compensatory workdays).\n */\nexport function isHoliday(dateStr: string, holidays?: HolidayCalendar): boolean {\n if (!holidays) return false;\n const entry = holidays.entries.find((e) => e.date === dateStr);\n return !!entry && !entry.isWorkday;\n}\n\n/**\n * Check if a date is a non-working day considering the full holiday calendar.\n * Priority: holiday entries > weekend check.\n */\nexport function isNonWorkingDay(\n date: Date,\n holidays?: HolidayCalendar,\n): boolean {\n if (!holidays) return isWeekend(date);\n\n const dateStr = formatDate(date);\n const entry = holidays.entries.find((e) => e.date === dateStr);\n\n if (entry) {\n return !entry.isWorkday;\n }\n\n if (holidays.weekendsOff !== false) {\n return isWeekend(date);\n }\n\n return false;\n}\n\n// ─── Calendar Grid Generation ─────────────────────────────────────\n\n/**\n * Generate a 42-cell (6 rows × 7 columns) grid for a month view.\n * Each cell is a Date object. Includes leading/trailing days from\n * the previous/next month to fill the grid.\n */\nexport function getMonthDates(year: number, month: number): Date[] {\n const dates: Date[] = [];\n const firstDay = getFirstDayOfMonth(year, month);\n const daysInMonth = getDaysInMonth(year, month);\n\n // Previous month trailing days\n const prevMonth = month === 1 ? 12 : month - 1;\n const prevYear = month === 1 ? year - 1 : year;\n const daysInPrevMonth = getDaysInMonth(prevYear, prevMonth);\n\n for (let i = firstDay - 1; i >= 0; i--) {\n dates.push(new Date(prevYear, prevMonth - 1, daysInPrevMonth - i));\n }\n\n // Current month days\n for (let d = 1; d <= daysInMonth; d++) {\n dates.push(new Date(year, month - 1, d));\n }\n\n // Next month leading days (fill to 42 cells)\n const nextMonth = month === 12 ? 1 : month + 1;\n const nextYear = month === 12 ? year + 1 : year;\n let nextDay = 1;\n while (dates.length < 42) {\n dates.push(new Date(nextYear, nextMonth - 1, nextDay));\n nextDay++;\n }\n\n return dates;\n}\n\n/**\n * Generate the 7 dates for a week containing the given date.\n * Week starts on Sunday.\n */\nexport function getWeekDates(date: Date): Date[] {\n const dates: Date[] = [];\n const dayOfWeek = date.getDay(); // 0 = Sun\n const start = new Date(date);\n start.setDate(start.getDate() - dayOfWeek);\n start.setHours(0, 0, 0, 0);\n\n for (let i = 0; i < 7; i++) {\n const d = new Date(start);\n d.setDate(d.getDate() + i);\n dates.push(d);\n }\n\n return dates;\n}\n\n// ─── Time Utilities ───────────────────────────────────────────────\n\n/**\n * Format minutes since midnight to HH:mm string.\n */\nexport function minutesToTime(minutes: number): string {\n const h = Math.floor(minutes / 60);\n const m = minutes % 60;\n return `${String(h).padStart(2, \"0\")}:${String(m).padStart(2, \"0\")}`;\n}\n\n/**\n * Parse an HH:mm string to minutes since midnight.\n * Returns 0 for invalid input.\n */\nexport function timeToMinutes(time: string | undefined): number {\n if (!time) return 0;\n const parts = time.split(\":\");\n if (parts.length < 2) return 0;\n const h = parseInt(parts[0], 10);\n const m = parseInt(parts[1], 10);\n if (isNaN(h) || isNaN(m)) return 0;\n return h * 60 + m;\n}\n\n/**\n * Format an HH:mm string for display (identity for now, but could localize).\n */\nexport function formatTime(time: string): string {\n return time;\n}\n\n/**\n * Parse an HH:mm string — validates format, returns the string or empty.\n */\nexport function parseTime(time: string | undefined): string {\n if (!time) return \"\";\n if (/^\\d{2}:\\d{2}$/.test(time)) return time;\n return \"\";\n}\n\n/**\n * Generate an array of time slot labels from 00:00 to 23:30 (30-minute intervals).\n * Returns 48 items.\n */\nexport function generateTimeSlots(): string[] {\n const slots: string[] = [];\n for (let h = 0; h < 24; h++) {\n slots.push(`${String(h).padStart(2, \"0\")}:00`);\n slots.push(`${String(h).padStart(2, \"0\")}:30`);\n }\n return slots;\n}\n\n/** Cached time slots — always 48 items (00:00 through 23:30) */\nexport const TIME_SLOTS = generateTimeSlots();\n\n/** Height of each 30-minute time slot in pixels */\nexport const SLOT_HEIGHT = 28;\n\n/** Total height of the time grid (48 slots × SLOT_HEIGHT) */\nexport const TIME_GRID_HEIGHT = TIME_SLOTS.length * SLOT_HEIGHT;\n\n// ─── Misc Helpers ─────────────────────────────────────────────────\n\n/** Add N days to a date (returns new Date) */\nexport function addDays(date: Date, days: number): Date {\n const result = new Date(date);\n result.setDate(result.getDate() + days);\n return result;\n}\n\n/** Generate a simple unique ID */\nexport function generateId(): string {\n return `cal-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * Clamp a number between min and max.\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"utils.js","names":["LABELS_EN","LABELS_ZH","DEFAULT_COLOR_OPTIONS","parseDate","value","y","m","d","date","formatDate","todayStr","isToday","now","isSameDay","a","b","isWeekend","getDaysInMonth","year","month","getFirstDayOfMonth","getHolidayEntry","holidays","e","dateStr","isHoliday","entry","isNonWorkingDay","getMonthDates","dates","firstDay","daysInMonth","prevMonth","prevYear","daysInPrevMonth","i","nextMonth","nextYear","nextDay","getWeekDates","dayOfWeek","start","minutesToTime","h","minutes","timeToMinutes","time","parts","formatTime","parseTime","generateTimeSlots","slots","TIME_SLOTS","SLOT_HEIGHT","TIME_GRID_HEIGHT","addDays","result","days","generateId","clamp","min","max"],"sources":["../../../../src/components/pro/Calendar/utils.ts"],"sourcesContent":["/**\n * DishUI Calendar Component - Utility Functions\n *\n * Core date/time helpers, grid generation, and holiday resolution.\n */\n\nimport type { HolidayCalendar, HolidayEntry, CalendarLabels } from \"./types\";\n\n// ─── Default Labels ───────────────────────────────────────────────\n\nexport const LABELS_EN: Required<CalendarLabels> = {\n today: \"Today\",\n month: \"Month\",\n week: \"Week\",\n day: \"Day\",\n addEntry: \"Add\",\n deleteEntry: \"Delete\",\n editEntry: \"Edit\",\n allDay: \"All day\",\n noEntries: \"No entries\",\n confirmDelete: \"Are you sure you want to delete this entry?\",\n cancel: \"Cancel\",\n save: \"Save\",\n title: \"Title\",\n date: \"Date\",\n startTime: \"Start time\",\n endTime: \"End time\",\n description: \"Description\",\n color: \"Color\",\n weekDays: [\"Sun\", \"Mon\", \"Tue\", \"Wed\", \"Thu\", \"Fri\", \"Sat\"],\n monthNames: [\n \"January\",\n \"February\",\n \"March\",\n \"April\",\n \"May\",\n \"June\",\n \"July\",\n \"August\",\n \"September\",\n \"October\",\n \"November\",\n \"December\",\n ],\n};\n\nexport const LABELS_ZH: Required<CalendarLabels> = {\n today: \"今天\",\n month: \"月\",\n week: \"周\",\n day: \"日\",\n addEntry: \"添加\",\n deleteEntry: \"删除\",\n editEntry: \"编辑\",\n allDay: \"全天\",\n noEntries: \"暂无日程\",\n confirmDelete: \"确定要删除此日程吗?\",\n cancel: \"取消\",\n save: \"保存\",\n title: \"标题\",\n date: \"日期\",\n startTime: \"开始时间\",\n endTime: \"结束时间\",\n description: \"描述\",\n color: \"颜色\",\n weekDays: [\"日\", \"一\", \"二\", \"三\", \"四\", \"五\", \"六\"],\n monthNames: [\n \"一月\",\n \"二月\",\n \"三月\",\n \"四月\",\n \"五月\",\n \"六月\",\n \"七月\",\n \"八月\",\n \"九月\",\n \"十月\",\n \"十一月\",\n \"十二月\",\n ],\n};\n\n/** Default entry color palette */\nexport const DEFAULT_COLOR_OPTIONS = [\n \"#3b82f6\", // blue\n \"#ef4444\", // red\n \"#22c55e\", // green\n \"#f59e0b\", // amber\n \"#8b5cf6\", // violet\n \"#ec4899\", // pink\n \"#06b6d4\", // cyan\n \"#f97316\", // orange\n];\n\n// ─── Date Parsing & Formatting ────────────────────────────────────\n\n/**\n * Parse a YYYY-MM-DD string into a local Date at midnight.\n * Returns null for invalid strings.\n */\nexport function parseDate(value: string | undefined | null): Date | null {\n if (!value) return null;\n if (/^\\d{4}-\\d{2}-\\d{2}$/.test(value)) {\n const [y, m, d] = value.split(\"-\").map(Number);\n const date = new Date(y, m - 1, d);\n return isNaN(date.getTime()) ? null : date;\n }\n return null;\n}\n\n/**\n * Format a Date to YYYY-MM-DD string (local timezone).\n */\nexport function formatDate(date: Date): string {\n const y = date.getFullYear();\n const m = String(date.getMonth() + 1).padStart(2, \"0\");\n const d = String(date.getDate()).padStart(2, \"0\");\n return `${y}-${m}-${d}`;\n}\n\n/**\n * Get today's date as a YYYY-MM-DD string.\n */\nexport function todayStr(): string {\n return formatDate(new Date());\n}\n\n// ─── Date Query Helpers ───────────────────────────────────────────\n\n/** Check if a Date is today */\nexport function isToday(date: Date): boolean {\n const now = new Date();\n return (\n date.getFullYear() === now.getFullYear() &&\n date.getMonth() === now.getMonth() &&\n date.getDate() === now.getDate()\n );\n}\n\n/** Check if two Dates represent the same calendar day */\nexport function isSameDay(a: Date, b: Date): boolean {\n return (\n a.getFullYear() === b.getFullYear() &&\n a.getMonth() === b.getMonth() &&\n a.getDate() === b.getDate()\n );\n}\n\n/** Check if a Date falls on Saturday or Sunday */\nexport function isWeekend(date: Date): boolean {\n const d = date.getDay();\n return d === 0 || d === 6;\n}\n\n/** Get the number of days in a given month (1-indexed month) */\nexport function getDaysInMonth(year: number, month: number): number {\n return new Date(year, month, 0).getDate();\n}\n\n/** Get the day-of-week (0 = Sun) of the 1st of a given month */\nexport function getFirstDayOfMonth(year: number, month: number): number {\n return new Date(year, month - 1, 1).getDay();\n}\n\n// ─── Holiday Helpers ──────────────────────────────────────────────\n\n/**\n * Find a holiday entry for a given date string.\n */\nexport function getHolidayEntry(\n dateStr: string,\n holidays?: HolidayCalendar,\n): HolidayEntry | undefined {\n if (!holidays) return undefined;\n return holidays.entries.find((e) => e.date === dateStr);\n}\n\n/**\n * Check if a date is a holiday (non-working day from the holiday calendar).\n * Returns true for declared holidays (not compensatory workdays).\n */\nexport function isHoliday(dateStr: string, holidays?: HolidayCalendar): boolean {\n if (!holidays) return false;\n const entry = holidays.entries.find((e) => e.date === dateStr);\n return !!entry && !entry.isWorkday;\n}\n\n/**\n * Check if a date is a non-working day considering the full holiday calendar.\n * Priority: holiday entries > weekend check.\n */\nexport function isNonWorkingDay(\n date: Date,\n holidays?: HolidayCalendar,\n): boolean {\n if (!holidays) return isWeekend(date);\n\n const dateStr = formatDate(date);\n const entry = holidays.entries.find((e) => e.date === dateStr);\n\n if (entry) {\n return !entry.isWorkday;\n }\n\n if (holidays.weekendsOff !== false) {\n return isWeekend(date);\n }\n\n return false;\n}\n\n// ─── Calendar Grid Generation ─────────────────────────────────────\n\n/**\n * Generate a 42-cell (6 rows × 7 columns) grid for a month view.\n * Each cell is a Date object. Includes leading/trailing days from\n * the previous/next month to fill the grid.\n */\nexport function getMonthDates(year: number, month: number): Date[] {\n const dates: Date[] = [];\n const firstDay = getFirstDayOfMonth(year, month);\n const daysInMonth = getDaysInMonth(year, month);\n\n // Previous month trailing days\n const prevMonth = month === 1 ? 12 : month - 1;\n const prevYear = month === 1 ? year - 1 : year;\n const daysInPrevMonth = getDaysInMonth(prevYear, prevMonth);\n\n for (let i = firstDay - 1; i >= 0; i--) {\n dates.push(new Date(prevYear, prevMonth - 1, daysInPrevMonth - i));\n }\n\n // Current month days\n for (let d = 1; d <= daysInMonth; d++) {\n dates.push(new Date(year, month - 1, d));\n }\n\n // Next month leading days (fill to 42 cells)\n const nextMonth = month === 12 ? 1 : month + 1;\n const nextYear = month === 12 ? year + 1 : year;\n let nextDay = 1;\n while (dates.length < 42) {\n dates.push(new Date(nextYear, nextMonth - 1, nextDay));\n nextDay++;\n }\n\n return dates;\n}\n\n/**\n * Generate the 7 dates for a week containing the given date.\n * Week starts on Sunday.\n */\nexport function getWeekDates(date: Date): Date[] {\n const dates: Date[] = [];\n const dayOfWeek = date.getDay(); // 0 = Sun\n const start = new Date(date);\n start.setDate(start.getDate() - dayOfWeek);\n start.setHours(0, 0, 0, 0);\n\n for (let i = 0; i < 7; i++) {\n const d = new Date(start);\n d.setDate(d.getDate() + i);\n dates.push(d);\n }\n\n return dates;\n}\n\n// ─── Time Utilities ───────────────────────────────────────────────\n\n/**\n * Format minutes since midnight to HH:mm string.\n */\nexport function minutesToTime(minutes: number): string {\n const h = Math.floor(minutes / 60);\n const m = minutes % 60;\n return `${String(h).padStart(2, \"0\")}:${String(m).padStart(2, \"0\")}`;\n}\n\n/**\n * Parse an HH:mm string to minutes since midnight.\n * Returns 0 for invalid input.\n */\nexport function timeToMinutes(time: string | undefined): number {\n if (!time) return 0;\n const parts = time.split(\":\");\n if (parts.length < 2) return 0;\n const h = parseInt(parts[0], 10);\n const m = parseInt(parts[1], 10);\n if (isNaN(h) || isNaN(m)) return 0;\n return h * 60 + m;\n}\n\n/**\n * Format an HH:mm string for display (identity for now, but could localize).\n */\nexport function formatTime(time: string): string {\n return time;\n}\n\n/**\n * Parse an HH:mm string — validates format, returns the string or empty.\n */\nexport function parseTime(time: string | undefined): string {\n if (!time) return \"\";\n if (/^\\d{2}:\\d{2}$/.test(time)) return time;\n return \"\";\n}\n\n/**\n * Generate an array of time slot labels from 00:00 to 23:30 (30-minute intervals).\n * Returns 48 items.\n */\nexport function generateTimeSlots(): string[] {\n const slots: string[] = [];\n for (let h = 0; h < 24; h++) {\n slots.push(`${String(h).padStart(2, \"0\")}:00`);\n slots.push(`${String(h).padStart(2, \"0\")}:30`);\n }\n return slots;\n}\n\n/** Cached time slots — always 48 items (00:00 through 23:30) */\nexport const TIME_SLOTS = generateTimeSlots();\n\n/** Height of each 30-minute time slot in pixels */\nexport const SLOT_HEIGHT = 28;\n\n/** Total height of the time grid (48 slots × SLOT_HEIGHT) */\nexport const TIME_GRID_HEIGHT = TIME_SLOTS.length * SLOT_HEIGHT;\n\n// ─── Misc Helpers ─────────────────────────────────────────────────\n\n/** Add N days to a date (returns new Date) */\nexport function addDays(date: Date, days: number): Date {\n const result = new Date(date);\n result.setDate(result.getDate() + days);\n return result;\n}\n\n/** Generate a simple unique ID */\nexport function generateId(): string {\n return `cal-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;\n}\n\n/**\n * Clamp a number between min and max.\n */\nexport function clamp(value: number, min: number, max: number): number {\n return Math.min(Math.max(value, min), max);\n}\n"],"mappings":"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"}
|