@k2b/ui 0.35.0 → 0.36.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/dist/browser/content/MapView.js +128 -13
- package/dist/browser/content/MapView.js.map +1 -1
- package/dist/browser/content/MapViewFrame.js +1 -1
- package/dist/browser/content/MapViewFrame.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/map-view-glyphs.js +23 -0
- package/dist/browser/content/map-view-glyphs.js.map +1 -0
- package/dist/browser/content/map-view-live.js +45 -22
- package/dist/browser/content/map-view-live.js.map +1 -1
- package/dist/browser/content/map-view-snapshot.js +191 -0
- package/dist/browser/content/map-view-snapshot.js.map +1 -0
- package/dist/browser/content/map-view-types.js.map +1 -1
- package/dist/browser/inputs/Combobox.js +139 -62
- package/dist/browser/inputs/Combobox.js.map +1 -1
- package/dist/browser/inputs/TextInput.js +1 -1
- package/dist/browser/inputs/TextInput.js.map +1 -1
- package/dist/browser/layout/AppWorkspace.js +73 -70
- package/dist/browser/layout/AppWorkspace.js.map +1 -1
- package/dist/browser/layout/ScrollArea.js +4 -1
- package/dist/browser/layout/ScrollArea.js.map +1 -1
- package/dist/ssr/content/MapView.js +128 -13
- package/dist/ssr/content/MapView.js.map +1 -1
- package/dist/ssr/content/MapViewFrame.js +1 -1
- package/dist/ssr/content/MapViewFrame.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/map-view-glyphs.js +23 -0
- package/dist/ssr/content/map-view-glyphs.js.map +1 -0
- package/dist/ssr/content/map-view-snapshot.js +191 -0
- package/dist/ssr/content/map-view-snapshot.js.map +1 -0
- package/dist/ssr/content/map-view-types.js.map +1 -1
- package/dist/ssr/inputs/Combobox.js +66 -20
- package/dist/ssr/inputs/Combobox.js.map +1 -1
- package/dist/ssr/inputs/TextInput.js.map +1 -1
- package/dist/ssr/layout/AppWorkspace.js +1 -1
- package/dist/ssr/layout/AppWorkspace.js.map +1 -1
- package/dist/ssr/layout/ScrollArea.js +4 -1
- package/dist/ssr/layout/ScrollArea.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/types/content/MapViewFrame.d.ts +1 -1
- package/dist/types/content/index.d.ts +2 -1
- package/dist/types/content/map-view-glyphs.d.ts +5 -0
- package/dist/types/content/map-view-live.d.ts +1 -1
- package/dist/types/content/map-view-snapshot.d.ts +13 -0
- package/dist/types/content/map-view-types.d.ts +15 -0
- package/dist/types/inputs/Combobox.d.ts +12 -0
- package/dist/types/inputs/TextInput.d.ts +2 -1
- package/dist/types/layout/AppWorkspace.d.ts +4 -1
- package/dist/types/layout/ScrollArea.d.ts +2 -0
- package/package.json +2 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["content/index.ts"],"sourcesContent":["export type {\n CalendarAttendee,\n CalendarCommand,\n CalendarController,\n CalendarCustomView,\n CalendarDayBadge,\n CalendarEvent,\n CalendarEventColor,\n CalendarEventRenderContext,\n CalendarEventTimeChange,\n CalendarExternalDrag,\n CalendarLabels,\n CalendarProps,\n CalendarQuickCreateControls,\n CalendarRange,\n CalendarRecurrence,\n CalendarResource,\n CalendarSelectionControls,\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, ChartLegendEntry } 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-core\";\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 { highlightSql } from \"./code-highlight\";\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 FileGridGroup, type FileGridOpenProps, type FileGridProps, type FileGridSelectableProps } from \"./FileGrid\";\nexport { default as FileTree, type FileTreeActions, type FileTreeEntry, type FileTreeProps } from \"./FileTree\";\nexport { type FileType, FileTypeIcon, type FileTypeIconProps, fileKindLabel, fileTypeOf } from \"./FileTypeIcon\";\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 { KbdProps } from \"./Kbd\";\nexport { Kbd } from \"./Kbd\";\nexport type { LabelChipProps } from \"./LabelChip\";\nexport { LabelChip } from \"./LabelChip\";\nexport type { LightboxAction, LightboxImage } from \"./Lightbox\";\nexport { default as Lightbox } from \"./Lightbox\";\nexport type { LogTableEntry } from \"./LogEntriesTable\";\nexport { default as LogEntriesTable } from \"./LogEntriesTable\";\nexport { MapAttribution } from \"./MapAttribution\";\nexport { MapView } from \"./MapView\";\nexport { MapViewFrame } from \"./MapViewFrame\";\nexport { default as MarkdownView, type MarkdownRenderOptions, type MarkdownViewProps, renderSafeMarkdown } from \"./MarkdownView\";\nexport { type MatchRange, MatchText, type MatchTextProps, matchParts } from \"./MatchText\";\nexport type {\n MessageEventGroupProps,\n MessageEventRowProps,\n MessageGroupEntry,\n MessageRowAction,\n MessageRowAttachment,\n MessageRowAuthor,\n MessageRowFile,\n MessageRowMedia,\n MessageRowOrigin,\n MessageRowProgress,\n MessageRowProps,\n MessageRowQuote,\n MessageRowReaction,\n MessageRowStatus,\n MessageRowThread,\n MessageSystemRowProps,\n} from \"./MessageRow\";\nexport { MessageEventGroup, MessageEventRow, MessageRow, MessageSystemRow, startsMessageGroup } from \"./MessageRow\";\nexport { type MapPaletteColors, type MapTheme, mapPalette } from \"./map-palette\";\nexport { type MapStyleOptions, mapAttribution, mapStyle } from \"./map-style\";\nexport type {\n MapViewArea,\n MapViewFit,\n MapViewFrameProps,\n MapViewLine,\n MapViewLineTone,\n MapViewMarker,\n MapViewOwnLocation,\n MapViewPoint,\n MapViewProps,\n MapViewStyle,\n MapViewStyleSource,\n MapViewTone,\n} from \"./map-view-types\";\nexport {\n MARKDOWN_CALLOUT_CLASSES,\n MARKDOWN_CALLOUT_ICON_ATTRIBUTES,\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 { ReactionBar, type ReactionBarItem, type ReactionBarProps } from \"./ReactionBar\";\nexport type {\n ReferenceChipObjectProps,\n ReferenceChipPersonProps,\n ReferenceChipProps,\n ReferenceChipTimeProps,\n} from \"./ReferenceChip\";\nexport { REFERENCE_CHIP_MAX_CHARACTERS, ReferenceChip, truncateMiddle } from \"./ReferenceChip\";\nexport type { StepsProps } from \"./Steps\";\nexport { Steps } from \"./Steps\";\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":"AAoBA,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,SAAS,YAAY,QAAQ,kBAAkB;AAC/C,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,QAAsG,YAAY;AACnI,SAAS,OAAO,IAAI,QAAQ,QAAsE,YAAY;AAC9G,SAAwB,YAAY,EAA0B,aAAa,EAAE,UAAU,QAAQ,gBAAgB;AAC/G,SACE,cAAc,EACd,OAAO,IAAI,QAAQ,EAOnB,kBAAkB,EAClB,sBAAsB,QACjB,YAAY;AAEnB,SAAS,GAAG,QAAQ,OAAO;AAE3B,SAAS,SAAS,QAAQ,aAAa;AAEvC,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAEhD,SAAS,OAAO,IAAI,eAAe,QAAQ,mBAAmB;AAC9D,SAAS,cAAc,QAAQ,kBAAkB;AACjD,SAAS,OAAO,QAAQ,WAAW;AACnC,SAAS,YAAY,QAAQ,gBAAgB;AAC7C,SAAS,OAAO,IAAI,YAAY,EAAsD,kBAAkB,QAAQ,gBAAgB;AAChI,SAA0B,SAAS,EAAuB,UAAU,QAAQ,aAAa;AAmBzF,SAAS,iBAAiB,EAAE,eAAe,EAAE,UAAU,EAAE,gBAAgB,EAAE,kBAAkB,QAAQ,cAAc;AACnH,SAA+C,UAAU,QAAQ,eAAe;AAChF,SAA+B,cAAc,EAAE,QAAQ,QAAQ,aAAa;
|
|
1
|
+
{"version":3,"names":[],"sources":["content/index.ts"],"sourcesContent":["export type {\n CalendarAttendee,\n CalendarCommand,\n CalendarController,\n CalendarCustomView,\n CalendarDayBadge,\n CalendarEvent,\n CalendarEventColor,\n CalendarEventRenderContext,\n CalendarEventTimeChange,\n CalendarExternalDrag,\n CalendarLabels,\n CalendarProps,\n CalendarQuickCreateControls,\n CalendarRange,\n CalendarRecurrence,\n CalendarResource,\n CalendarSelectionControls,\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, ChartLegendEntry } 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-core\";\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 { highlightSql } from \"./code-highlight\";\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 FileGridGroup, type FileGridOpenProps, type FileGridProps, type FileGridSelectableProps } from \"./FileGrid\";\nexport { default as FileTree, type FileTreeActions, type FileTreeEntry, type FileTreeProps } from \"./FileTree\";\nexport { type FileType, FileTypeIcon, type FileTypeIconProps, fileKindLabel, fileTypeOf } from \"./FileTypeIcon\";\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 { KbdProps } from \"./Kbd\";\nexport { Kbd } from \"./Kbd\";\nexport type { LabelChipProps } from \"./LabelChip\";\nexport { LabelChip } from \"./LabelChip\";\nexport type { LightboxAction, LightboxImage } from \"./Lightbox\";\nexport { default as Lightbox } from \"./Lightbox\";\nexport type { LogTableEntry } from \"./LogEntriesTable\";\nexport { default as LogEntriesTable } from \"./LogEntriesTable\";\nexport { MapAttribution } from \"./MapAttribution\";\nexport { MapView } from \"./MapView\";\nexport { MapViewFrame } from \"./MapViewFrame\";\nexport { default as MarkdownView, type MarkdownRenderOptions, type MarkdownViewProps, renderSafeMarkdown } from \"./MarkdownView\";\nexport { type MatchRange, MatchText, type MatchTextProps, matchParts } from \"./MatchText\";\nexport type {\n MessageEventGroupProps,\n MessageEventRowProps,\n MessageGroupEntry,\n MessageRowAction,\n MessageRowAttachment,\n MessageRowAuthor,\n MessageRowFile,\n MessageRowMedia,\n MessageRowOrigin,\n MessageRowProgress,\n MessageRowProps,\n MessageRowQuote,\n MessageRowReaction,\n MessageRowStatus,\n MessageRowThread,\n MessageSystemRowProps,\n} from \"./MessageRow\";\nexport { MessageEventGroup, MessageEventRow, MessageRow, MessageSystemRow, startsMessageGroup } from \"./MessageRow\";\nexport { type MapPaletteColors, type MapTheme, mapPalette } from \"./map-palette\";\nexport { type MapStyleOptions, mapAttribution, mapStyle } from \"./map-style\";\nexport { MAP_SNAPSHOT_MAX_PIXELS, MapSnapshotError, type MapSnapshotErrorCode } from \"./map-view-snapshot\";\nexport type {\n MapViewArea,\n MapViewFit,\n MapViewFrameProps,\n MapViewHandle,\n MapViewLine,\n MapViewLineTone,\n MapViewMarker,\n MapViewOwnLocation,\n MapViewPoint,\n MapViewProps,\n MapViewSnapshot,\n MapViewSnapshotOptions,\n MapViewStyle,\n MapViewStyleSource,\n MapViewTone,\n} from \"./map-view-types\";\nexport {\n MARKDOWN_CALLOUT_CLASSES,\n MARKDOWN_CALLOUT_ICON_ATTRIBUTES,\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 { ReactionBar, type ReactionBarItem, type ReactionBarProps } from \"./ReactionBar\";\nexport type {\n ReferenceChipObjectProps,\n ReferenceChipPersonProps,\n ReferenceChipProps,\n ReferenceChipTimeProps,\n} from \"./ReferenceChip\";\nexport { REFERENCE_CHIP_MAX_CHARACTERS, ReferenceChip, truncateMiddle } from \"./ReferenceChip\";\nexport type { StepsProps } from \"./Steps\";\nexport { Steps } from \"./Steps\";\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":"AAoBA,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,SAAS,YAAY,QAAQ,kBAAkB;AAC/C,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,QAAsG,YAAY;AACnI,SAAS,OAAO,IAAI,QAAQ,QAAsE,YAAY;AAC9G,SAAwB,YAAY,EAA0B,aAAa,EAAE,UAAU,QAAQ,gBAAgB;AAC/G,SACE,cAAc,EACd,OAAO,IAAI,QAAQ,EAOnB,kBAAkB,EAClB,sBAAsB,QACjB,YAAY;AAEnB,SAAS,GAAG,QAAQ,OAAO;AAE3B,SAAS,SAAS,QAAQ,aAAa;AAEvC,SAAS,OAAO,IAAI,QAAQ,QAAQ,YAAY;AAEhD,SAAS,OAAO,IAAI,eAAe,QAAQ,mBAAmB;AAC9D,SAAS,cAAc,QAAQ,kBAAkB;AACjD,SAAS,OAAO,QAAQ,WAAW;AACnC,SAAS,YAAY,QAAQ,gBAAgB;AAC7C,SAAS,OAAO,IAAI,YAAY,EAAsD,kBAAkB,QAAQ,gBAAgB;AAChI,SAA0B,SAAS,EAAuB,UAAU,QAAQ,aAAa;AAmBzF,SAAS,iBAAiB,EAAE,eAAe,EAAE,UAAU,EAAE,gBAAgB,EAAE,kBAAkB,QAAQ,cAAc;AACnH,SAA+C,UAAU,QAAQ,eAAe;AAChF,SAA+B,cAAc,EAAE,QAAQ,QAAQ,aAAa;AAC5E,SAAS,uBAAuB,EAAE,gBAAgB,QAAmC,qBAAqB;AAkB1G,SACE,wBAAwB,EACxB,gCAAgC,EAIhC,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;AACtD,SAAS,WAAW,QAAqD,eAAe;AAOxF,SAAS,6BAA6B,EAAE,aAAa,EAAE,cAAc,QAAQ,iBAAiB;AAE9F,SAAS,KAAK,QAAQ,SAAS;AAE/B,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,23 @@
|
|
|
1
|
+
import { PbfReader } from "pbf";
|
|
2
|
+
const readGlyph = (tag, glyph, pbf) => {
|
|
3
|
+
if (tag === 1) glyph.id = pbf.readVarint();else if (tag === 2) glyph.bitmap = pbf.readBytes();else if (tag === 3) glyph.width = pbf.readVarint();else if (tag === 4) glyph.height = pbf.readVarint();else if (tag === 5) glyph.left = pbf.readSVarint();else if (tag === 6) glyph.top = pbf.readSVarint();else if (tag === 7) glyph.advance = pbf.readVarint();
|
|
4
|
+
};
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Validate the glyphs MapLibre will decode before it can silently substitute local fonts.
|
|
8
|
+
* Uses the same PBF reader and 3px bitmap border as MapLibre 6, without allocating glyph images.
|
|
9
|
+
*/
|
|
10
|
+
export function validateMapGlyphs(data) {
|
|
11
|
+
let count = 0;
|
|
12
|
+
new PbfReader(data).readFields((tag, _unused, pbf) => {
|
|
13
|
+
if (tag !== 1) return;
|
|
14
|
+
pbf.readMessage((tag, _unused, pbf) => {
|
|
15
|
+
if (tag !== 3) return;
|
|
16
|
+
const glyph = pbf.readMessage(readGlyph, {});
|
|
17
|
+
if (glyph.id === undefined || !Number.isInteger(glyph.id) || glyph.id < 0 || glyph.id > 0x10ffff || glyph.width === undefined || glyph.height === undefined || glyph.left === undefined || glyph.top === undefined || glyph.advance === undefined || !glyph.bitmap && (glyph.width !== 0 || glyph.height !== 0) || glyph.bitmap !== undefined && glyph.bitmap.byteLength !== (glyph.width + 6) * (glyph.height + 6)) throw new Error("Invalid map glyph data");
|
|
18
|
+
count++;
|
|
19
|
+
}, null);
|
|
20
|
+
}, null);
|
|
21
|
+
if (!count) throw new Error("Missing map glyph data");
|
|
22
|
+
}
|
|
23
|
+
//# sourceMappingURL=map-view-glyphs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["content/map-view-glyphs.ts"],"sourcesContent":["import { PbfReader } from \"pbf\";\n\ntype Glyph = { id?: number; bitmap?: Uint8Array; width?: number; height?: number; left?: number; top?: number; advance?: number };\nconst readGlyph = (tag: number, glyph: Glyph, pbf: PbfReader) => {\n if (tag === 1) glyph.id = pbf.readVarint();\n else if (tag === 2) glyph.bitmap = pbf.readBytes();\n else if (tag === 3) glyph.width = pbf.readVarint();\n else if (tag === 4) glyph.height = pbf.readVarint();\n else if (tag === 5) glyph.left = pbf.readSVarint();\n else if (tag === 6) glyph.top = pbf.readSVarint();\n else if (tag === 7) glyph.advance = pbf.readVarint();\n};\n\n/**\n * Validate the glyphs MapLibre will decode before it can silently substitute local fonts.\n * Uses the same PBF reader and 3px bitmap border as MapLibre 6, without allocating glyph images.\n */\nexport function validateMapGlyphs(data: ArrayBuffer): void {\n let count = 0;\n new PbfReader(data).readFields((tag, _unused, pbf) => {\n if (tag !== 1) return;\n pbf.readMessage((tag, _unused, pbf) => {\n if (tag !== 3) return;\n const glyph = pbf.readMessage<Glyph>(readGlyph, {});\n if (\n glyph.id === undefined ||\n !Number.isInteger(glyph.id) ||\n glyph.id < 0 ||\n glyph.id > 0x10ffff ||\n glyph.width === undefined ||\n glyph.height === undefined ||\n glyph.left === undefined ||\n glyph.top === undefined ||\n glyph.advance === undefined ||\n (!glyph.bitmap && (glyph.width !== 0 || glyph.height !== 0)) ||\n (glyph.bitmap !== undefined && glyph.bitmap.byteLength !== (glyph.width + 6) * (glyph.height + 6))\n )\n throw new Error(\"Invalid map glyph data\");\n count++;\n }, null);\n }, null);\n if (!count) throw new Error(\"Missing map glyph data\");\n}\n"],"mappings":"AAAA,SAAS,SAAS,QAAQ,KAAK;AAG/B,MAAM,SAAS,GAAG,CAAC,GAAW,EAAE,KAAY,EAAE,GAAc,KAAK;EAC/D,IAAI,GAAG,KAAK,CAAC,EAAE,KAAK,CAAC,EAAE,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,KACtC,IAAI,GAAG,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,SAAS,CAAC,CAAC,CAAC,KAC9C,IAAI,GAAG,KAAK,CAAC,EAAE,KAAK,CAAC,KAAK,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,KAC9C,IAAI,GAAG,KAAK,CAAC,EAAE,KAAK,CAAC,MAAM,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,KAC/C,IAAI,GAAG,KAAK,CAAC,EAAE,KAAK,CAAC,IAAI,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,KAC9C,IAAI,GAAG,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,GAAG,GAAG,CAAC,WAAW,CAAC,CAAC,CAAC,KAC7C,IAAI,GAAG,KAAK,CAAC,EAAE,KAAK,CAAC,OAAO,GAAG,GAAG,CAAC,UAAU,CAAC,CAAC;AACtD,CAAC;;AAED;AACA;AACA;AACA;AACA,OAAO,SAAS,iBAAiB,CAAC,IAAiB,EAAQ;EACzD,IAAI,KAAK,GAAG,CAAC;EACb,IAAI,SAAS,CAAC,IAAI,CAAC,CAAC,UAAU,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,KAAK;IACpD,IAAI,GAAG,KAAK,CAAC,EAAE;IACf,GAAG,CAAC,WAAW,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,KAAK;MACrC,IAAI,GAAG,KAAK,CAAC,EAAE;MACf,MAAM,KAAK,GAAG,GAAG,CAAC,WAAW,CAAQ,SAAS,EAAE,CAAC,CAAC,CAAC;MACnD,IACE,KAAK,CAAC,EAAE,KAAK,SAAS,IACtB,CAAC,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC,IAC3B,KAAK,CAAC,EAAE,GAAG,CAAC,IACZ,KAAK,CAAC,EAAE,GAAG,QAAQ,IACnB,KAAK,CAAC,KAAK,KAAK,SAAS,IACzB,KAAK,CAAC,MAAM,KAAK,SAAS,IAC1B,KAAK,CAAC,IAAI,KAAK,SAAS,IACxB,KAAK,CAAC,GAAG,KAAK,SAAS,IACvB,KAAK,CAAC,OAAO,KAAK,SAAS,IAC1B,CAAC,KAAK,CAAC,MAAM,KAAK,KAAK,CAAC,KAAK,KAAK,CAAC,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAE,IAC3D,KAAK,CAAC,MAAM,KAAK,SAAS,IAAI,KAAK,CAAC,MAAM,CAAC,UAAU,KAAK,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,KAAK,KAAK,CAAC,MAAM,GAAG,CAAC,CAAE,EAElG,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC;MAC3C,KAAK,EAAE;IACT,CAAC,EAAE,IAAI,CAAC;EACV,CAAC,EAAE,IAAI,CAAC;EACR,IAAI,CAAC,KAAK,EAAE,MAAM,IAAI,KAAK,CAAC,wBAAwB,CAAC;AACvD","ignoreList":[]}
|
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
/** Same decoded RGBA budget as Cloud's isolated print-image worker (64 MiB). */
|
|
2
|
+
export const MAP_SNAPSHOT_MAX_PIXELS = 16 * 1024 * 1024;
|
|
3
|
+
export class MapSnapshotError extends Error {
|
|
4
|
+
constructor(code) {
|
|
5
|
+
super(`Map snapshot: ${code}`);
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.name = "MapSnapshotError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function snapshotAborted(signal) {
|
|
11
|
+
return signal.reason instanceof MapSnapshotError ? signal.reason : new MapSnapshotError("aborted");
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Also observes late rejection; a cancelled image/font/encoding operation never becomes unhandled. */
|
|
15
|
+
export function snapshotWait(promise, signal) {
|
|
16
|
+
return new Promise((resolve, reject) => {
|
|
17
|
+
const abort = () => reject(snapshotAborted(signal));
|
|
18
|
+
if (signal.aborted) return abort();
|
|
19
|
+
signal.addEventListener("abort", abort, {
|
|
20
|
+
once: true
|
|
21
|
+
});
|
|
22
|
+
promise.then(resolve, reject).finally(() => signal.removeEventListener("abort", abort));
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
const svgNamespace = "http://www.w3.org/2000/svg";
|
|
26
|
+
// Only renderer-owned shapes and paint cross into the rasterizer; no foreignObject, links, images or CSS URLs.
|
|
27
|
+
const paints = ["fill", "fill-opacity", "stroke", "stroke-width", "stroke-opacity", "stroke-dasharray", "stroke-linecap", "stroke-linejoin"];
|
|
28
|
+
function geometryImage(root, bounds) {
|
|
29
|
+
const source = root.querySelector(".k2b-map-view__geometry");
|
|
30
|
+
if (!source) return;
|
|
31
|
+
const svg = document.createElementNS(svgNamespace, "svg");
|
|
32
|
+
svg.setAttribute("width", String(bounds.width));
|
|
33
|
+
svg.setAttribute("height", String(bounds.height));
|
|
34
|
+
const geometry = document.createElementNS(svgNamespace, "svg");
|
|
35
|
+
const box = source.getBoundingClientRect();
|
|
36
|
+
for (const [name, value] of Object.entries({
|
|
37
|
+
x: box.left - bounds.left,
|
|
38
|
+
y: box.top - bounds.top,
|
|
39
|
+
width: box.width,
|
|
40
|
+
height: box.height,
|
|
41
|
+
viewBox: source.getAttribute("viewBox") ?? "",
|
|
42
|
+
preserveAspectRatio: "none",
|
|
43
|
+
overflow: "visible"
|
|
44
|
+
})) geometry.setAttribute(name, String(value));
|
|
45
|
+
for (const shape of source.querySelectorAll("path")) {
|
|
46
|
+
const copy = document.createElementNS(svgNamespace, shape.localName);
|
|
47
|
+
for (const name of ["d", "cx", "cy", "r", "vector-effect"]) {
|
|
48
|
+
const value = shape.getAttribute(name);
|
|
49
|
+
if (value !== null) copy.setAttribute(name, value);
|
|
50
|
+
}
|
|
51
|
+
const style = getComputedStyle(shape);
|
|
52
|
+
for (const name of paints) {
|
|
53
|
+
const value = style.getPropertyValue(name);
|
|
54
|
+
if (value.includes("url(")) throw new MapSnapshotError("capture_failed");
|
|
55
|
+
copy.setAttribute(name, value);
|
|
56
|
+
}
|
|
57
|
+
geometry.append(copy);
|
|
58
|
+
}
|
|
59
|
+
svg.append(geometry);
|
|
60
|
+
const image = new Image();
|
|
61
|
+
image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(new XMLSerializer().serializeToString(svg))}`;
|
|
62
|
+
return image;
|
|
63
|
+
}
|
|
64
|
+
function drawPins(ctx, root, bounds) {
|
|
65
|
+
const pins = Array.from(root.querySelectorAll(".k2b-map-view__pin"));
|
|
66
|
+
// The selected pin paints above the others, as in the DOM.
|
|
67
|
+
pins.sort((a, b) => Number(a.getAttribute("aria-pressed") === "true") - Number(b.getAttribute("aria-pressed") === "true"));
|
|
68
|
+
for (const pin of pins) {
|
|
69
|
+
const box = pin.getBoundingClientRect();
|
|
70
|
+
const style = getComputedStyle(pin);
|
|
71
|
+
const x = box.left - bounds.left,
|
|
72
|
+
y = box.top - bounds.top;
|
|
73
|
+
// Resolve CSS variables through a paint property, since custom properties can still contain var().
|
|
74
|
+
const selected = pin.getAttribute("aria-pressed") === "true" || pin.hasAttribute("data-selected");
|
|
75
|
+
const stop = pin.hasAttribute("data-stop");
|
|
76
|
+
const surface = getComputedStyle(root.querySelector(".k2b-map-view__attribution") ?? root).backgroundColor;
|
|
77
|
+
ctx.save();
|
|
78
|
+
if (pin.hasAttribute("data-drop")) {
|
|
79
|
+
const span = pin.querySelector("span");
|
|
80
|
+
ctx.fillStyle = getComputedStyle(span).backgroundColor;
|
|
81
|
+
ctx.beginPath();
|
|
82
|
+
ctx.ellipse(x + box.width / 2, y + box.height / 2, box.width / 2, box.height / 2, 0, Math.PI, Math.PI * 2);
|
|
83
|
+
ctx.lineTo(x + box.width, y + box.height * 0.68);
|
|
84
|
+
ctx.lineTo(x + box.width / 2, y + box.height);
|
|
85
|
+
ctx.lineTo(x, y + box.height * 0.68);
|
|
86
|
+
ctx.closePath();
|
|
87
|
+
ctx.fill();
|
|
88
|
+
if (selected) {
|
|
89
|
+
ctx.fillStyle = surface;
|
|
90
|
+
ctx.beginPath();
|
|
91
|
+
ctx.arc(x + box.width / 2, y + box.height * 0.34, 4, 0, Math.PI * 2);
|
|
92
|
+
ctx.fill();
|
|
93
|
+
}
|
|
94
|
+
} else {
|
|
95
|
+
ctx.beginPath();
|
|
96
|
+
ctx.arc(x + box.width / 2, y + box.height / 2, box.width / 2 + 2.5, 0, Math.PI * 2);
|
|
97
|
+
ctx.fillStyle = surface;
|
|
98
|
+
ctx.fill();
|
|
99
|
+
ctx.beginPath();
|
|
100
|
+
ctx.arc(x + box.width / 2, y + box.height / 2, box.width / 2, 0, Math.PI * 2);
|
|
101
|
+
ctx.fillStyle = style.backgroundColor;
|
|
102
|
+
ctx.fill();
|
|
103
|
+
// The first box-shadow is the resolved inset tone ring. Text ink can differ in dark mode.
|
|
104
|
+
const ring = style.boxShadow.match(/(?:rgba?|color|oklch|oklab)\([^)]*\)/)?.[0];
|
|
105
|
+
if (!ring && !stop && !selected) throw new MapSnapshotError("capture_failed");
|
|
106
|
+
ctx.strokeStyle = stop || selected ? style.backgroundColor : ring;
|
|
107
|
+
ctx.lineWidth = 2;
|
|
108
|
+
ctx.beginPath();
|
|
109
|
+
ctx.arc(x + box.width / 2, y + box.height / 2, box.width / 2 - 1, 0, Math.PI * 2);
|
|
110
|
+
ctx.stroke();
|
|
111
|
+
ctx.fillStyle = style.color;
|
|
112
|
+
ctx.font = `${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;
|
|
113
|
+
ctx.textAlign = "center";
|
|
114
|
+
ctx.textBaseline = "middle";
|
|
115
|
+
ctx.fillText(pin.textContent ?? "", x + box.width / 2, y + box.height / 2);
|
|
116
|
+
}
|
|
117
|
+
ctx.restore();
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function drawAttribution(ctx, root, bounds) {
|
|
121
|
+
const attribution = root.querySelector(".k2b-map-view__attribution");
|
|
122
|
+
if (!attribution?.textContent?.trim()) throw new MapSnapshotError("capture_failed");
|
|
123
|
+
const box = attribution.getBoundingClientRect();
|
|
124
|
+
const style = getComputedStyle(attribution);
|
|
125
|
+
ctx.fillStyle = style.backgroundColor;
|
|
126
|
+
ctx.fillRect(box.left - bounds.left, box.top - bounds.top, box.width, box.height);
|
|
127
|
+
const walker = document.createTreeWalker(attribution, NodeFilter.SHOW_TEXT);
|
|
128
|
+
while (walker.nextNode()) {
|
|
129
|
+
const node = walker.currentNode;
|
|
130
|
+
const parent = node.parentElement;
|
|
131
|
+
const ink = getComputedStyle(parent);
|
|
132
|
+
ctx.fillStyle = ink.color;
|
|
133
|
+
ctx.font = `${ink.fontWeight} ${ink.fontSize} ${ink.fontFamily}`;
|
|
134
|
+
ctx.textBaseline = "top";
|
|
135
|
+
ctx.textAlign = "left";
|
|
136
|
+
// Per-character ranges preserve line breaks, wrapping, and bidi positions without capturing arbitrary HTML.
|
|
137
|
+
const range = document.createRange();
|
|
138
|
+
for (let i = 0; i < (node.textContent?.length ?? 0); i++) {
|
|
139
|
+
range.setStart(node, i);
|
|
140
|
+
range.setEnd(node, i + 1);
|
|
141
|
+
const r = range.getBoundingClientRect();
|
|
142
|
+
ctx.fillText(node.textContent[i], r.left - bounds.left, r.top - bounds.top);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/** Called synchronously in MapLibre's render event, before WebGL clears its drawing buffer. */
|
|
148
|
+
export async function rasterizeMapView(root, base, signal) {
|
|
149
|
+
const bounds = root.getBoundingClientRect();
|
|
150
|
+
const width = Math.round(bounds.width),
|
|
151
|
+
height = Math.round(bounds.height);
|
|
152
|
+
if (width < 1 || height < 1 || width > 16383 || height > 16383 || width * height > MAP_SNAPSHOT_MAX_PIXELS) throw new MapSnapshotError("too_large");
|
|
153
|
+
const canvas = document.createElement("canvas");
|
|
154
|
+
canvas.width = width;
|
|
155
|
+
canvas.height = height;
|
|
156
|
+
const ctx = canvas.getContext("2d");
|
|
157
|
+
if (!ctx) throw new MapSnapshotError("capture_failed");
|
|
158
|
+
try {
|
|
159
|
+
// Copy the GPU frame immediately. No preserveDrawingBuffer overhead for ordinary interactive maps.
|
|
160
|
+
ctx.drawImage(base, 0, 0, width, height);
|
|
161
|
+
const geometry = geometryImage(root, bounds);
|
|
162
|
+
// Save all overlay pixels before yielding, so subsequent DOM changes cannot mix revisions.
|
|
163
|
+
const overlay = document.createElement("canvas");
|
|
164
|
+
overlay.width = width;
|
|
165
|
+
overlay.height = height;
|
|
166
|
+
try {
|
|
167
|
+
const ink = overlay.getContext("2d");
|
|
168
|
+
drawPins(ink, root, bounds);
|
|
169
|
+
drawAttribution(ink, root, bounds);
|
|
170
|
+
if (geometry) {
|
|
171
|
+
await snapshotWait(geometry.decode(), signal);
|
|
172
|
+
ctx.drawImage(geometry, 0, 0, width, height);
|
|
173
|
+
}
|
|
174
|
+
ctx.drawImage(overlay, 0, 0);
|
|
175
|
+
} finally {
|
|
176
|
+
overlay.width = overlay.height = 0;
|
|
177
|
+
}
|
|
178
|
+
const data = await snapshotWait(new Promise((resolve, reject) => canvas.toBlob(blob => blob ? resolve(blob) : reject(new MapSnapshotError("capture_failed")), "image/png")), signal);
|
|
179
|
+
return {
|
|
180
|
+
data,
|
|
181
|
+
width,
|
|
182
|
+
height
|
|
183
|
+
};
|
|
184
|
+
} catch (error) {
|
|
185
|
+
if (error instanceof MapSnapshotError) throw error;
|
|
186
|
+
throw new MapSnapshotError("capture_failed");
|
|
187
|
+
} finally {
|
|
188
|
+
canvas.width = canvas.height = 0;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=map-view-snapshot.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["content/map-view-snapshot.ts"],"sourcesContent":["import type { MapViewSnapshot } from \"./map-view-types\";\n\n/** Same decoded RGBA budget as Cloud's isolated print-image worker (64 MiB). */\nexport const MAP_SNAPSHOT_MAX_PIXELS = 16 * 1024 * 1024;\nexport type MapSnapshotErrorCode =\n | \"not_ready\"\n | \"resource_failed\"\n | \"changed\"\n | \"disposed\"\n | \"busy\"\n | \"too_large\"\n | \"timed_out\"\n | \"aborted\"\n | \"capture_failed\";\nexport class MapSnapshotError extends Error {\n constructor(readonly code: MapSnapshotErrorCode) {\n super(`Map snapshot: ${code}`);\n this.name = \"MapSnapshotError\";\n }\n}\n\nexport function snapshotAborted(signal: AbortSignal): MapSnapshotError {\n return signal.reason instanceof MapSnapshotError ? signal.reason : new MapSnapshotError(\"aborted\");\n}\n\n/** Also observes late rejection; a cancelled image/font/encoding operation never becomes unhandled. */\nexport function snapshotWait<T>(promise: Promise<T>, signal: AbortSignal): Promise<T> {\n return new Promise((resolve, reject) => {\n const abort = () => reject(snapshotAborted(signal));\n if (signal.aborted) return abort();\n signal.addEventListener(\"abort\", abort, { once: true });\n promise.then(resolve, reject).finally(() => signal.removeEventListener(\"abort\", abort));\n });\n}\n\nconst svgNamespace = \"http://www.w3.org/2000/svg\";\n// Only renderer-owned shapes and paint cross into the rasterizer; no foreignObject, links, images or CSS URLs.\nconst paints = [\n \"fill\",\n \"fill-opacity\",\n \"stroke\",\n \"stroke-width\",\n \"stroke-opacity\",\n \"stroke-dasharray\",\n \"stroke-linecap\",\n \"stroke-linejoin\",\n] as const;\nfunction geometryImage(root: HTMLElement, bounds: DOMRect): HTMLImageElement | undefined {\n const source = root.querySelector<SVGSVGElement>(\".k2b-map-view__geometry\");\n if (!source) return;\n const svg = document.createElementNS(svgNamespace, \"svg\");\n svg.setAttribute(\"width\", String(bounds.width));\n svg.setAttribute(\"height\", String(bounds.height));\n const geometry = document.createElementNS(svgNamespace, \"svg\");\n const box = source.getBoundingClientRect();\n for (const [name, value] of Object.entries({\n x: box.left - bounds.left,\n y: box.top - bounds.top,\n width: box.width,\n height: box.height,\n viewBox: source.getAttribute(\"viewBox\") ?? \"\",\n preserveAspectRatio: \"none\",\n overflow: \"visible\",\n }))\n geometry.setAttribute(name, String(value));\n for (const shape of source.querySelectorAll(\"path\")) {\n const copy = document.createElementNS(svgNamespace, shape.localName);\n for (const name of [\"d\", \"cx\", \"cy\", \"r\", \"vector-effect\"]) {\n const value = shape.getAttribute(name);\n if (value !== null) copy.setAttribute(name, value);\n }\n const style = getComputedStyle(shape);\n for (const name of paints) {\n const value = style.getPropertyValue(name);\n if (value.includes(\"url(\")) throw new MapSnapshotError(\"capture_failed\");\n copy.setAttribute(name, value);\n }\n geometry.append(copy);\n }\n svg.append(geometry);\n const image = new Image();\n image.src = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(new XMLSerializer().serializeToString(svg))}`;\n return image;\n}\n\nfunction drawPins(ctx: CanvasRenderingContext2D, root: HTMLElement, bounds: DOMRect) {\n const pins = Array.from(root.querySelectorAll<HTMLElement>(\".k2b-map-view__pin\"));\n // The selected pin paints above the others, as in the DOM.\n pins.sort((a, b) => Number(a.getAttribute(\"aria-pressed\") === \"true\") - Number(b.getAttribute(\"aria-pressed\") === \"true\"));\n for (const pin of pins) {\n const box = pin.getBoundingClientRect();\n const style = getComputedStyle(pin);\n const x = box.left - bounds.left,\n y = box.top - bounds.top;\n // Resolve CSS variables through a paint property, since custom properties can still contain var().\n const selected = pin.getAttribute(\"aria-pressed\") === \"true\" || pin.hasAttribute(\"data-selected\");\n const stop = pin.hasAttribute(\"data-stop\");\n const surface = getComputedStyle(root.querySelector(\".k2b-map-view__attribution\") ?? root).backgroundColor;\n ctx.save();\n if (pin.hasAttribute(\"data-drop\")) {\n const span = pin.querySelector(\"span\")!;\n ctx.fillStyle = getComputedStyle(span).backgroundColor;\n ctx.beginPath();\n ctx.ellipse(x + box.width / 2, y + box.height / 2, box.width / 2, box.height / 2, 0, Math.PI, Math.PI * 2);\n ctx.lineTo(x + box.width, y + box.height * 0.68);\n ctx.lineTo(x + box.width / 2, y + box.height);\n ctx.lineTo(x, y + box.height * 0.68);\n ctx.closePath();\n ctx.fill();\n if (selected) {\n ctx.fillStyle = surface;\n ctx.beginPath();\n ctx.arc(x + box.width / 2, y + box.height * 0.34, 4, 0, Math.PI * 2);\n ctx.fill();\n }\n } else {\n ctx.beginPath();\n ctx.arc(x + box.width / 2, y + box.height / 2, box.width / 2 + 2.5, 0, Math.PI * 2);\n ctx.fillStyle = surface;\n ctx.fill();\n ctx.beginPath();\n ctx.arc(x + box.width / 2, y + box.height / 2, box.width / 2, 0, Math.PI * 2);\n ctx.fillStyle = style.backgroundColor;\n ctx.fill();\n // The first box-shadow is the resolved inset tone ring. Text ink can differ in dark mode.\n const ring = style.boxShadow.match(/(?:rgba?|color|oklch|oklab)\\([^)]*\\)/)?.[0];\n if (!ring && !stop && !selected) throw new MapSnapshotError(\"capture_failed\");\n ctx.strokeStyle = stop || selected ? style.backgroundColor : ring!;\n ctx.lineWidth = 2;\n ctx.beginPath();\n ctx.arc(x + box.width / 2, y + box.height / 2, box.width / 2 - 1, 0, Math.PI * 2);\n ctx.stroke();\n ctx.fillStyle = style.color;\n ctx.font = `${style.fontWeight} ${style.fontSize} ${style.fontFamily}`;\n ctx.textAlign = \"center\";\n ctx.textBaseline = \"middle\";\n ctx.fillText(pin.textContent ?? \"\", x + box.width / 2, y + box.height / 2);\n }\n ctx.restore();\n }\n}\n\nfunction drawAttribution(ctx: CanvasRenderingContext2D, root: HTMLElement, bounds: DOMRect) {\n const attribution = root.querySelector<HTMLElement>(\".k2b-map-view__attribution\");\n if (!attribution?.textContent?.trim()) throw new MapSnapshotError(\"capture_failed\");\n const box = attribution.getBoundingClientRect();\n const style = getComputedStyle(attribution);\n ctx.fillStyle = style.backgroundColor;\n ctx.fillRect(box.left - bounds.left, box.top - bounds.top, box.width, box.height);\n const walker = document.createTreeWalker(attribution, NodeFilter.SHOW_TEXT);\n while (walker.nextNode()) {\n const node = walker.currentNode;\n const parent = node.parentElement!;\n const ink = getComputedStyle(parent);\n ctx.fillStyle = ink.color;\n ctx.font = `${ink.fontWeight} ${ink.fontSize} ${ink.fontFamily}`;\n ctx.textBaseline = \"top\";\n ctx.textAlign = \"left\";\n // Per-character ranges preserve line breaks, wrapping, and bidi positions without capturing arbitrary HTML.\n const range = document.createRange();\n for (let i = 0; i < (node.textContent?.length ?? 0); i++) {\n range.setStart(node, i);\n range.setEnd(node, i + 1);\n const r = range.getBoundingClientRect();\n ctx.fillText(node.textContent![i]!, r.left - bounds.left, r.top - bounds.top);\n }\n }\n}\n\n/** Called synchronously in MapLibre's render event, before WebGL clears its drawing buffer. */\nexport async function rasterizeMapView(root: HTMLElement, base: HTMLCanvasElement, signal: AbortSignal): Promise<MapViewSnapshot> {\n const bounds = root.getBoundingClientRect();\n const width = Math.round(bounds.width),\n height = Math.round(bounds.height);\n if (width < 1 || height < 1 || width > 16383 || height > 16383 || width * height > MAP_SNAPSHOT_MAX_PIXELS)\n throw new MapSnapshotError(\"too_large\");\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n const ctx = canvas.getContext(\"2d\");\n if (!ctx) throw new MapSnapshotError(\"capture_failed\");\n try {\n // Copy the GPU frame immediately. No preserveDrawingBuffer overhead for ordinary interactive maps.\n ctx.drawImage(base, 0, 0, width, height);\n const geometry = geometryImage(root, bounds);\n // Save all overlay pixels before yielding, so subsequent DOM changes cannot mix revisions.\n const overlay = document.createElement(\"canvas\");\n overlay.width = width;\n overlay.height = height;\n try {\n const ink = overlay.getContext(\"2d\")!;\n drawPins(ink, root, bounds);\n drawAttribution(ink, root, bounds);\n if (geometry) {\n await snapshotWait(geometry.decode(), signal);\n ctx.drawImage(geometry, 0, 0, width, height);\n }\n ctx.drawImage(overlay, 0, 0);\n } finally {\n overlay.width = overlay.height = 0;\n }\n const data = await snapshotWait(\n new Promise<Blob>((resolve, reject) =>\n canvas.toBlob((blob) => (blob ? resolve(blob) : reject(new MapSnapshotError(\"capture_failed\"))), \"image/png\"),\n ),\n signal,\n );\n return { data, width, height };\n } catch (error) {\n if (error instanceof MapSnapshotError) throw error;\n throw new MapSnapshotError(\"capture_failed\");\n } finally {\n canvas.width = canvas.height = 0;\n }\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["content/map-view-types.ts"],"sourcesContent":["import type { StyleSpecification } from \"maplibre-gl\";\nimport type { JSX } from \"solid-js\";\n\nexport type MapViewPoint = { lat: number; lon: number };\nexport type MapViewTone = \"accent\" | \"second\" | \"neutral\" | \"warning\";\nexport type MapViewMarker = {\n id: string;\n point: MapViewPoint;\n /** A number, a single letter, or \"+\". Omit for a drop pin. */\n mark?: number | string;\n label: string;\n tone?: MapViewTone;\n selected?: boolean;\n};\nexport type MapViewLineTone = \"accent\" | \"muted\" | \"preview\" | \"previous\";\nexport type MapViewLine = { id: string; tone?: MapViewLineTone } & ({ polyline6: string } | { points: MapViewPoint[] });\nexport type MapViewArea = { id: string; polygon: MapViewPoint[]; tone?: MapViewTone };\nexport type MapViewOwnLocation = { point: MapViewPoint; accuracyMeters?: number };\nexport type MapViewStyle = string | StyleSpecification;\nexport type MapViewStyleSource = MapViewStyle | { light: MapViewStyle; dark: MapViewStyle };\nexport type MapViewFit = \"all\" | \"markers\";\nexport type MapViewFrameProps = {\n markers?: MapViewMarker[];\n lines?: MapViewLine[];\n areas?: MapViewArea[];\n ownLocation?: MapViewOwnLocation;\n height: number;\n padding?: number;\n fit?: MapViewFit;\n selectedId?: string;\n attribution?: JSX.Element;\n class?: string;\n};\nexport type MapViewProps = Omit<MapViewFrameProps, \"attribution\"> & {\n style: MapViewStyleSource;\n attribution: JSX.Element;\n onSelect?: (id: string) => void;\n infoTile?: (marker: MapViewMarker) => JSX.Element;\n notice?: JSX.Element;\n empty?: JSX.Element;\n onLocate?: () => void;\n /** Observe rendering and resource failures without receiving provider responses or URLs. */\n onStateChange?: (state: \"static\" | \"loading\" | \"ready\" | \"error\" | \"no-webgl\" | \"empty\") => void;\n};\n"],"mappings":"","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["content/map-view-types.ts"],"sourcesContent":["import type { StyleSpecification } from \"maplibre-gl\";\nimport type { JSX } from \"solid-js\";\n\nexport type MapViewPoint = { lat: number; lon: number };\nexport type MapViewTone = \"accent\" | \"second\" | \"neutral\" | \"warning\";\nexport type MapViewMarker = {\n id: string;\n point: MapViewPoint;\n /** A number, a single letter, or \"+\". Omit for a drop pin. */\n mark?: number | string;\n label: string;\n tone?: MapViewTone;\n selected?: boolean;\n};\nexport type MapViewLineTone = \"accent\" | \"muted\" | \"preview\" | \"previous\";\nexport type MapViewLine = { id: string; tone?: MapViewLineTone } & ({ polyline6: string } | { points: MapViewPoint[] });\nexport type MapViewArea = { id: string; polygon: MapViewPoint[]; tone?: MapViewTone };\nexport type MapViewOwnLocation = { point: MapViewPoint; accuracyMeters?: number };\nexport type MapViewStyle = string | StyleSpecification;\nexport type MapViewStyleSource = MapViewStyle | { light: MapViewStyle; dark: MapViewStyle };\nexport type MapViewFit = \"all\" | \"markers\";\nexport type MapViewFrameProps = {\n markers?: MapViewMarker[];\n lines?: MapViewLine[];\n areas?: MapViewArea[];\n ownLocation?: MapViewOwnLocation;\n height: number;\n padding?: number;\n fit?: MapViewFit;\n selectedId?: string;\n attribution?: JSX.Element;\n class?: string;\n};\nexport type MapViewSnapshot = { data: Blob; width: number; height: number };\nexport type MapViewSnapshotOptions = { signal?: AbortSignal; timeoutMs?: number };\nexport type MapViewHandle = {\n /** Capture the current complete view; never loads, pans, refits, or retries provider resources. */\n snapshot: (options?: MapViewSnapshotOptions) => Promise<MapViewSnapshot>;\n};\nexport type MapViewProps = Omit<MapViewFrameProps, \"attribution\"> & {\n style: MapViewStyleSource;\n /** Browser-only handle; remains safely callable after disposal (rejects disposed). */\n ref?: (handle: MapViewHandle) => void;\n attribution: JSX.Element;\n onSelect?: (id: string) => void;\n infoTile?: (marker: MapViewMarker) => JSX.Element;\n notice?: JSX.Element;\n empty?: JSX.Element;\n onLocate?: () => void;\n /** Observe rendering and resource failures without receiving provider responses or URLs. */\n onStateChange?: (state: \"static\" | \"loading\" | \"ready\" | \"error\" | \"no-webgl\" | \"empty\") => void;\n};\n"],"mappings":"","ignoreList":[]}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { ssrStyleProperty as _$ssrStyleProperty } from "solid-js/web";
|
|
1
2
|
import { memo as _$memo } from "solid-js/web";
|
|
2
3
|
import { createComponent as _$createComponent } from "solid-js/web";
|
|
3
4
|
import { ssr as _$ssr } from "solid-js/web";
|
|
@@ -6,14 +7,17 @@ import { mergeProps as _$mergeProps } from "solid-js/web";
|
|
|
6
7
|
import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
|
|
7
8
|
import { escape as _$escape } from "solid-js/web";
|
|
8
9
|
var _tmpl$ = "<i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\"></i>",
|
|
9
|
-
_tmpl$2 = ["<div class=\"k2b-combobox\"", "><div class=\"k2b-combobox__input\"><i class=\"ti ti-search\" aria-hidden=\"true\"></i>", "", "</div><div popover=\"manual\" class=\"k2b-choice-popover\"><div class=\"k2b-choice-options\" role=\"listbox\"", ">", "", "</div
|
|
10
|
+
_tmpl$2 = ["<div class=\"k2b-combobox\"", "><div class=\"k2b-combobox__input\"><i class=\"ti ti-search\" aria-hidden=\"true\"></i>", "", "</div><div popover=\"manual\" class=\"k2b-choice-popover\"><div class=\"k2b-choice-options\" role=\"listbox\"", ">", "", "", "</div>", "</div></div>"],
|
|
10
11
|
_tmpl$3 = "<i class=\"ti ti-chevron-down\" aria-hidden=\"true\"></i>",
|
|
11
|
-
_tmpl$4 = ["<div class=\"k2b-choice-status\"
|
|
12
|
-
_tmpl$5 = ["<div class=\"k2b-choice-status\">", "</div>"],
|
|
13
|
-
_tmpl$6 = ["<
|
|
14
|
-
_tmpl$7 = ["<
|
|
15
|
-
_tmpl$8 = ["<
|
|
16
|
-
|
|
12
|
+
_tmpl$4 = ["<div class=\"k2b-choice-status\">", "</div>"],
|
|
13
|
+
_tmpl$5 = ["<div class=\"k2b-choice-status\" data-tone=\"danger\"><span>", "</span><button type=\"button\">", "</button></div>"],
|
|
14
|
+
_tmpl$6 = ["<div class=\"k2b-choice-loading\" role=\"status\"><span class=\"k2b-sr-only\">", "</span>", "</div>"],
|
|
15
|
+
_tmpl$7 = ["<div class=\"k2b-choice-loading__row\" aria-hidden=\"true\"><span class=\"k2b-skeleton\"></span><span class=\"k2b-skeleton\" style=\"", "\"></span></div>"],
|
|
16
|
+
_tmpl$8 = ["<button type=\"button\" id=\"", "\" class=\"k2b-choice-option\" role=\"option\"", ">", "<span><strong>", "</strong>", "</span></button>"],
|
|
17
|
+
_tmpl$9 = ["<i", " aria-hidden=\"true\"></i>"],
|
|
18
|
+
_tmpl$0 = ["<small>", "</small>"],
|
|
19
|
+
_tmpl$1 = ["<div class=\"k2b-choice-footer\">", "</div>"];
|
|
20
|
+
import { createEffect, createSignal, For, on, Show } from "solid-js";
|
|
17
21
|
import { createFieldMeta, Field, fieldControlAria } from "../internal/field";
|
|
18
22
|
import { useUiMessages } from "../intl/messages";
|
|
19
23
|
import { createChoiceLoader, createChoicePopover, nextEnabledChoiceIndex } from "./choice";
|
|
@@ -45,8 +49,10 @@ export function Combobox(props) {
|
|
|
45
49
|
// 150ms keeps typing responsive without hammering the loader.
|
|
46
50
|
() => props.debounceMs ?? 150);
|
|
47
51
|
const popover = createChoicePopover(() => Boolean(props.disabled || props.readOnly));
|
|
48
|
-
|
|
52
|
+
// A host notice or an error replaces the results; hidden results are never selectable.
|
|
53
|
+
const options = () => props.status || loader.error() ? [] : loader.options();
|
|
49
54
|
const focusedOption = () => options()[focusedIndex()];
|
|
55
|
+
const searchable = value => value.trim().length >= (props.minQueryLength ?? 0);
|
|
50
56
|
const focus = index => {
|
|
51
57
|
setFocusedIndex(index);
|
|
52
58
|
optionRefs[index]?.scrollIntoView({
|
|
@@ -56,9 +62,9 @@ export function Combobox(props) {
|
|
|
56
62
|
/** `eager` loads the currently controlled query on open; typing supplies its
|
|
57
63
|
* own query, so opening from `onInput` skips the duplicate request. */
|
|
58
64
|
const open = (eager = true) => {
|
|
59
|
-
if (props.disabled || props.readOnly || popover.open()) return;
|
|
65
|
+
if (props.disabled || props.readOnly || popover.open() || !searchable(query())) return;
|
|
60
66
|
popover.show();
|
|
61
|
-
if (eager) loader.load(query(), true);
|
|
67
|
+
if (eager && !props.status) loader.load(query(), true);
|
|
62
68
|
focus(-1);
|
|
63
69
|
};
|
|
64
70
|
const reset = () => {
|
|
@@ -68,8 +74,14 @@ export function Combobox(props) {
|
|
|
68
74
|
};
|
|
69
75
|
const close = () => {
|
|
70
76
|
popover.hide();
|
|
71
|
-
reset();
|
|
77
|
+
if (!props.freeText) return reset();
|
|
78
|
+
loader.cancel();
|
|
79
|
+
focus(-1);
|
|
72
80
|
};
|
|
81
|
+
createEffect(on(options, list => {
|
|
82
|
+
const index = props.autoHighlight;
|
|
83
|
+
if (index !== undefined && focusedIndex() < 0 && popover.open() && list.length > 0) setFocusedIndex(Math.min(index, list.length - 1));
|
|
84
|
+
}));
|
|
73
85
|
createEffect(() => {
|
|
74
86
|
if (props.readOnly) {
|
|
75
87
|
popover.hide();
|
|
@@ -86,7 +98,10 @@ export function Combobox(props) {
|
|
|
86
98
|
description: option.description,
|
|
87
99
|
icon: option.icon
|
|
88
100
|
});
|
|
89
|
-
|
|
101
|
+
if (props.freeText) {
|
|
102
|
+
loader.cancel();
|
|
103
|
+
focus(-1);
|
|
104
|
+
} else reset();
|
|
90
105
|
inputRef?.focus();
|
|
91
106
|
};
|
|
92
107
|
const onKeyDown = event => {
|
|
@@ -98,7 +113,7 @@ export function Combobox(props) {
|
|
|
98
113
|
// While the results are open, Enter belongs to them: it never submits an enclosing form, even with no option active.
|
|
99
114
|
event.preventDefault();
|
|
100
115
|
const option = options()[focusedIndex()];
|
|
101
|
-
if (option) select(option);
|
|
116
|
+
if (option) select(option);else if (props.freeText) close();
|
|
102
117
|
} else if ((event.key === "Escape" || event.key === "Tab") && popover.open()) {
|
|
103
118
|
// Escape is only ours while the list is open; otherwise it has to reach an
|
|
104
119
|
// enclosing dialog or drawer.
|
|
@@ -148,6 +163,7 @@ export function Combobox(props) {
|
|
|
148
163
|
return props.readOnly ? "true" : undefined;
|
|
149
164
|
},
|
|
150
165
|
role: "combobox",
|
|
166
|
+
autocomplete: "off",
|
|
151
167
|
"aria-autocomplete": "list",
|
|
152
168
|
get ["aria-expanded"]() {
|
|
153
169
|
return popover.open();
|
|
@@ -168,27 +184,57 @@ export function Combobox(props) {
|
|
|
168
184
|
}
|
|
169
185
|
})), _$ssrAttribute("id", _$escape(listboxId, true), false), _$escape(_$createComponent(Show, {
|
|
170
186
|
get when() {
|
|
171
|
-
return
|
|
187
|
+
return props.status;
|
|
172
188
|
},
|
|
173
|
-
children:
|
|
189
|
+
children: status => _$ssr(_tmpl$4, _$escape(status()))
|
|
190
|
+
})), _$escape(_$createComponent(Show, {
|
|
191
|
+
get when() {
|
|
192
|
+
return _$memo(() => !!!props.status)() && loader.error();
|
|
193
|
+
},
|
|
194
|
+
children: message => _$ssr(_tmpl$5, _$escape(message()), _$escape(messages().retry))
|
|
174
195
|
})), _$escape(_$createComponent(For, {
|
|
175
196
|
get each() {
|
|
176
|
-
return
|
|
197
|
+
return options();
|
|
177
198
|
},
|
|
178
199
|
get fallback() {
|
|
179
|
-
return _$
|
|
200
|
+
return _$createComponent(Show, {
|
|
201
|
+
get when() {
|
|
202
|
+
return _$memo(() => !!!props.status)() && !loader.error();
|
|
203
|
+
},
|
|
204
|
+
get children() {
|
|
205
|
+
return _$createComponent(Show, {
|
|
206
|
+
get when() {
|
|
207
|
+
return loader.loading();
|
|
208
|
+
},
|
|
209
|
+
get fallback() {
|
|
210
|
+
return _$ssr(_tmpl$4, query().length >= 2 ? _$escape(props.empty ?? selectMessages().noResultsFound) : _$escape(selectMessages().typeToSearch));
|
|
211
|
+
},
|
|
212
|
+
get children() {
|
|
213
|
+
return _$ssr(_tmpl$6, _$escape(messages().loading), _$escape(_$createComponent(For, {
|
|
214
|
+
each: [62, 48, 55],
|
|
215
|
+
children: width => _$ssr(_tmpl$7, _$ssrStyleProperty("width:", `${_$escape(width, true)}%`))
|
|
216
|
+
})));
|
|
217
|
+
}
|
|
218
|
+
});
|
|
219
|
+
}
|
|
220
|
+
});
|
|
180
221
|
},
|
|
181
|
-
children: (option, index) => _$ssr(_tmpl$
|
|
222
|
+
children: (option, index) => _$ssr(_tmpl$8, `${_$escape(listboxId, true)}-${_$escape(index(), true)}`, _$ssrAttribute("aria-selected", _$escape(index(), true) === _$escape(focusedIndex(), true), false) + _$ssrAttribute("data-focused", index() === focusedIndex() ? "true" : _$escape(undefined, true), false), _$escape(_$createComponent(Show, {
|
|
182
223
|
get when() {
|
|
183
224
|
return option.icon;
|
|
184
225
|
},
|
|
185
|
-
children: icon => _$ssr(_tmpl$
|
|
226
|
+
children: icon => _$ssr(_tmpl$9, _$ssrAttribute("class", _$escape(iconClass(icon()), true), false))
|
|
186
227
|
})), _$escape(option.label), _$escape(_$createComponent(Show, {
|
|
187
228
|
get when() {
|
|
188
229
|
return option.description;
|
|
189
230
|
},
|
|
190
|
-
children: description => _$ssr(_tmpl$
|
|
231
|
+
children: description => _$ssr(_tmpl$0, _$escape(description()))
|
|
191
232
|
})))
|
|
233
|
+
})), _$escape(_$createComponent(Show, {
|
|
234
|
+
get when() {
|
|
235
|
+
return props.footer;
|
|
236
|
+
},
|
|
237
|
+
children: footer => _$ssr(_tmpl$1, _$escape(footer()))
|
|
192
238
|
})));
|
|
193
239
|
}
|
|
194
240
|
});
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["inputs/Combobox.tsx"],"sourcesContent":["import { createEffect, createSignal, For, type JSX, Show } from \"solid-js\";\nimport { createFieldMeta, Field, fieldControlAria } from \"../internal/field\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { type ChoiceOption, createChoiceLoader, createChoicePopover, nextEnabledChoiceIndex } from \"./choice\";\nimport type { FieldProps, MaybeAccessor } from \"./field-contract\";\nimport { resolveMaybeAccessor } from \"./field-contract\";\nimport { useSelectMessages } from \"./select-messages\";\n\nexport type ComboboxOption = { id: string; label: string; description?: string; icon?: string };\nexport type ComboboxProps = FieldProps & {\n readOnly?: boolean;\n query?: MaybeAccessor<string>;\n onQueryChange?: (query: string) => void;\n placeholder?: string;\n fetchData: (query: string, signal: AbortSignal) => Promise<ComboboxOption[]>;\n onSelect: (option: ComboboxOption) => void;\n debounceMs?: number;\n};\n\nconst iconClass = (icon: string) => (icon.split(/\\s+/).includes(\"ti\") ? icon : `ti ${icon}`);\n\nexport function Combobox(props: ComboboxProps): JSX.Element {\n const messages = useUiMessages();\n const selectMessages = useSelectMessages();\n const meta = createFieldMeta(props.id);\n const listboxId = `${meta.controlId}-listbox`;\n const [localQuery, setLocalQuery] = createSignal(\"\");\n const query = () => resolveMaybeAccessor(props.query) ?? localQuery();\n const setQuery = (value: string) => {\n setLocalQuery(value);\n props.onQueryChange?.(value);\n };\n const error = () => resolveMaybeAccessor(props.error);\n const [focusedIndex, setFocusedIndex] = createSignal(-1);\n let inputRef: HTMLInputElement | undefined;\n let optionRefs: HTMLButtonElement[] = [];\n const loader = createChoiceLoader(\n () => async (value, signal) => (await props.fetchData(value, signal)).map((option) => ({ ...option, value: option.id })),\n // Cloud debounces at 200ms and then pads the response with a 200ms minimum\n // load time to stop the spinner flickering. The spinner here sits in the\n // chevron slot and stale results stay visible, so there is nothing to pad —\n // 150ms keeps typing responsive without hammering the loader.\n () => props.debounceMs ?? 150,\n );\n const popover = createChoicePopover(() => Boolean(props.disabled || props.readOnly));\n const options = loader.options;\n const focusedOption = () => options()[focusedIndex()];\n const focus = (index: number) => {\n setFocusedIndex(index);\n optionRefs[index]?.scrollIntoView({ block: \"nearest\" });\n };\n /** `eager` loads the currently controlled query on open; typing supplies its\n * own query, so opening from `onInput` skips the duplicate request. */\n const open = (eager = true) => {\n if (props.disabled || props.readOnly || popover.open()) return;\n popover.show();\n if (eager) loader.load(query(), true);\n focus(-1);\n };\n const reset = () => {\n loader.cancel();\n setQuery(\"\");\n focus(-1);\n };\n const close = () => {\n popover.hide();\n reset();\n };\n createEffect(() => {\n if (props.readOnly) {\n popover.hide();\n loader.cancel();\n setFocusedIndex(-1);\n }\n });\n const select = (option: ChoiceOption<string>) => {\n if (props.disabled || props.readOnly) return;\n popover.hide();\n props.onSelect({ id: option.value, label: option.label, description: option.description, icon: option.icon });\n reset();\n inputRef?.focus();\n };\n const onKeyDown = (event: KeyboardEvent) => {\n if (props.disabled || props.readOnly) return;\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n if (!popover.open()) open();\n else focus(nextEnabledChoiceIndex(options(), focusedIndex(), event.key === \"ArrowDown\" ? 1 : -1));\n } else if (event.key === \"Enter\" && popover.open()) {\n // While the results are open, Enter belongs to them: it never submits an enclosing form, even with no option active.\n event.preventDefault();\n const option = options()[focusedIndex()];\n if (option) select(option);\n } else if ((event.key === \"Escape\" || event.key === \"Tab\") && popover.open()) {\n // Escape is only ours while the list is open; otherwise it has to reach an\n // enclosing dialog or drawer.\n if (event.key === \"Escape\") event.preventDefault();\n close();\n }\n };\n\n return (\n <Field\n class={props.class}\n label={props.label}\n description={props.description}\n error={error()}\n meta={meta}\n required={props.required}\n disabled={props.disabled}\n >\n <div class=\"k2b-combobox\" data-invalid={error() ? \"true\" : undefined}>\n <div ref={popover.setTrigger} class=\"k2b-combobox__input\">\n <i class=\"ti ti-search\" aria-hidden=\"true\" />\n {/* A click, typing, or an arrow key opens the list. Focus alone (Tab, a dialog's initial focus) does not, so Escape still closes the enclosing dialog. */}\n <input\n ref={(element) => {\n inputRef = element;\n }}\n id={meta.controlId}\n type=\"text\"\n value={query()}\n placeholder={props.placeholder ?? messages().search}\n disabled={props.disabled}\n readOnly={props.readOnly}\n aria-readonly={props.readOnly ? \"true\" : undefined}\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-expanded={popover.open()}\n aria-controls={listboxId}\n aria-activedescendant={focusedOption() ? `${listboxId}-${focusedIndex()}` : undefined}\n {...fieldControlAria(meta, props)}\n onClick={() => open()}\n onInput={(event) => {\n if (props.disabled || props.readOnly) {\n event.currentTarget.value = query();\n return;\n }\n setQuery(event.currentTarget.value);\n open(false);\n loader.load(event.currentTarget.value);\n focus(-1);\n }}\n onKeyDown={onKeyDown}\n />\n <Show when={loader.loading()} fallback={<i class=\"ti ti-chevron-down\" aria-hidden=\"true\" />}>\n <i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\" />\n </Show>\n </div>\n <div ref={popover.setPopover} popover=\"manual\" class=\"k2b-choice-popover\">\n <div class=\"k2b-choice-options\" role=\"listbox\" id={listboxId}>\n <Show when={loader.error()}>\n {(message) => (\n <div class=\"k2b-choice-status\" data-tone=\"danger\">\n <span>{message()}</span>\n <button type=\"button\" onClick={loader.retry}>\n {messages().retry}\n </button>\n </div>\n )}\n </Show>\n <For\n each={loader.error() ? [] : options()}\n fallback={\n <div class=\"k2b-choice-status\">{query().length >= 2 ? selectMessages().noResultsFound : selectMessages().typeToSearch}</div>\n }\n >\n {(option, index) => (\n <button\n ref={(element) => (optionRefs[index()] = element)}\n type=\"button\"\n id={`${listboxId}-${index()}`}\n class=\"k2b-choice-option\"\n role=\"option\"\n aria-selected={index() === focusedIndex()}\n data-focused={index() === focusedIndex() ? \"true\" : undefined}\n onPointerMove={() => focus(index())}\n onPointerDown={(event) => event.preventDefault()}\n onClick={() => select(option)}\n >\n <Show when={option.icon}>{(icon) => <i class={iconClass(icon())} aria-hidden=\"true\" />}</Show>\n <span>\n <strong>{option.label}</strong>\n <Show when={option.description}>{(description) => <small>{description()}</small>}</Show>\n </span>\n </button>\n )}\n </For>\n </div>\n </div>\n </div>\n </Field>\n );\n}\n\nexport default Combobox;\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["inputs/Combobox.tsx"],"sourcesContent":["import { createEffect, createSignal, For, type JSX, on, Show } from \"solid-js\";\nimport { createFieldMeta, Field, fieldControlAria } from \"../internal/field\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { type ChoiceOption, createChoiceLoader, createChoicePopover, nextEnabledChoiceIndex } from \"./choice\";\nimport type { FieldProps, MaybeAccessor } from \"./field-contract\";\nimport { resolveMaybeAccessor } from \"./field-contract\";\nimport { useSelectMessages } from \"./select-messages\";\n\nexport type ComboboxOption = { id: string; label: string; description?: string; icon?: string };\nexport type ComboboxProps = FieldProps & {\n readOnly?: boolean;\n query?: MaybeAccessor<string>;\n onQueryChange?: (query: string) => void;\n placeholder?: string;\n fetchData: (query: string, signal: AbortSignal) => Promise<ComboboxOption[]>;\n onSelect: (option: ComboboxOption) => void;\n debounceMs?: number;\n /** The query is the field's own value: picking or closing keeps the typed text, and Enter without an active option only closes the list. */\n freeText?: boolean;\n /** Below this many characters nothing loads and the list stays closed. */\n minQueryLength?: number;\n /** This result (by position) is active as soon as results arrive, so Enter takes it. */\n autoHighlight?: number;\n /** Replaces the results with a host-owned notice, such as an exhausted quota; nothing loads while it is set. */\n status?: JSX.Element;\n /** Shown instead of the default text when a search finds nothing. */\n empty?: JSX.Element;\n /** One line under the results, such as their source. */\n footer?: JSX.Element;\n};\n\nconst iconClass = (icon: string) => (icon.split(/\\s+/).includes(\"ti\") ? icon : `ti ${icon}`);\n\nexport function Combobox(props: ComboboxProps): JSX.Element {\n const messages = useUiMessages();\n const selectMessages = useSelectMessages();\n const meta = createFieldMeta(props.id);\n const listboxId = `${meta.controlId}-listbox`;\n const [localQuery, setLocalQuery] = createSignal(\"\");\n const query = () => resolveMaybeAccessor(props.query) ?? localQuery();\n const setQuery = (value: string) => {\n setLocalQuery(value);\n props.onQueryChange?.(value);\n };\n const error = () => resolveMaybeAccessor(props.error);\n const [focusedIndex, setFocusedIndex] = createSignal(-1);\n let inputRef: HTMLInputElement | undefined;\n let optionRefs: HTMLButtonElement[] = [];\n const loader = createChoiceLoader(\n () => async (value, signal) => (await props.fetchData(value, signal)).map((option) => ({ ...option, value: option.id })),\n // Cloud debounces at 200ms and then pads the response with a 200ms minimum\n // load time to stop the spinner flickering. The spinner here sits in the\n // chevron slot and stale results stay visible, so there is nothing to pad —\n // 150ms keeps typing responsive without hammering the loader.\n () => props.debounceMs ?? 150,\n );\n const popover = createChoicePopover(() => Boolean(props.disabled || props.readOnly));\n // A host notice or an error replaces the results; hidden results are never selectable.\n const options = () => (props.status || loader.error() ? [] : loader.options());\n const focusedOption = () => options()[focusedIndex()];\n const searchable = (value: string) => value.trim().length >= (props.minQueryLength ?? 0);\n const focus = (index: number) => {\n setFocusedIndex(index);\n optionRefs[index]?.scrollIntoView({ block: \"nearest\" });\n };\n /** `eager` loads the currently controlled query on open; typing supplies its\n * own query, so opening from `onInput` skips the duplicate request. */\n const open = (eager = true) => {\n if (props.disabled || props.readOnly || popover.open() || !searchable(query())) return;\n popover.show();\n if (eager && !props.status) loader.load(query(), true);\n focus(-1);\n };\n const reset = () => {\n loader.cancel();\n setQuery(\"\");\n focus(-1);\n };\n const close = () => {\n popover.hide();\n if (!props.freeText) return reset();\n loader.cancel();\n focus(-1);\n };\n createEffect(\n on(options, (list) => {\n const index = props.autoHighlight;\n if (index !== undefined && focusedIndex() < 0 && popover.open() && list.length > 0) setFocusedIndex(Math.min(index, list.length - 1));\n }),\n );\n createEffect(() => {\n if (props.readOnly) {\n popover.hide();\n loader.cancel();\n setFocusedIndex(-1);\n }\n });\n const select = (option: ChoiceOption<string>) => {\n if (props.disabled || props.readOnly) return;\n popover.hide();\n props.onSelect({ id: option.value, label: option.label, description: option.description, icon: option.icon });\n if (props.freeText) {\n loader.cancel();\n focus(-1);\n } else reset();\n inputRef?.focus();\n };\n const onKeyDown = (event: KeyboardEvent) => {\n if (props.disabled || props.readOnly) return;\n if (event.key === \"ArrowDown\" || event.key === \"ArrowUp\") {\n event.preventDefault();\n if (!popover.open()) open();\n else focus(nextEnabledChoiceIndex(options(), focusedIndex(), event.key === \"ArrowDown\" ? 1 : -1));\n } else if (event.key === \"Enter\" && popover.open()) {\n // While the results are open, Enter belongs to them: it never submits an enclosing form, even with no option active.\n event.preventDefault();\n const option = options()[focusedIndex()];\n if (option) select(option);\n else if (props.freeText) close();\n } else if ((event.key === \"Escape\" || event.key === \"Tab\") && popover.open()) {\n // Escape is only ours while the list is open; otherwise it has to reach an\n // enclosing dialog or drawer.\n if (event.key === \"Escape\") event.preventDefault();\n close();\n }\n };\n\n return (\n <Field\n class={props.class}\n label={props.label}\n description={props.description}\n error={error()}\n meta={meta}\n required={props.required}\n disabled={props.disabled}\n >\n <div class=\"k2b-combobox\" data-invalid={error() ? \"true\" : undefined}>\n <div ref={popover.setTrigger} class=\"k2b-combobox__input\">\n <i class=\"ti ti-search\" aria-hidden=\"true\" />\n {/* A click, typing, or an arrow key opens the list. Focus alone (Tab, a dialog's initial focus) does not, so Escape still closes the enclosing dialog. */}\n <input\n ref={(element) => {\n inputRef = element;\n }}\n id={meta.controlId}\n type=\"text\"\n value={query()}\n placeholder={props.placeholder ?? messages().search}\n disabled={props.disabled}\n readOnly={props.readOnly}\n aria-readonly={props.readOnly ? \"true\" : undefined}\n role=\"combobox\"\n autocomplete=\"off\"\n aria-autocomplete=\"list\"\n aria-expanded={popover.open()}\n aria-controls={listboxId}\n aria-activedescendant={focusedOption() ? `${listboxId}-${focusedIndex()}` : undefined}\n {...fieldControlAria(meta, props)}\n onClick={() => open()}\n onInput={(event) => {\n if (props.disabled || props.readOnly) {\n event.currentTarget.value = query();\n return;\n }\n const value = event.currentTarget.value;\n setQuery(value);\n focus(-1);\n if (!searchable(value)) {\n popover.hide();\n loader.cancel();\n return;\n }\n open(false);\n if (!props.status) loader.load(value);\n }}\n onKeyDown={onKeyDown}\n />\n <Show when={loader.loading()} fallback={<i class=\"ti ti-chevron-down\" aria-hidden=\"true\" />}>\n <i class=\"ti ti-loader-2 k2b-spin\" aria-hidden=\"true\" />\n </Show>\n </div>\n <div ref={popover.setPopover} popover=\"manual\" class=\"k2b-choice-popover\">\n <div class=\"k2b-choice-options\" role=\"listbox\" id={listboxId}>\n <Show when={props.status}>{(status) => <div class=\"k2b-choice-status\">{status()}</div>}</Show>\n <Show when={!props.status && loader.error()}>\n {(message) => (\n <div class=\"k2b-choice-status\" data-tone=\"danger\">\n <span>{message()}</span>\n <button type=\"button\" onClick={loader.retry}>\n {messages().retry}\n </button>\n </div>\n )}\n </Show>\n <For\n each={options()}\n fallback={\n <Show when={!props.status && !loader.error()}>\n <Show\n when={loader.loading()}\n fallback={\n <div class=\"k2b-choice-status\">\n {query().length >= 2 ? (props.empty ?? selectMessages().noResultsFound) : selectMessages().typeToSearch}\n </div>\n }\n >\n {/* The first search has nothing to keep visible yet: rows of the final size wait in its place. */}\n <div class=\"k2b-choice-loading\" role=\"status\">\n <span class=\"k2b-sr-only\">{messages().loading}</span>\n <For each={[62, 48, 55]}>\n {(width) => (\n <div class=\"k2b-choice-loading__row\" aria-hidden=\"true\">\n <span class=\"k2b-skeleton\" />\n <span class=\"k2b-skeleton\" style={{ width: `${width}%` }} />\n </div>\n )}\n </For>\n </div>\n </Show>\n </Show>\n }\n >\n {(option, index) => (\n <button\n ref={(element) => (optionRefs[index()] = element)}\n type=\"button\"\n id={`${listboxId}-${index()}`}\n class=\"k2b-choice-option\"\n role=\"option\"\n aria-selected={index() === focusedIndex()}\n data-focused={index() === focusedIndex() ? \"true\" : undefined}\n onPointerMove={() => focus(index())}\n onPointerDown={(event) => event.preventDefault()}\n onClick={() => select(option)}\n >\n <Show when={option.icon}>{(icon) => <i class={iconClass(icon())} aria-hidden=\"true\" />}</Show>\n <span>\n <strong>{option.label}</strong>\n <Show when={option.description}>{(description) => <small>{description()}</small>}</Show>\n </span>\n </button>\n )}\n </For>\n </div>\n <Show when={props.footer}>{(footer) => <div class=\"k2b-choice-footer\">{footer()}</div>}</Show>\n </div>\n </div>\n </Field>\n );\n}\n\nexport default Combobox;\n"],"mappings":"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","ignoreList":[]}
|