@k2b/ui 0.30.0 → 0.32.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 +1 -1
- package/dist/base.css +2 -0
- package/dist/browser/actions/Toolbar.js +2 -0
- package/dist/browser/actions/Toolbar.js.map +1 -1
- package/dist/browser/actions/ViewTabs.js +8 -0
- package/dist/browser/actions/ViewTabs.js.map +1 -1
- package/dist/browser/chat/ChatDecision.js +28 -2
- package/dist/browser/chat/ChatDecision.js.map +1 -1
- package/dist/browser/content/Chart.js +104 -55
- package/dist/browser/content/Chart.js.map +1 -1
- package/dist/browser/content/ChartExplorer.js +5 -3
- package/dist/browser/content/ChartExplorer.js.map +1 -1
- package/dist/browser/content/MapAttribution.js +27 -0
- package/dist/browser/content/MapAttribution.js.map +1 -0
- package/dist/browser/content/MapView.js +553 -0
- package/dist/browser/content/MapView.js.map +1 -0
- package/dist/browser/content/MapViewFrame.js +395 -0
- package/dist/browser/content/MapViewFrame.js.map +1 -0
- package/dist/browser/content/chart-inspection-core.js +4 -1
- package/dist/browser/content/chart-inspection-core.js.map +1 -1
- package/dist/browser/content/code-highlight.js +66 -0
- package/dist/browser/content/code-highlight.js.map +1 -1
- package/dist/browser/content/index.js +6 -0
- package/dist/browser/content/index.js.map +1 -1
- package/dist/browser/content/map-palette.js +39 -0
- package/dist/browser/content/map-palette.js.map +1 -0
- package/dist/browser/content/map-style.js +276 -0
- package/dist/browser/content/map-style.js.map +1 -0
- package/dist/browser/content/map-view-geometry.js +269 -0
- package/dist/browser/content/map-view-geometry.js.map +1 -0
- package/dist/browser/content/map-view-live.js +65 -0
- package/dist/browser/content/map-view-live.js.map +1 -0
- package/dist/browser/content/map-view-messages.js +53 -0
- package/dist/browser/content/map-view-messages.js.map +1 -0
- package/dist/browser/content/map-view-types.js +2 -0
- package/dist/browser/content/map-view-types.js.map +1 -0
- package/dist/browser/content/map-view-worker.js +10 -0
- package/dist/browser/feedback/dialog-core.js +19 -0
- package/dist/browser/feedback/dialog-core.js.map +1 -1
- package/dist/browser/inputs/AutocompleteEditor.js +167 -104
- package/dist/browser/inputs/AutocompleteEditor.js.map +1 -1
- package/dist/browser/inputs/RichSelect.js +1474 -0
- package/dist/browser/inputs/RichSelect.js.map +1 -0
- package/dist/browser/inputs/TextInput.js +95 -60
- package/dist/browser/inputs/TextInput.js.map +1 -1
- package/dist/browser/inputs/choice.js +19 -8
- package/dist/browser/inputs/choice.js.map +1 -1
- package/dist/browser/inputs/index.js +1 -0
- package/dist/browser/inputs/index.js.map +1 -1
- package/dist/browser/inputs/rich-select-messages.js +46 -0
- package/dist/browser/inputs/rich-select-messages.js.map +1 -0
- package/dist/browser/layout/AppWorkspace.js +194 -144
- package/dist/browser/layout/AppWorkspace.js.map +1 -1
- package/dist/browser/layout/DetailPanel.js +10 -1
- package/dist/browser/layout/DetailPanel.js.map +1 -1
- package/dist/browser/layout/Navigation.js +14 -5
- package/dist/browser/layout/Navigation.js.map +1 -1
- package/dist/browser/layout/PageTitle.js +9 -3
- package/dist/browser/layout/PageTitle.js.map +1 -1
- package/dist/browser/layout/ResizeSeparator.js +8 -2
- package/dist/browser/layout/ResizeSeparator.js.map +1 -1
- package/dist/browser/layout/app-workspace-controller.js +301 -34
- package/dist/browser/layout/app-workspace-controller.js.map +1 -1
- package/dist/browser/layout/app-workspace-width.js +26 -0
- package/dist/browser/layout/app-workspace-width.js.map +1 -0
- package/dist/browser/layout/index.js +1 -1
- package/dist/browser/layout/index.js.map +1 -1
- package/dist/browser/layout/navigation-model.js.map +1 -1
- package/dist/browser/layout/resize-separator.js +25 -1
- package/dist/browser/layout/resize-separator.js.map +1 -1
- package/dist/browser/surfaces/Skeleton.js +14 -0
- package/dist/browser/surfaces/Skeleton.js.map +1 -0
- package/dist/browser/surfaces/index.js +1 -0
- package/dist/browser/surfaces/index.js.map +1 -1
- package/dist/licenses/maplibre-gl.txt +116 -0
- package/dist/ssr/actions/Toolbar.js +2 -0
- package/dist/ssr/actions/Toolbar.js.map +1 -1
- package/dist/ssr/actions/ViewTabs.js +8 -0
- package/dist/ssr/actions/ViewTabs.js.map +1 -1
- package/dist/ssr/chat/ChatDecision.js +11 -0
- package/dist/ssr/chat/ChatDecision.js.map +1 -1
- package/dist/ssr/content/Chart.js +27 -7
- package/dist/ssr/content/Chart.js.map +1 -1
- package/dist/ssr/content/ChartExplorer.js +5 -3
- package/dist/ssr/content/ChartExplorer.js.map +1 -1
- package/dist/ssr/content/MapAttribution.js +19 -0
- package/dist/ssr/content/MapAttribution.js.map +1 -0
- package/dist/ssr/content/MapView.js +474 -0
- package/dist/ssr/content/MapView.js.map +1 -0
- package/dist/ssr/content/MapViewFrame.js +193 -0
- package/dist/ssr/content/MapViewFrame.js.map +1 -0
- package/dist/ssr/content/chart-inspection-core.js +4 -1
- package/dist/ssr/content/chart-inspection-core.js.map +1 -1
- package/dist/ssr/content/code-highlight.js +66 -0
- package/dist/ssr/content/code-highlight.js.map +1 -1
- package/dist/ssr/content/index.js +6 -0
- package/dist/ssr/content/index.js.map +1 -1
- package/dist/ssr/content/map-palette.js +39 -0
- package/dist/ssr/content/map-palette.js.map +1 -0
- package/dist/ssr/content/map-style.js +276 -0
- package/dist/ssr/content/map-style.js.map +1 -0
- package/dist/ssr/content/map-view-geometry.js +269 -0
- package/dist/ssr/content/map-view-geometry.js.map +1 -0
- package/dist/ssr/content/map-view-live.js +1 -0
- package/dist/ssr/content/map-view-messages.js +53 -0
- package/dist/ssr/content/map-view-messages.js.map +1 -0
- package/dist/ssr/content/map-view-types.js +2 -0
- package/dist/ssr/content/map-view-types.js.map +1 -0
- package/dist/ssr/feedback/dialog-core.js +19 -0
- package/dist/ssr/feedback/dialog-core.js.map +1 -1
- package/dist/ssr/inputs/AutocompleteEditor.js +64 -24
- package/dist/ssr/inputs/AutocompleteEditor.js.map +1 -1
- package/dist/ssr/inputs/RichSelect.js +950 -0
- package/dist/ssr/inputs/RichSelect.js.map +1 -0
- package/dist/ssr/inputs/TextInput.js +40 -16
- package/dist/ssr/inputs/TextInput.js.map +1 -1
- package/dist/ssr/inputs/choice.js +19 -8
- package/dist/ssr/inputs/choice.js.map +1 -1
- package/dist/ssr/inputs/index.js +1 -0
- package/dist/ssr/inputs/index.js.map +1 -1
- package/dist/ssr/inputs/rich-select-messages.js +46 -0
- package/dist/ssr/inputs/rich-select-messages.js.map +1 -0
- package/dist/ssr/layout/AppWorkspace.js +53 -16
- package/dist/ssr/layout/AppWorkspace.js.map +1 -1
- package/dist/ssr/layout/DetailPanel.js +1 -1
- package/dist/ssr/layout/DetailPanel.js.map +1 -1
- package/dist/ssr/layout/Navigation.js +1 -1
- package/dist/ssr/layout/Navigation.js.map +1 -1
- package/dist/ssr/layout/PageTitle.js +9 -3
- package/dist/ssr/layout/PageTitle.js.map +1 -1
- package/dist/ssr/layout/ResizeSeparator.js +4 -1
- package/dist/ssr/layout/ResizeSeparator.js.map +1 -1
- package/dist/ssr/layout/app-workspace-controller.js +301 -34
- package/dist/ssr/layout/app-workspace-controller.js.map +1 -1
- package/dist/ssr/layout/app-workspace-width.js +26 -0
- package/dist/ssr/layout/app-workspace-width.js.map +1 -0
- package/dist/ssr/layout/index.js +1 -1
- package/dist/ssr/layout/index.js.map +1 -1
- package/dist/ssr/layout/navigation-model.js.map +1 -1
- package/dist/ssr/layout/resize-separator.js +25 -1
- package/dist/ssr/layout/resize-separator.js.map +1 -1
- package/dist/ssr/surfaces/Skeleton.js +8 -0
- package/dist/ssr/surfaces/Skeleton.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/content/Chart.d.ts +4 -2
- package/dist/types/content/ChartExplorer.d.ts +0 -1
- package/dist/types/content/MapAttribution.d.ts +6 -0
- package/dist/types/content/MapView.d.ts +5 -0
- package/dist/types/content/MapViewFrame.d.ts +31 -0
- package/dist/types/content/code-highlight.d.ts +3 -1
- package/dist/types/content/index.d.ts +7 -0
- package/dist/types/content/map-palette.d.ts +58 -0
- package/dist/types/content/map-style.d.ts +29 -0
- package/dist/types/content/map-view-geometry.d.ts +55 -0
- package/dist/types/content/map-view-live.d.ts +2 -0
- package/dist/types/content/map-view-messages.d.ts +21 -0
- package/dist/types/content/map-view-types.d.ts +63 -0
- package/dist/types/feedback/dialog-core.d.ts +1 -0
- package/dist/types/inputs/AutocompleteEditor.d.ts +6 -1
- package/dist/types/inputs/RichSelect.d.ts +93 -0
- package/dist/types/inputs/TextInput.d.ts +5 -2
- package/dist/types/inputs/choice.d.ts +24 -4
- package/dist/types/inputs/index.d.ts +2 -0
- package/dist/types/inputs/rich-select-messages.d.ts +21 -0
- package/dist/types/layout/AppWorkspace.d.ts +14 -3
- package/dist/types/layout/DetailPanel.d.ts +2 -0
- package/dist/types/layout/app-workspace-controller.d.ts +2 -0
- package/dist/types/layout/app-workspace-width.d.ts +13 -0
- package/dist/types/layout/index.d.ts +2 -2
- package/dist/types/layout/navigation-model.d.ts +2 -0
- package/dist/types/layout/resize-separator.d.ts +1 -0
- package/dist/types/surfaces/Skeleton.d.ts +6 -0
- package/dist/types/surfaces/index.d.ts +1 -0
- package/map-glyphs/IBM Plex Sans Medium/0-255.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/1024-1279.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/256-511.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/512-767.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/768-1023.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/7680-7935.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/8192-8447.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/8448-8703.pbf +0 -0
- package/map-glyphs/IBM Plex Sans Medium/8704-8959.pbf +0 -0
- package/map-glyphs/OFL.txt +93 -0
- package/package.json +11 -6
|
@@ -31,8 +31,10 @@ import DataTable from "./DataTable";
|
|
|
31
31
|
|
|
32
32
|
/** One legend entry; `color` is the palette index the renderer gave the series (0 is `--stdlib-chart-c1`). */
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
// Only the copied text can prove that a cell is a number; a numeric sort key cannot.
|
|
35
|
+
const plainNumber = /^[-+]?\p{Sc}?\s?\d[\d.,\s]*(?:\s?(?:%|\p{Sc}))?$/u;
|
|
36
|
+
const tsvCell = value => {
|
|
37
|
+
const safe = /^[=+\-@\t\r]/.test(value) && !plainNumber.test(value) ? `'${value}` : value;
|
|
36
38
|
return /[\t\r\n"]/.test(safe) ? `"${safe.replaceAll('"', '""')}"` : safe;
|
|
37
39
|
};
|
|
38
40
|
export function ChartExplorer(props) {
|
|
@@ -111,7 +113,7 @@ export function ChartExplorer(props) {
|
|
|
111
113
|
setView("chart");
|
|
112
114
|
props.onChartChange?.(value.slice("chart:".length));
|
|
113
115
|
};
|
|
114
|
-
const copyText = () => [props.columns.map(c => tsvCell(c.label)).join("\t"), ...rows().map(row => props.columns.map(c => tsvCell(c.value(row)
|
|
116
|
+
const copyText = () => [props.columns.map(c => tsvCell(c.label)).join("\t"), ...rows().map(row => props.columns.map(c => tsvCell(c.value(row))).join("\t"))].join("\n");
|
|
115
117
|
return _$ssr(_tmpl$5, `k2b-chart-explorer ${_$escape(props.class ?? "", true)}`, _$ssrAttribute("aria-labelledby", _$escape(id, true), false), _$escape(_$createComponent(Dynamic, {
|
|
116
118
|
get component() {
|
|
117
119
|
return `h${props.headingLevel ?? 3}`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":[],"sources":["content/ChartExplorer.tsx"],"sourcesContent":["import { createEffect, createMemo, createSignal, createUniqueId, For, type JSX, Show } from \"solid-js\";\nimport { Dynamic } from \"solid-js/web\";\nimport { Button, IconButton } from \"../actions/Button\";\nimport { CopyButton } from \"../actions/CopyButton\";\nimport { SelectChip } from \"../inputs/SelectChip\";\nimport { useLocale } from \"../intl/locale\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { DescriptionList } from \"../surfaces/DescriptionList\";\nimport { Paper } from \"../surfaces/Paper\";\nimport { ChartSnapshotView } from \"./ChartSnapshotView\";\nimport type { ChartCursor } from \"./chart-cursor\";\nimport { type ChartExplorerData, type ChartExplorerRow, validateChartExplorerData } from \"./chart-explorer\";\nimport { useChartMessages } from \"./chart-messages\";\nimport DataTable, { type DataTableSort } from \"./DataTable\";\n\nexport type ChartExplorerColumn<T> = {\n id: string;\n label: string;\n value: (row: T) => string;\n /** Optional table cell presentation; value remains the plain text used for copy/details. */\n render?: (row: T) => JSX.Element;\n /**\n * Explicit comparable value. Omit to leave the column unsortable.\n * Returning a number also exempts the cell from spreadsheet formula guarding on copy.\n */\n sortValue?: (row: T) => string | number | null;\n align?: \"left\" | \"center\" | \"right\";\n};\n/** One legend entry; `color` is the palette index the renderer gave the series (0 is `--stdlib-chart-c1`). */\nexport type ChartLegendEntry = {\n label: string;\n color: number;\n /** Line pattern of a line series; a solid series shows a filled swatch. */\n style?: \"solid\" | \"dashed\" | \"dotted\";\n /** A comparison series, drawn hollow like its marks. */\n reference?: boolean;\n};\nexport type ChartExplorerProps<T extends ChartExplorerRow> = {\n cursor?: ChartCursor;\n title: string;\n /** Level of the title heading. Defaults to 3; a chart inside a titled group of charts sits one level below it. */\n headingLevel?: 2 | 3 | 4 | 5 | 6;\n description?: JSX.Element;\n data: ChartExplorerData<T>;\n columns: readonly ChartExplorerColumn<T>[];\n view?: \"chart\" | \"table\";\n defaultView?: \"chart\" | \"table\";\n onViewChange?: (view: \"chart\" | \"table\") => void;\n /**\n * Several charts of the same rows. The switcher then lists each chart and the table; `chart` names the one `data`\n * holds, and `onChartChange` reports a pick, so the owner passes the matching data.\n */\n charts?: readonly { id: string; label: string }[];\n chart?: string;\n onChartChange?: (id: string) => void;\n /** Filter controls between the header and the viewport, such as ChartFilterControls. */\n controls?: JSX.Element;\n /** The table renders at most this many rows of the sorted order; sorting and Copy data use every row. */\n tableRows?: number;\n sort?: DataTableSort | null;\n defaultSort?: DataTableSort | null;\n onSortChange?: (sort: DataTableSort | null) => void;\n selectedKey?: string | null;\n onSelectedKeyChange?: (key: string | null) => void;\n /** false hides the complete local detail area for a shared selection panel. */\n renderDetails?: false | ((row: T) => JSX.Element);\n height?: string;\n /**\n * Series names below the chart, wrapping inside the fixed viewport height. Use it instead of the renderer's legend,\n * which draws in one row and runs out of narrow columns.\n */\n legend?: readonly ChartLegendEntry[];\n /** What the viewport shows without rows, for example while they load; defaults to \"No data\". */\n empty?: JSX.Element;\n class?: string;\n};\n\nconst tsvCell = (value: string, numeric = false) => {\n const safe = !numeric && /^[=+\\-@\\t\\r]/.test(value) ? `'${value}` : value;\n return /[\\t\\r\\n\"]/.test(safe) ? `\"${safe.replaceAll('\"', '\"\"')}\"` : safe;\n};\n\nexport function ChartExplorer<T extends ChartExplorerRow>(props: ChartExplorerProps<T>) {\n const messages = useUiMessages();\n const chartMessages = useChartMessages();\n const locale = useLocale();\n const id = `k2b-chart-explorer-${createUniqueId()}`;\n const data = () => props.data;\n const [copyFailed, setCopyFailed] = createSignal(false);\n const [localSelection, setLocalSelection] = createSignal<string | null>(null);\n const [localView, setLocalView] = createSignal(props.defaultView ?? \"chart\");\n const [localSort, setLocalSort] = createSignal<DataTableSort | null>(props.defaultSort ?? null);\n const view = () => props.view ?? localView();\n const sort = () => (props.sort === undefined ? localSort() : props.sort);\n const setView = (value: \"chart\" | \"table\") => {\n if (props.view === undefined) setLocalView(value);\n props.onViewChange?.(value);\n };\n const setSort = (value: DataTableSort | null) => {\n if (props.sort === undefined) setLocalSort(value);\n props.onSortChange?.(value);\n };\n const rowsByKey = createMemo(() => new Map(data().rows.map((row) => [row.key, row])));\n const rawSelected = () => (props.selectedKey !== undefined ? props.selectedKey : localSelection());\n const selected = () => (rowsByKey().has(rawSelected() ?? \"\") ? rawSelected() : null);\n const selectedRow = () => rowsByKey().get(selected() ?? \"\");\n const select = (key: string | null) => {\n if (key !== null && !rowsByKey().has(key)) return;\n setLocalSelection(key);\n props.onSelectedKeyChange?.(key);\n };\n if (\n !props.columns.length ||\n props.columns.some((column) => !column.id) ||\n new Set(props.columns.map((column) => column.id)).size !== props.columns.length\n ) {\n throw new Error(\"Chart columns require unique nonempty IDs and at least one column\");\n }\n validateChartExplorerData(data());\n createEffect(() => validateChartExplorerData(data()));\n createEffect(() => {\n if (props.selectedKey === undefined && rawSelected() !== null && selected() === null) select(null);\n });\n const rows = createMemo(() => {\n const result = [...data().rows];\n const order = sort();\n const column = props.columns.find((c) => c.id === order?.key);\n const value = column?.sortValue;\n if (!order || !value) return result;\n const collator = new Intl.Collator(locale(), { numeric: true });\n return result.sort((a, b) => {\n const av = value(a),\n bv = value(b);\n if (av === null) return bv === null ? 0 : 1;\n if (bv === null) return -1;\n const comparison = typeof av === \"number\" && typeof bv === \"number\" ? av - bv : collator.compare(String(av), String(bv));\n return order.direction === \"asc\" ? comparison : -comparison;\n });\n });\n const shownRows = () => (props.tableRows === undefined ? rows() : rows().slice(0, props.tableRows));\n // Chart ids are data; a prefix keeps one named \"table\" apart from the table entry.\n const switcherValue = () => (view() === \"table\" || !props.charts?.length ? view() : `chart:${props.chart ?? props.charts[0]?.id}`);\n const switcherOptions = () =>\n props.charts?.length\n ? [\n ...props.charts.map((chart) => ({ value: `chart:${chart.id}`, label: chart.label })),\n { value: \"table\", label: chartMessages().chartData },\n ]\n : [\n { value: \"chart\", label: chartMessages().chartDiagram },\n { value: \"table\", label: chartMessages().chartData },\n ];\n const pick = (value: string) => {\n if (value === \"table\" || value === \"chart\") return setView(value);\n setView(\"chart\");\n props.onChartChange?.(value.slice(\"chart:\".length));\n };\n const copyText = () =>\n [\n props.columns.map((c) => tsvCell(c.label)).join(\"\\t\"),\n ...rows().map((row) => props.columns.map((c) => tsvCell(c.value(row), typeof c.sortValue?.(row) === \"number\")).join(\"\\t\")),\n ].join(\"\\n\");\n return (\n <section class={`k2b-chart-explorer ${props.class ?? \"\"}`} aria-labelledby={id}>\n <header class=\"k2b-chart-explorer__header\">\n <div>\n <Dynamic component={`h${props.headingLevel ?? 3}`} id={id} class=\"k2b-chart-explorer__title\">\n {props.title}\n </Dynamic>\n <Show when={props.description}>\n <div>{props.description}</div>\n </Show>\n </div>\n <div class=\"k2b-chart-explorer__actions\">\n <CopyButton\n text={copyText()}\n label={chartMessages().chartCopyData}\n iconOnly\n size=\"sm\"\n variant=\"ghost\"\n disabled={!rows().length}\n onCopyError={() => setCopyFailed(true)}\n onCopied={() => setCopyFailed(false)}\n />\n <SelectChip<string>\n value={switcherValue()}\n onValueChange={pick}\n aria-label={chartMessages().chartView}\n options={switcherOptions()}\n />\n </div>\n </header>\n <Show when={copyFailed()}>\n <span role=\"alert\">{chartMessages().chartCopyFailed}</span>\n </Show>\n {props.controls}\n <div\n class=\"k2b-chart-explorer__viewport\"\n data-legend={view() === \"chart\" && props.legend?.length && data().rows.length > 0 ? \"true\" : undefined}\n data-capped={view() === \"table\" && shownRows().length < rows().length ? \"true\" : undefined}\n style={{ height: props.height ?? \"18rem\" }}\n >\n <Show when={data().rows.length > 0} fallback={<div class=\"k2b-chart__empty\">{props.empty ?? messages().noData}</div>}>\n <Show\n when={view() === \"chart\"}\n fallback={\n <>\n <DataTable\n rows={shownRows()}\n columns={props.columns.map((column) => ({\n id: column.id,\n header: column.label,\n value: column.value,\n align: column.align,\n sortable: Boolean(column.sortValue),\n }))}\n ariaLabelledBy={id}\n getRowId={(row) => row.key}\n selectedRowId={selected()}\n sort={sort()}\n fillHeight\n surface=\"paper\"\n onRowClick={(row) => select(row.key)}\n renderCell={({ row, col, value, render }) => {\n const custom = props.columns.find((column) => column.id === col.id)?.render;\n const cell = () => (custom ? custom(row) : render(value));\n return !custom && col.id === props.columns[0]?.id ? (\n <Button variant=\"text\" size=\"sm\" onClick={() => select(row.key)}>\n {cell()}\n </Button>\n ) : (\n cell()\n );\n }}\n renderHeader={({ col, render }) =>\n col.sortable ? (\n <Button\n variant=\"text\"\n size=\"sm\"\n onClick={() =>\n setSort({\n key: col.id,\n direction: sort()?.key === col.id && sort()?.direction === \"asc\" ? \"desc\" : \"asc\",\n })\n }\n >\n {props.columns.find((column) => column.id === col.id)?.label}\n {/* The data table's own marker: inactive columns keep a dimmed arrow so the header reads as sortable. */}\n <i\n class={`k2b-data-table__sort-icon ti ${sort()?.key === col.id && sort()?.direction === \"asc\" ? \"ti-arrow-up\" : \"ti-arrow-down\"}`}\n data-active={sort()?.key === col.id ? \"true\" : undefined}\n aria-hidden=\"true\"\n />\n </Button>\n ) : (\n render()\n )\n }\n />\n <Show when={shownRows().length < rows().length}>\n <p class=\"k2b-chart-explorer__note\">\n {chartMessages().chartRowsShown({\n shown: new Intl.NumberFormat(locale()).format(shownRows().length),\n total: new Intl.NumberFormat(locale()).format(rows().length),\n })}\n </p>\n </Show>\n </>\n }\n >\n <ChartSnapshotView\n cursor={props.cursor}\n snapshot={data().chart}\n selectedKey={selected()}\n onSelect={select}\n style={props.legend?.length ? undefined : { height: \"100%\" }}\n />\n <Show when={props.legend?.length}>\n <ul class=\"k2b-chart-legend\" aria-label={chartMessages().chartLegend}>\n <For each={props.legend}>\n {(entry) => (\n <li>\n <span\n class=\"k2b-chart-legend__swatch\"\n data-style={entry.style && entry.style !== \"solid\" ? entry.style : undefined}\n data-reference={entry.reference ? \"true\" : undefined}\n style={{ \"--k2b-chart-legend-color\": `var(--stdlib-chart-c${(Math.abs(Math.trunc(entry.color)) % 8) + 1})` }}\n aria-hidden=\"true\"\n />\n {entry.label}\n </li>\n )}\n </For>\n </ul>\n </Show>\n </Show>\n </Show>\n </div>\n <Show when={props.renderDetails !== false}>\n <div class=\"k2b-chart-explorer__details\" aria-live=\"polite\">\n <Show when={selectedRow()}>\n {(row) => (\n <Paper class=\"k2b-chart-explorer__detail-paper\">\n <div class=\"k2b-chart-explorer__detail-header\">\n <span>{chartMessages().chartSelectedDatum}</span>\n <IconButton variant=\"text\" size=\"sm\" label={messages().clearSelection} onClick={() => select(null)}>\n <i class=\"ti ti-x\" aria-hidden=\"true\" />\n </IconButton>\n </div>\n {props.renderDetails ? (\n props.renderDetails(row())\n ) : (\n <DescriptionList\n columns={3}\n size=\"sm\"\n items={props.columns.map((column) => ({\n term: column.label,\n description: column.value(row()),\n }))}\n />\n )}\n </Paper>\n )}\n </Show>\n </div>\n </Show>\n </section>\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
1
|
+
{"version":3,"names":[],"sources":["content/ChartExplorer.tsx"],"sourcesContent":["import { createEffect, createMemo, createSignal, createUniqueId, For, type JSX, Show } from \"solid-js\";\nimport { Dynamic } from \"solid-js/web\";\nimport { Button, IconButton } from \"../actions/Button\";\nimport { CopyButton } from \"../actions/CopyButton\";\nimport { SelectChip } from \"../inputs/SelectChip\";\nimport { useLocale } from \"../intl/locale\";\nimport { useUiMessages } from \"../intl/messages\";\nimport { DescriptionList } from \"../surfaces/DescriptionList\";\nimport { Paper } from \"../surfaces/Paper\";\nimport { ChartSnapshotView } from \"./ChartSnapshotView\";\nimport type { ChartCursor } from \"./chart-cursor\";\nimport { type ChartExplorerData, type ChartExplorerRow, validateChartExplorerData } from \"./chart-explorer\";\nimport { useChartMessages } from \"./chart-messages\";\nimport DataTable, { type DataTableSort } from \"./DataTable\";\n\nexport type ChartExplorerColumn<T> = {\n id: string;\n label: string;\n value: (row: T) => string;\n /** Optional table cell presentation; value remains the plain text used for copy/details. */\n render?: (row: T) => JSX.Element;\n /**\n * Explicit comparable value. Omit to leave the column unsortable.\n */\n sortValue?: (row: T) => string | number | null;\n align?: \"left\" | \"center\" | \"right\";\n};\n/** One legend entry; `color` is the palette index the renderer gave the series (0 is `--stdlib-chart-c1`). */\nexport type ChartLegendEntry = {\n label: string;\n color: number;\n /** Line pattern of a line series; a solid series shows a filled swatch. */\n style?: \"solid\" | \"dashed\" | \"dotted\";\n /** A comparison series, drawn hollow like its marks. */\n reference?: boolean;\n};\nexport type ChartExplorerProps<T extends ChartExplorerRow> = {\n cursor?: ChartCursor;\n title: string;\n /** Level of the title heading. Defaults to 3; a chart inside a titled group of charts sits one level below it. */\n headingLevel?: 2 | 3 | 4 | 5 | 6;\n description?: JSX.Element;\n data: ChartExplorerData<T>;\n columns: readonly ChartExplorerColumn<T>[];\n view?: \"chart\" | \"table\";\n defaultView?: \"chart\" | \"table\";\n onViewChange?: (view: \"chart\" | \"table\") => void;\n /**\n * Several charts of the same rows. The switcher then lists each chart and the table; `chart` names the one `data`\n * holds, and `onChartChange` reports a pick, so the owner passes the matching data.\n */\n charts?: readonly { id: string; label: string }[];\n chart?: string;\n onChartChange?: (id: string) => void;\n /** Filter controls between the header and the viewport, such as ChartFilterControls. */\n controls?: JSX.Element;\n /** The table renders at most this many rows of the sorted order; sorting and Copy data use every row. */\n tableRows?: number;\n sort?: DataTableSort | null;\n defaultSort?: DataTableSort | null;\n onSortChange?: (sort: DataTableSort | null) => void;\n selectedKey?: string | null;\n onSelectedKeyChange?: (key: string | null) => void;\n /** false hides the complete local detail area for a shared selection panel. */\n renderDetails?: false | ((row: T) => JSX.Element);\n height?: string;\n /**\n * Series names below the chart, wrapping inside the fixed viewport height. Use it instead of the renderer's legend,\n * which draws in one row and runs out of narrow columns.\n */\n legend?: readonly ChartLegendEntry[];\n /** What the viewport shows without rows, for example while they load; defaults to \"No data\". */\n empty?: JSX.Element;\n class?: string;\n};\n\n// Only the copied text can prove that a cell is a number; a numeric sort key cannot.\nconst plainNumber = /^[-+]?\\p{Sc}?\\s?\\d[\\d.,\\s]*(?:\\s?(?:%|\\p{Sc}))?$/u;\nconst tsvCell = (value: string) => {\n const safe = /^[=+\\-@\\t\\r]/.test(value) && !plainNumber.test(value) ? `'${value}` : value;\n return /[\\t\\r\\n\"]/.test(safe) ? `\"${safe.replaceAll('\"', '\"\"')}\"` : safe;\n};\n\nexport function ChartExplorer<T extends ChartExplorerRow>(props: ChartExplorerProps<T>) {\n const messages = useUiMessages();\n const chartMessages = useChartMessages();\n const locale = useLocale();\n const id = `k2b-chart-explorer-${createUniqueId()}`;\n const data = () => props.data;\n const [copyFailed, setCopyFailed] = createSignal(false);\n const [localSelection, setLocalSelection] = createSignal<string | null>(null);\n const [localView, setLocalView] = createSignal(props.defaultView ?? \"chart\");\n const [localSort, setLocalSort] = createSignal<DataTableSort | null>(props.defaultSort ?? null);\n const view = () => props.view ?? localView();\n const sort = () => (props.sort === undefined ? localSort() : props.sort);\n const setView = (value: \"chart\" | \"table\") => {\n if (props.view === undefined) setLocalView(value);\n props.onViewChange?.(value);\n };\n const setSort = (value: DataTableSort | null) => {\n if (props.sort === undefined) setLocalSort(value);\n props.onSortChange?.(value);\n };\n const rowsByKey = createMemo(() => new Map(data().rows.map((row) => [row.key, row])));\n const rawSelected = () => (props.selectedKey !== undefined ? props.selectedKey : localSelection());\n const selected = () => (rowsByKey().has(rawSelected() ?? \"\") ? rawSelected() : null);\n const selectedRow = () => rowsByKey().get(selected() ?? \"\");\n const select = (key: string | null) => {\n if (key !== null && !rowsByKey().has(key)) return;\n setLocalSelection(key);\n props.onSelectedKeyChange?.(key);\n };\n if (\n !props.columns.length ||\n props.columns.some((column) => !column.id) ||\n new Set(props.columns.map((column) => column.id)).size !== props.columns.length\n ) {\n throw new Error(\"Chart columns require unique nonempty IDs and at least one column\");\n }\n validateChartExplorerData(data());\n createEffect(() => validateChartExplorerData(data()));\n createEffect(() => {\n if (props.selectedKey === undefined && rawSelected() !== null && selected() === null) select(null);\n });\n const rows = createMemo(() => {\n const result = [...data().rows];\n const order = sort();\n const column = props.columns.find((c) => c.id === order?.key);\n const value = column?.sortValue;\n if (!order || !value) return result;\n const collator = new Intl.Collator(locale(), { numeric: true });\n return result.sort((a, b) => {\n const av = value(a),\n bv = value(b);\n if (av === null) return bv === null ? 0 : 1;\n if (bv === null) return -1;\n const comparison = typeof av === \"number\" && typeof bv === \"number\" ? av - bv : collator.compare(String(av), String(bv));\n return order.direction === \"asc\" ? comparison : -comparison;\n });\n });\n const shownRows = () => (props.tableRows === undefined ? rows() : rows().slice(0, props.tableRows));\n // Chart ids are data; a prefix keeps one named \"table\" apart from the table entry.\n const switcherValue = () => (view() === \"table\" || !props.charts?.length ? view() : `chart:${props.chart ?? props.charts[0]?.id}`);\n const switcherOptions = () =>\n props.charts?.length\n ? [\n ...props.charts.map((chart) => ({ value: `chart:${chart.id}`, label: chart.label })),\n { value: \"table\", label: chartMessages().chartData },\n ]\n : [\n { value: \"chart\", label: chartMessages().chartDiagram },\n { value: \"table\", label: chartMessages().chartData },\n ];\n const pick = (value: string) => {\n if (value === \"table\" || value === \"chart\") return setView(value);\n setView(\"chart\");\n props.onChartChange?.(value.slice(\"chart:\".length));\n };\n const copyText = () =>\n [\n props.columns.map((c) => tsvCell(c.label)).join(\"\\t\"),\n ...rows().map((row) => props.columns.map((c) => tsvCell(c.value(row))).join(\"\\t\")),\n ].join(\"\\n\");\n return (\n <section class={`k2b-chart-explorer ${props.class ?? \"\"}`} aria-labelledby={id}>\n <header class=\"k2b-chart-explorer__header\">\n <div>\n <Dynamic component={`h${props.headingLevel ?? 3}`} id={id} class=\"k2b-chart-explorer__title\">\n {props.title}\n </Dynamic>\n <Show when={props.description}>\n <div>{props.description}</div>\n </Show>\n </div>\n <div class=\"k2b-chart-explorer__actions\">\n <CopyButton\n text={copyText()}\n label={chartMessages().chartCopyData}\n iconOnly\n size=\"sm\"\n variant=\"ghost\"\n disabled={!rows().length}\n onCopyError={() => setCopyFailed(true)}\n onCopied={() => setCopyFailed(false)}\n />\n <SelectChip<string>\n value={switcherValue()}\n onValueChange={pick}\n aria-label={chartMessages().chartView}\n options={switcherOptions()}\n />\n </div>\n </header>\n <Show when={copyFailed()}>\n <span role=\"alert\">{chartMessages().chartCopyFailed}</span>\n </Show>\n {props.controls}\n <div\n class=\"k2b-chart-explorer__viewport\"\n data-legend={view() === \"chart\" && props.legend?.length && data().rows.length > 0 ? \"true\" : undefined}\n data-capped={view() === \"table\" && shownRows().length < rows().length ? \"true\" : undefined}\n style={{ height: props.height ?? \"18rem\" }}\n >\n <Show when={data().rows.length > 0} fallback={<div class=\"k2b-chart__empty\">{props.empty ?? messages().noData}</div>}>\n <Show\n when={view() === \"chart\"}\n fallback={\n <>\n <DataTable\n rows={shownRows()}\n columns={props.columns.map((column) => ({\n id: column.id,\n header: column.label,\n value: column.value,\n align: column.align,\n sortable: Boolean(column.sortValue),\n }))}\n ariaLabelledBy={id}\n getRowId={(row) => row.key}\n selectedRowId={selected()}\n sort={sort()}\n fillHeight\n surface=\"paper\"\n onRowClick={(row) => select(row.key)}\n renderCell={({ row, col, value, render }) => {\n const custom = props.columns.find((column) => column.id === col.id)?.render;\n const cell = () => (custom ? custom(row) : render(value));\n return !custom && col.id === props.columns[0]?.id ? (\n <Button variant=\"text\" size=\"sm\" onClick={() => select(row.key)}>\n {cell()}\n </Button>\n ) : (\n cell()\n );\n }}\n renderHeader={({ col, render }) =>\n col.sortable ? (\n <Button\n variant=\"text\"\n size=\"sm\"\n onClick={() =>\n setSort({\n key: col.id,\n direction: sort()?.key === col.id && sort()?.direction === \"asc\" ? \"desc\" : \"asc\",\n })\n }\n >\n {props.columns.find((column) => column.id === col.id)?.label}\n {/* The data table's own marker: inactive columns keep a dimmed arrow so the header reads as sortable. */}\n <i\n class={`k2b-data-table__sort-icon ti ${sort()?.key === col.id && sort()?.direction === \"asc\" ? \"ti-arrow-up\" : \"ti-arrow-down\"}`}\n data-active={sort()?.key === col.id ? \"true\" : undefined}\n aria-hidden=\"true\"\n />\n </Button>\n ) : (\n render()\n )\n }\n />\n <Show when={shownRows().length < rows().length}>\n <p class=\"k2b-chart-explorer__note\">\n {chartMessages().chartRowsShown({\n shown: new Intl.NumberFormat(locale()).format(shownRows().length),\n total: new Intl.NumberFormat(locale()).format(rows().length),\n })}\n </p>\n </Show>\n </>\n }\n >\n <ChartSnapshotView\n cursor={props.cursor}\n snapshot={data().chart}\n selectedKey={selected()}\n onSelect={select}\n style={props.legend?.length ? undefined : { height: \"100%\" }}\n />\n <Show when={props.legend?.length}>\n <ul class=\"k2b-chart-legend\" aria-label={chartMessages().chartLegend}>\n <For each={props.legend}>\n {(entry) => (\n <li>\n <span\n class=\"k2b-chart-legend__swatch\"\n data-style={entry.style && entry.style !== \"solid\" ? entry.style : undefined}\n data-reference={entry.reference ? \"true\" : undefined}\n style={{ \"--k2b-chart-legend-color\": `var(--stdlib-chart-c${(Math.abs(Math.trunc(entry.color)) % 8) + 1})` }}\n aria-hidden=\"true\"\n />\n {entry.label}\n </li>\n )}\n </For>\n </ul>\n </Show>\n </Show>\n </Show>\n </div>\n <Show when={props.renderDetails !== false}>\n <div class=\"k2b-chart-explorer__details\" aria-live=\"polite\">\n <Show when={selectedRow()}>\n {(row) => (\n <Paper class=\"k2b-chart-explorer__detail-paper\">\n <div class=\"k2b-chart-explorer__detail-header\">\n <span>{chartMessages().chartSelectedDatum}</span>\n <IconButton variant=\"text\" size=\"sm\" label={messages().clearSelection} onClick={() => select(null)}>\n <i class=\"ti ti-x\" aria-hidden=\"true\" />\n </IconButton>\n </div>\n {props.renderDetails ? (\n props.renderDetails(row())\n ) : (\n <DescriptionList\n columns={3}\n size=\"sm\"\n items={props.columns.map((column) => ({\n term: column.label,\n description: column.value(row()),\n }))}\n />\n )}\n </Paper>\n )}\n </Show>\n </div>\n </Show>\n </section>\n );\n}\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ssr as _$ssr } from "solid-js/web";
|
|
2
|
+
import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
|
|
3
|
+
import { escape as _$escape } from "solid-js/web";
|
|
4
|
+
import { createComponent as _$createComponent } from "solid-js/web";
|
|
5
|
+
var _tmpl$ = ["<a", " target=\"_blank\" rel=\"noopener noreferrer\">", "</a>"];
|
|
6
|
+
import { For } from "solid-js";
|
|
7
|
+
import { mapAttribution } from "./map-style";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The required credits for maps built with `mapStyle()`, each one a link, for MapView's `attribution` slot.
|
|
11
|
+
* @example <MapView style={style} attribution={<MapAttribution />} height={320} />
|
|
12
|
+
*/
|
|
13
|
+
export function MapAttribution() {
|
|
14
|
+
return _$createComponent(For, {
|
|
15
|
+
each: mapAttribution,
|
|
16
|
+
children: (part, index) => [index() > 0 ? " · " : "", _$ssr(_tmpl$, _$ssrAttribute("href", _$escape(part.href, true), false), _$escape(part.label))]
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
//# sourceMappingURL=MapAttribution.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"names":[],"sources":["content/MapAttribution.tsx"],"sourcesContent":["import { For, type JSX } from \"solid-js\";\nimport { mapAttribution } from \"./map-style\";\n\n/**\n * The required credits for maps built with `mapStyle()`, each one a link, for MapView's `attribution` slot.\n * @example <MapView style={style} attribution={<MapAttribution />} height={320} />\n */\nexport function MapAttribution(): JSX.Element {\n return (\n <For each={mapAttribution}>\n {(part, index) => (\n <>\n {index() > 0 ? \" · \" : \"\"}\n <a href={part.href} target=\"_blank\" rel=\"noopener noreferrer\">\n {part.label}\n </a>\n </>\n )}\n </For>\n );\n}\n"],"mappings":";;;;;AAAA,SAAS,GAAG,QAAkB,UAAU;AACxC,SAAS,cAAc,QAAQ,aAAa;;AAE5C;AACA;AACA;AACA;AACA,OAAO,SAAS,cAAc,GAAgB;EAC5C,yBACG,GAAG;IAAC,IAAI,EAAE,cAAc;IAAA,UACtB,CAAC,IAAI,EAAE,KAAK,MAER,KAAK,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,GAAG,EAAE,gDAChB,IAAI,CAAC,IAAI,0BACf,IAAI,CAAC,KAAK;EAGhB;AAGP","ignoreList":[]}
|
|
@@ -0,0 +1,474 @@
|
|
|
1
|
+
import { mergeProps as _$mergeProps } from "solid-js/web";
|
|
2
|
+
import { memo as _$memo } from "solid-js/web";
|
|
3
|
+
import { ssr as _$ssr } from "solid-js/web";
|
|
4
|
+
import { createComponent as _$createComponent } from "solid-js/web";
|
|
5
|
+
import { ssrAttribute as _$ssrAttribute } from "solid-js/web";
|
|
6
|
+
import { ssrStyleProperty as _$ssrStyleProperty } from "solid-js/web";
|
|
7
|
+
import { escape as _$escape } from "solid-js/web";
|
|
8
|
+
var _tmpl$ = ["<div class=\"k2b-map-view__notice\">", "</div>"],
|
|
9
|
+
_tmpl$2 = ["<div class=\"k2b-map-view__status\" role=\"status\"><i aria-hidden=\"true\"", "></i><span>", "</span>", "</div>"],
|
|
10
|
+
_tmpl$3 = "<i class=\"ti ti-plus\" aria-hidden=\"true\"></i>",
|
|
11
|
+
_tmpl$4 = "<i class=\"ti ti-minus\" aria-hidden=\"true\"></i>",
|
|
12
|
+
_tmpl$5 = "<i class=\"ti ti-current-location\" aria-hidden=\"true\"></i>",
|
|
13
|
+
_tmpl$6 = ["<div class=\"", "\" style=\"", "\"", " role=\"region\"", "><span class=\"k2b-sr-only\">", "</span><div class=\"k2b-map-view__live\"></div>", "<div class=\"k2b-map-view__messages\">", "", "</div><div class=\"k2b-map-view__controls\"><div class=\"k2b-map-view__zoom\">", "", "</div>", "</div><div class=\"k2b-map-view__foot\"><div class=\"k2b-map-view__info\" aria-live=\"polite\">", "</div>", "</div></div>"],
|
|
14
|
+
_tmpl$7 = ["<span>", "</span>"],
|
|
15
|
+
_tmpl$8 = ["<div class=\"k2b-map-view__empty\">", "</div>"];
|
|
16
|
+
import { children, createEffect, createMemo, createSignal, on, onCleanup, onMount, Show, untrack } from "solid-js";
|
|
17
|
+
import { Button, IconButton } from "../actions/Button";
|
|
18
|
+
import { prefersReducedMotion } from "../motion";
|
|
19
|
+
import { MapFootRow, MapGeometry, referenceScale, selectedMarker } from "./MapViewFrame";
|
|
20
|
+
import { DEFAULT_PADDING, fitCamera, mapFrame, metersPerPixel, pinOffsets, scaleBar, sortMarkers, unprojectPoint } from "./map-view-geometry";
|
|
21
|
+
import { useMapViewMessages } from "./map-view-messages";
|
|
22
|
+
function supportsWebgl() {
|
|
23
|
+
try {
|
|
24
|
+
const canvas = document.createElement("canvas");
|
|
25
|
+
// MapLibre 6 draws with WebGL 2 only; a browser with just WebGL 1 gets the static frame and the notice.
|
|
26
|
+
const gl = canvas.getContext("webgl2");
|
|
27
|
+
if (!gl) return false;
|
|
28
|
+
gl.getExtension("WEBGL_lose_context")?.loseContext();
|
|
29
|
+
return true;
|
|
30
|
+
} catch {
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/** A static server frame enhanced with a same-origin MapLibre worker only after it enters the viewport. */
|
|
36
|
+
export function MapView(props) {
|
|
37
|
+
const messages = useMapViewMessages();
|
|
38
|
+
const frame = createMemo(() => mapFrame(props, props.height, props.padding ?? DEFAULT_PADDING));
|
|
39
|
+
const selected = createMemo(() => selectedMarker(props.markers ?? [], props.selectedId));
|
|
40
|
+
const selectedId = createMemo(() => selected()?.id);
|
|
41
|
+
const notice = children(() => props.notice);
|
|
42
|
+
const empty = children(() => props.empty);
|
|
43
|
+
const [state, setState] = createSignal("static");
|
|
44
|
+
createEffect(() => props.onStateChange?.(state()));
|
|
45
|
+
const [dark, setDark] = createSignal(false);
|
|
46
|
+
const [boxStyle, setBoxStyle] = createSignal();
|
|
47
|
+
const [liveScale, setLiveScale] = createSignal();
|
|
48
|
+
const [measuredWidth, setMeasuredWidth] = createSignal();
|
|
49
|
+
// The zoom the person chose, once they zoomed; pin separation follows it relative to the current fit.
|
|
50
|
+
const [chosenZoom, setChosenZoom] = createSignal();
|
|
51
|
+
const zoomFactor = createMemo(() => {
|
|
52
|
+
const zoom = chosenZoom();
|
|
53
|
+
const f = frame();
|
|
54
|
+
const width = measuredWidth();
|
|
55
|
+
if (zoom === undefined || !f || width === undefined) return 1;
|
|
56
|
+
return 2 ** (zoom - fitCamera(f, width, props.padding ?? DEFAULT_PADDING).zoom);
|
|
57
|
+
});
|
|
58
|
+
const [needsReload, setNeedsReload] = createSignal(false);
|
|
59
|
+
// The server picks the scale bar for the widest fit; once the width is known, the bar names a distance that fits it.
|
|
60
|
+
const scale = createMemo(() => {
|
|
61
|
+
const f = frame();
|
|
62
|
+
if (!f) return undefined;
|
|
63
|
+
const live = liveScale();
|
|
64
|
+
if (live) return live;
|
|
65
|
+
const width = measuredWidth();
|
|
66
|
+
if (width === undefined) return referenceScale(f, props.padding ?? DEFAULT_PADDING);
|
|
67
|
+
const camera = fitCamera(f, width, props.padding ?? DEFAULT_PADDING);
|
|
68
|
+
const mpp = metersPerPixel(camera.center[1], camera.zoom);
|
|
69
|
+
const bar = scaleBar(mpp);
|
|
70
|
+
return {
|
|
71
|
+
label: bar.label,
|
|
72
|
+
width: `${bar.meters / mpp}px`
|
|
73
|
+
};
|
|
74
|
+
});
|
|
75
|
+
const resolvedStyle = () => {
|
|
76
|
+
const source = props.style;
|
|
77
|
+
return typeof source === "object" && "dark" in source ? dark() ? source.dark : source.light : source;
|
|
78
|
+
};
|
|
79
|
+
let root;
|
|
80
|
+
let live;
|
|
81
|
+
let info;
|
|
82
|
+
let map;
|
|
83
|
+
let currentStyle;
|
|
84
|
+
let disposed = false;
|
|
85
|
+
let visible = false;
|
|
86
|
+
let mounted = false;
|
|
87
|
+
let touched = false;
|
|
88
|
+
let replacingStyle = false;
|
|
89
|
+
let generation = 0;
|
|
90
|
+
let intersection;
|
|
91
|
+
let resize;
|
|
92
|
+
let infoResize;
|
|
93
|
+
const themes = [];
|
|
94
|
+
|
|
95
|
+
// From the person's first camera change on, the camera stays theirs; pin separation follows its zoom.
|
|
96
|
+
const touch = () => {
|
|
97
|
+
touched = true;
|
|
98
|
+
if (map) setChosenZoom(map.getZoom());
|
|
99
|
+
};
|
|
100
|
+
const removeMap = () => {
|
|
101
|
+
generation++;
|
|
102
|
+
const previous = map;
|
|
103
|
+
map = undefined;
|
|
104
|
+
previous?.remove();
|
|
105
|
+
setBoxStyle(undefined);
|
|
106
|
+
setLiveScale(undefined);
|
|
107
|
+
setChosenZoom(undefined);
|
|
108
|
+
currentStyle = undefined;
|
|
109
|
+
replacingStyle = false;
|
|
110
|
+
};
|
|
111
|
+
// MapLibre wraps its center across the antimeridian; geometry is drawn in the world copy nearest the camera.
|
|
112
|
+
const worldCopy = (live, f) => Math.round((live.getCenter().lng + 180) / 360 - (f.minX + f.spanX / 2));
|
|
113
|
+
const syncOverlay = () => {
|
|
114
|
+
const f = frame();
|
|
115
|
+
if (!map || !f || state() !== "ready") return;
|
|
116
|
+
const copy = worldCopy(map, f);
|
|
117
|
+
const a = unprojectPoint(f.minX + copy, f.minY);
|
|
118
|
+
const b = unprojectPoint(f.minX + copy + f.spanX, f.minY + f.spanY);
|
|
119
|
+
const top = map.project([a.lon, a.lat]);
|
|
120
|
+
const bottom = map.project([b.lon, b.lat]);
|
|
121
|
+
setBoxStyle({
|
|
122
|
+
left: `${top.x}px`,
|
|
123
|
+
top: `${top.y}px`,
|
|
124
|
+
width: `${bottom.x - top.x}px`,
|
|
125
|
+
height: `${bottom.y - top.y}px`,
|
|
126
|
+
transform: "none"
|
|
127
|
+
});
|
|
128
|
+
const mpp = metersPerPixel(map.getCenter().lat, map.getZoom());
|
|
129
|
+
const bar = scaleBar(mpp);
|
|
130
|
+
setLiveScale({
|
|
131
|
+
label: bar.label,
|
|
132
|
+
width: `${bar.meters / mpp}px`
|
|
133
|
+
});
|
|
134
|
+
};
|
|
135
|
+
const refit = () => {
|
|
136
|
+
const f = frame();
|
|
137
|
+
if (map && f && !touched) map.jumpTo(fitCamera(f, root.clientWidth, props.padding ?? DEFAULT_PADDING));
|
|
138
|
+
syncOverlay();
|
|
139
|
+
};
|
|
140
|
+
const start = async () => {
|
|
141
|
+
if (disposed || !visible || !frame()) return;
|
|
142
|
+
removeMap();
|
|
143
|
+
touched = false;
|
|
144
|
+
if (!supportsWebgl()) {
|
|
145
|
+
setState("no-webgl");
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
const attempt = generation;
|
|
149
|
+
setState("loading");
|
|
150
|
+
const fail = () => {
|
|
151
|
+
if (disposed || attempt !== generation) return;
|
|
152
|
+
removeMap();
|
|
153
|
+
setState("error");
|
|
154
|
+
};
|
|
155
|
+
let engine;
|
|
156
|
+
try {
|
|
157
|
+
engine = await import("./map-view-live");
|
|
158
|
+
} catch {
|
|
159
|
+
// The map code did not load, for example because the page outlived a release that removed its files.
|
|
160
|
+
// Importing the same address again cannot help; a reload fetches the current page and its map code.
|
|
161
|
+
if (disposed || attempt !== generation) return;
|
|
162
|
+
setNeedsReload(true);
|
|
163
|
+
fail();
|
|
164
|
+
return;
|
|
165
|
+
}
|
|
166
|
+
try {
|
|
167
|
+
const {
|
|
168
|
+
createMap
|
|
169
|
+
} = engine;
|
|
170
|
+
if (disposed || attempt !== generation) return;
|
|
171
|
+
const f = frame();
|
|
172
|
+
if (!f) return;
|
|
173
|
+
currentStyle = resolvedStyle();
|
|
174
|
+
const t = messages();
|
|
175
|
+
const instance = createMap({
|
|
176
|
+
container: live,
|
|
177
|
+
style: currentStyle,
|
|
178
|
+
...fitCamera(f, root.clientWidth, props.padding ?? DEFAULT_PADDING),
|
|
179
|
+
attributionControl: false,
|
|
180
|
+
cooperativeGestures: true,
|
|
181
|
+
dragRotate: false,
|
|
182
|
+
// Shift-drag box zoom needs MapLibre's own stylesheet for its rectangle and starts without an input event.
|
|
183
|
+
boxZoom: false,
|
|
184
|
+
pitchWithRotate: false,
|
|
185
|
+
touchPitch: false,
|
|
186
|
+
maxPitch: 0,
|
|
187
|
+
bearing: 0,
|
|
188
|
+
pitch: 0,
|
|
189
|
+
keyboard: true,
|
|
190
|
+
trackResize: false,
|
|
191
|
+
// MapLibre's own gestures (keyboard and double-click zoom, inertia) follow the same choice as our buttons.
|
|
192
|
+
// The value is read when the map starts; MapLibre keeps one setting for all maps on the page.
|
|
193
|
+
reduceMotion: prefersReducedMotion(),
|
|
194
|
+
locale: {
|
|
195
|
+
"Map.Title": t.canvasTitle,
|
|
196
|
+
"CooperativeGesturesHandler.WindowsHelpText": t.gestureWindows,
|
|
197
|
+
"CooperativeGesturesHandler.MacHelpText": t.gestureMac,
|
|
198
|
+
"CooperativeGesturesHandler.MobileHelpText": t.gestureMobile
|
|
199
|
+
}
|
|
200
|
+
}, fail);
|
|
201
|
+
map = instance;
|
|
202
|
+
instance.on("error", event => {
|
|
203
|
+
// A missing paid resource must remain visible and recoverable, including failures after initial load.
|
|
204
|
+
if ("sourceId" in event || "tile" in event || state() === "loading" || replacingStyle) fail();
|
|
205
|
+
});
|
|
206
|
+
instance.on("style.load", () => {
|
|
207
|
+
replacingStyle = false;
|
|
208
|
+
});
|
|
209
|
+
instance.on("load", () => {
|
|
210
|
+
if (disposed || attempt !== generation) return;
|
|
211
|
+
setState("ready");
|
|
212
|
+
refit();
|
|
213
|
+
});
|
|
214
|
+
instance.on("movestart", event => {
|
|
215
|
+
if (event.originalEvent) touch();
|
|
216
|
+
});
|
|
217
|
+
instance.on("move", syncOverlay);
|
|
218
|
+
// Pin separation is decided again once a zoom ends, not on every animation frame.
|
|
219
|
+
instance.on("zoomend", () => {
|
|
220
|
+
if (map === instance && touched) setChosenZoom(instance.getZoom());
|
|
221
|
+
});
|
|
222
|
+
instance.on("resize", syncOverlay);
|
|
223
|
+
} catch {
|
|
224
|
+
fail();
|
|
225
|
+
}
|
|
226
|
+
};
|
|
227
|
+
const clearSelectedPin = () => {
|
|
228
|
+
const marker = selected();
|
|
229
|
+
if (!marker || state() !== "ready" || !map) return;
|
|
230
|
+
const f = frame();
|
|
231
|
+
const lon = marker.point.lon + (f ? worldCopy(map, f) * 360 : 0);
|
|
232
|
+
const p = map.project([lon, marker.point.lat]);
|
|
233
|
+
const offset = f ? pinOffsets(sortMarkers(props.markers ?? []), f, zoomFactor(), props.ownLocation?.point).get(marker.id) : undefined;
|
|
234
|
+
const x = p.x + (offset?.dx ?? 0);
|
|
235
|
+
const y = p.y + (offset?.dy ?? 0);
|
|
236
|
+
const w = root.clientWidth;
|
|
237
|
+
const h = root.clientHeight;
|
|
238
|
+
const tile = info.getBoundingClientRect();
|
|
239
|
+
const bounds = root.getBoundingClientRect();
|
|
240
|
+
const tileTop = tile.top - bounds.top - 8;
|
|
241
|
+
const covered = tile.height > 0 && x >= tile.left - bounds.left - 8 && x <= tile.right - bounds.left + 8 && y >= tileTop && y <= tile.bottom - bounds.top + 8;
|
|
242
|
+
if (!covered && x >= 16 && x <= w - 16 && y >= 16 && y <= h - 16) return;
|
|
243
|
+
touch();
|
|
244
|
+
// Put the drawn pin, offset included, in the free band above the tile.
|
|
245
|
+
const targetX = w / 2;
|
|
246
|
+
const targetY = tile.height > 0 ? tileTop / 2 : h / 2;
|
|
247
|
+
const target = map.unproject([x + w / 2 - targetX, y + h / 2 - targetY]);
|
|
248
|
+
const camera = {
|
|
249
|
+
center: [target.lng, target.lat]
|
|
250
|
+
};
|
|
251
|
+
if (prefersReducedMotion()) map.jumpTo(camera);else map.easeTo(camera);
|
|
252
|
+
};
|
|
253
|
+
onMount(() => {
|
|
254
|
+
mounted = true;
|
|
255
|
+
const theme = () => setDark(!!root.closest('.dark, .k2b-dark, [data-theme="dark"]'));
|
|
256
|
+
theme();
|
|
257
|
+
for (let ancestor = root; ancestor; ancestor = ancestor.parentElement) {
|
|
258
|
+
const observer = new MutationObserver(theme);
|
|
259
|
+
observer.observe(ancestor, {
|
|
260
|
+
attributes: true,
|
|
261
|
+
attributeFilter: ["class", "data-theme"]
|
|
262
|
+
});
|
|
263
|
+
themes.push(observer);
|
|
264
|
+
}
|
|
265
|
+
setMeasuredWidth(root.clientWidth);
|
|
266
|
+
resize = new ResizeObserver(() => {
|
|
267
|
+
setMeasuredWidth(root.clientWidth);
|
|
268
|
+
map?.resize();
|
|
269
|
+
refit();
|
|
270
|
+
});
|
|
271
|
+
resize.observe(root);
|
|
272
|
+
infoResize = new ResizeObserver(clearSelectedPin);
|
|
273
|
+
infoResize.observe(info);
|
|
274
|
+
const enter = () => {
|
|
275
|
+
visible = true;
|
|
276
|
+
intersection?.disconnect();
|
|
277
|
+
if (frame()) void start();
|
|
278
|
+
};
|
|
279
|
+
if (!frame()) setState("empty");
|
|
280
|
+
if (typeof IntersectionObserver === "undefined") enter();else {
|
|
281
|
+
intersection = new IntersectionObserver(entries => {
|
|
282
|
+
if (entries.some(entry => entry.isIntersecting)) enter();
|
|
283
|
+
}, {
|
|
284
|
+
threshold: 0,
|
|
285
|
+
rootMargin: "0px"
|
|
286
|
+
});
|
|
287
|
+
intersection.observe(root);
|
|
288
|
+
}
|
|
289
|
+
});
|
|
290
|
+
createEffect(on(frame, value => {
|
|
291
|
+
if (!mounted) return;
|
|
292
|
+
if (!value) {
|
|
293
|
+
removeMap();
|
|
294
|
+
setState("empty");
|
|
295
|
+
} else if (state() === "empty") {
|
|
296
|
+
setState("static");
|
|
297
|
+
if (visible) void start();
|
|
298
|
+
} else refit();
|
|
299
|
+
}, {
|
|
300
|
+
defer: true
|
|
301
|
+
}));
|
|
302
|
+
createEffect(() => {
|
|
303
|
+
const style = resolvedStyle();
|
|
304
|
+
if (state() === "ready" && map && style !== currentStyle) {
|
|
305
|
+
currentStyle = style;
|
|
306
|
+
replacingStyle = true;
|
|
307
|
+
map.setStyle(style);
|
|
308
|
+
}
|
|
309
|
+
});
|
|
310
|
+
createEffect(() => {
|
|
311
|
+
selected();
|
|
312
|
+
state();
|
|
313
|
+
untrack(clearSelectedPin);
|
|
314
|
+
});
|
|
315
|
+
onCleanup(() => {
|
|
316
|
+
disposed = true;
|
|
317
|
+
intersection?.disconnect();
|
|
318
|
+
resize?.disconnect();
|
|
319
|
+
infoResize?.disconnect();
|
|
320
|
+
for (const observer of themes) observer.disconnect();
|
|
321
|
+
removeMap();
|
|
322
|
+
});
|
|
323
|
+
const zoom = direction => {
|
|
324
|
+
if (!map || state() !== "ready") return;
|
|
325
|
+
touch();
|
|
326
|
+
const options = {
|
|
327
|
+
animate: !prefersReducedMotion()
|
|
328
|
+
};
|
|
329
|
+
if (direction === "in") map.zoomIn(options);else map.zoomOut(options);
|
|
330
|
+
};
|
|
331
|
+
return _$ssr(_tmpl$6, `k2b-map-view${props.class ? ` ${_$escape(props.class, true)}` : ""}`, _$ssrStyleProperty("height:", `${_$escape(props.height, true)}px`), _$ssrAttribute("data-state", _$escape(state(), true), false), _$ssrAttribute("aria-label", _$escape(messages().mapLabel, true), false), _$escape(messages().summary({
|
|
332
|
+
count: props.markers?.length ?? 0
|
|
333
|
+
})), _$escape(_$createComponent(Show, {
|
|
334
|
+
get when() {
|
|
335
|
+
return frame();
|
|
336
|
+
},
|
|
337
|
+
get fallback() {
|
|
338
|
+
return _$ssr(_tmpl$8, _$escape(_$createComponent(Show, {
|
|
339
|
+
get when() {
|
|
340
|
+
return empty();
|
|
341
|
+
},
|
|
342
|
+
get children() {
|
|
343
|
+
return _$ssr(_tmpl$7, _$escape(empty()));
|
|
344
|
+
}
|
|
345
|
+
})));
|
|
346
|
+
},
|
|
347
|
+
children: value => _$createComponent(MapGeometry, _$mergeProps(props, {
|
|
348
|
+
get frame() {
|
|
349
|
+
return value();
|
|
350
|
+
},
|
|
351
|
+
get boxStyle() {
|
|
352
|
+
return boxStyle();
|
|
353
|
+
},
|
|
354
|
+
get zoomFactor() {
|
|
355
|
+
return zoomFactor();
|
|
356
|
+
},
|
|
357
|
+
get onSelect() {
|
|
358
|
+
return props.onSelect;
|
|
359
|
+
},
|
|
360
|
+
interactive: true
|
|
361
|
+
}))
|
|
362
|
+
})), _$escape(_$createComponent(Show, {
|
|
363
|
+
get when() {
|
|
364
|
+
return notice();
|
|
365
|
+
},
|
|
366
|
+
get children() {
|
|
367
|
+
return _$ssr(_tmpl$, _$escape(notice()));
|
|
368
|
+
}
|
|
369
|
+
})), _$escape(_$createComponent(Show, {
|
|
370
|
+
get when() {
|
|
371
|
+
return state() === "loading" || state() === "error" || state() === "no-webgl";
|
|
372
|
+
},
|
|
373
|
+
get children() {
|
|
374
|
+
return _$ssr(_tmpl$2, _$ssrAttribute("class", state() === "loading" ? "ti ti-loader-2 k2b-spin" : state() === "error" ? "ti ti-alert-circle" : "ti ti-info-circle", false), state() === "loading" ? _$escape(messages().loading) : state() === "error" ? _$escape(messages().error) : _$escape(messages().noWebgl), _$escape(_$createComponent(Show, {
|
|
375
|
+
get when() {
|
|
376
|
+
return state() === "error";
|
|
377
|
+
},
|
|
378
|
+
get children() {
|
|
379
|
+
return _$createComponent(Button, {
|
|
380
|
+
size: "xs",
|
|
381
|
+
variant: "text",
|
|
382
|
+
onClick: () => needsReload() ? window.location.reload() : void start(),
|
|
383
|
+
get children() {
|
|
384
|
+
return needsReload() ? messages().reload : messages().retry;
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
})));
|
|
389
|
+
}
|
|
390
|
+
})), _$escape(_$createComponent(IconButton, {
|
|
391
|
+
get label() {
|
|
392
|
+
return messages().zoomIn;
|
|
393
|
+
},
|
|
394
|
+
tooltip: false,
|
|
395
|
+
get disabled() {
|
|
396
|
+
return state() !== "ready";
|
|
397
|
+
},
|
|
398
|
+
onClick: () => zoom("in"),
|
|
399
|
+
get children() {
|
|
400
|
+
return _$ssr(_tmpl$3);
|
|
401
|
+
}
|
|
402
|
+
})), _$escape(_$createComponent(IconButton, {
|
|
403
|
+
get label() {
|
|
404
|
+
return messages().zoomOut;
|
|
405
|
+
},
|
|
406
|
+
tooltip: false,
|
|
407
|
+
get disabled() {
|
|
408
|
+
return state() !== "ready";
|
|
409
|
+
},
|
|
410
|
+
onClick: () => zoom("out"),
|
|
411
|
+
get children() {
|
|
412
|
+
return _$ssr(_tmpl$4);
|
|
413
|
+
}
|
|
414
|
+
})), _$escape(_$createComponent(Show, {
|
|
415
|
+
get when() {
|
|
416
|
+
return props.onLocate;
|
|
417
|
+
},
|
|
418
|
+
get children() {
|
|
419
|
+
return _$createComponent(IconButton, {
|
|
420
|
+
"class": "k2b-map-view__locate",
|
|
421
|
+
tooltip: false,
|
|
422
|
+
get label() {
|
|
423
|
+
return messages().locate;
|
|
424
|
+
},
|
|
425
|
+
get ["aria-pressed"]() {
|
|
426
|
+
return !!props.ownLocation;
|
|
427
|
+
},
|
|
428
|
+
onClick: () => props.onLocate?.(),
|
|
429
|
+
get children() {
|
|
430
|
+
return _$ssr(_tmpl$5);
|
|
431
|
+
}
|
|
432
|
+
});
|
|
433
|
+
}
|
|
434
|
+
})), _$escape(_$createComponent(Show, {
|
|
435
|
+
get when() {
|
|
436
|
+
return selectedId();
|
|
437
|
+
},
|
|
438
|
+
keyed: true,
|
|
439
|
+
children: _id => {
|
|
440
|
+
// Keep the tile's state and focus for this selection while its marker data refreshes.
|
|
441
|
+
const initial = selected();
|
|
442
|
+
const current = () => selected() ?? initial;
|
|
443
|
+
const marker = {
|
|
444
|
+
get id() {
|
|
445
|
+
return current().id;
|
|
446
|
+
},
|
|
447
|
+
get point() {
|
|
448
|
+
return current().point;
|
|
449
|
+
},
|
|
450
|
+
get mark() {
|
|
451
|
+
return current().mark;
|
|
452
|
+
},
|
|
453
|
+
get label() {
|
|
454
|
+
return current().label;
|
|
455
|
+
},
|
|
456
|
+
get tone() {
|
|
457
|
+
return current().tone;
|
|
458
|
+
},
|
|
459
|
+
get selected() {
|
|
460
|
+
return current().selected;
|
|
461
|
+
}
|
|
462
|
+
};
|
|
463
|
+
return props.infoTile?.(marker);
|
|
464
|
+
}
|
|
465
|
+
})), _$escape(_$createComponent(MapFootRow, {
|
|
466
|
+
get scale() {
|
|
467
|
+
return scale();
|
|
468
|
+
},
|
|
469
|
+
get children() {
|
|
470
|
+
return props.attribution;
|
|
471
|
+
}
|
|
472
|
+
})));
|
|
473
|
+
}
|
|
474
|
+
//# sourceMappingURL=MapView.js.map
|