@k2b/ui 0.18.0 → 0.20.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -2
- package/dist/browser/actions/ContextMenu.js +3 -1
- package/dist/browser/actions/ContextMenu.js.map +1 -1
- package/dist/browser/actions/Dropdown.js +91 -46
- package/dist/browser/actions/Dropdown.js.map +1 -1
- package/dist/browser/actions/FilterChip.js +4 -1
- package/dist/browser/actions/FilterChip.js.map +1 -1
- package/dist/browser/actions/GestureMenu.js +467 -0
- package/dist/browser/actions/GestureMenu.js.map +1 -0
- package/dist/browser/actions/index.js +1 -0
- package/dist/browser/actions/index.js.map +1 -1
- package/dist/browser/chat/ChatComposer.js +1255 -534
- package/dist/browser/chat/ChatComposer.js.map +1 -1
- package/dist/browser/chat/ChatPrimitives.js +9 -3
- package/dist/browser/chat/ChatPrimitives.js.map +1 -1
- package/dist/browser/chat/ChatTimeline.js +19 -1
- package/dist/browser/chat/ChatTimeline.js.map +1 -1
- package/dist/browser/chat/chat-behavior.js +9 -0
- package/dist/browser/chat/chat-behavior.js.map +1 -1
- package/dist/browser/chat/index.js.map +1 -1
- package/dist/browser/chat/types.js.map +1 -1
- package/dist/browser/content/Calendar.js +579 -533
- package/dist/browser/content/Calendar.js.map +1 -1
- package/dist/browser/content/Lightbox.js +76 -40
- package/dist/browser/content/Lightbox.js.map +1 -1
- package/dist/browser/content/MessageRow.js +2 -2
- package/dist/browser/content/MessageRow.js.map +1 -1
- package/dist/browser/content/PdfPreview.js +146 -79
- package/dist/browser/content/PdfPreview.js.map +1 -1
- package/dist/browser/content/Timeline.js +1038 -0
- package/dist/browser/content/Timeline.js.map +1 -0
- package/dist/browser/content/VideoPlayer.js +29 -9
- package/dist/browser/content/VideoPlayer.js.map +1 -1
- package/dist/browser/content/chart-inspection.js +7 -2
- package/dist/browser/content/chart-inspection.js.map +1 -1
- package/dist/browser/content/index.js +1 -0
- package/dist/browser/content/index.js.map +1 -1
- package/dist/browser/content/timeline-model.js +579 -0
- package/dist/browser/content/timeline-model.js.map +1 -0
- package/dist/browser/feedback/dialog-core.js +26 -14
- package/dist/browser/feedback/dialog-core.js.map +1 -1
- package/dist/browser/inputs/ChoiceChips.js +66 -0
- package/dist/browser/inputs/ChoiceChips.js.map +1 -0
- package/dist/browser/inputs/ChoiceGroups.js +53 -12
- package/dist/browser/inputs/ChoiceGroups.js.map +1 -1
- package/dist/browser/inputs/FileInputs.js +48 -33
- package/dist/browser/inputs/FileInputs.js.map +1 -1
- package/dist/browser/inputs/SignatureInput.js +558 -0
- package/dist/browser/inputs/SignatureInput.js.map +1 -0
- package/dist/browser/inputs/TextInput.js +13 -1
- package/dist/browser/inputs/TextInput.js.map +1 -1
- package/dist/browser/inputs/emoji/EmojiPicker.js +841 -0
- package/dist/browser/inputs/emoji/EmojiPicker.js.map +1 -0
- package/dist/browser/inputs/emoji/emoji-data.js +1903 -0
- package/dist/browser/inputs/emoji/emoji-data.js.map +1 -0
- package/dist/browser/inputs/emoji/emoji-index.js +209 -0
- package/dist/browser/inputs/emoji/emoji-index.js.map +1 -0
- package/dist/browser/inputs/emoji/emoji-messages.js +64 -0
- package/dist/browser/inputs/emoji/emoji-messages.js.map +1 -0
- package/dist/browser/inputs/index.js +5 -0
- package/dist/browser/inputs/index.js.map +1 -1
- package/dist/browser/inputs/markdown/actions.js +6 -2
- package/dist/browser/inputs/markdown/actions.js.map +1 -1
- package/dist/browser/inputs/markdown/code-zone.js +38 -2
- package/dist/browser/inputs/markdown/code-zone.js.map +1 -1
- package/dist/browser/inputs/signature-messages.js +39 -0
- package/dist/browser/inputs/signature-messages.js.map +1 -0
- package/dist/browser/inputs/signature.js +208 -0
- package/dist/browser/inputs/signature.js.map +1 -0
- package/dist/browser/internal/render-error.js +49 -0
- package/dist/browser/internal/render-error.js.map +1 -0
- package/dist/browser/intl/messages.js +102 -0
- package/dist/browser/intl/messages.js.map +1 -1
- package/dist/browser/layout/AppWorkspace.js +321 -222
- package/dist/browser/layout/AppWorkspace.js.map +1 -1
- package/dist/browser/layout/FloatingWindow.js +4 -1
- package/dist/browser/layout/FloatingWindow.js.map +1 -1
- package/dist/browser/layout/SidebarItemPreview.js +5 -1
- package/dist/browser/layout/SidebarItemPreview.js.map +1 -1
- package/dist/browser/layout/index.js.map +1 -1
- package/dist/browser/surfaces/ResourceCard.js +212 -0
- package/dist/browser/surfaces/ResourceCard.js.map +1 -0
- package/dist/browser/surfaces/index.js +1 -0
- package/dist/browser/surfaces/index.js.map +1 -1
- package/dist/licenses/emojibase-data.txt +21 -0
- package/dist/ssr/actions/ContextMenu.js +3 -1
- package/dist/ssr/actions/ContextMenu.js.map +1 -1
- package/dist/ssr/actions/Dropdown.js +49 -12
- package/dist/ssr/actions/Dropdown.js.map +1 -1
- package/dist/ssr/actions/FilterChip.js +4 -1
- package/dist/ssr/actions/FilterChip.js.map +1 -1
- package/dist/ssr/actions/GestureMenu.js +411 -0
- package/dist/ssr/actions/GestureMenu.js.map +1 -0
- package/dist/ssr/actions/index.js +1 -0
- package/dist/ssr/actions/index.js.map +1 -1
- package/dist/ssr/chat/ChatComposer.js +767 -214
- package/dist/ssr/chat/ChatComposer.js.map +1 -1
- package/dist/ssr/chat/ChatPrimitives.js +9 -3
- package/dist/ssr/chat/ChatPrimitives.js.map +1 -1
- package/dist/ssr/chat/ChatTimeline.js +19 -1
- package/dist/ssr/chat/ChatTimeline.js.map +1 -1
- package/dist/ssr/chat/chat-behavior.js +9 -0
- package/dist/ssr/chat/chat-behavior.js.map +1 -1
- package/dist/ssr/chat/index.js.map +1 -1
- package/dist/ssr/chat/types.js.map +1 -1
- package/dist/ssr/content/Calendar.js +183 -142
- package/dist/ssr/content/Calendar.js.map +1 -1
- package/dist/ssr/content/Lightbox.js +19 -5
- package/dist/ssr/content/Lightbox.js.map +1 -1
- package/dist/ssr/content/MessageRow.js +2 -2
- package/dist/ssr/content/MessageRow.js.map +1 -1
- package/dist/ssr/content/PdfPreview.js +77 -36
- package/dist/ssr/content/PdfPreview.js.map +1 -1
- package/dist/ssr/content/Timeline.js +804 -0
- package/dist/ssr/content/Timeline.js.map +1 -0
- package/dist/ssr/content/VideoPlayer.js +29 -9
- package/dist/ssr/content/VideoPlayer.js.map +1 -1
- package/dist/ssr/content/chart-inspection.js +7 -2
- package/dist/ssr/content/chart-inspection.js.map +1 -1
- package/dist/ssr/content/index.js +1 -0
- package/dist/ssr/content/index.js.map +1 -1
- package/dist/ssr/content/timeline-model.js +579 -0
- package/dist/ssr/content/timeline-model.js.map +1 -0
- package/dist/ssr/feedback/dialog-core.js +26 -14
- package/dist/ssr/feedback/dialog-core.js.map +1 -1
- package/dist/ssr/inputs/ChoiceChips.js +66 -0
- package/dist/ssr/inputs/ChoiceChips.js.map +1 -0
- package/dist/ssr/inputs/ChoiceGroups.js +12 -4
- package/dist/ssr/inputs/ChoiceGroups.js.map +1 -1
- package/dist/ssr/inputs/FileInputs.js +19 -4
- package/dist/ssr/inputs/FileInputs.js.map +1 -1
- package/dist/ssr/inputs/SignatureInput.js +404 -0
- package/dist/ssr/inputs/SignatureInput.js.map +1 -0
- package/dist/ssr/inputs/TextInput.js +13 -1
- package/dist/ssr/inputs/TextInput.js.map +1 -1
- package/dist/ssr/inputs/emoji/EmojiPicker.js +551 -0
- package/dist/ssr/inputs/emoji/EmojiPicker.js.map +1 -0
- package/dist/ssr/inputs/emoji/emoji-data.js +1903 -0
- package/dist/ssr/inputs/emoji/emoji-data.js.map +1 -0
- package/dist/ssr/inputs/emoji/emoji-index.js +209 -0
- package/dist/ssr/inputs/emoji/emoji-index.js.map +1 -0
- package/dist/ssr/inputs/emoji/emoji-messages.js +64 -0
- package/dist/ssr/inputs/emoji/emoji-messages.js.map +1 -0
- package/dist/ssr/inputs/index.js +5 -0
- package/dist/ssr/inputs/index.js.map +1 -1
- package/dist/ssr/inputs/markdown/actions.js +6 -2
- package/dist/ssr/inputs/markdown/actions.js.map +1 -1
- package/dist/ssr/inputs/markdown/code-zone.js +38 -2
- package/dist/ssr/inputs/markdown/code-zone.js.map +1 -1
- package/dist/ssr/inputs/signature-messages.js +39 -0
- package/dist/ssr/inputs/signature-messages.js.map +1 -0
- package/dist/ssr/inputs/signature.js +208 -0
- package/dist/ssr/inputs/signature.js.map +1 -0
- package/dist/ssr/internal/render-error.js +49 -0
- package/dist/ssr/internal/render-error.js.map +1 -0
- package/dist/ssr/intl/messages.js +102 -0
- package/dist/ssr/intl/messages.js.map +1 -1
- package/dist/ssr/layout/AppWorkspace.js +112 -41
- package/dist/ssr/layout/AppWorkspace.js.map +1 -1
- package/dist/ssr/layout/FloatingWindow.js +4 -1
- package/dist/ssr/layout/FloatingWindow.js.map +1 -1
- package/dist/ssr/layout/SidebarItemPreview.js +5 -1
- package/dist/ssr/layout/SidebarItemPreview.js.map +1 -1
- package/dist/ssr/layout/index.js.map +1 -1
- package/dist/ssr/surfaces/ResourceCard.js +115 -0
- package/dist/ssr/surfaces/ResourceCard.js.map +1 -0
- package/dist/ssr/surfaces/index.js +1 -0
- package/dist/ssr/surfaces/index.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/actions/Dropdown.d.ts +13 -1
- package/dist/types/actions/FilterChip.d.ts +5 -0
- package/dist/types/actions/GestureMenu.d.ts +35 -0
- package/dist/types/actions/index.d.ts +2 -0
- package/dist/types/chat/ChatComposer.d.ts +27 -1
- package/dist/types/chat/chat-behavior.d.ts +15 -0
- package/dist/types/chat/index.d.ts +1 -1
- package/dist/types/chat/types.d.ts +34 -0
- package/dist/types/content/Calendar.d.ts +28 -8
- package/dist/types/content/Lightbox.d.ts +8 -0
- package/dist/types/content/Timeline.d.ts +49 -0
- package/dist/types/content/index.d.ts +4 -2
- package/dist/types/content/timeline-model.d.ts +148 -0
- package/dist/types/feedback/dialog-core.d.ts +1 -1
- package/dist/types/inputs/ChoiceChips.d.ts +20 -0
- package/dist/types/inputs/ChoiceGroups.d.ts +11 -2
- package/dist/types/inputs/FileInputs.d.ts +6 -0
- package/dist/types/inputs/SignatureInput.d.ts +22 -0
- package/dist/types/inputs/emoji/EmojiPicker.d.ts +42 -0
- package/dist/types/inputs/emoji/emoji-data.d.ts +2 -0
- package/dist/types/inputs/emoji/emoji-index.d.ts +109 -0
- package/dist/types/inputs/emoji/emoji-messages.d.ts +30 -0
- package/dist/types/inputs/index.d.ts +10 -0
- package/dist/types/inputs/markdown/actions.d.ts +2 -0
- package/dist/types/inputs/markdown/code-zone.d.ts +7 -0
- package/dist/types/inputs/signature-messages.d.ts +11 -0
- package/dist/types/inputs/signature.d.ts +72 -0
- package/dist/types/internal/render-error.d.ts +13 -0
- package/dist/types/intl/messages.d.ts +51 -0
- package/dist/types/layout/AppWorkspace.d.ts +17 -0
- package/dist/types/layout/SidebarItemPreview.d.ts +2 -0
- package/dist/types/layout/index.d.ts +1 -1
- package/dist/types/surfaces/ResourceCard.d.ts +31 -0
- package/dist/types/surfaces/index.d.ts +2 -0
- package/package.json +6 -5
|
@@ -25,6 +25,7 @@ export { Pagination } from "./Pagination";
|
|
|
25
25
|
export { default as PdfPreview } from "./PdfPreview";
|
|
26
26
|
export { default as RangePicker } from "./RangePicker";
|
|
27
27
|
export { default as StructuredDataPreview, isStructuredDataValue } from "./StructuredDataPreview";
|
|
28
|
+
export { default as Timeline } from "./Timeline";
|
|
28
29
|
export { VideoPlayer } from "./VideoPlayer";
|
|
29
30
|
export { VirtualFeed } from "./VirtualFeed";
|
|
30
31
|
export { ZoomPanViewport } from "./ZoomPanViewport";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["content/index.ts"],"sourcesContent":["export type {\n CalendarAttendee,\n CalendarDayBadge,\n CalendarEvent,\n CalendarEventColor,\n CalendarEventRenderContext,\n CalendarEventTimeChange,\n CalendarLabels,\n CalendarProps,\n CalendarRecurrence,\n CalendarResource,\n CalendarView,\n} from \"./Calendar\";\nexport { default as Calendar } from \"./Calendar\";\n\nexport type { ChartKind, ChartLabels, ChartProps, ChartRenderOptions } from \"./Chart\";\nexport { default as Chart } from \"./Chart\";\nexport type { ChartExplorerColumn, ChartExplorerProps } from \"./ChartExplorer\";\nexport { ChartExplorer } from \"./ChartExplorer\";\nexport type { ChartExplorerControlsProps } from \"./ChartExplorerControls\";\nexport { ChartExplorerControls } from \"./ChartExplorerControls\";\nexport { ChartFilterControls, type ChartFilterControlsProps } from \"./ChartFilterControls\";\nexport { ChartSnapshotView } from \"./ChartSnapshotView\";\nexport type { CodeDisplayLanguage, CodeDisplayProps } from \"./CodeDisplay\";\nexport { default as CodeDisplay } from \"./CodeDisplay\";\nexport { type ChartCursor, createChartCursor } from \"./chart-cursor\";\nexport type {\n ChartExplorerCharts,\n ChartExplorerController,\n ChartExplorerData,\n ChartExplorerOptions,\n ChartExplorerRequest,\n ChartExplorerRow,\n ChartExplorerSnapshot,\n} from \"./chart-explorer\";\nexport { createChartExplorer } from \"./chart-explorer\";\nexport type { ChartDatumRef, ChartSelection, ChartTooltip, ChartTooltipFormatter } from \"./chart-inspection\";\nexport type { ChartSnapshot } from \"./chart-snapshot\";\nexport { prepareChartSnapshot } from \"./chart-snapshot\";\nexport type {\n StateTimelineChartOptions,\n StateTimelineDomain,\n StateTimelineInterval,\n StateTimelineRow,\n StateTimelineState,\n} from \"./chart-state-timeline\";\nexport { type CollectionSelection, createCollectionSelection, type SelectionModifiers } from \"./collection-selection\";\nexport type {\n DataTableColumn,\n DataTableControlsProps,\n DataTableFooter,\n DataTableHeaderProps,\n DataTablePanelFooterProps,\n DataTablePanelProps,\n DataTableProps,\n DataTableRenderCell,\n DataTableRenderHeader,\n DataTableSort,\n} from \"./DataTable\";\nexport { default as DataTable } from \"./DataTable\";\nexport type { DocCodeHighlighter, DocCodeProps, DocConcept, DocNoteVariant, DocRow } from \"./Docs\";\nexport { DocCode, DocConceptGrid, DocInlineCode, DocLead, DocNote, DocPage, DocRows, DocSection } from \"./Docs\";\nexport { FileBrowserPanel, type FileBrowserPanelProps, type FileSource, openFileBrowser } from \"./FileBrowser\";\nexport { FileGrid, type FileGridProps } from \"./FileGrid\";\nexport { default as FileTree, type FileTreeActions, type FileTreeEntry, type FileTreeProps } from \"./FileTree\";\nexport {\n canPreviewFile,\n default as FileView,\n type FileViewContent,\n type FileViewFile,\n type FileViewPreviewKind,\n type FileViewProps,\n type FileViewRenderer,\n type FileViewRendererProps,\n formatFileViewSize,\n getFileViewPreviewKind,\n} from \"./FileView\";\nexport type { LightboxImage } from \"./Lightbox\";\nexport { default as Lightbox } from \"./Lightbox\";\nexport type { LogTableEntry } from \"./LogEntriesTable\";\nexport { default as LogEntriesTable } from \"./LogEntriesTable\";\nexport { default as MarkdownView, type MarkdownRenderOptions, type MarkdownViewProps, renderSafeMarkdown } from \"./MarkdownView\";\nexport type {\n MessageGroupEntry,\n MessageRowAction,\n MessageRowAttachment,\n MessageRowAuthor,\n MessageRowFile,\n MessageRowMedia,\n MessageRowProgress,\n MessageRowProps,\n MessageRowQuote,\n MessageRowReaction,\n MessageRowStatus,\n MessageRowThread,\n MessageSystemRowProps,\n} from \"./MessageRow\";\nexport { MessageRow, MessageSystemRow, startsMessageGroup } from \"./MessageRow\";\nexport {\n type MarkdownInfoBlock,\n type MarkdownInfoBlockRenderInput,\n type MarkdownInfoBlockType,\n markdownInfoBlocks,\n renderMarkdownInfoBlock,\n scanMarkdownInfoBlock,\n} from \"./markdown-info-blocks\";\nexport {\n type MarkdownLinkInput,\n type MarkdownReference,\n type MarkdownReferenceKind,\n type MarkdownReferenceType,\n markdownLinkReference,\n markdownReferenceIcon,\n markdownReferenceType,\n markdownReferenceTypeLabel,\n markStandaloneLinks,\n renderMarkdownLink,\n} from \"./markdown-links\";\nexport { Pagination, type PaginationProps } from \"./Pagination\";\nexport type { PdfPreviewProps, PdfPreviewRequest } from \"./PdfPreview\";\nexport { default as PdfPreview } from \"./PdfPreview\";\nexport type { RangeOption, RangePickerProps } from \"./RangePicker\";\nexport { default as RangePicker } from \"./RangePicker\";\nexport type { StructuredDataPreviewMode, StructuredDataPreviewProps, StructuredDataValue } from \"./StructuredDataPreview\";\nexport { default as StructuredDataPreview, isStructuredDataValue } from \"./StructuredDataPreview\";\nexport { VideoPlayer, type VideoPlayerProps } from \"./VideoPlayer\";\nexport type { VirtualFeedController, VirtualFeedProps, VirtualFeedScrollOptions } from \"./VirtualFeed\";\nexport { VirtualFeed } from \"./VirtualFeed\";\nexport type { ZoomPanAction, ZoomPanFullscreen, ZoomPanViewportProps } from \"./ZoomPanViewport\";\nexport { ZoomPanViewport } from \"./ZoomPanViewport\";\n"],"mappings":"
|
|
1
|
+
{"version":3,"names":[],"sources":["content/index.ts"],"sourcesContent":["export type {\n CalendarAttendee,\n CalendarCustomView,\n CalendarDayBadge,\n CalendarEvent,\n CalendarEventColor,\n CalendarEventRenderContext,\n CalendarEventTimeChange,\n CalendarLabels,\n CalendarProps,\n CalendarRecurrence,\n CalendarResource,\n CalendarView,\n} from \"./Calendar\";\nexport { default as Calendar } from \"./Calendar\";\n\nexport type { ChartKind, ChartLabels, ChartProps, ChartRenderOptions } from \"./Chart\";\nexport { default as Chart } from \"./Chart\";\nexport type { ChartExplorerColumn, ChartExplorerProps } from \"./ChartExplorer\";\nexport { ChartExplorer } from \"./ChartExplorer\";\nexport type { ChartExplorerControlsProps } from \"./ChartExplorerControls\";\nexport { ChartExplorerControls } from \"./ChartExplorerControls\";\nexport { ChartFilterControls, type ChartFilterControlsProps } from \"./ChartFilterControls\";\nexport { ChartSnapshotView } from \"./ChartSnapshotView\";\nexport type { CodeDisplayLanguage, CodeDisplayProps } from \"./CodeDisplay\";\nexport { default as CodeDisplay } from \"./CodeDisplay\";\nexport { type ChartCursor, createChartCursor } from \"./chart-cursor\";\nexport type {\n ChartExplorerCharts,\n ChartExplorerController,\n ChartExplorerData,\n ChartExplorerOptions,\n ChartExplorerRequest,\n ChartExplorerRow,\n ChartExplorerSnapshot,\n} from \"./chart-explorer\";\nexport { createChartExplorer } from \"./chart-explorer\";\nexport type { ChartDatumRef, ChartSelection, ChartTooltip, ChartTooltipFormatter } from \"./chart-inspection\";\nexport type { ChartSnapshot } from \"./chart-snapshot\";\nexport { prepareChartSnapshot } from \"./chart-snapshot\";\nexport type {\n StateTimelineChartOptions,\n StateTimelineDomain,\n StateTimelineInterval,\n StateTimelineRow,\n StateTimelineState,\n} from \"./chart-state-timeline\";\nexport { type CollectionSelection, createCollectionSelection, type SelectionModifiers } from \"./collection-selection\";\nexport type {\n DataTableColumn,\n DataTableControlsProps,\n DataTableFooter,\n DataTableHeaderProps,\n DataTablePanelFooterProps,\n DataTablePanelProps,\n DataTableProps,\n DataTableRenderCell,\n DataTableRenderHeader,\n DataTableSort,\n} from \"./DataTable\";\nexport { default as DataTable } from \"./DataTable\";\nexport type { DocCodeHighlighter, DocCodeProps, DocConcept, DocNoteVariant, DocRow } from \"./Docs\";\nexport { DocCode, DocConceptGrid, DocInlineCode, DocLead, DocNote, DocPage, DocRows, DocSection } from \"./Docs\";\nexport { FileBrowserPanel, type FileBrowserPanelProps, type FileSource, openFileBrowser } from \"./FileBrowser\";\nexport { FileGrid, type FileGridProps } from \"./FileGrid\";\nexport { default as FileTree, type FileTreeActions, type FileTreeEntry, type FileTreeProps } from \"./FileTree\";\nexport {\n canPreviewFile,\n default as FileView,\n type FileViewContent,\n type FileViewFile,\n type FileViewPreviewKind,\n type FileViewProps,\n type FileViewRenderer,\n type FileViewRendererProps,\n formatFileViewSize,\n getFileViewPreviewKind,\n} from \"./FileView\";\nexport type { LightboxAction, LightboxImage } from \"./Lightbox\";\nexport { default as Lightbox } from \"./Lightbox\";\nexport type { LogTableEntry } from \"./LogEntriesTable\";\nexport { default as LogEntriesTable } from \"./LogEntriesTable\";\nexport { default as MarkdownView, type MarkdownRenderOptions, type MarkdownViewProps, renderSafeMarkdown } from \"./MarkdownView\";\nexport type {\n MessageGroupEntry,\n MessageRowAction,\n MessageRowAttachment,\n MessageRowAuthor,\n MessageRowFile,\n MessageRowMedia,\n MessageRowProgress,\n MessageRowProps,\n MessageRowQuote,\n MessageRowReaction,\n MessageRowStatus,\n MessageRowThread,\n MessageSystemRowProps,\n} from \"./MessageRow\";\nexport { MessageRow, MessageSystemRow, startsMessageGroup } from \"./MessageRow\";\nexport {\n type MarkdownInfoBlock,\n type MarkdownInfoBlockRenderInput,\n type MarkdownInfoBlockType,\n markdownInfoBlocks,\n renderMarkdownInfoBlock,\n scanMarkdownInfoBlock,\n} from \"./markdown-info-blocks\";\nexport {\n type MarkdownLinkInput,\n type MarkdownReference,\n type MarkdownReferenceKind,\n type MarkdownReferenceType,\n markdownLinkReference,\n markdownReferenceIcon,\n markdownReferenceType,\n markdownReferenceTypeLabel,\n markStandaloneLinks,\n renderMarkdownLink,\n} from \"./markdown-links\";\nexport { Pagination, type PaginationProps } from \"./Pagination\";\nexport type { PdfPreviewProps, PdfPreviewRequest } from \"./PdfPreview\";\nexport { default as PdfPreview } from \"./PdfPreview\";\nexport type { RangeOption, RangePickerProps } from \"./RangePicker\";\nexport { default as RangePicker } from \"./RangePicker\";\nexport type { StructuredDataPreviewMode, StructuredDataPreviewProps, StructuredDataValue } from \"./StructuredDataPreview\";\nexport { default as StructuredDataPreview, isStructuredDataValue } from \"./StructuredDataPreview\";\nexport type { TimelineColor, TimelineController, TimelineItem, TimelineProps } from \"./Timeline\";\nexport { default as Timeline } from \"./Timeline\";\nexport { VideoPlayer, type VideoPlayerProps } from \"./VideoPlayer\";\nexport type { VirtualFeedController, VirtualFeedProps, VirtualFeedScrollOptions } from \"./VirtualFeed\";\nexport { VirtualFeed } from \"./VirtualFeed\";\nexport type { ZoomPanAction, ZoomPanFullscreen, ZoomPanViewportProps } from \"./ZoomPanViewport\";\nexport { ZoomPanViewport } from \"./ZoomPanViewport\";\n"],"mappings":"AAcA,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAGhD,SAAS,OAAO,IAAI,KAAK,QAAQ,SAAS;AAE1C,SAAS,aAAa,QAAQ,iBAAiB;AAE/C,SAAS,qBAAqB,QAAQ,yBAAyB;AAC/D,SAAS,mBAAmB,QAAuC,uBAAuB;AAC1F,SAAS,iBAAiB,QAAQ,qBAAqB;AAEvD,SAAS,OAAO,IAAI,WAAW,QAAQ,eAAe;AACtD,SAA2B,iBAAiB,QAAQ,gBAAgB;AAUpE,SAAS,mBAAmB,QAAQ,kBAAkB;AAGtD,SAAS,oBAAoB,QAAQ,kBAAkB;AAQvD,SAAmC,yBAAyB,QAAiC,wBAAwB;AAarH,SAAS,OAAO,IAAI,SAAS,QAAQ,aAAa;AAElD,SAAS,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,UAAU,QAAQ,QAAQ;AAC/G,SAAS,gBAAgB,EAA+C,eAAe,QAAQ,eAAe;AAC9G,SAAS,QAAQ,QAA4B,YAAY;AACzD,SAAS,OAAO,IAAI,QAAQ,QAAsE,YAAY;AAC9G,SACE,cAAc,EACd,OAAO,IAAI,QAAQ,EAOnB,kBAAkB,EAClB,sBAAsB,QACjB,YAAY;AAEnB,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAEhD,SAAS,OAAO,IAAI,eAAe,QAAQ,mBAAmB;AAC9D,SAAS,OAAO,IAAI,YAAY,EAAsD,kBAAkB,QAAQ,gBAAgB;AAgBhI,SAAS,UAAU,EAAE,gBAAgB,EAAE,kBAAkB,QAAQ,cAAc;AAC/E,SAIE,kBAAkB,EAClB,uBAAuB,EACvB,qBAAqB,QAChB,wBAAwB;AAC/B,SAKE,qBAAqB,EACrB,qBAAqB,EACrB,qBAAqB,EACrB,0BAA0B,EAC1B,mBAAmB,EACnB,kBAAkB,QACb,kBAAkB;AACzB,SAAS,UAAU,QAA8B,cAAc;AAE/D,SAAS,OAAO,IAAI,UAAU,QAAQ,cAAc;AAEpD,SAAS,OAAO,IAAI,WAAW,QAAQ,eAAe;AAEtD,SAAS,OAAO,IAAI,qBAAqB,EAAE,qBAAqB,QAAQ,yBAAyB;AAEjG,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAChD,SAAS,WAAW,QAA+B,eAAe;AAElE,SAAS,WAAW,QAAQ,eAAe;AAE3C,SAAS,eAAe,QAAQ,mBAAmB","ignoreList":[]}
|
|
@@ -0,0 +1,579 @@
|
|
|
1
|
+
import { dates as calendar } from "@k2b/stdlib";
|
|
2
|
+
import { layoutCalendarIntervals } from "./calendar-event-layout";
|
|
3
|
+
|
|
4
|
+
/** A color token of the shared calendar palette, or a hex color such as a label color. */
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* A place on the main axis as the count of each unit before it: hours of waking time, nights, folds of empty days,
|
|
8
|
+
* day headings, and all-day rows. The axis gives every unit its size, so one model serves both axes.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const TIMELINE_UNIT_NAMES = ["hour", "night", "fold", "head", "row"];
|
|
12
|
+
/** Waking hours; the night between them is folded. */
|
|
13
|
+
export const TIMELINE_WAKE_HOURS = [6, 22];
|
|
14
|
+
/** Visible lanes of overlapping bands and of all-day items; more overlaps collapse into a "+n" entry in the last lane. */
|
|
15
|
+
export const TIMELINE_BAND_LANES = 3;
|
|
16
|
+
export const TIMELINE_ALL_DAY_LANES = 2;
|
|
17
|
+
/** Visible items stacked in one night or fold; more collapse into a "+n" entry in the last place. */
|
|
18
|
+
export const TIMELINE_FOLD_LANES = 3;
|
|
19
|
+
/** Free time shorter than this gets no label. */
|
|
20
|
+
const MIN_GAP = 3_600_000;
|
|
21
|
+
const HOUR = 3_600_000;
|
|
22
|
+
const DAY = 86_400_000;
|
|
23
|
+
const ZERO = [0, 0, 0, 0, 0];
|
|
24
|
+
const NIGHT = [0, 1, 0, 0, 0];
|
|
25
|
+
const FOLD = [0, 0, 1, 0, 0];
|
|
26
|
+
const HEAD = [0, 0, 0, 1, 0];
|
|
27
|
+
const ROW = [0, 0, 0, 0, 1];
|
|
28
|
+
const hours = ms => [ms / HOUR, 0, 0, 0, 0];
|
|
29
|
+
export const addSpan = (a, b) => [a[0] + b[0], a[1] + b[1], a[2] + b[2], a[3] + b[3], a[4] + b[4]];
|
|
30
|
+
export const subtractSpan = (a, b) => [a[0] - b[0], a[1] - b[1], a[2] - b[2], a[3] - b[3], a[4] - b[4]];
|
|
31
|
+
const scaleSpan = (a, factor) => [a[0] * factor, a[1] * factor, a[2] * factor, a[3] * factor, a[4] * factor];
|
|
32
|
+
export const spanPx = (span, units) => span[0] * units[0] + span[1] * units[1] + span[2] * units[2] + span[3] * units[3] + span[4] * units[4];
|
|
33
|
+
const round = value => Math.round(value * 10_000) / 10_000;
|
|
34
|
+
/** The span as a CSS length over the `--k2b-timeline-<unit>` sizes the axis sets. */
|
|
35
|
+
export const spanCss = span => {
|
|
36
|
+
const terms = span.flatMap((count, index) => Math.abs(count) < 1e-6 ? [] : [`${round(count)} * var(--k2b-timeline-${TIMELINE_UNIT_NAMES[index]})`]);
|
|
37
|
+
return terms.length === 0 ? "0px" : `calc(${terms.join(" + ")})`;
|
|
38
|
+
};
|
|
39
|
+
const dateOnly = /^\d{4}-\d{2}-\d{2}$/;
|
|
40
|
+
export const parseTimelineTime = (value, context) => {
|
|
41
|
+
if (value === undefined) return Number.NaN;
|
|
42
|
+
if (value instanceof Date) return value.getTime();
|
|
43
|
+
if (dateOnly.test(value)) return calendar.parseCalendarDate(value, context).getTime();
|
|
44
|
+
return new Date(value).getTime();
|
|
45
|
+
};
|
|
46
|
+
const pad = value => String(value).padStart(2, "0");
|
|
47
|
+
const zoneFormats = new Map();
|
|
48
|
+
/** Wall-clock parts of an instant in a time zone, with one cached formatter per zone: a year of items calls this often. */
|
|
49
|
+
const zonedParts = (time, timeZone) => {
|
|
50
|
+
let format = zoneFormats.get(timeZone);
|
|
51
|
+
if (!format) {
|
|
52
|
+
format = new Intl.DateTimeFormat("en-US", {
|
|
53
|
+
timeZone,
|
|
54
|
+
hourCycle: "h23",
|
|
55
|
+
year: "numeric",
|
|
56
|
+
month: "numeric",
|
|
57
|
+
day: "numeric",
|
|
58
|
+
hour: "numeric",
|
|
59
|
+
minute: "numeric",
|
|
60
|
+
second: "numeric"
|
|
61
|
+
});
|
|
62
|
+
zoneFormats.set(timeZone, format);
|
|
63
|
+
}
|
|
64
|
+
const parts = {};
|
|
65
|
+
for (const part of format.formatToParts(time)) if (part.type !== "literal") parts[part.type] = Number(part.value);
|
|
66
|
+
return [parts.year ?? 1970, parts.month ?? 1, parts.day ?? 1, parts.hour ?? 0, parts.minute ?? 0, parts.second ?? 0];
|
|
67
|
+
};
|
|
68
|
+
const zoneOffset = (time, timeZone) => {
|
|
69
|
+
const [year, month, day, hour, minute, second] = zonedParts(time, timeZone);
|
|
70
|
+
return Date.UTC(year, month - 1, day, hour, minute, second) - Math.floor(time / 1_000) * 1_000;
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
/** The day (`YYYY-MM-DD`) of an instant in the context's time zone. */
|
|
74
|
+
export const timelineDayKey = (time, context) => {
|
|
75
|
+
if (!context?.timeZone) {
|
|
76
|
+
const date = new Date(time);
|
|
77
|
+
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`;
|
|
78
|
+
}
|
|
79
|
+
const [year, month, day] = zonedParts(time, context.timeZone);
|
|
80
|
+
return `${year}-${pad(month)}-${pad(day)}`;
|
|
81
|
+
};
|
|
82
|
+
export const shiftDayKey = (key, days) => {
|
|
83
|
+
const [year = 1970, month = 1, day = 1] = key.split("-").map(Number);
|
|
84
|
+
return new Date(Date.UTC(year, month - 1, day + days)).toISOString().slice(0, 10);
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
/**
|
|
88
|
+
* The instant of a whole hour (0–24) on a day in the context's time zone. As stdlib's `compatible` disambiguation, a
|
|
89
|
+
* repeated hour resolves to its first occurrence and a skipped one to the time after the gap.
|
|
90
|
+
*/
|
|
91
|
+
export const timelineHourOf = (key, hour, context) => {
|
|
92
|
+
const [year = 1970, month = 1, day = 1] = key.split("-").map(Number);
|
|
93
|
+
if (!context?.timeZone) return new Date(year, month - 1, day, hour).getTime();
|
|
94
|
+
const zone = context.timeZone;
|
|
95
|
+
const wall = Date.UTC(year, month - 1, day, hour);
|
|
96
|
+
// The offsets a day before and after are the ones on either side of a clock change at this time.
|
|
97
|
+
const before = wall - zoneOffset(wall - DAY, zone);
|
|
98
|
+
const after = wall - zoneOffset(wall + DAY, zone);
|
|
99
|
+
const valid = [before, after].filter(time => zoneOffset(time, zone) === wall - time);
|
|
100
|
+
return valid.length > 0 ? Math.min(...valid) : before;
|
|
101
|
+
};
|
|
102
|
+
const normalize = (item, keyOf, context) => {
|
|
103
|
+
const start = parseTimelineTime(item.start, context);
|
|
104
|
+
if (!Number.isFinite(start)) return null;
|
|
105
|
+
const parsedEnd = parseTimelineTime(item.end, context);
|
|
106
|
+
const firstKey = keyOf(start);
|
|
107
|
+
if (item.allDay) {
|
|
108
|
+
const end = Number.isFinite(parsedEnd) && parsedEnd > start ? parsedEnd : start;
|
|
109
|
+
const endKey = keyOf(end);
|
|
110
|
+
const exclusive = end > start && timelineHourOf(endKey, 0, context) === end;
|
|
111
|
+
const lastKey = exclusive ? shiftDayKey(endKey, -1) : endKey;
|
|
112
|
+
return {
|
|
113
|
+
item,
|
|
114
|
+
start,
|
|
115
|
+
end,
|
|
116
|
+
allDay: true,
|
|
117
|
+
point: false,
|
|
118
|
+
firstKey,
|
|
119
|
+
lastKey: lastKey < firstKey ? firstKey : lastKey
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const point = item.kind === "marker" || !Number.isFinite(parsedEnd) || parsedEnd <= start;
|
|
123
|
+
return {
|
|
124
|
+
item,
|
|
125
|
+
start,
|
|
126
|
+
end: point ? start : parsedEnd,
|
|
127
|
+
allDay: false,
|
|
128
|
+
point,
|
|
129
|
+
firstKey,
|
|
130
|
+
lastKey: firstKey
|
|
131
|
+
};
|
|
132
|
+
};
|
|
133
|
+
const byStart = (a, b) => a.start - b.start || b.end - a.end || (a.id < b.id ? -1 : a.id > b.id ? 1 : 0);
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Lays out overlapping entries in at most `max` lanes. Where a cluster needs more, the entries beyond the last lane and
|
|
137
|
+
* the ones they meet in it collapse into "+n" entries in the last lane, one per run of overlapping hidden entries.
|
|
138
|
+
*/
|
|
139
|
+
const laneEntries = (entries, max) => {
|
|
140
|
+
const clusters = new Map();
|
|
141
|
+
for (const layout of layoutCalendarIntervals(entries, entry => entry)) {
|
|
142
|
+
const cluster = clusters.get(layout.groupId) ?? [];
|
|
143
|
+
cluster.push({
|
|
144
|
+
entry: layout.item,
|
|
145
|
+
lane: layout.lane,
|
|
146
|
+
lanes: layout.lanes
|
|
147
|
+
});
|
|
148
|
+
clusters.set(layout.groupId, cluster);
|
|
149
|
+
}
|
|
150
|
+
const runs = [];
|
|
151
|
+
for (const cluster of clusters.values()) {
|
|
152
|
+
const lanes = cluster[0]?.lanes ?? 1;
|
|
153
|
+
if (lanes <= max) {
|
|
154
|
+
for (const {
|
|
155
|
+
entry,
|
|
156
|
+
lane
|
|
157
|
+
} of cluster) {
|
|
158
|
+
entry.lane = lane;
|
|
159
|
+
entry.lanes = lanes;
|
|
160
|
+
}
|
|
161
|
+
continue;
|
|
162
|
+
}
|
|
163
|
+
// The last visible lane keeps its entries where they meet nothing beyond it.
|
|
164
|
+
const beyond = cluster.filter(({
|
|
165
|
+
lane
|
|
166
|
+
}) => lane >= max).map(({
|
|
167
|
+
entry
|
|
168
|
+
}) => entry);
|
|
169
|
+
const hidden = [];
|
|
170
|
+
for (const {
|
|
171
|
+
entry,
|
|
172
|
+
lane
|
|
173
|
+
} of cluster) {
|
|
174
|
+
const overlapsBeyond = beyond.some(other => other.start < entry.end && entry.start < other.end);
|
|
175
|
+
if (lane >= max || lane === max - 1 && overlapsBeyond) hidden.push(entry);else {
|
|
176
|
+
entry.lane = lane;
|
|
177
|
+
entry.lanes = max;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
// Runs of hidden entries that overlap each other become one "+n" entry each.
|
|
181
|
+
let run;
|
|
182
|
+
let runEnd = Number.NEGATIVE_INFINITY;
|
|
183
|
+
for (const entry of hidden.sort(byStart)) {
|
|
184
|
+
const {
|
|
185
|
+
start,
|
|
186
|
+
end
|
|
187
|
+
} = entry;
|
|
188
|
+
if (run && start < runEnd) {
|
|
189
|
+
run.push(entry);
|
|
190
|
+
runEnd = Math.max(runEnd, end);
|
|
191
|
+
} else {
|
|
192
|
+
run = [entry];
|
|
193
|
+
runs.push(run);
|
|
194
|
+
runEnd = end;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
return runs;
|
|
199
|
+
};
|
|
200
|
+
/**
|
|
201
|
+
* Builds the filmstrip: waking hours (6–22) are proportional, every night is one fold, and a run of days without
|
|
202
|
+
* timed items is one fold. Positions depend only on the times and the items, never on measurement.
|
|
203
|
+
*/
|
|
204
|
+
export const buildTimelineModel = ({
|
|
205
|
+
from,
|
|
206
|
+
to,
|
|
207
|
+
today,
|
|
208
|
+
items,
|
|
209
|
+
context
|
|
210
|
+
}) => {
|
|
211
|
+
const [wakeStart, wakeEnd] = TIMELINE_WAKE_HOURS;
|
|
212
|
+
const segments = [];
|
|
213
|
+
const groups = [];
|
|
214
|
+
const groupOfKey = new Map();
|
|
215
|
+
const valid = Number.isFinite(from) && Number.isFinite(to) && to > from;
|
|
216
|
+
const firstKey = valid ? timelineDayKey(from, context) : "";
|
|
217
|
+
const lastKey = valid ? timelineDayKey(to - 1, context) : "";
|
|
218
|
+
const todayKey = today;
|
|
219
|
+
const wake = new Map();
|
|
220
|
+
const wakeOf = key => {
|
|
221
|
+
let value = wake.get(key);
|
|
222
|
+
if (!value) {
|
|
223
|
+
// Clocks change at night, so the waking hours of a day are whole hours after its 06:00.
|
|
224
|
+
const open = timelineHourOf(key, wakeStart, context);
|
|
225
|
+
value = [open, open + (wakeEnd - wakeStart) * HOUR];
|
|
226
|
+
wake.set(key, value);
|
|
227
|
+
}
|
|
228
|
+
return value;
|
|
229
|
+
};
|
|
230
|
+
// Day keys of loaded times by binary search over the midnights, instead of a time-zone lookup per item.
|
|
231
|
+
const dayKeys = [];
|
|
232
|
+
const midnights = [];
|
|
233
|
+
for (let key = firstKey; valid && key <= shiftDayKey(lastKey, 1); key = shiftDayKey(key, 1)) {
|
|
234
|
+
dayKeys.push(key);
|
|
235
|
+
midnights.push(timelineHourOf(key, 0, context));
|
|
236
|
+
}
|
|
237
|
+
const keyOf = t => {
|
|
238
|
+
if (midnights.length < 2 || t < midnights[0] || t >= midnights.at(-1)) return timelineDayKey(t, context);
|
|
239
|
+
let low = 0;
|
|
240
|
+
let high = midnights.length - 2;
|
|
241
|
+
while (low < high) {
|
|
242
|
+
const middle = low + high + 1 >> 1;
|
|
243
|
+
if (midnights[middle] <= t) low = middle;else high = middle - 1;
|
|
244
|
+
}
|
|
245
|
+
return dayKeys[low];
|
|
246
|
+
};
|
|
247
|
+
const normalized = items.flatMap(item => normalize(item, keyOf, context) ?? []);
|
|
248
|
+
|
|
249
|
+
// Days with timed items in their waking hours stay open; today always does. So does a day at either edge whose
|
|
250
|
+
// waking hours are loaded only in part: whether it is empty is not known yet, and folding it now would reshape it
|
|
251
|
+
// when the rest loads.
|
|
252
|
+
const busy = new Set([todayKey]);
|
|
253
|
+
if (valid) {
|
|
254
|
+
const [firstOpen, firstClose] = wakeOf(firstKey);
|
|
255
|
+
if (from > firstOpen && from < firstClose) busy.add(firstKey);
|
|
256
|
+
const [lastOpen, lastClose] = wakeOf(lastKey);
|
|
257
|
+
if (to > lastOpen && to < lastClose) busy.add(lastKey);
|
|
258
|
+
}
|
|
259
|
+
const covered = new Set();
|
|
260
|
+
for (const entry of valid ? normalized : []) {
|
|
261
|
+
if (entry.allDay) {
|
|
262
|
+
const first = entry.firstKey < firstKey ? firstKey : entry.firstKey;
|
|
263
|
+
const last = entry.lastKey > lastKey ? lastKey : entry.lastKey;
|
|
264
|
+
for (let key = first; key <= last; key = shiftDayKey(key, 1)) covered.add(key);
|
|
265
|
+
continue;
|
|
266
|
+
}
|
|
267
|
+
const start = Math.max(entry.start, from);
|
|
268
|
+
const end = Math.min(entry.end, to);
|
|
269
|
+
if (entry.point ? entry.start < from || entry.start >= to : end <= start) continue;
|
|
270
|
+
const last = keyOf(entry.point ? start : end - 1);
|
|
271
|
+
for (let key = keyOf(start); key <= last; key = shiftDayKey(key, 1)) {
|
|
272
|
+
const [open, close] = wakeOf(key);
|
|
273
|
+
if (entry.point ? start >= open && start < close : start < close && end > open) busy.add(key);
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
let pos = ZERO;
|
|
277
|
+
let time = from;
|
|
278
|
+
const push = (kind, t0, t1, size) => {
|
|
279
|
+
segments.push({
|
|
280
|
+
kind,
|
|
281
|
+
t0,
|
|
282
|
+
t1,
|
|
283
|
+
at: pos,
|
|
284
|
+
size,
|
|
285
|
+
group: groups.length - 1
|
|
286
|
+
});
|
|
287
|
+
pos = addSpan(pos, size);
|
|
288
|
+
};
|
|
289
|
+
const openGroup = (kind, first, last) => {
|
|
290
|
+
let row = false;
|
|
291
|
+
for (let key = first; key <= last; key = shiftDayKey(key, 1)) {
|
|
292
|
+
groupOfKey.set(key, groups.length);
|
|
293
|
+
row ||= covered.has(key);
|
|
294
|
+
}
|
|
295
|
+
groups.push({
|
|
296
|
+
index: groups.length,
|
|
297
|
+
key: kind === "day" ? `day:${first}` : `fold:${first}:${last}`,
|
|
298
|
+
kind,
|
|
299
|
+
firstKey: first,
|
|
300
|
+
lastKey: last,
|
|
301
|
+
t0: time,
|
|
302
|
+
t1: time,
|
|
303
|
+
reach: [time, time],
|
|
304
|
+
at: pos,
|
|
305
|
+
size: ZERO,
|
|
306
|
+
row,
|
|
307
|
+
today: first <= todayKey && todayKey <= last,
|
|
308
|
+
past: last < todayKey,
|
|
309
|
+
entries: [],
|
|
310
|
+
echoes: [],
|
|
311
|
+
hours: [],
|
|
312
|
+
folds: [],
|
|
313
|
+
gaps: []
|
|
314
|
+
});
|
|
315
|
+
push("head", time, time, HEAD);
|
|
316
|
+
if (row) push("row", time, time, ROW);
|
|
317
|
+
};
|
|
318
|
+
|
|
319
|
+
// Every loaded day gets a group, also one whose time an earlier night already reaches to the end: its items are
|
|
320
|
+
// placed in that night.
|
|
321
|
+
for (let key = firstKey; valid && key <= lastKey;) {
|
|
322
|
+
const next = shiftDayKey(key, 1);
|
|
323
|
+
if (busy.has(key)) {
|
|
324
|
+
const [open, close] = wakeOf(key);
|
|
325
|
+
openGroup("day", key, key);
|
|
326
|
+
if (time < to && time < open) {
|
|
327
|
+
const end = Math.min(open, to);
|
|
328
|
+
push("night", time, end, NIGHT);
|
|
329
|
+
time = end;
|
|
330
|
+
}
|
|
331
|
+
if (time < to && time < close) {
|
|
332
|
+
const end = Math.min(close, to);
|
|
333
|
+
push("wake", time, end, hours(end - time));
|
|
334
|
+
time = end;
|
|
335
|
+
}
|
|
336
|
+
// The night after a day belongs to the fold when empty days follow.
|
|
337
|
+
if (time < to && (next > lastKey || busy.has(next))) {
|
|
338
|
+
const end = Math.min(wakeOf(next)[0], to);
|
|
339
|
+
push("night", time, end, NIGHT);
|
|
340
|
+
time = end;
|
|
341
|
+
}
|
|
342
|
+
key = next;
|
|
343
|
+
continue;
|
|
344
|
+
}
|
|
345
|
+
let last = key;
|
|
346
|
+
while (shiftDayKey(last, 1) <= lastKey && !busy.has(shiftDayKey(last, 1))) last = shiftDayKey(last, 1);
|
|
347
|
+
openGroup("fold", key, last);
|
|
348
|
+
const end = Math.min(wakeOf(shiftDayKey(last, 1))[0], to);
|
|
349
|
+
push("fold", time, end, FOLD);
|
|
350
|
+
time = end;
|
|
351
|
+
key = shiftDayKey(last, 1);
|
|
352
|
+
}
|
|
353
|
+
const total = pos;
|
|
354
|
+
for (const [index, group] of groups.entries()) {
|
|
355
|
+
const next = groups[index + 1];
|
|
356
|
+
group.t1 = next ? next.t0 : to;
|
|
357
|
+
group.size = subtractSpan(next ? next.at : total, group.at);
|
|
358
|
+
group.reach = [group.t0, group.t1];
|
|
359
|
+
}
|
|
360
|
+
const timed = segments.filter(segment => segment.t1 > segment.t0);
|
|
361
|
+
const segmentAt = t => {
|
|
362
|
+
let low = 0;
|
|
363
|
+
let high = timed.length - 1;
|
|
364
|
+
let found;
|
|
365
|
+
while (low <= high) {
|
|
366
|
+
const middle = low + high >> 1;
|
|
367
|
+
const segment = timed[middle];
|
|
368
|
+
if (segment.t0 <= t) {
|
|
369
|
+
found = segment;
|
|
370
|
+
low = middle + 1;
|
|
371
|
+
} else high = middle - 1;
|
|
372
|
+
}
|
|
373
|
+
return found;
|
|
374
|
+
};
|
|
375
|
+
const posOf = t => {
|
|
376
|
+
const segment = segmentAt(t);
|
|
377
|
+
if (!segment) return timed[0]?.at ?? ZERO;
|
|
378
|
+
if (t >= segment.t1) return addSpan(segment.at, segment.size);
|
|
379
|
+
return addSpan(segment.at, scaleSpan(segment.size, (t - segment.t0) / (segment.t1 - segment.t0)));
|
|
380
|
+
};
|
|
381
|
+
const timeAt = (px, units) => {
|
|
382
|
+
for (const segment of segments) {
|
|
383
|
+
const start = spanPx(segment.at, units);
|
|
384
|
+
const size = spanPx(segment.size, units);
|
|
385
|
+
if (px < start + size || segment === segments.at(-1)) {
|
|
386
|
+
if (segment.t1 === segment.t0 || size <= 0) return segment.t0;
|
|
387
|
+
return segment.t0 + Math.max(0, Math.min(1, (px - start) / size)) * (segment.t1 - segment.t0);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
return from;
|
|
391
|
+
};
|
|
392
|
+
|
|
393
|
+
// Decorations: hour ticks, folds, and free time per waking segment.
|
|
394
|
+
const relative = (group, span) => subtractSpan(span, group.at);
|
|
395
|
+
for (const segment of segments) {
|
|
396
|
+
const group = groups[segment.group];
|
|
397
|
+
if (segment.kind === "night" || segment.kind === "fold") group.folds.push({
|
|
398
|
+
kind: segment.kind,
|
|
399
|
+
at: relative(group, segment.at),
|
|
400
|
+
size: segment.size
|
|
401
|
+
});
|
|
402
|
+
if (segment.kind !== "wake") continue;
|
|
403
|
+
// Clocks change at night, so waking hours are whole hours after the day's 06:00.
|
|
404
|
+
const open = wakeOf(keyOf(segment.t0))[0];
|
|
405
|
+
for (let hour = 0; hour < wakeEnd - wakeStart; hour++) {
|
|
406
|
+
const tick = open + hour * HOUR;
|
|
407
|
+
if (tick >= segment.t0 && tick < segment.t1) group.hours.push({
|
|
408
|
+
time: tick,
|
|
409
|
+
at: relative(group, posOf(tick))
|
|
410
|
+
});
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
// Entries.
|
|
415
|
+
const entries = new Map();
|
|
416
|
+
const bands = [];
|
|
417
|
+
const allDay = [];
|
|
418
|
+
const timedEntries = [];
|
|
419
|
+
const foldStacks = new Map();
|
|
420
|
+
for (const entry of valid ? normalized : []) {
|
|
421
|
+
if (entries.has(entry.item.id)) continue;
|
|
422
|
+
if (entry.allDay) {
|
|
423
|
+
const first = entry.firstKey < firstKey ? firstKey : entry.firstKey;
|
|
424
|
+
const last = entry.lastKey > lastKey ? lastKey : entry.lastKey;
|
|
425
|
+
if (first > last) continue;
|
|
426
|
+
const groupIndex = groupOfKey.get(first);
|
|
427
|
+
const lastGroup = groupOfKey.get(last);
|
|
428
|
+
if (groupIndex === undefined || lastGroup === undefined) continue;
|
|
429
|
+
const group = groups[groupIndex];
|
|
430
|
+
const end = groups[lastGroup];
|
|
431
|
+
const placed = {
|
|
432
|
+
kind: "all-day",
|
|
433
|
+
id: entry.item.id,
|
|
434
|
+
item: entry.item,
|
|
435
|
+
group: groupIndex,
|
|
436
|
+
start: groupIndex,
|
|
437
|
+
end: lastGroup + 1,
|
|
438
|
+
at: ZERO,
|
|
439
|
+
size: subtractSpan(addSpan(end.at, end.size), group.at),
|
|
440
|
+
lane: 0,
|
|
441
|
+
lanes: 1,
|
|
442
|
+
firstKey: first,
|
|
443
|
+
lastKey: last
|
|
444
|
+
};
|
|
445
|
+
entries.set(placed.id, placed);
|
|
446
|
+
allDay.push(placed);
|
|
447
|
+
continue;
|
|
448
|
+
}
|
|
449
|
+
// Placed by the part inside the range, labelled by the item's own times.
|
|
450
|
+
const start = entry.point ? entry.start : Math.max(entry.start, from);
|
|
451
|
+
const end = entry.point ? entry.start : Math.min(entry.end, to);
|
|
452
|
+
if (entry.point ? start < from || start >= to : end <= start) continue;
|
|
453
|
+
const groupIndex = groupOfKey.get(keyOf(start));
|
|
454
|
+
if (groupIndex === undefined) continue;
|
|
455
|
+
const group = groups[groupIndex];
|
|
456
|
+
const startSegment = segmentAt(start);
|
|
457
|
+
const endSegment = entry.point ? startSegment : segmentAt(end - 1);
|
|
458
|
+
const folded = startSegment !== undefined && startSegment.kind !== "wake" && startSegment === endSegment;
|
|
459
|
+
const at = folded ? startSegment.at : posOf(start);
|
|
460
|
+
const placed = {
|
|
461
|
+
kind: folded ? "folded" : entry.point ? "marker" : "band",
|
|
462
|
+
id: entry.item.id,
|
|
463
|
+
item: entry.item,
|
|
464
|
+
group: groupIndex,
|
|
465
|
+
start: entry.start,
|
|
466
|
+
end: entry.end,
|
|
467
|
+
at: relative(group, at),
|
|
468
|
+
size: folded ? startSegment.size : entry.point ? ZERO : subtractSpan(posOf(end), at),
|
|
469
|
+
lane: 0,
|
|
470
|
+
lanes: 1,
|
|
471
|
+
firstKey: keyOf(start),
|
|
472
|
+
lastKey: keyOf(start)
|
|
473
|
+
};
|
|
474
|
+
entries.set(placed.id, placed);
|
|
475
|
+
timedEntries.push(placed);
|
|
476
|
+
if (placed.kind === "band") bands.push(placed);
|
|
477
|
+
if (folded) foldStacks.set(startSegment, [...(foldStacks.get(startSegment) ?? []), placed]);
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
// Items inside a fold stack in its middle, in time order, in up to three places; the rest share a "+n" entry.
|
|
481
|
+
const foldRuns = [];
|
|
482
|
+
for (const stack of foldStacks.values()) {
|
|
483
|
+
stack.sort(byStart);
|
|
484
|
+
const shown = stack.length > TIMELINE_FOLD_LANES ? TIMELINE_FOLD_LANES - 1 : stack.length;
|
|
485
|
+
for (const [lane, entry] of stack.slice(0, shown).entries()) {
|
|
486
|
+
entry.lane = lane;
|
|
487
|
+
entry.lanes = Math.min(stack.length, TIMELINE_FOLD_LANES);
|
|
488
|
+
}
|
|
489
|
+
if (shown < stack.length) foldRuns.push(stack.slice(shown));
|
|
490
|
+
}
|
|
491
|
+
const more = [];
|
|
492
|
+
// Item ids are the application's, so a "+n" id steps aside where an item already has it.
|
|
493
|
+
const ids = new Set(normalized.map(entry => entry.item.id));
|
|
494
|
+
const collapse = (runs, area, lanes) => {
|
|
495
|
+
for (const run of runs) {
|
|
496
|
+
const first = run[0];
|
|
497
|
+
const group = groups[first.group];
|
|
498
|
+
let start = first.start;
|
|
499
|
+
let end = first.end;
|
|
500
|
+
for (const entry of run) {
|
|
501
|
+
start = Math.min(start, entry.start);
|
|
502
|
+
end = Math.max(end, entry.end);
|
|
503
|
+
}
|
|
504
|
+
let id = `more:${first.id}`;
|
|
505
|
+
for (let suffix = 2; ids.has(id); suffix++) id = `more:${first.id}:${suffix}`;
|
|
506
|
+
ids.add(id);
|
|
507
|
+
// A fold's "+n" takes the place of the fold; all-day entries count in groups, `end` is the index after their last.
|
|
508
|
+
const lastGroup = area === "all-day" ? groups[Math.max(first.group, end - 1)] : group;
|
|
509
|
+
const placed = {
|
|
510
|
+
kind: "more",
|
|
511
|
+
area,
|
|
512
|
+
id,
|
|
513
|
+
hidden: run,
|
|
514
|
+
group: first.group,
|
|
515
|
+
start,
|
|
516
|
+
end,
|
|
517
|
+
at: area === "band" ? relative(group, posOf(start)) : area === "fold" ? first.at : ZERO,
|
|
518
|
+
size: area === "band" ? subtractSpan(posOf(end), posOf(start)) : area === "fold" ? first.size : subtractSpan(addSpan(lastGroup.at, lastGroup.size), group.at),
|
|
519
|
+
lane: lanes - 1,
|
|
520
|
+
lanes
|
|
521
|
+
};
|
|
522
|
+
for (const hidden of run) entries.delete(hidden.id);
|
|
523
|
+
entries.set(placed.id, placed);
|
|
524
|
+
more.push(placed);
|
|
525
|
+
}
|
|
526
|
+
};
|
|
527
|
+
collapse(laneEntries(bands, TIMELINE_BAND_LANES), "band", TIMELINE_BAND_LANES);
|
|
528
|
+
collapse(foldRuns, "fold", TIMELINE_FOLD_LANES);
|
|
529
|
+
collapse(laneEntries(allDay, TIMELINE_ALL_DAY_LANES), "all-day", TIMELINE_ALL_DAY_LANES);
|
|
530
|
+
const visibleAllDay = [...allDay.filter(entry => entries.has(entry.id)), ...more.filter(entry => entry.area === "all-day")];
|
|
531
|
+
for (const entry of visibleAllDay.sort(byStart)) {
|
|
532
|
+
groups[entry.group].entries.push(entry);
|
|
533
|
+
for (let index = entry.start + 1; index < entry.end; index++) groups[index]?.echoes.push(entry);
|
|
534
|
+
}
|
|
535
|
+
const visibleTimed = [...timedEntries.filter(entry => entries.has(entry.id)), ...more.filter(entry => entry.area !== "all-day")];
|
|
536
|
+
for (const entry of visibleTimed.sort(byStart)) groups[entry.group].entries.push(entry);
|
|
537
|
+
for (const group of groups) {
|
|
538
|
+
for (const entry of group.entries) {
|
|
539
|
+
const allDayEntry = entry.kind === "all-day" || entry.kind === "more" && entry.area === "all-day";
|
|
540
|
+
const [start, end] = allDayEntry ? [group.t0, groups[entry.end - 1].t1] : [entry.start, entry.end];
|
|
541
|
+
group.reach = [Math.min(group.reach[0], start), Math.max(group.reach[1], end)];
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
// Free time: gaps of an hour or more between bands within waking hours.
|
|
546
|
+
const busyBands = bands.toSorted(byStart);
|
|
547
|
+
for (const segment of timed) {
|
|
548
|
+
if (segment.kind !== "wake") continue;
|
|
549
|
+
const group = groups[segment.group];
|
|
550
|
+
let cursor = segment.t0;
|
|
551
|
+
const addGap = end => {
|
|
552
|
+
if (end - cursor < MIN_GAP) return;
|
|
553
|
+
group.gaps.push({
|
|
554
|
+
at: relative(group, posOf((cursor + end) / 2)),
|
|
555
|
+
end,
|
|
556
|
+
hours: Math.round((end - cursor) / HOUR * 2) / 2,
|
|
557
|
+
long: end - cursor >= 3 * HOUR
|
|
558
|
+
});
|
|
559
|
+
};
|
|
560
|
+
for (const band of busyBands) {
|
|
561
|
+
if (band.end <= segment.t0 || band.start >= segment.t1) continue;
|
|
562
|
+
addGap(Math.max(cursor, band.start));
|
|
563
|
+
cursor = Math.max(cursor, band.end);
|
|
564
|
+
}
|
|
565
|
+
addGap(segment.t1);
|
|
566
|
+
}
|
|
567
|
+
return {
|
|
568
|
+
from,
|
|
569
|
+
to,
|
|
570
|
+
segments,
|
|
571
|
+
groups,
|
|
572
|
+
total,
|
|
573
|
+
entries,
|
|
574
|
+
order: groups.flatMap(group => group.entries.map(entry => entry.id)),
|
|
575
|
+
posOf,
|
|
576
|
+
timeAt
|
|
577
|
+
};
|
|
578
|
+
};
|
|
579
|
+
//# sourceMappingURL=timeline-model.js.map
|