mirror of
https://github.com/zadam/trilium.git
synced 2026-02-16 11:26:55 +01:00
Compare commits
19 Commits
feat/ui/im
...
copilot/sw
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
638beb5b03 | ||
|
|
156aa55ee7 | ||
|
|
f197756bb4 | ||
|
|
ad1f83fc75 | ||
|
|
12d9391b1e | ||
|
|
60facceef1 | ||
|
|
8d0c61ea37 | ||
|
|
3865d96d44 | ||
|
|
647ba85211 | ||
|
|
ea09018acd | ||
|
|
e53fd1b059 | ||
|
|
9e3bb585a2 | ||
|
|
aa9b05641f | ||
|
|
cffcfeb734 | ||
|
|
6e32aaac33 | ||
|
|
f0d91d45f4 | ||
|
|
29ebb316b6 | ||
|
|
89ea0a2730 | ||
|
|
d847d966bc |
@@ -23,7 +23,6 @@
|
||||
"@fullcalendar/list": "6.1.20",
|
||||
"@fullcalendar/multimonth": "6.1.20",
|
||||
"@fullcalendar/timegrid": "6.1.20",
|
||||
"@maplibre/maplibre-gl-leaflet": "0.1.3",
|
||||
"@mermaid-js/layout-elk": "0.2.0",
|
||||
"@mind-elixir/node-menu": "5.0.1",
|
||||
"@popperjs/core": "2.11.8",
|
||||
@@ -51,9 +50,8 @@
|
||||
"jsplumb": "2.15.6",
|
||||
"katex": "0.16.28",
|
||||
"knockout": "3.5.1",
|
||||
"leaflet": "1.9.4",
|
||||
"leaflet-gpx": "2.2.0",
|
||||
"mark.js": "8.11.1",
|
||||
"maplibre-gl": "5.18.0",
|
||||
"marked": "17.0.2",
|
||||
"mermaid": "11.12.2",
|
||||
"mind-elixir": "5.8.0",
|
||||
@@ -72,8 +70,6 @@
|
||||
"@prefresh/vite": "2.4.11",
|
||||
"@types/bootstrap": "5.2.10",
|
||||
"@types/jquery": "3.5.33",
|
||||
"@types/leaflet": "1.9.21",
|
||||
"@types/leaflet-gpx": "1.3.8",
|
||||
"@types/mark.js": "8.11.12",
|
||||
"@types/reveal.js": "5.2.2",
|
||||
"@types/tabulator-tables": "6.3.1",
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { LeafletMouseEvent } from "leaflet";
|
||||
import type { GeoMouseEvent } from "../widgets/collections/geomap/map.js";
|
||||
|
||||
import appContext, { type CommandNames } from "../components/app_context.js";
|
||||
import { t } from "../services/i18n.js";
|
||||
@@ -16,7 +16,7 @@ function openContextMenu(notePath: string, e: ContextMenuEvent, viewScope: ViewS
|
||||
});
|
||||
}
|
||||
|
||||
function getItems(e: ContextMenuEvent | LeafletMouseEvent): MenuItem<CommandNames>[] {
|
||||
function getItems(e: ContextMenuEvent | GeoMouseEvent): MenuItem<CommandNames>[] {
|
||||
const ntxId = getNtxId(e);
|
||||
const isMobileSplitOpen = isMobile() && appContext.tabManager.getNoteContextById(ntxId).getMainContext().getSubContexts().length > 1;
|
||||
|
||||
@@ -28,7 +28,7 @@ function getItems(e: ContextMenuEvent | LeafletMouseEvent): MenuItem<CommandName
|
||||
];
|
||||
}
|
||||
|
||||
function handleLinkContextMenuItem(command: string | undefined, e: ContextMenuEvent | LeafletMouseEvent, notePath: string, viewScope = {}, hoistedNoteId: string | null = null) {
|
||||
function handleLinkContextMenuItem(command: string | undefined, e: ContextMenuEvent | GeoMouseEvent, notePath: string, viewScope = {}, hoistedNoteId: string | null = null) {
|
||||
if (!hoistedNoteId) {
|
||||
hoistedNoteId = appContext.tabManager.getActiveContext()?.hoistedNoteId ?? null;
|
||||
}
|
||||
@@ -52,7 +52,7 @@ function handleLinkContextMenuItem(command: string | undefined, e: ContextMenuEv
|
||||
return false;
|
||||
}
|
||||
|
||||
function getNtxId(e: ContextMenuEvent | LeafletMouseEvent) {
|
||||
function getNtxId(e: ContextMenuEvent | GeoMouseEvent) {
|
||||
if (utils.isDesktop()) {
|
||||
const subContexts = appContext.tabManager.getActiveContext()?.getSubContexts();
|
||||
if (!subContexts) return null;
|
||||
|
||||
@@ -291,15 +291,6 @@
|
||||
--ck-editor-toolbar-button-on-shadow: 1px 1px 2px rgba(0, 0, 0, .75);
|
||||
--ck-editor-toolbar-dropdown-button-open-background: #ffffff14;
|
||||
|
||||
--note-list-view-icon-color: var(--left-pane-icon-color);
|
||||
--note-list-view-large-icon-background: var(--note-icon-background-color);
|
||||
--note-list-view-large-icon-color: var(--note-icon-color);
|
||||
--note-list-view-search-result-highlight-background: transparent;
|
||||
--note-list-view-search-result-highlight-color: var(--quick-search-result-highlight-color);
|
||||
--note-list-view-content-background: rgba(0, 0, 0, .2);
|
||||
--note-list-view-content-search-result-highlight-background: var(--quick-search-result-highlight-color);
|
||||
--note-list-view-content-search-result-highlight-color: black;
|
||||
|
||||
--calendar-coll-event-background-saturation: 25%;
|
||||
--calendar-coll-event-background-lightness: 20%;
|
||||
--calendar-coll-event-background-color: #3c3c3c;
|
||||
@@ -313,8 +304,7 @@
|
||||
* Dark color scheme tweaks
|
||||
*/
|
||||
|
||||
#left-pane .fancytree-node.tinted,
|
||||
.nested-note-list-item.use-note-color {
|
||||
#left-pane .fancytree-node.tinted {
|
||||
--custom-color: var(--dark-theme-custom-color);
|
||||
|
||||
/* The background color of the active item in the note tree.
|
||||
@@ -364,8 +354,7 @@ body .todo-list input[type="checkbox"]:not(:checked):before {
|
||||
}
|
||||
|
||||
.note-split.with-hue,
|
||||
.quick-edit-dialog-wrapper.with-hue,
|
||||
.nested-note-list-item.with-hue {
|
||||
.quick-edit-dialog-wrapper.with-hue {
|
||||
--note-icon-custom-background-color: hsl(var(--custom-color-hue), 15.8%, 30.9%);
|
||||
--note-icon-custom-color: hsl(var(--custom-color-hue), 100%, 76.5%);
|
||||
--note-icon-hover-custom-background-color: hsl(var(--custom-color-hue), 28.3%, 36.7%);
|
||||
|
||||
@@ -289,15 +289,6 @@
|
||||
--ck-editor-toolbar-button-on-shadow: none;
|
||||
--ck-editor-toolbar-dropdown-button-open-background: #0000000f;
|
||||
|
||||
--note-list-view-icon-color: var(--left-pane-icon-color);
|
||||
--note-list-view-large-icon-background: var(--note-icon-background-color);
|
||||
--note-list-view-large-icon-color: var(--note-icon-color);
|
||||
--note-list-view-search-result-highlight-background: transparent;
|
||||
--note-list-view-search-result-highlight-color: var(--quick-search-result-highlight-color);
|
||||
--note-list-view-content-background: #b1b1b133;
|
||||
--note-list-view-content-search-result-highlight-background: var(--quick-search-result-highlight-color);
|
||||
--note-list-view-content-search-result-highlight-color: white;
|
||||
|
||||
--calendar-coll-event-background-lightness: 95%;
|
||||
--calendar-coll-event-background-saturation: 80%;
|
||||
--calendar-coll-event-background-color: #eaeaea;
|
||||
@@ -307,8 +298,7 @@
|
||||
--calendar-coll-today-background-color: #00000006;
|
||||
}
|
||||
|
||||
#left-pane .fancytree-node.tinted,
|
||||
.nested-note-list-item.use-note-color {
|
||||
#left-pane .fancytree-node.tinted {
|
||||
--custom-color: var(--light-theme-custom-color);
|
||||
|
||||
/* The background color of the active item in the note tree.
|
||||
@@ -334,8 +324,7 @@
|
||||
}
|
||||
|
||||
.note-split.with-hue,
|
||||
.quick-edit-dialog-wrapper.with-hue,
|
||||
.nested-note-list-item.with-hue {
|
||||
.quick-edit-dialog-wrapper.with-hue {
|
||||
--note-icon-custom-background-color: hsl(var(--custom-color-hue), 44.5%, 43.1%);
|
||||
--note-icon-custom-color: hsl(var(--custom-color-hue), 91.3%, 91%);
|
||||
--note-icon-hover-custom-background-color: hsl(var(--custom-color-hue), 55.1%, 50.2%);
|
||||
|
||||
@@ -751,14 +751,12 @@ body[dir=rtl] #left-pane span.fancytree-node.protected > span.fancytree-custom-i
|
||||
}
|
||||
}
|
||||
|
||||
#left-pane .fancytree-expander,
|
||||
.nested-note-list-item .note-expander {
|
||||
#left-pane .fancytree-expander {
|
||||
opacity: 0.65;
|
||||
transition: opacity 150ms ease-in;
|
||||
}
|
||||
|
||||
#left-pane .fancytree-expander:hover,
|
||||
.nested-note-list-item .note-expander:hover {
|
||||
#left-pane .fancytree-expander:hover {
|
||||
opacity: 1;
|
||||
transition: opacity 300ms ease-out;
|
||||
}
|
||||
|
||||
@@ -1010,7 +1010,7 @@
|
||||
"no_attachments": "This note has no attachments."
|
||||
},
|
||||
"book": {
|
||||
"no_children_help": "This collection doesn't have any child notes so there's nothing to display. See <a href=\"https://triliumnext.github.io/Docs/Wiki/book-note.html\">wiki</a> for details.",
|
||||
"no_children_help": "This collection doesn't have any child notes so there's nothing to display.",
|
||||
"drag_locked_title": "Locked for editing",
|
||||
"drag_locked_message": "Dragging not allowed since the collection is locked for editing."
|
||||
},
|
||||
@@ -2190,9 +2190,8 @@
|
||||
"percentage": "%"
|
||||
},
|
||||
"pagination": {
|
||||
"total_notes": "{{count}} notes",
|
||||
"prev_page": "Previous page",
|
||||
"next_page": "Next page"
|
||||
"page_title": "Page of {{startIndex}} - {{endIndex}}",
|
||||
"total_notes": "{{count}} notes"
|
||||
},
|
||||
"collections": {
|
||||
"rendering_error": "Unable to show content due to an error."
|
||||
|
||||
20
apps/client/src/types-lib.d.ts
vendored
20
apps/client/src/types-lib.d.ts
vendored
@@ -32,26 +32,6 @@ declare module "katex/contrib/auto-render" {
|
||||
export default renderMathInElement;
|
||||
}
|
||||
|
||||
import * as L from "leaflet";
|
||||
|
||||
declare module "leaflet" {
|
||||
interface GPXMarker {
|
||||
startIcon?: DivIcon | Icon | string | undefined;
|
||||
endIcon?: DivIcon | Icon | string | undefined;
|
||||
wptIcons?: {
|
||||
[key: string]: DivIcon | Icon | string;
|
||||
};
|
||||
wptTypeIcons?: {
|
||||
[key: string]: DivIcon | Icon | string;
|
||||
};
|
||||
pointMatchers?: Array<{ regex: RegExp; icon: DivIcon | Icon | string}>;
|
||||
}
|
||||
|
||||
interface GPXOptions {
|
||||
markers?: GPXMarker | undefined;
|
||||
}
|
||||
}
|
||||
|
||||
declare global {
|
||||
interface Navigator {
|
||||
/** Returns a boolean indicating whether the browser is running in standalone mode. Available on Apple's iOS Safari only. */
|
||||
|
||||
@@ -89,7 +89,7 @@
|
||||
/* #endregion */
|
||||
|
||||
/* #region Geo map buttons */
|
||||
.leaflet-pane {
|
||||
.maplibregl-canvas-container {
|
||||
z-index: 50;
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import "./NoteDetail.css";
|
||||
|
||||
import clsx from "clsx";
|
||||
import { note } from "mermaid/dist/rendering-util/rendering-elements/shapes/note.js";
|
||||
import { isValidElement, VNode } from "preact";
|
||||
import { useEffect, useRef, useState } from "preact/hooks";
|
||||
|
||||
@@ -355,6 +356,14 @@ export function checkFullHeight(noteContext: NoteContext | undefined, type: Exte
|
||||
// https://github.com/zadam/trilium/issues/2522
|
||||
const isBackendNote = noteContext?.noteId === "_backendLog";
|
||||
const isFullHeightNoteType = type && TYPE_MAPPINGS[type].isFullHeight;
|
||||
|
||||
// Allow vertical centering when there are no results.
|
||||
if (type === "book" &&
|
||||
[ "grid", "list" ].includes(noteContext.note?.getLabelValue("viewType") ?? "grid") &&
|
||||
!noteContext.note?.hasChildren()) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return (!noteContext?.hasNoteList() && isFullHeightNoteType)
|
||||
|| noteContext?.viewScope?.viewMode === "attachments"
|
||||
|| isBackendNote;
|
||||
|
||||
@@ -2,8 +2,12 @@
|
||||
min-height: 0;
|
||||
max-width: var(--max-content-width); /* Inherited from .note-split */
|
||||
|
||||
overflow: auto;
|
||||
overflow: visible;
|
||||
contain: none !important;
|
||||
|
||||
&.full-height {
|
||||
overflow: auto;
|
||||
}
|
||||
}
|
||||
|
||||
body.prefers-centered-content .note-list-widget:not(.full-height) {
|
||||
@@ -19,3 +23,14 @@ body.prefers-centered-content .note-list-widget:not(.full-height) {
|
||||
.note-list-widget video {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* #region Pagination */
|
||||
.note-list-pager {
|
||||
font-size: 1rem;
|
||||
|
||||
span.current-page {
|
||||
text-decoration: underline;
|
||||
font-weight: bold;
|
||||
}
|
||||
}
|
||||
/* #endregion */
|
||||
|
||||
@@ -4,8 +4,6 @@ import FNote from "../../entities/fnote";
|
||||
import froca from "../../services/froca";
|
||||
import { useNoteLabelInt } from "../react/hooks";
|
||||
import { t } from "../../services/i18n";
|
||||
import ActionButton from "../react/ActionButton";
|
||||
import Button from "../react/Button";
|
||||
|
||||
interface PaginationContext {
|
||||
page: number;
|
||||
@@ -19,79 +17,46 @@ interface PaginationContext {
|
||||
export function Pager({ page, pageSize, setPage, pageCount, totalNotes }: Omit<PaginationContext, "pageNotes">) {
|
||||
if (pageCount < 2) return;
|
||||
|
||||
const children = createPageButtons(page, setPage, pageCount);
|
||||
let lastPrinted = false;
|
||||
let children: ComponentChildren[] = [];
|
||||
for (let i = 1; i <= pageCount; i++) {
|
||||
if (pageCount < 20 || i <= 5 || pageCount - i <= 5 || Math.abs(page - i) <= 2) {
|
||||
lastPrinted = true;
|
||||
|
||||
const startIndex = (i - 1) * pageSize + 1;
|
||||
const endIndex = Math.min(totalNotes, i * pageSize);
|
||||
|
||||
if (i !== page) {
|
||||
children.push((
|
||||
<a
|
||||
href="javascript:"
|
||||
title={t("pagination.page_title", { startIndex, endIndex })}
|
||||
onClick={() => setPage(i)}
|
||||
>
|
||||
{i}
|
||||
</a>
|
||||
))
|
||||
} else {
|
||||
// Current page
|
||||
children.push(<span className="current-page">{i}</span>)
|
||||
}
|
||||
|
||||
children.push(<>{" "} {" "}</>);
|
||||
} else if (lastPrinted) {
|
||||
children.push(<>{"... "} {" "}</>);
|
||||
lastPrinted = false;
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<div class="note-list-pager">
|
||||
<ActionButton
|
||||
icon="bx bx-caret-left"
|
||||
disabled={(page === 1)}
|
||||
text={t("pagination.prev_page")}
|
||||
onClick={() => {setPage(page - 1)}}
|
||||
/>
|
||||
|
||||
{children}
|
||||
|
||||
<ActionButton
|
||||
icon="bx bx-caret-right"
|
||||
disabled={(page === pageCount)}
|
||||
text={t("pagination.next_page")}
|
||||
onClick={() => {setPage(page + 1)}}
|
||||
/>
|
||||
|
||||
<span className="note-list-pager-total-count">({t("pagination.total_notes", { count: totalNotes })})</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
function createPageButtons(page: number, setPage: Dispatch<StateUpdater<number>>, pageCount: number): ComponentChildren[] {
|
||||
const maxButtonCount = 9;
|
||||
const maxLeftRightSegmentLength = 2;
|
||||
|
||||
// The left-side segment
|
||||
const leftLength = Math.min(pageCount, maxLeftRightSegmentLength);
|
||||
const leftStart = 1;
|
||||
|
||||
// The middle segment
|
||||
const middleMaxLength = maxButtonCount - maxLeftRightSegmentLength * 2;
|
||||
const middleLength = Math.min(pageCount - leftLength, middleMaxLength);
|
||||
let middleStart = page - Math.floor(middleLength / 2);
|
||||
middleStart = Math.max(middleStart, leftLength + 1);
|
||||
|
||||
// The right-side segment
|
||||
const rightLength = Math.min(pageCount - (middleLength + leftLength), maxLeftRightSegmentLength);
|
||||
const rightStart = pageCount - rightLength + 1;
|
||||
middleStart = Math.min(middleStart, rightStart - middleLength);
|
||||
|
||||
return [
|
||||
...createSegment(leftStart, leftLength, page, setPage, false),
|
||||
...createSegment(middleStart, middleLength, page, setPage, (middleStart - leftLength > 1)),
|
||||
...createSegment(rightStart, rightLength, page, setPage, (rightStart - (middleStart + middleLength - 1) > 1)),
|
||||
];
|
||||
|
||||
}
|
||||
|
||||
function createSegment(start: number, length: number, currentPage: number, setPage: Dispatch<StateUpdater<number>>, prependEllipsis: boolean): ComponentChildren[] {
|
||||
const children: ComponentChildren[] = [];
|
||||
|
||||
if (prependEllipsis) {
|
||||
children.push("...");
|
||||
}
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const pageNum = start + i;
|
||||
|
||||
children.push((
|
||||
<Button
|
||||
text={pageNum.toString()}
|
||||
disabled={(pageNum === currentPage)}
|
||||
onClick={() => setPage(pageNum)}
|
||||
/>
|
||||
));
|
||||
}
|
||||
|
||||
return children;
|
||||
}
|
||||
|
||||
export function usePagination(note: FNote, noteIds: string[]): PaginationContext {
|
||||
const [ page, setPage ] = useState(1);
|
||||
const [ pageNotes, setPageNotes ] = useState<FNote[]>();
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { LatLng, LeafletMouseEvent } from "leaflet";
|
||||
import type { GeoMouseEvent } from "./map";
|
||||
import { LOCATION_ATTRIBUTE } from ".";
|
||||
import attributes from "../../../services/attributes";
|
||||
import { prompt } from "../../../services/dialog";
|
||||
@@ -8,12 +8,12 @@ import { CreateChildrenResponse } from "@triliumnext/commons";
|
||||
|
||||
const CHILD_NOTE_ICON = "bx bx-pin";
|
||||
|
||||
export async function moveMarker(noteId: string, latLng: LatLng | null) {
|
||||
export async function moveMarker(noteId: string, latLng: { lat: number; lng: number } | null) {
|
||||
const value = latLng ? [latLng.lat, latLng.lng].join(",") : "";
|
||||
await attributes.setLabel(noteId, LOCATION_ATTRIBUTE, value);
|
||||
}
|
||||
|
||||
export async function createNewNote(noteId: string, e: LeafletMouseEvent) {
|
||||
export async function createNewNote(noteId: string, e: GeoMouseEvent) {
|
||||
const title = await prompt({ message: t("relation_map.enter_title_of_new_note"), defaultValue: t("relation_map.default_new_note_title") });
|
||||
|
||||
if (title?.trim()) {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import type { LatLng, LeafletMouseEvent } from "leaflet";
|
||||
import type { GeoMouseEvent } from "./map.js";
|
||||
import appContext, { type CommandMappings } from "../../../components/app_context.js";
|
||||
import contextMenu, { type MenuItem } from "../../../menus/context_menu.js";
|
||||
import linkContextMenu from "../../../menus/link_context_menu.js";
|
||||
@@ -8,7 +8,7 @@ import { createNewNote } from "./api.js";
|
||||
import { copyTextWithToast } from "../../../services/clipboard_ext.js";
|
||||
import link from "../../../services/link.js";
|
||||
|
||||
export default function openContextMenu(noteId: string, e: LeafletMouseEvent, isEditable: boolean) {
|
||||
export default function openContextMenu(noteId: string, e: GeoMouseEvent, isEditable: boolean) {
|
||||
let items: MenuItem<keyof CommandMappings>[] = [
|
||||
...buildGeoLocationItem(e),
|
||||
{ kind: "separator" },
|
||||
@@ -44,7 +44,7 @@ export default function openContextMenu(noteId: string, e: LeafletMouseEvent, is
|
||||
});
|
||||
}
|
||||
|
||||
export function openMapContextMenu(noteId: string, e: LeafletMouseEvent, isEditable: boolean) {
|
||||
export function openMapContextMenu(noteId: string, e: GeoMouseEvent, isEditable: boolean) {
|
||||
let items: MenuItem<keyof CommandMappings>[] = [
|
||||
...buildGeoLocationItem(e)
|
||||
];
|
||||
@@ -71,8 +71,8 @@ export function openMapContextMenu(noteId: string, e: LeafletMouseEvent, isEdita
|
||||
});
|
||||
}
|
||||
|
||||
function buildGeoLocationItem(e: LeafletMouseEvent) {
|
||||
function formatGeoLocation(latlng: LatLng, precision: number = 6) {
|
||||
function buildGeoLocationItem(e: GeoMouseEvent) {
|
||||
function formatGeoLocation(latlng: { lat: number; lng: number }, precision: number = 6) {
|
||||
return `${latlng.lat.toFixed(precision)}, ${latlng.lng.toFixed(precision)}`;
|
||||
}
|
||||
|
||||
|
||||
@@ -18,14 +18,16 @@ body.mobile .geo-view > .collection-properties {
|
||||
.geo-map-container {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.maplibregl-canvas-container {
|
||||
position: relative;
|
||||
}
|
||||
}
|
||||
|
||||
.leaflet-pane {
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
.leaflet-top,
|
||||
.leaflet-bottom {
|
||||
.maplibregl-ctrl-top-left,
|
||||
.maplibregl-ctrl-top-right,
|
||||
.maplibregl-ctrl-bottom-left,
|
||||
.maplibregl-ctrl-bottom-right {
|
||||
z-index: 997 !important;
|
||||
}
|
||||
|
||||
@@ -33,28 +35,25 @@ body.mobile .geo-view > .collection-properties {
|
||||
cursor: crosshair;
|
||||
}
|
||||
|
||||
.geo-map-container .marker-pin {
|
||||
.geo-map-container .geo-marker {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.geo-map-container .leaflet-div-icon {
|
||||
position: relative;
|
||||
background: transparent;
|
||||
border: 0;
|
||||
cursor: pointer;
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.geo-map-container .leaflet-div-icon .icon-shadow {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
inset-inline-start: 0;
|
||||
z-index: -1;
|
||||
.geo-map-container .geo-marker .marker-pin {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.geo-map-container .leaflet-div-icon .tn-icon {
|
||||
.geo-map-container .geo-marker .marker-pin svg {
|
||||
display: block;
|
||||
filter: drop-shadow(2px 2px 2px rgba(0, 0, 0, 0.3));
|
||||
}
|
||||
|
||||
.geo-map-container .geo-marker .tn-icon {
|
||||
position: absolute;
|
||||
top: 3px;
|
||||
inset-inline-start: 2px;
|
||||
inset-inline-start: 4px;
|
||||
background-color: white;
|
||||
color: var(--light-theme-custom-color, black);
|
||||
padding: 2px;
|
||||
@@ -62,7 +61,7 @@ body.mobile .geo-view > .collection-properties {
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
.geo-map-container .leaflet-div-icon .title-label {
|
||||
.geo-map-container .geo-marker .title-label {
|
||||
display: block;
|
||||
position: absolute;
|
||||
top: 100%;
|
||||
@@ -75,19 +74,19 @@ body.mobile .geo-view > .collection-properties {
|
||||
text-align: center;
|
||||
text-overflow: ellipsis;
|
||||
text-shadow: -1px -1px 0 white, 1px -1px 0 white, -1px 1px 0 white, 1px 1px 0 white;
|
||||
white-space: no-wrap;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
body[dir=rtl] .geo-map-container .leaflet-div-icon .title-label {
|
||||
body[dir=rtl] .geo-map-container .geo-marker .title-label {
|
||||
transform: translateX(50%);
|
||||
}
|
||||
|
||||
.geo-map-container .leaflet-div-icon .archived {
|
||||
.geo-map-container .geo-marker .archived {
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.geo-map-container.dark .leaflet-div-icon .title-label {
|
||||
.geo-map-container.dark .geo-marker .title-label {
|
||||
color: white;
|
||||
text-shadow: -1px -1px 0 black, 1px -1px 0 black, -1px 1px 0 black, 1px 1px 0 black;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import "./index.css";
|
||||
|
||||
import { divIcon, GPXOptions, LatLng, LeafletMouseEvent } from "leaflet";
|
||||
import markerIcon from "leaflet/dist/images/marker-icon.png";
|
||||
import markerIconShadow from "leaflet/dist/images/marker-shadow.png";
|
||||
import type maplibregl from "maplibre-gl";
|
||||
import { useCallback, useContext, useEffect, useMemo, useRef, useState } from "preact/hooks";
|
||||
|
||||
import appContext from "../../../components/app_context";
|
||||
@@ -21,9 +19,10 @@ import TouchBar, { TouchBarButton, TouchBarSlider } from "../../react/TouchBar";
|
||||
import { ViewModeProps } from "../interface";
|
||||
import { createNewNote, moveMarker } from "./api";
|
||||
import openContextMenu, { openMapContextMenu } from "./context_menu";
|
||||
import Map from "./map";
|
||||
import Map, { GeoMouseEvent } from "./map";
|
||||
import { DEFAULT_MAP_LAYER_NAME, MAP_LAYERS, MapLayer } from "./map_layer";
|
||||
import Marker, { GpxTrack } from "./marker";
|
||||
import { MARKER_SVG, useMarkerData } from "./marker_data";
|
||||
|
||||
const DEFAULT_COORDINATES: [number, number] = [3.878638227135724, 446.6630455551659];
|
||||
const DEFAULT_ZOOM = 2;
|
||||
@@ -31,7 +30,7 @@ export const LOCATION_ATTRIBUTE = "geolocation";
|
||||
|
||||
interface MapData {
|
||||
view?: {
|
||||
center?: LatLng | [number, number];
|
||||
center?: { lat: number; lng: number } | [number, number];
|
||||
zoom?: number;
|
||||
};
|
||||
}
|
||||
@@ -90,7 +89,7 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM
|
||||
moveMarker(noteId, null);
|
||||
});
|
||||
|
||||
const onClick = useCallback(async (e: LeafletMouseEvent) => {
|
||||
const onClick = useCallback(async (e: GeoMouseEvent) => {
|
||||
if (state === State.NewNote) {
|
||||
toast.closePersistent("geo-new-note");
|
||||
await createNewNote(note.noteId, e);
|
||||
@@ -98,13 +97,15 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM
|
||||
}
|
||||
}, [ state ]);
|
||||
|
||||
const onContextMenu = useCallback((e: LeafletMouseEvent) => {
|
||||
const onContextMenu = useCallback((e: GeoMouseEvent) => {
|
||||
openMapContextMenu(note.noteId, e, !isReadOnly);
|
||||
}, [ note.noteId, isReadOnly ]);
|
||||
|
||||
// Dragging
|
||||
const containerRef = useRef<HTMLDivElement>(null);
|
||||
const apiRef = useRef<L.Map>(null);
|
||||
const apiRef = useRef<maplibregl.Map>(null);
|
||||
useMarkerData(note, apiRef);
|
||||
|
||||
useNoteTreeDrag(containerRef, {
|
||||
dragEnabled: !isReadOnly,
|
||||
dragNotEnabledMessage: {
|
||||
@@ -121,15 +122,15 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM
|
||||
const offset = containerRef.current?.getBoundingClientRect();
|
||||
const x = e.clientX - (offset?.left ?? 0);
|
||||
const y = e.clientY - (offset?.top ?? 0);
|
||||
const latlng = api.containerPointToLatLng([ x, y ]);
|
||||
const lngLat = api.unproject([x, y]);
|
||||
|
||||
const targetNote = await froca.getNote(noteId, true);
|
||||
const parents = targetNote?.getParentNoteIds();
|
||||
if (parents?.includes(note.noteId)) {
|
||||
await moveMarker(noteId, latlng);
|
||||
await moveMarker(noteId, { lat: lngLat.lat, lng: lngLat.lng });
|
||||
} else {
|
||||
await branches.cloneNoteToParentNote(noteId, noteId);
|
||||
await moveMarker(noteId, latlng);
|
||||
await moveMarker(noteId, { lat: lngLat.lat, lng: lngLat.lng });
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -163,7 +164,7 @@ export default function GeoView({ note, noteIds, viewConfig, saveConfig }: ViewM
|
||||
onContextMenu={onContextMenu}
|
||||
scale={hasScale}
|
||||
>
|
||||
{notes.map(note => <NoteWrapper note={note} isReadOnly={isReadOnly} hideLabels={hideLabels} />)}
|
||||
{/* {notes.map(note => <NoteWrapper note={note} isReadOnly={isReadOnly} hideLabels={hideLabels} />)} */}
|
||||
</Map>}
|
||||
<GeoMapTouchBar state={state} map={apiRef.current} />
|
||||
</div>
|
||||
@@ -228,10 +229,10 @@ function NoteMarker({ note, editable, latLng, hideLabels }: { note: FNote, edita
|
||||
const [ archived ] = useNoteLabelBoolean(note, "archived");
|
||||
|
||||
const title = useNoteProperty(note, "title");
|
||||
const icon = useMemo(() => {
|
||||
const iconHtml = useMemo(() => {
|
||||
const titleOrNone = hideLabels ? undefined : title;
|
||||
return buildIcon(note.getIcon(), note.getColorClass() ?? undefined, titleOrNone, note.noteId, archived);
|
||||
}, [ iconClass, color, title, note.noteId, archived, hideLabels ]);
|
||||
return buildIconHtml(note.getIcon(), note.getColorClass() ?? undefined, titleOrNone, note.noteId, archived);
|
||||
}, [ note, iconClass, color, title, archived, hideLabels ]);
|
||||
|
||||
const onClick = useCallback(() => {
|
||||
appContext.triggerCommand("openInPopup", { noteIdOrPath: note.noteId });
|
||||
@@ -246,15 +247,17 @@ function NoteMarker({ note, editable, latLng, hideLabels }: { note: FNote, edita
|
||||
}
|
||||
}, [ note.noteId ]);
|
||||
|
||||
const onDragged = useCallback((newCoordinates: LatLng) => {
|
||||
const onDragged = useCallback((newCoordinates: { lat: number; lng: number }) => {
|
||||
moveMarker(note.noteId, newCoordinates);
|
||||
}, [ note.noteId ]);
|
||||
|
||||
const onContextMenu = useCallback((e: LeafletMouseEvent) => openContextMenu(note.noteId, e, editable), [ note.noteId, editable ]);
|
||||
const onContextMenu = useCallback((e: GeoMouseEvent) => openContextMenu(note.noteId, e, editable), [ note.noteId, editable ]);
|
||||
|
||||
return latLng && <Marker
|
||||
coordinates={latLng}
|
||||
icon={icon}
|
||||
iconHtml={iconHtml}
|
||||
iconSize={[25, 41]}
|
||||
iconAnchor={[12, 41]}
|
||||
draggable={editable}
|
||||
onMouseDown={onMouseDown}
|
||||
onDragged={editable ? onDragged : undefined}
|
||||
@@ -282,40 +285,34 @@ function NoteGpxTrack({ note, hideLabels }: { note: FNote, hideLabels?: boolean
|
||||
const color = useNoteLabel(note, "color");
|
||||
const iconClass = useNoteLabel(note, "iconClass");
|
||||
|
||||
const options = useMemo<GPXOptions>(() => ({
|
||||
markers: {
|
||||
startIcon: buildIcon(note.getIcon(), note.getColorClass(), hideLabels ? undefined : note.title),
|
||||
endIcon: buildIcon("bxs-flag-checkered"),
|
||||
wptIcons: {
|
||||
"": buildIcon("bx bx-pin")
|
||||
}
|
||||
},
|
||||
polyline_options: {
|
||||
color: note.getLabelValue("color") ?? "blue"
|
||||
}
|
||||
}), [ color, iconClass, hideLabels ]);
|
||||
return xmlString && <GpxTrack gpxXmlString={xmlString} options={options} />;
|
||||
const trackColor = useMemo(() => note.getLabelValue("color") ?? "blue", [ color ]);
|
||||
const startIconHtml = useMemo(() => buildIconHtml(note.getIcon(), note.getColorClass() ?? undefined, note.title), [ iconClass, color ]);
|
||||
const endIconHtml = useMemo(() => buildIconHtml("bxs-flag-checkered"), [ ]);
|
||||
const waypointIconHtml = useMemo(() => buildIconHtml("bx bx-pin"), [ ]);
|
||||
|
||||
return xmlString && <GpxTrack
|
||||
gpxXmlString={xmlString}
|
||||
trackColor={trackColor}
|
||||
startIconHtml={startIconHtml}
|
||||
endIconHtml={endIconHtml}
|
||||
waypointIconHtml={waypointIconHtml}
|
||||
/>;
|
||||
}
|
||||
|
||||
function buildIcon(bxIconClass: string, colorClass?: string, title?: string, noteIdLink?: string, archived?: boolean) {
|
||||
function buildIconHtml(bxIconClass: string, colorClass?: string, title?: string, noteIdLink?: string, archived?: boolean) {
|
||||
let html = /*html*/`\
|
||||
<img class="icon" src="${markerIcon}" />
|
||||
<img class="icon-shadow" src="${markerIconShadow}" />
|
||||
<span class="bx ${bxIconClass} ${colorClass ?? ""}"></span>
|
||||
<div class="marker-pin">${MARKER_SVG}</div>
|
||||
<span class="bx ${bxIconClass} tn-icon ${colorClass ?? ""}"></span>
|
||||
<span class="title-label">${title ?? ""}</span>`;
|
||||
|
||||
if (noteIdLink) {
|
||||
html = `<div data-href="#root/${noteIdLink}" class="${archived ? "archived" : ""}">${html}</div>`;
|
||||
}
|
||||
|
||||
return divIcon({
|
||||
html,
|
||||
iconSize: [25, 41],
|
||||
iconAnchor: [12, 41]
|
||||
});
|
||||
return html;
|
||||
}
|
||||
|
||||
function GeoMapTouchBar({ state, map }: { state: State, map: L.Map | null | undefined }) {
|
||||
function GeoMapTouchBar({ state, map }: { state: State, map: maplibregl.Map | null | undefined }) {
|
||||
const [ currentZoom, setCurrentZoom ] = useState<number>();
|
||||
const parentComponent = useContext(ParentComponent);
|
||||
|
||||
@@ -327,7 +324,7 @@ function GeoMapTouchBar({ state, map }: { state: State, map: L.Map | null | unde
|
||||
}
|
||||
|
||||
map.on("zoom", onZoomChanged);
|
||||
return () => map.off("zoom", onZoomChanged);
|
||||
return () => { map.off("zoom", onZoomChanged); };
|
||||
}, [ map ]);
|
||||
|
||||
return map && currentZoom && (
|
||||
|
||||
@@ -1,137 +1,210 @@
|
||||
import { useEffect, useImperativeHandle, useRef, useState } from "preact/hooks";
|
||||
import L, { control, LatLng, Layer, LeafletMouseEvent } from "leaflet";
|
||||
import "leaflet/dist/leaflet.css";
|
||||
import { MAP_LAYERS, type MapLayer } from "./map_layer";
|
||||
import { ComponentChildren, createContext, RefObject } from "preact";
|
||||
import { useElementSize, useSyncedRef } from "../../react/hooks";
|
||||
import "maplibre-gl/dist/maplibre-gl.css";
|
||||
|
||||
export const ParentMap = createContext<L.Map | null>(null);
|
||||
import maplibregl from "maplibre-gl";
|
||||
import { ComponentChildren, createContext, RefObject } from "preact";
|
||||
import { useEffect, useImperativeHandle, useRef } from "preact/hooks";
|
||||
|
||||
import { useElementSize, useSyncedRef } from "../../react/hooks";
|
||||
import { MapLayer } from "./map_layer";
|
||||
|
||||
export interface GeoMouseEvent {
|
||||
latlng: { lat: number; lng: number };
|
||||
originalEvent: MouseEvent;
|
||||
}
|
||||
|
||||
export const ParentMap = createContext<maplibregl.Map | null>(null);
|
||||
|
||||
interface MapProps {
|
||||
apiRef?: RefObject<L.Map | null>;
|
||||
apiRef?: RefObject<maplibregl.Map | null>;
|
||||
containerRef?: RefObject<HTMLDivElement>;
|
||||
coordinates: LatLng | [number, number];
|
||||
coordinates: { lat: number; lng: number } | [number, number];
|
||||
zoom: number;
|
||||
layerData: MapLayer;
|
||||
viewportChanged: (coordinates: LatLng, zoom: number) => void;
|
||||
viewportChanged: (coordinates: { lat: number; lng: number }, zoom: number) => void;
|
||||
children: ComponentChildren;
|
||||
onClick?: (e: LeafletMouseEvent) => void;
|
||||
onContextMenu?: (e: LeafletMouseEvent) => void;
|
||||
onClick?: (e: GeoMouseEvent) => void;
|
||||
onContextMenu?: (e: GeoMouseEvent) => void;
|
||||
onZoom?: () => void;
|
||||
scale: boolean;
|
||||
}
|
||||
|
||||
function toMapLibreEvent(e: maplibregl.MapMouseEvent): GeoMouseEvent {
|
||||
return {
|
||||
latlng: { lat: e.lngLat.lat, lng: e.lngLat.lng },
|
||||
originalEvent: e.originalEvent
|
||||
};
|
||||
}
|
||||
|
||||
export default function Map({ coordinates, zoom, layerData, viewportChanged, children, onClick, onContextMenu, scale, apiRef, containerRef: _containerRef, onZoom }: MapProps) {
|
||||
const mapRef = useRef<L.Map>(null);
|
||||
const mapRef = useRef<maplibregl.Map>(null);
|
||||
const containerRef = useSyncedRef<HTMLDivElement>(_containerRef);
|
||||
|
||||
useImperativeHandle(apiRef ?? null, () => mapRef.current);
|
||||
|
||||
// Initialize the map.
|
||||
useEffect(() => {
|
||||
if (!containerRef.current) return;
|
||||
const mapInstance = L.map(containerRef.current, {
|
||||
worldCopyJump: false,
|
||||
maxBounds: [
|
||||
[-90, -180],
|
||||
[90, 180]
|
||||
],
|
||||
minZoom: 2
|
||||
|
||||
let style: maplibregl.StyleSpecification | string;
|
||||
|
||||
if (layerData.type === "vector") {
|
||||
style = typeof layerData.style === "string"
|
||||
? layerData.style
|
||||
: layerData.styleFallback;
|
||||
} else {
|
||||
style = {
|
||||
version: 8,
|
||||
sources: {
|
||||
"raster-tiles": {
|
||||
type: "raster",
|
||||
tiles: [layerData.url],
|
||||
tileSize: 256,
|
||||
attribution: layerData.attribution
|
||||
}
|
||||
},
|
||||
layers: [
|
||||
{
|
||||
id: "raster-layer",
|
||||
type: "raster",
|
||||
source: "raster-tiles"
|
||||
}
|
||||
]
|
||||
};
|
||||
}
|
||||
|
||||
const center = Array.isArray(coordinates)
|
||||
? [coordinates[1], coordinates[0]] as [number, number]
|
||||
: [coordinates.lng, coordinates.lat] as [number, number];
|
||||
|
||||
const mapInstance = new maplibregl.Map({
|
||||
container: containerRef.current,
|
||||
style,
|
||||
center,
|
||||
zoom,
|
||||
minZoom: 1,
|
||||
renderWorldCopies: false
|
||||
});
|
||||
|
||||
mapRef.current = mapInstance;
|
||||
|
||||
// Load async vector style if needed.
|
||||
if (layerData.type === "vector" && typeof layerData.style !== "string") {
|
||||
layerData.style().then(asyncStyle => {
|
||||
mapInstance.setStyle(asyncStyle as maplibregl.StyleSpecification);
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
mapInstance.off();
|
||||
mapInstance.remove();
|
||||
mapRef.current = null;
|
||||
};
|
||||
}, []);
|
||||
|
||||
// Load the layer asynchronously.
|
||||
const [ layer, setLayer ] = useState<Layer>();
|
||||
useEffect(() => {
|
||||
async function load() {
|
||||
if (layerData.type === "vector") {
|
||||
const style = (typeof layerData.style === "string" ? layerData.style : await layerData.style());
|
||||
await import("@maplibre/maplibre-gl-leaflet");
|
||||
|
||||
setLayer(L.maplibreGL({
|
||||
style: style as any
|
||||
}));
|
||||
} else {
|
||||
setLayer(L.tileLayer(layerData.url, {
|
||||
attribution: layerData.attribution,
|
||||
detectRetina: true,
|
||||
noWrap: true
|
||||
}));
|
||||
}
|
||||
}
|
||||
|
||||
load();
|
||||
}, [ layerData ]);
|
||||
|
||||
// Attach layer to the map.
|
||||
// React to layer changes.
|
||||
useEffect(() => {
|
||||
const map = mapRef.current;
|
||||
const layerToAdd = layer;
|
||||
if (!map || !layerToAdd) return;
|
||||
layerToAdd.addTo(map);
|
||||
return () => layerToAdd.removeFrom(map);
|
||||
}, [ mapRef, layer ]);
|
||||
if (!map) return;
|
||||
|
||||
if (layerData.type === "vector") {
|
||||
if (typeof layerData.style === "string") {
|
||||
map.setStyle(layerData.style);
|
||||
} else {
|
||||
layerData.style().then(asyncStyle => {
|
||||
map.setStyle(asyncStyle as maplibregl.StyleSpecification);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
map.setStyle({
|
||||
version: 8,
|
||||
sources: {
|
||||
"raster-tiles": {
|
||||
type: "raster",
|
||||
tiles: [layerData.url],
|
||||
tileSize: 256,
|
||||
attribution: layerData.attribution
|
||||
}
|
||||
},
|
||||
layers: [
|
||||
{
|
||||
id: "raster-layer",
|
||||
type: "raster",
|
||||
source: "raster-tiles"
|
||||
}
|
||||
]
|
||||
});
|
||||
}
|
||||
}, [ layerData ]);
|
||||
|
||||
// React to coordinate changes.
|
||||
useEffect(() => {
|
||||
if (!mapRef.current) return;
|
||||
mapRef.current.setView(coordinates, zoom);
|
||||
}, [ mapRef, coordinates, zoom ]);
|
||||
|
||||
const center = Array.isArray(coordinates)
|
||||
? [coordinates[1], coordinates[0]] as [number, number]
|
||||
: [coordinates.lng, coordinates.lat] as [number, number];
|
||||
|
||||
mapRef.current.setCenter(center);
|
||||
mapRef.current.setZoom(zoom);
|
||||
}, [ coordinates, zoom ]);
|
||||
|
||||
// Viewport callback.
|
||||
useEffect(() => {
|
||||
const map = mapRef.current;
|
||||
if (!map) return;
|
||||
|
||||
const updateFn = () => viewportChanged(map.getBounds().getCenter(), map.getZoom());
|
||||
const updateFn = () => {
|
||||
const center = map.getCenter();
|
||||
viewportChanged({ lat: center.lat, lng: center.lng }, map.getZoom());
|
||||
};
|
||||
map.on("moveend", updateFn);
|
||||
map.on("zoomend", updateFn);
|
||||
|
||||
return () => {
|
||||
map.off("moveend", updateFn);
|
||||
map.off("zoomend", updateFn);
|
||||
};
|
||||
}, [ mapRef, viewportChanged ]);
|
||||
}, [ viewportChanged ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (onClick && mapRef.current) {
|
||||
mapRef.current.on("click", onClick);
|
||||
return () => mapRef.current?.off("click", onClick);
|
||||
}
|
||||
}, [ mapRef, onClick ]);
|
||||
const map = mapRef.current;
|
||||
if (!onClick || !map) return;
|
||||
|
||||
const handler = (e: maplibregl.MapMouseEvent) => onClick(toMapLibreEvent(e));
|
||||
map.on("click", handler);
|
||||
return () => { map.off("click", handler); };
|
||||
}, [ onClick ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (onContextMenu && mapRef.current) {
|
||||
mapRef.current.on("contextmenu", onContextMenu);
|
||||
return () => mapRef.current?.off("contextmenu", onContextMenu);
|
||||
}
|
||||
}, [ mapRef, onContextMenu ]);
|
||||
const map = mapRef.current;
|
||||
if (!onContextMenu || !map) return;
|
||||
|
||||
const handler = (e: maplibregl.MapMouseEvent) => {
|
||||
e.preventDefault();
|
||||
onContextMenu(toMapLibreEvent(e));
|
||||
};
|
||||
map.on("contextmenu", handler);
|
||||
return () => { map.off("contextmenu", handler); };
|
||||
}, [ onContextMenu ]);
|
||||
|
||||
useEffect(() => {
|
||||
if (onZoom && mapRef.current) {
|
||||
mapRef.current.on("zoom", onZoom);
|
||||
return () => mapRef.current?.off("zoom", onZoom);
|
||||
}
|
||||
}, [ mapRef, onZoom ]);
|
||||
const map = mapRef.current;
|
||||
if (!onZoom || !map) return;
|
||||
|
||||
map.on("zoom", onZoom);
|
||||
return () => { map.off("zoom", onZoom); };
|
||||
}, [ onZoom ]);
|
||||
|
||||
// Scale
|
||||
useEffect(() => {
|
||||
const map = mapRef.current;
|
||||
if (!scale || !map) return;
|
||||
const scaleControl = control.scale();
|
||||
scaleControl.addTo(map);
|
||||
return () => scaleControl.remove();
|
||||
}, [ mapRef, scale ]);
|
||||
const scaleControl = new maplibregl.ScaleControl();
|
||||
map.addControl(scaleControl);
|
||||
return () => { map.removeControl(scaleControl); };
|
||||
}, [ scale ]);
|
||||
|
||||
// Adapt to container size changes.
|
||||
const size = useElementSize(containerRef);
|
||||
useEffect(() => {
|
||||
mapRef.current?.invalidateSize();
|
||||
mapRef.current?.resize();
|
||||
}, [ size?.width, size?.height ]);
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
import { type StyleSpecification } from "maplibre-gl";
|
||||
|
||||
export type MapLayer = ({
|
||||
type: "vector";
|
||||
style: string | (() => Promise<{}>)
|
||||
style: string | (() => Promise<StyleSpecification>);
|
||||
styleFallback: StyleSpecification;
|
||||
} | {
|
||||
type: "raster";
|
||||
url: string;
|
||||
@@ -11,6 +14,9 @@ export type MapLayer = ({
|
||||
isDarkTheme?: boolean;
|
||||
};
|
||||
|
||||
// Minimal empty style used as a placeholder while the real style loads asynchronously.
|
||||
const EMPTY_STYLE: StyleSpecification = { version: 8, sources: {}, layers: [] };
|
||||
|
||||
export const MAP_LAYERS: Record<string, MapLayer> = {
|
||||
"openstreetmap": {
|
||||
name: "OpenStreetMap",
|
||||
@@ -21,28 +27,33 @@ export const MAP_LAYERS: Record<string, MapLayer> = {
|
||||
"versatiles-colorful": {
|
||||
name: "VersaTiles Colorful",
|
||||
type: "vector",
|
||||
style: async () => (await import("./styles/colorful/en.json")).default
|
||||
style: async () => (await import("./styles/colorful/en.json")).default as unknown as StyleSpecification,
|
||||
styleFallback: EMPTY_STYLE
|
||||
},
|
||||
"versatiles-eclipse": {
|
||||
name: "VersaTiles Eclipse",
|
||||
type: "vector",
|
||||
style: async () => (await import("./styles/eclipse/en.json")).default,
|
||||
style: async () => (await import("./styles/eclipse/en.json")).default as unknown as StyleSpecification,
|
||||
styleFallback: EMPTY_STYLE,
|
||||
isDarkTheme: true
|
||||
},
|
||||
"versatiles-graybeard": {
|
||||
name: "VersaTiles Graybeard",
|
||||
type: "vector",
|
||||
style: async () => (await import("./styles/graybeard/en.json")).default
|
||||
style: async () => (await import("./styles/graybeard/en.json")).default as unknown as StyleSpecification,
|
||||
styleFallback: EMPTY_STYLE,
|
||||
},
|
||||
"versatiles-neutrino": {
|
||||
name: "VersaTiles Neutrino",
|
||||
type: "vector",
|
||||
style: async () => (await import("./styles/neutrino/en.json")).default
|
||||
style: async () => (await import("./styles/neutrino/en.json")).default as unknown as StyleSpecification,
|
||||
styleFallback: EMPTY_STYLE,
|
||||
},
|
||||
"versatiles-shadow": {
|
||||
name: "VersaTiles Shadow",
|
||||
type: "vector",
|
||||
style: async () => (await import("./styles/shadow/en.json")).default,
|
||||
style: async () => (await import("./styles/shadow/en.json")).default as unknown as StyleSpecification,
|
||||
styleFallback: EMPTY_STYLE,
|
||||
isDarkTheme: true
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1,71 +1,207 @@
|
||||
import { useContext, useEffect } from "preact/hooks";
|
||||
import { ParentMap } from "./map";
|
||||
import { DivIcon, GPX, GPXOptions, Icon, LatLng, Marker as LeafletMarker, LeafletMouseEvent, marker, MarkerOptions } from "leaflet";
|
||||
import "leaflet-gpx";
|
||||
import { useContext, useEffect, useRef } from "preact/hooks";
|
||||
import { ParentMap, GeoMouseEvent } from "./map";
|
||||
import maplibregl from "maplibre-gl";
|
||||
|
||||
export interface MarkerProps {
|
||||
coordinates: [ number, number ];
|
||||
icon?: Icon | DivIcon;
|
||||
iconHtml?: string;
|
||||
iconSize?: [number, number];
|
||||
iconAnchor?: [number, number];
|
||||
onClick?: () => void;
|
||||
onMouseDown?: (e: MouseEvent) => void;
|
||||
onDragged?: ((newCoordinates: LatLng) => void);
|
||||
onContextMenu: (e: LeafletMouseEvent) => void;
|
||||
onDragged?: ((newCoordinates: { lat: number; lng: number }) => void);
|
||||
onContextMenu: (e: GeoMouseEvent) => void;
|
||||
draggable?: boolean;
|
||||
}
|
||||
|
||||
export default function Marker({ coordinates, icon, draggable, onClick, onDragged, onMouseDown, onContextMenu }: MarkerProps) {
|
||||
export default function Marker({ coordinates, iconHtml, iconSize, iconAnchor, draggable, onClick, onDragged, onMouseDown, onContextMenu }: MarkerProps) {
|
||||
const parentMap = useContext(ParentMap);
|
||||
const markerRef = useRef<maplibregl.Marker>(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!parentMap) return;
|
||||
|
||||
const options: MarkerOptions = { icon };
|
||||
if (draggable) {
|
||||
options.draggable = true;
|
||||
options.autoPan = true;
|
||||
options.autoPanSpeed = 5;
|
||||
const el = document.createElement("div");
|
||||
el.className = "geo-marker";
|
||||
if (iconHtml) {
|
||||
el.innerHTML = iconHtml;
|
||||
}
|
||||
if (iconSize) {
|
||||
el.style.width = `${iconSize[0]}px`;
|
||||
el.style.height = `${iconSize[1]}px`;
|
||||
}
|
||||
|
||||
const newMarker = marker(coordinates, options);
|
||||
const newMarker = new maplibregl.Marker({
|
||||
element: el,
|
||||
draggable: !!draggable,
|
||||
anchor: "bottom"
|
||||
})
|
||||
.setLngLat([coordinates[1], coordinates[0]])
|
||||
.addTo(parentMap);
|
||||
|
||||
markerRef.current = newMarker;
|
||||
|
||||
if (onClick) {
|
||||
newMarker.on("click", () => onClick());
|
||||
el.addEventListener("click", (e) => {
|
||||
e.stopPropagation();
|
||||
onClick();
|
||||
});
|
||||
}
|
||||
|
||||
if (onMouseDown) {
|
||||
newMarker.on("mousedown", e => onMouseDown(e.originalEvent));
|
||||
el.addEventListener("mousedown", (e) => {
|
||||
if (e.button === 1) {
|
||||
e.stopPropagation();
|
||||
onMouseDown(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (onDragged) {
|
||||
newMarker.on("moveend", e => {
|
||||
const coordinates = (e.target as LeafletMarker).getLatLng();
|
||||
onDragged(coordinates);
|
||||
newMarker.on("dragend", () => {
|
||||
const lngLat = newMarker.getLngLat();
|
||||
onDragged({ lat: lngLat.lat, lng: lngLat.lng });
|
||||
});
|
||||
}
|
||||
|
||||
if (onContextMenu) {
|
||||
newMarker.on("contextmenu", e => onContextMenu(e))
|
||||
el.addEventListener("contextmenu", (e) => {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
const lngLat = newMarker.getLngLat();
|
||||
onContextMenu({
|
||||
latlng: { lat: lngLat.lat, lng: lngLat.lng },
|
||||
originalEvent: e
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
newMarker.addTo(parentMap);
|
||||
return () => {
|
||||
newMarker.remove();
|
||||
markerRef.current = null;
|
||||
};
|
||||
}, [ parentMap, coordinates, onMouseDown, onDragged, iconHtml ]);
|
||||
|
||||
return () => newMarker.removeFrom(parentMap);
|
||||
}, [ parentMap, coordinates, onMouseDown, onDragged, icon ]);
|
||||
|
||||
return (<div />)
|
||||
return (<div />);
|
||||
}
|
||||
|
||||
export function GpxTrack({ gpxXmlString, options }: { gpxXmlString: string, options: GPXOptions }) {
|
||||
export interface GpxTrackProps {
|
||||
gpxXmlString: string;
|
||||
trackColor?: string;
|
||||
startIconHtml?: string;
|
||||
endIconHtml?: string;
|
||||
waypointIconHtml?: string;
|
||||
}
|
||||
|
||||
export function GpxTrack({ gpxXmlString, trackColor, startIconHtml, endIconHtml, waypointIconHtml }: GpxTrackProps) {
|
||||
const parentMap = useContext(ParentMap);
|
||||
|
||||
useEffect(() => {
|
||||
if (!parentMap) return;
|
||||
|
||||
const track = new GPX(gpxXmlString, options);
|
||||
track.addTo(parentMap);
|
||||
const markers: maplibregl.Marker[] = [];
|
||||
const sourceId = `gpx-source-${Math.random().toString(36).slice(2)}`;
|
||||
const layerId = `gpx-layer-${sourceId}`;
|
||||
|
||||
return () => track.removeFrom(parentMap);
|
||||
}, [ parentMap, gpxXmlString, options ]);
|
||||
function addGpxToMap() {
|
||||
const parser = new DOMParser();
|
||||
const gpxDoc = parser.parseFromString(gpxXmlString, "application/xml");
|
||||
|
||||
// Parse tracks.
|
||||
const coordinates: [number, number][] = [];
|
||||
const trackPoints = gpxDoc.querySelectorAll("trkpt, rtept");
|
||||
for (const pt of trackPoints) {
|
||||
const lat = parseFloat(pt.getAttribute("lat") ?? "0");
|
||||
const lon = parseFloat(pt.getAttribute("lon") ?? "0");
|
||||
coordinates.push([lon, lat]);
|
||||
}
|
||||
|
||||
// Add GeoJSON line for the track.
|
||||
if (coordinates.length > 0) {
|
||||
parentMap.addSource(sourceId, {
|
||||
type: "geojson",
|
||||
data: {
|
||||
type: "Feature",
|
||||
properties: {},
|
||||
geometry: {
|
||||
type: "LineString",
|
||||
coordinates
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
parentMap.addLayer({
|
||||
id: layerId,
|
||||
type: "line",
|
||||
source: sourceId,
|
||||
paint: {
|
||||
"line-color": trackColor ?? "blue",
|
||||
"line-width": 3
|
||||
}
|
||||
});
|
||||
|
||||
// Start marker
|
||||
if (startIconHtml) {
|
||||
const startEl = document.createElement("div");
|
||||
startEl.className = "geo-marker";
|
||||
startEl.innerHTML = startIconHtml;
|
||||
const startMarker = new maplibregl.Marker({ element: startEl, anchor: "bottom" })
|
||||
.setLngLat(coordinates[0])
|
||||
.addTo(parentMap);
|
||||
markers.push(startMarker);
|
||||
}
|
||||
|
||||
// End marker
|
||||
if (endIconHtml && coordinates.length > 1) {
|
||||
const endEl = document.createElement("div");
|
||||
endEl.className = "geo-marker";
|
||||
endEl.innerHTML = endIconHtml;
|
||||
const endMarker = new maplibregl.Marker({ element: endEl, anchor: "bottom" })
|
||||
.setLngLat(coordinates[coordinates.length - 1])
|
||||
.addTo(parentMap);
|
||||
markers.push(endMarker);
|
||||
}
|
||||
}
|
||||
|
||||
// Parse waypoints.
|
||||
const waypoints = gpxDoc.querySelectorAll("wpt");
|
||||
for (const wpt of waypoints) {
|
||||
const lat = parseFloat(wpt.getAttribute("lat") ?? "0");
|
||||
const lon = parseFloat(wpt.getAttribute("lon") ?? "0");
|
||||
if (waypointIconHtml) {
|
||||
const wptEl = document.createElement("div");
|
||||
wptEl.className = "geo-marker";
|
||||
wptEl.innerHTML = waypointIconHtml;
|
||||
const wptMarker = new maplibregl.Marker({ element: wptEl, anchor: "bottom" })
|
||||
.setLngLat([lon, lat])
|
||||
.addTo(parentMap);
|
||||
markers.push(wptMarker);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (parentMap.isStyleLoaded()) {
|
||||
addGpxToMap();
|
||||
} else {
|
||||
parentMap.once("style.load", addGpxToMap);
|
||||
}
|
||||
|
||||
return () => {
|
||||
for (const m of markers) {
|
||||
m.remove();
|
||||
}
|
||||
try {
|
||||
if (parentMap.getLayer(layerId)) {
|
||||
parentMap.removeLayer(layerId);
|
||||
}
|
||||
if (parentMap.getSource(sourceId)) {
|
||||
parentMap.removeSource(sourceId);
|
||||
}
|
||||
} catch {
|
||||
// Map may be already removed.
|
||||
}
|
||||
};
|
||||
}, [ parentMap, gpxXmlString, trackColor, startIconHtml, endIconHtml, waypointIconHtml ]);
|
||||
|
||||
return <div />;
|
||||
}
|
||||
|
||||
86
apps/client/src/widgets/collections/geomap/marker_data.ts
Normal file
86
apps/client/src/widgets/collections/geomap/marker_data.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
import { MutableRef, useEffect } from "preact/hooks";
|
||||
|
||||
import FNote from "../../../entities/fnote";
|
||||
import { useChildNotes } from "../../react/hooks";
|
||||
import { LOCATION_ATTRIBUTE } from ".";
|
||||
import { buildMarkerIcon, svgToImage } from "./marker_renderer";
|
||||
|
||||
const DEFAULT_MARKER_COLOR = "#2A81CB";
|
||||
|
||||
// SVG marker pin shape (replaces the Leaflet marker PNG).
|
||||
export const MARKER_SVG = "foo"; // TODO: Fix
|
||||
const iconSvgCache = new Map<string, string>();
|
||||
|
||||
export function useMarkerData(note: FNote | null | undefined, apiRef: MutableRef<maplibregl.Map>) {
|
||||
const childNotes = useChildNotes(note?.noteId);
|
||||
|
||||
async function refresh() {
|
||||
const map = apiRef.current as maplibregl.Map | undefined;
|
||||
if (!map) return;
|
||||
|
||||
|
||||
async function ensureIcon(color: string, iconClass: string) {
|
||||
const key = `marker-${color}-${iconClass}`;
|
||||
|
||||
if (!iconSvgCache.has(key)) {
|
||||
const svg = await buildMarkerIcon(color, iconClass);
|
||||
iconSvgCache.set(key, svg);
|
||||
}
|
||||
|
||||
return key;
|
||||
}
|
||||
|
||||
const features: maplibregl.GeoJSONFeature[] = [];
|
||||
for (const childNote of childNotes) {
|
||||
const location = childNote.getLabelValue(LOCATION_ATTRIBUTE);
|
||||
const latLng = location?.split(",", 2).map((el) => parseFloat(el)) as [ number, number ] | undefined;
|
||||
if (!latLng) continue;
|
||||
latLng.reverse();
|
||||
|
||||
const color = childNote.getLabelValue("color") ?? DEFAULT_MARKER_COLOR;
|
||||
features.push({
|
||||
type: "Feature",
|
||||
geometry: {
|
||||
type: "Point",
|
||||
coordinates: latLng
|
||||
},
|
||||
properties: {
|
||||
id: childNote.noteId,
|
||||
name: childNote.title,
|
||||
icon: await ensureIcon(color, childNote.getIcon())
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Build all the icons.
|
||||
await Promise.all(iconSvgCache.entries().map(async ([ key, svg ]) => {
|
||||
const image = await svgToImage(svg);
|
||||
map.addImage(key, image, {
|
||||
pixelRatio: window.devicePixelRatio
|
||||
});
|
||||
}));
|
||||
|
||||
map.addSource("points", {
|
||||
type: "geojson",
|
||||
data: {
|
||||
type: "FeatureCollection",
|
||||
features
|
||||
}
|
||||
});
|
||||
map.addLayer({
|
||||
id: "points-layer",
|
||||
type: "symbol",
|
||||
source: "points",
|
||||
layout: {
|
||||
"icon-image": [ "get", "icon" ],
|
||||
"icon-size": 1,
|
||||
"icon-anchor": "bottom",
|
||||
"icon-allow-overlap": true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
refresh();
|
||||
}, [ apiRef, childNotes ]);
|
||||
}
|
||||
112
apps/client/src/widgets/collections/geomap/marker_renderer.ts
Normal file
112
apps/client/src/widgets/collections/geomap/marker_renderer.ts
Normal file
@@ -0,0 +1,112 @@
|
||||
const iconClassToBitmapCache = new Map<string, string | undefined>();
|
||||
|
||||
export async function buildMarkerIcon(color: string, iconClass: string, scale = window.devicePixelRatio || 1) {
|
||||
const iconUrl = iconClassToBitmapCache.get(iconClass) ?? await snapshotIcon(iconClass, 16 * scale);
|
||||
return `\
|
||||
<svg width="${25 * scale}" height="${41 * scale}" viewBox="0 0 25 41" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M12.5 0C5.6 0 0 5.6 0 12.5C0 21.9 12.5 41 12.5 41S25 21.9 25 12.5C25 5.6 19.4 0 12.5 0Z" fill="${color}" />
|
||||
<circle cx="12.5" cy="12.5" r="8" fill="white" />
|
||||
<image href="${iconUrl}" x="4.5" y="4.5" width="16" height="16" preserveAspectRatio="xMidYMid meet" />
|
||||
</svg>
|
||||
`;
|
||||
}
|
||||
|
||||
async function snapshotIcon(iconClass: string, size: number) {
|
||||
const cachedIcon = iconClassToBitmapCache.get(iconClass);
|
||||
if (cachedIcon) return cachedIcon;
|
||||
|
||||
await document.fonts.ready;
|
||||
const glyph = getGlyphFromClass(iconClass);
|
||||
const rendered = renderMarkerCanvas({
|
||||
color: "black",
|
||||
glyph,
|
||||
size
|
||||
});
|
||||
const dataUrl = rendered?.toDataURL();
|
||||
iconClassToBitmapCache.set(iconClass, dataUrl);
|
||||
return dataUrl;
|
||||
}
|
||||
|
||||
function renderMarkerCanvas({
|
||||
color,
|
||||
glyph, // e.g. "\uf123"
|
||||
size = 32,
|
||||
scale = window.devicePixelRatio || 1
|
||||
}) {
|
||||
const canvas = document.createElement("canvas");
|
||||
|
||||
// High-DPI canvas
|
||||
canvas.width = size * scale;
|
||||
canvas.height = size * scale;
|
||||
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (!ctx) return null;
|
||||
|
||||
// Scale for retina
|
||||
ctx.scale(scale, scale);
|
||||
|
||||
ctx.clearRect(0, 0, size, size);
|
||||
|
||||
// Set font
|
||||
ctx.font = `${size}px ${glyph.fontFamily}`;
|
||||
ctx.fillStyle = color;
|
||||
|
||||
// Measure glyph
|
||||
const metrics = ctx.measureText(glyph.content);
|
||||
|
||||
const glyphWidth =
|
||||
metrics.actualBoundingBoxLeft +
|
||||
metrics.actualBoundingBoxRight;
|
||||
|
||||
const glyphHeight =
|
||||
metrics.actualBoundingBoxAscent +
|
||||
metrics.actualBoundingBoxDescent;
|
||||
|
||||
// Center position
|
||||
const x = (size - glyphWidth) / 2 + metrics.actualBoundingBoxLeft;
|
||||
const y = (size - glyphHeight) / 2 + metrics.actualBoundingBoxAscent;
|
||||
|
||||
// Draw
|
||||
ctx.fillText(glyph.content, x, y);
|
||||
|
||||
return canvas;
|
||||
}
|
||||
|
||||
function getGlyphFromClass(iconClass: string) {
|
||||
const el = document.createElement("span");
|
||||
el.className = iconClass;
|
||||
|
||||
document.body.appendChild(el);
|
||||
|
||||
const style = window.getComputedStyle(el, "::before");
|
||||
const content = style.getPropertyValue("content");
|
||||
const fontFamily = style.getPropertyValue("font-family");
|
||||
|
||||
document.body.removeChild(el);
|
||||
|
||||
if (!content || content === "none") {
|
||||
return null;
|
||||
}
|
||||
|
||||
// content is usually quoted like: '"\f123"'
|
||||
return {
|
||||
fontFamily,
|
||||
content: content.replace(/^["']|["']$/g, "")
|
||||
};
|
||||
}
|
||||
|
||||
export function svgToImage(svgString: string){
|
||||
return new Promise<HTMLImageElement>(resolve => {
|
||||
const svgBlob = new Blob([svgString], { type: "image/svg+xml" });
|
||||
const url = URL.createObjectURL(svgBlob);
|
||||
|
||||
const img = new Image();
|
||||
|
||||
img.onload = () => {
|
||||
URL.revokeObjectURL(url);
|
||||
resolve(img);
|
||||
};
|
||||
|
||||
img.src = url;
|
||||
});
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
.note-list {
|
||||
overflow: hidden;
|
||||
overflow: visible;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
}
|
||||
@@ -100,206 +100,23 @@
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
/* #region List view */
|
||||
|
||||
@keyframes note-preview-show {
|
||||
from {
|
||||
opacity: 0;
|
||||
} to {
|
||||
opacity: 1;
|
||||
}
|
||||
.note-expander {
|
||||
font-size: x-large;
|
||||
position: relative;
|
||||
top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nested-note-list {
|
||||
--card-nested-section-indent: 25px;
|
||||
|
||||
&.search-results {
|
||||
--card-nested-section-indent: 32px;
|
||||
}
|
||||
.note-list-pager {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* List item */
|
||||
.nested-note-list-item {
|
||||
h5 {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
font-size: 1em;
|
||||
font-weight: normal;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.note-expander {
|
||||
margin-inline-end: 4px;
|
||||
font-size: x-large;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tn-icon {
|
||||
margin-inline-end: 8px;
|
||||
color: var(--note-list-view-icon-color);
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.note-book-title {
|
||||
--link-hover-background: transparent;
|
||||
--link-hover-color: currentColor;
|
||||
color: inherit;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.note-path {
|
||||
margin-left: 0.5em;
|
||||
vertical-align: middle;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.note-list-attributes {
|
||||
flex-grow: 1;
|
||||
margin-inline-start: 1em;
|
||||
text-align: right;
|
||||
font-size: .75em;
|
||||
opacity: .75;
|
||||
}
|
||||
|
||||
.nested-note-list-item-menu {
|
||||
margin-inline-start: 8px;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
&.archived {
|
||||
span.tn-icon + span,
|
||||
.tn-icon {
|
||||
opacity: .6;
|
||||
}
|
||||
}
|
||||
|
||||
&.use-note-color {
|
||||
span.tn-icon + span,
|
||||
.nested-note-list:not(.search-results) & .tn-icon,
|
||||
.rendered-note-attributes {
|
||||
color: var(--custom-color);
|
||||
}
|
||||
}
|
||||
.note-list.list-view .note-path {
|
||||
margin-left: 0.5em;
|
||||
vertical-align: middle;
|
||||
opacity: 0.5;
|
||||
}
|
||||
|
||||
.nested-note-list:not(.search-results) h5 {
|
||||
span.tn-icon + span,
|
||||
.note-list-attributes {
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
}
|
||||
|
||||
/* List item (search results view) */
|
||||
.nested-note-list.search-results .nested-note-list-item {
|
||||
span.tn-icon + span > span {
|
||||
display: flex;
|
||||
flex-direction: column-reverse;
|
||||
align-items: flex-start;
|
||||
}
|
||||
|
||||
small {
|
||||
line-height: .85em;
|
||||
}
|
||||
|
||||
.note-path {
|
||||
margin-left: 0;
|
||||
font-size: .85em;
|
||||
line-height: .85em;
|
||||
font-weight: 500;
|
||||
letter-spacing: .5pt;
|
||||
}
|
||||
|
||||
.tn-icon {
|
||||
display: flex;
|
||||
flex-shrink: 0;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 1.75em;
|
||||
height: 1.75em;
|
||||
margin-inline-end: 12px;
|
||||
border-radius: 50%;
|
||||
background: var(--note-icon-custom-background-color, var(--note-list-view-large-icon-background));
|
||||
font-size: 1.2em;
|
||||
color: var(--note-icon-custom-color, var(--note-list-view-large-icon-color));
|
||||
}
|
||||
|
||||
h5 .ck-find-result {
|
||||
background: var(--note-list-view-search-result-highlight-background);
|
||||
color: var(--note-list-view-search-result-highlight-color);
|
||||
font-weight: 600;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
/* Note content preview */
|
||||
.nested-note-list .note-book-content {
|
||||
display: none;
|
||||
outline: 1px solid var(--note-list-view-content-background);
|
||||
border-radius: 8px;
|
||||
background-color: var(--note-list-view-content-background);
|
||||
overflow: hidden;
|
||||
user-select: text;
|
||||
font-size: .85rem;
|
||||
animation: note-preview-show .25s ease-out;
|
||||
will-change: opacity;
|
||||
|
||||
&.note-book-content-ready {
|
||||
display: block;
|
||||
}
|
||||
|
||||
> .rendered-content > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
&.type-text {
|
||||
padding: 8px 24px;
|
||||
|
||||
.ck-content > *:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
&.type-protectedSession {
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
&.type-image {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
&.type-pdf {
|
||||
iframe {
|
||||
height: 50vh;
|
||||
}
|
||||
|
||||
.file-footer {
|
||||
padding: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
&.type-webView {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
min-height: 50vh;
|
||||
}
|
||||
|
||||
.ck-find-result {
|
||||
outline: 2px solid var(--note-list-view-content-search-result-highlight-background);
|
||||
border-radius: 4px;
|
||||
background: var(--note-list-view-content-search-result-highlight-background);
|
||||
color: var(--note-list-view-content-search-result-highlight-color);
|
||||
}
|
||||
}
|
||||
|
||||
.note-content-preview:has(.note-book-content:empty) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* #endregion */
|
||||
|
||||
/* #region Grid view */
|
||||
.note-list.grid-view .note-list-container {
|
||||
display: flex;
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import "./ListOrGridView.css";
|
||||
import { Card, CardSection } from "../../react/Card";
|
||||
|
||||
import { useEffect, useRef, useState } from "preact/hooks";
|
||||
|
||||
@@ -15,11 +14,6 @@ import NoteLink from "../../react/NoteLink";
|
||||
import { ViewModeProps } from "../interface";
|
||||
import { Pager, usePagination } from "../Pagination";
|
||||
import { filterChildNotes, useFilteredNoteIds } from "./utils";
|
||||
import { JSX } from "preact/jsx-runtime";
|
||||
import { clsx } from "clsx";
|
||||
import ActionButton from "../../react/ActionButton";
|
||||
import linkContextMenuService from "../../../menus/link_context_menu";
|
||||
import { TargetedMouseEvent } from "preact";
|
||||
|
||||
export function ListView({ note, noteIds: unfilteredNoteIds, highlightedTokens }: ViewModeProps<{}>) {
|
||||
const expandDepth = useExpansionDepth(note);
|
||||
@@ -39,7 +33,7 @@ export function ListView({ note, noteIds: unfilteredNoteIds, highlightedTokens }
|
||||
{ noteIds.length > 0 && <div class="note-list-wrapper">
|
||||
{!hasCollectionProperties && <Pager {...pagination} />}
|
||||
|
||||
<Card className={clsx("nested-note-list", {"search-results": (noteType === "search")})}>
|
||||
<div class="note-list-container use-tn-links">
|
||||
{pageNotes?.map(childNote => (
|
||||
<ListNoteCard
|
||||
key={childNote.noteId}
|
||||
@@ -47,7 +41,7 @@ export function ListView({ note, noteIds: unfilteredNoteIds, highlightedTokens }
|
||||
expandDepth={expandDepth} highlightedTokens={highlightedTokens}
|
||||
currentLevel={1} includeArchived={includeArchived} />
|
||||
))}
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
<Pager {...pagination} />
|
||||
</div>}
|
||||
@@ -99,52 +93,27 @@ function ListNoteCard({ note, parentNote, highlightedTokens, currentLevel, expan
|
||||
// Reset expand state if switching to another note, or if user manually toggled expansion state.
|
||||
useEffect(() => setExpanded(currentLevel <= expandDepth), [ note, currentLevel, expandDepth ]);
|
||||
|
||||
let subSections: JSX.Element | undefined = undefined;
|
||||
if (isExpanded) {
|
||||
subSections = <>
|
||||
<CardSection className="note-content-preview">
|
||||
<NoteContent note={note}
|
||||
highlightedTokens={highlightedTokens}
|
||||
noChildrenList
|
||||
includeArchivedNotes={includeArchived} />
|
||||
</CardSection>
|
||||
|
||||
<NoteChildren note={note}
|
||||
parentNote={parentNote}
|
||||
highlightedTokens={highlightedTokens}
|
||||
currentLevel={currentLevel}
|
||||
expandDepth={expandDepth}
|
||||
includeArchived={includeArchived} />
|
||||
</>
|
||||
}
|
||||
|
||||
return (
|
||||
<CardSection
|
||||
className={clsx("nested-note-list-item", "no-tooltip-preview", note.getColorClass(), {
|
||||
"expanded": isExpanded,
|
||||
"archived": note.isArchived
|
||||
})}
|
||||
subSections={subSections}
|
||||
subSectionsVisible={isExpanded}
|
||||
highlightOnHover
|
||||
<div
|
||||
className={`note-book-card no-tooltip-preview ${isExpanded ? "expanded" : ""} ${note.isArchived ? "archived" : ""}`}
|
||||
data-note-id={note.noteId}
|
||||
>
|
||||
<h5>
|
||||
<span className={`note-expander ${isExpanded ? "bx bx-chevron-down" : "bx bx-chevron-right"}`}
|
||||
onClick={() => setExpanded(!isExpanded)}/>
|
||||
<Icon className="note-icon" icon={note.getIcon()} />
|
||||
<NoteLink className="note-book-title"
|
||||
notePath={notePath}
|
||||
noPreview
|
||||
showNotePath={parentNote.type === "search"}
|
||||
highlightedTokens={highlightedTokens} />
|
||||
<NoteAttributes note={note} />
|
||||
<ActionButton className="nested-note-list-item-menu"
|
||||
icon="bx bx-dots-vertical-rounded" text=""
|
||||
onClick={(e) => openNoteMenu(notePath, e)}
|
||||
<h5 className="note-book-header">
|
||||
<span
|
||||
className={`note-expander ${isExpanded ? "bx bx-chevron-down" : "bx bx-chevron-right"}`}
|
||||
onClick={() => setExpanded(!isExpanded)}
|
||||
/>
|
||||
|
||||
<Icon className="note-icon" icon={note.getIcon()} />
|
||||
<NoteLink className="note-book-title" notePath={notePath} noPreview showNotePath={parentNote.type === "search"} highlightedTokens={highlightedTokens} />
|
||||
<NoteAttributes note={note} />
|
||||
</h5>
|
||||
</CardSection>
|
||||
|
||||
{isExpanded && <>
|
||||
<NoteContent note={note} highlightedTokens={highlightedTokens} noChildrenList includeArchivedNotes={includeArchived} />
|
||||
<NoteChildren note={note} parentNote={parentNote} highlightedTokens={highlightedTokens} currentLevel={currentLevel} expandDepth={expandDepth} includeArchived={includeArchived} />
|
||||
</>}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -196,9 +165,6 @@ export function NoteContent({ note, trim, noChildrenList, highlightedTokens, inc
|
||||
const contentRef = useRef<HTMLDivElement>(null);
|
||||
const highlightSearch = useImperativeSearchHighlighlighting(highlightedTokens);
|
||||
|
||||
const [ready, setReady] = useState(false);
|
||||
const [noteType, setNoteType] = useState<string>("none");
|
||||
|
||||
useEffect(() => {
|
||||
content_renderer.getRenderedContent(note, {
|
||||
trim,
|
||||
@@ -213,19 +179,17 @@ export function NoteContent({ note, trim, noChildrenList, highlightedTokens, inc
|
||||
} else {
|
||||
contentRef.current.replaceChildren();
|
||||
}
|
||||
contentRef.current.classList.add(`type-${type}`);
|
||||
highlightSearch(contentRef.current);
|
||||
setNoteType(type);
|
||||
setReady(true);
|
||||
})
|
||||
.catch(e => {
|
||||
console.warn(`Caught error while rendering note '${note.noteId}' of type '${note.type}'`);
|
||||
console.error(e);
|
||||
contentRef.current?.replaceChildren(t("collections.rendering_error"));
|
||||
setReady(true);
|
||||
});
|
||||
}, [ note, highlightedTokens ]);
|
||||
|
||||
return <div ref={contentRef} className={clsx("note-book-content", `type-${noteType}`, {"note-book-content-ready": ready})} />;
|
||||
return <div ref={contentRef} className="note-book-content" />;
|
||||
}
|
||||
|
||||
function NoteChildren({ note, parentNote, highlightedTokens, currentLevel, expandDepth, includeArchived }: {
|
||||
@@ -274,8 +238,3 @@ function useExpansionDepth(note: FNote) {
|
||||
return parseInt(expandDepth, 10);
|
||||
|
||||
}
|
||||
|
||||
function openNoteMenu(notePath, e: TargetedMouseEvent<HTMLElement>) {
|
||||
linkContextMenuService.openContextMenu(notePath, e);
|
||||
e.stopPropagation()
|
||||
}
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
max-width: unset;
|
||||
font-size: 0.8em;
|
||||
font-size: 0.8rem;
|
||||
|
||||
.dropdown-menu {
|
||||
input.form-control {
|
||||
|
||||
@@ -1,47 +0,0 @@
|
||||
:where(.tn-card) {
|
||||
--card-border-radius: 8px;
|
||||
--card-padding-block: 8px;
|
||||
--card-padding-inline: 16px;
|
||||
--card-section-gap: 3px;
|
||||
--card-nested-section-indent: 30px;
|
||||
}
|
||||
|
||||
.tn-card-heading {
|
||||
margin-bottom: 10px;
|
||||
font-size: .75rem;
|
||||
font-weight: 600;
|
||||
letter-spacing: .4pt;
|
||||
text-transform: uppercase;
|
||||
}
|
||||
|
||||
.tn-card-body {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--card-section-gap);
|
||||
|
||||
.tn-card-section {
|
||||
padding: var(--card-padding-block) var(--card-padding-inline);
|
||||
border: 1px solid var(--card-border-color, var(--main-border-color));
|
||||
background: var(--card-background-color);
|
||||
|
||||
&:first-of-type {
|
||||
border-top-left-radius: var(--card-border-radius);
|
||||
border-top-right-radius: var(--card-border-radius);
|
||||
}
|
||||
|
||||
&:last-of-type {
|
||||
border-bottom-left-radius: var(--card-border-radius);
|
||||
border-bottom-right-radius: var(--card-border-radius);
|
||||
}
|
||||
|
||||
&.tn-card-section-nested {
|
||||
padding-left: calc(var(--card-padding-inline) + var(--card-nested-section-indent) * var(--tn-card-section-nesting-level));
|
||||
background-color: color-mix(in srgb, var(--card-background-color) calc(100% / (var(--tn-card-section-nesting-level) + 1)) , transparent);
|
||||
}
|
||||
|
||||
&.tn-card-section-highlight-on-hover:hover {
|
||||
background-color: var(--card-background-hover-color);
|
||||
transition: background-color .2s ease-out;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
import "./Card.css";
|
||||
import { ComponentChildren, createContext } from "preact";
|
||||
import { JSX } from "preact";
|
||||
import { useContext } from "preact/hooks";
|
||||
import clsx from "clsx";
|
||||
|
||||
// #region Card
|
||||
|
||||
export interface CardProps {
|
||||
className?: string;
|
||||
heading?: string;
|
||||
}
|
||||
|
||||
export function Card(props: {children: ComponentChildren} & CardProps) {
|
||||
return <div className={clsx("tn-card", props.className)}>
|
||||
{props.heading && <h5 class="tn-card-heading">{props.heading}</h5>}
|
||||
<div className="tn-card-body">
|
||||
{props.children}
|
||||
</div>
|
||||
</div>;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
|
||||
// #region Card Section
|
||||
|
||||
export interface CardSectionProps {
|
||||
className?: string;
|
||||
subSections?: JSX.Element | JSX.Element[];
|
||||
subSectionsVisible?: boolean;
|
||||
highlightOnHover?: boolean;
|
||||
onAction?: () => void;
|
||||
}
|
||||
|
||||
interface CardSectionContextType {
|
||||
nestingLevel: number;
|
||||
}
|
||||
|
||||
const CardSectionContext = createContext<CardSectionContextType | undefined>(undefined);
|
||||
|
||||
export function CardSection(props: {children: ComponentChildren} & CardSectionProps) {
|
||||
const parentContext = useContext(CardSectionContext);
|
||||
const nestingLevel = (parentContext && parentContext.nestingLevel + 1) ?? 0;
|
||||
|
||||
return <>
|
||||
<section className={clsx("tn-card-section", props.className, {
|
||||
"tn-card-section-nested": nestingLevel > 0,
|
||||
"tn-card-section-highlight-on-hover": props.highlightOnHover || props.onAction
|
||||
})}
|
||||
style={{"--tn-card-section-nesting-level": (nestingLevel) ? nestingLevel : null}}
|
||||
onClick={props.onAction}>
|
||||
{props.children}
|
||||
</section>
|
||||
|
||||
{props.subSectionsVisible && props.subSections &&
|
||||
<CardSectionContext.Provider value={{nestingLevel}}>
|
||||
{props.subSections}
|
||||
</CardSectionContext.Provider>
|
||||
}
|
||||
</>;
|
||||
}
|
||||
|
||||
// #endregion
|
||||
@@ -1,11 +1,13 @@
|
||||
import { t } from "../../services/i18n";
|
||||
import Alert from "../react/Alert";
|
||||
import { useNoteLabelWithDefault, useTriliumEvent } from "../react/hooks";
|
||||
import RawHtml from "../react/RawHtml";
|
||||
import { TypeWidgetProps } from "./type_widget";
|
||||
import "./Book.css";
|
||||
|
||||
import { useEffect, useState } from "preact/hooks";
|
||||
|
||||
import { t } from "../../services/i18n";
|
||||
import { ViewTypeOptions } from "../collections/interface";
|
||||
import CollectionProperties from "../note_bars/CollectionProperties";
|
||||
import { useNoteLabelWithDefault, useTriliumEvent } from "../react/hooks";
|
||||
import NoItems from "../react/NoItems";
|
||||
import { TypeWidgetProps } from "./type_widget";
|
||||
|
||||
const VIEW_TYPES: ViewTypeOptions[] = [ "list", "grid", "presentation" ];
|
||||
|
||||
@@ -27,10 +29,12 @@ export default function Book({ note }: TypeWidgetProps) {
|
||||
return (
|
||||
<>
|
||||
{shouldDisplayNoChildrenWarning && (
|
||||
<Alert type="warning" className="note-detail-book-empty-help">
|
||||
<RawHtml html={t("book.no_children_help")} />
|
||||
</Alert>
|
||||
<>
|
||||
<CollectionProperties note={note} />
|
||||
|
||||
<NoItems icon="bx bx-collection" text={t("book.no_children_help")} />
|
||||
</>
|
||||
)}
|
||||
</>
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
230
pnpm-lock.yaml
generated
230
pnpm-lock.yaml
generated
@@ -200,9 +200,6 @@ importers:
|
||||
'@fullcalendar/timegrid':
|
||||
specifier: 6.1.20
|
||||
version: 6.1.20(@fullcalendar/core@6.1.20)
|
||||
'@maplibre/maplibre-gl-leaflet':
|
||||
specifier: 0.1.3
|
||||
version: 0.1.3(@types/leaflet@1.9.21)(leaflet@1.9.4)(maplibre-gl@5.6.1)
|
||||
'@mermaid-js/layout-elk':
|
||||
specifier: 0.2.0
|
||||
version: 0.2.0(mermaid@11.12.2)
|
||||
@@ -284,12 +281,9 @@ importers:
|
||||
knockout:
|
||||
specifier: 3.5.1
|
||||
version: 3.5.1
|
||||
leaflet:
|
||||
specifier: 1.9.4
|
||||
version: 1.9.4
|
||||
leaflet-gpx:
|
||||
specifier: 2.2.0
|
||||
version: 2.2.0
|
||||
maplibre-gl:
|
||||
specifier: 5.18.0
|
||||
version: 5.18.0
|
||||
mark.js:
|
||||
specifier: 8.11.1
|
||||
version: 8.11.1
|
||||
@@ -342,12 +336,6 @@ importers:
|
||||
'@types/jquery':
|
||||
specifier: 3.5.33
|
||||
version: 3.5.33
|
||||
'@types/leaflet':
|
||||
specifier: 1.9.21
|
||||
version: 1.9.21
|
||||
'@types/leaflet-gpx':
|
||||
specifier: 1.3.8
|
||||
version: 1.3.8
|
||||
'@types/mark.js':
|
||||
specifier: 8.11.12
|
||||
version: 8.11.12
|
||||
@@ -3986,8 +3974,8 @@ packages:
|
||||
resolution: {integrity: sha512-rY0o9A5ECsTQRVhv7tL/OyDpGAoUB4tTvLiW1DSzQGq4bvTPhNw1VpSNjDJc5GFZ2XuyOtSWSVN05qOtcD71qQ==}
|
||||
engines: {node: '>= 0.6'}
|
||||
|
||||
'@mapbox/point-geometry@0.1.0':
|
||||
resolution: {integrity: sha512-6j56HdLTwWGO0fJPlrZtdU/B13q8Uwmo18Ck2GnGgN9PCFyKTZ3UbXeEdRFh18i9XQ92eH2VdtpJHpBD3aripQ==}
|
||||
'@mapbox/point-geometry@1.1.0':
|
||||
resolution: {integrity: sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==}
|
||||
|
||||
'@mapbox/tiny-sdf@2.0.7':
|
||||
resolution: {integrity: sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==}
|
||||
@@ -3995,24 +3983,26 @@ packages:
|
||||
'@mapbox/unitbezier@0.0.1':
|
||||
resolution: {integrity: sha512-nMkuDXFv60aBr9soUG5q+GvZYL+2KZHVvsqFCzqnkGEf46U2fvmytHaEVc1/YZbiLn8X+eR3QzX1+dwDO1lxlw==}
|
||||
|
||||
'@mapbox/vector-tile@1.3.1':
|
||||
resolution: {integrity: sha512-MCEddb8u44/xfQ3oD+Srl/tNcQoqTw3goGk2oLsrFxOTc3dUp+kAnby3PvAeeBYSMSjSPD1nd1AJA6W49WnoUw==}
|
||||
'@mapbox/vector-tile@2.0.4':
|
||||
resolution: {integrity: sha512-AkOLcbgGTdXScosBWwmmD7cDlvOjkg/DetGva26pIRiZPdeJYjYKarIlb4uxVzi6bwHO6EWH82eZ5Nuv4T5DUg==}
|
||||
|
||||
'@mapbox/whoots-js@3.1.0':
|
||||
resolution: {integrity: sha512-Es6WcD0nO5l+2BOQS4uLfNPYQaNDfbot3X1XUoloz+x0mPDS3eeORZJl06HXjwBG1fOGwCRnzK88LMdxKRrd6Q==}
|
||||
engines: {node: '>=6.0.0'}
|
||||
|
||||
'@maplibre/maplibre-gl-leaflet@0.1.3':
|
||||
resolution: {integrity: sha512-9+hp1PSJcxuuj5/Zta9zbQ8+ZvN4doWXPtlY7ikNtUZY1VbkamY0uTqzHp9kxRPqpgeKGrI7MjzXvwzU88wWCw==}
|
||||
peerDependencies:
|
||||
'@types/leaflet': ^1.9.0
|
||||
leaflet: ^1.9.3
|
||||
maplibre-gl: ^2.4.0 || ^3.3.1 || ^4.3.2 || ^5.0.0
|
||||
'@maplibre/geojson-vt@5.0.4':
|
||||
resolution: {integrity: sha512-KGg9sma45S+stfH9vPCJk1J0lSDLWZgCT9Y8u8qWZJyjFlP8MNP1WGTxIMYJZjDvVT3PDn05kN1C95Sut1HpgQ==}
|
||||
|
||||
'@maplibre/maplibre-gl-style-spec@23.3.0':
|
||||
resolution: {integrity: sha512-IGJtuBbaGzOUgODdBRg66p8stnwj9iDXkgbYKoYcNiiQmaez5WVRfXm4b03MCDwmZyX93csbfHFWEJJYHnn5oA==}
|
||||
'@maplibre/maplibre-gl-style-spec@24.4.1':
|
||||
resolution: {integrity: sha512-UKhA4qv1h30XT768ccSv5NjNCX+dgfoq2qlLVmKejspPcSQTYD4SrVucgqegmYcKcmwf06wcNAa/kRd0NHWbUg==}
|
||||
hasBin: true
|
||||
|
||||
'@maplibre/mlt@1.1.6':
|
||||
resolution: {integrity: sha512-rgtY3x65lrrfXycLf6/T22ZnjTg5WgIOsptOIoCaMZy4O4UAKTyZlYY0h6v8le721pTptF94U65yMDQkug+URw==}
|
||||
|
||||
'@maplibre/vt-pbf@4.2.1':
|
||||
resolution: {integrity: sha512-IxZBGq/+9cqf2qdWlFuQ+ZfoMhWpxDUGQZ/poPHOJBvwMUT1GuxLo6HgYTou+xxtsOsjfbcjI8PZaPCtmt97rA==}
|
||||
|
||||
'@marijn/find-cluster-break@1.0.2':
|
||||
resolution: {integrity: sha512-l0h88YhZFyKdXIFNfSWpyjStDjGHwZ/U7iobcK1cQQD8sejsONdQtTVU+1wVN1PBw40PiiHB1vA5S7VTfQiP9g==}
|
||||
|
||||
@@ -5787,9 +5777,6 @@ packages:
|
||||
'@types/fs-extra@9.0.13':
|
||||
resolution: {integrity: sha512-nEnwB++1u5lVDM2UI4c1+5R+FYaKfaAzS4OococimjVm3nQw3TuzH5UNsocrcTBbhnerblyHj4A49qXbIiZdpA==}
|
||||
|
||||
'@types/geojson-vt@3.2.5':
|
||||
resolution: {integrity: sha512-qDO7wqtprzlpe8FfQ//ClPV9xiuoh2nkIgiouIptON9w5jvD/fA4szvP9GBlDVdJ5dldAl0kX/sy3URbWwLx0g==}
|
||||
|
||||
'@types/geojson@7946.0.16':
|
||||
resolution: {integrity: sha512-6C8nqWur3j98U6+lXDfTUWIfgvZU+EumvpHKcYjujKH7woYyLj2sUmff0tRhrqM7BohUw7Pz3ZB1jj2gW9Fvmg==}
|
||||
|
||||
@@ -5829,12 +5816,6 @@ packages:
|
||||
'@types/keyv@3.1.4':
|
||||
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
|
||||
|
||||
'@types/leaflet-gpx@1.3.8':
|
||||
resolution: {integrity: sha512-woIh3APM4FbrEQ+go3yaa4k5j4yn49YLVa1xfSB+T5aYwJn+O3pYhBBQvuxQJW68jpjcaAX/PTJRJLFJ+XT6ow==}
|
||||
|
||||
'@types/leaflet@1.9.21':
|
||||
resolution: {integrity: sha512-TbAd9DaPGSnzp6QvtYngntMZgcRk+igFELwR2N99XZn7RXUdKgsXMR+28bUO0rPsWp8MIu/f47luLIQuSLYv/w==}
|
||||
|
||||
'@types/lodash-es@4.17.12':
|
||||
resolution: {integrity: sha512-0NgftHUcV4v34VhXm8QBSftKVXtbkBG3ViCjs6+eJ5a6y6Mi/jiFGPc1sC7QK+9BFhWrURE3EOggmWaSxL9OzQ==}
|
||||
|
||||
@@ -5844,12 +5825,6 @@ packages:
|
||||
'@types/luxon@3.6.2':
|
||||
resolution: {integrity: sha512-R/BdP7OxEMc44l2Ex5lSXHoIXTB2JLNa3y2QISIbr58U/YcsffyQrYW//hZSdrfxrjRZj3GcUoxMPGdO8gSYuw==}
|
||||
|
||||
'@types/mapbox__point-geometry@0.1.4':
|
||||
resolution: {integrity: sha512-mUWlSxAmYLfwnRBmgYV86tgYmMIICX4kza8YnE/eIlywGe2XoOxlpVnXWwir92xRLjwyarqwpu2EJKD2pk0IUA==}
|
||||
|
||||
'@types/mapbox__vector-tile@1.3.4':
|
||||
resolution: {integrity: sha512-bpd8dRn9pr6xKvuEBQup8pwQfD4VUyqO/2deGjfpe6AwC8YRlyEipvefyRJUSiCJTZuCb8Pl1ciVV5ekqJ96Bg==}
|
||||
|
||||
'@types/mark.js@8.11.12':
|
||||
resolution: {integrity: sha512-244ZnaIBpz4c6xutliAnYVZp6xJlmC569jZqnR3ElO1Y01ooYASSVQEqpd2x0A2UfrgVMs5V9/9tUAdZaDMytQ==}
|
||||
|
||||
@@ -5910,9 +5885,6 @@ packages:
|
||||
'@types/parse-json@4.0.2':
|
||||
resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==}
|
||||
|
||||
'@types/pbf@3.0.5':
|
||||
resolution: {integrity: sha512-j3pOPiEcWZ34R6a6mN07mUkM4o4Lwf6hPNt8eilOeZhTFbxFXmKhvXl9Y28jotFPaI1bpPDJsbCprUoNke6OrA==}
|
||||
|
||||
'@types/postcss-import@14.0.3':
|
||||
resolution: {integrity: sha512-raZhRVTf6Vw5+QbmQ7LOHSDML71A5rj4+EqDzAbrZPfxfoGzFxMHRCq16VlddGIZpHELw0BG4G0YE2ANkdZiIQ==}
|
||||
|
||||
@@ -9126,9 +9098,6 @@ packages:
|
||||
resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==}
|
||||
engines: {node: '>=6.9.0'}
|
||||
|
||||
geojson-vt@4.0.2:
|
||||
resolution: {integrity: sha512-AV9ROqlNqoZEIJGfm1ncNjEXfkz2hdFlZf0qkVfmkwdKa8vj7H16YUOT81rJw1rdFhyEDlN2Tds91p/glzbl5A==}
|
||||
|
||||
get-caller-file@2.0.5:
|
||||
resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==}
|
||||
engines: {node: 6.* || 8.* || >= 10.*}
|
||||
@@ -9260,10 +9229,6 @@ packages:
|
||||
resolution: {integrity: sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
global-prefix@4.0.0:
|
||||
resolution: {integrity: sha512-w0Uf9Y9/nyHinEk5vMJKRie+wa4kR5hmDbEhGGds/kG1PwGLLHKRoNMeJOyCQjjBkANlnScqgzcFwGHgmgLkVA==}
|
||||
engines: {node: '>=16'}
|
||||
|
||||
globals@15.15.0:
|
||||
resolution: {integrity: sha512-7ACyT3wmyp3I61S4fG682L0VA2RGD9otkqGJIwNUMF1SWUombIIk+af1unuDYgMm082aHYwD+mzJvv9Iu8dsgg==}
|
||||
engines: {node: '>=18'}
|
||||
@@ -10406,12 +10371,6 @@ packages:
|
||||
leac@0.6.0:
|
||||
resolution: {integrity: sha512-y+SqErxb8h7nE/fiEX07jsbuhrpO9lL8eca7/Y1nuWV2moNlXhyd59iDGcRf6moVyDMbmTNzL40SUyrFU/yDpg==}
|
||||
|
||||
leaflet-gpx@2.2.0:
|
||||
resolution: {integrity: sha512-iVUx6o0ydLn2ikYSVLuWnr0k/CDAOIUtmvQ91AI24/PXuIFIb+iEIJMHTQfvGhMCKcFrwd9ZaFYH7P/46tgGhw==}
|
||||
|
||||
leaflet@1.9.4:
|
||||
resolution: {integrity: sha512-nxS1ynzJOmOlHp+iL3FyWqK89GtNL8U8rvlMOsQdTTssxZwCXh8N2NB3GDQOL+YR3XnWyZAxwQixURb+FA74PA==}
|
||||
|
||||
less@4.1.3:
|
||||
resolution: {integrity: sha512-w16Xk/Ta9Hhyei0Gpz9m7VS8F28nieJaL/VyShID7cYvP6IL5oHeL6p4TXSDJqZE/lNv0oJ2pGVjJsRkfwm5FA==}
|
||||
engines: {node: '>=6'}
|
||||
@@ -10763,8 +10722,8 @@ packages:
|
||||
resolution: {integrity: sha512-bJzx6nMoP6PDLPBFmg7+xRKeFZvFboMrGlxmNj9ClvX53KrmvM5bXFXEWjbz4cz1AFn+jWJ9z/DJSz7hrs0w3w==}
|
||||
engines: {node: '>=6'}
|
||||
|
||||
maplibre-gl@5.6.1:
|
||||
resolution: {integrity: sha512-TTSfoTaF7RqKUR9wR5qDxCHH2J1XfZ1E85luiLOx0h8r50T/LnwAwwfV0WVNh9o8dA7rwt57Ucivf1emyeukXg==}
|
||||
maplibre-gl@5.18.0:
|
||||
resolution: {integrity: sha512-UtWxPBpHuFvEkM+5FVfcFG9ZKEWZQI6+PZkvLErr8Zs5ux+O7/KQ3JjSUvAfOlMeMgd/77qlHpOw0yHL7JU5cw==}
|
||||
engines: {node: '>=16.14.0', npm: '>=8.1.0'}
|
||||
|
||||
mark.js@8.11.1:
|
||||
@@ -11868,8 +11827,8 @@ packages:
|
||||
pathe@2.0.3:
|
||||
resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==}
|
||||
|
||||
pbf@3.3.0:
|
||||
resolution: {integrity: sha512-XDF38WCH3z5OV/OVa8GKUNtLAyneuzbCisx7QUCF8Q6Nutx0WnJrQe5O+kOtBlLfRNUws98Y58Lblp+NJG5T4Q==}
|
||||
pbf@4.0.1:
|
||||
resolution: {integrity: sha512-SuLdBvS42z33m8ejRbInMapQe8n0D3vN/Xd5fmWM3tufNgRQFBpaW2YVJxQZV4iPNqb0vEFvssMEo5w9c6BTIA==}
|
||||
hasBin: true
|
||||
|
||||
pdfjs-dist@5.4.530:
|
||||
@@ -14849,9 +14808,6 @@ packages:
|
||||
vscode-uri@3.1.0:
|
||||
resolution: {integrity: sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==}
|
||||
|
||||
vt-pbf@3.1.3:
|
||||
resolution: {integrity: sha512-2LzDFzt0mZKZ9IpVF2r69G9bXaP2Q2sArJCmcCgvfTdCCZzSyz4aCLoQyUilu37Ll56tCblIZrXFIjNUpGIlmA==}
|
||||
|
||||
w3c-hr-time@1.0.2:
|
||||
resolution: {integrity: sha512-z8P5DvDNjKDoFIHK7q8r8lackT6l+jo/Ye3HOle7l9nICP9lf1Ci25fy9vHd0JOWewkIFzXIEig3TdKT7JQ5fQ==}
|
||||
deprecated: Use your platform's native performance.now() and performance.timeOrigin.
|
||||
@@ -15059,11 +15015,6 @@ packages:
|
||||
engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0}
|
||||
hasBin: true
|
||||
|
||||
which@4.0.0:
|
||||
resolution: {integrity: sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==}
|
||||
engines: {node: ^16.13.0 || >=18.0.0}
|
||||
hasBin: true
|
||||
|
||||
which@5.0.0:
|
||||
resolution: {integrity: sha512-JEdGzHwwkrbWoGOlIHqQ5gtprKGOenpDHpxE9zVR1bWbOtYRyPPHMe9FaP6x61CmNaTThSkb0DAJte5jD+DmzQ==}
|
||||
engines: {node: ^18.17.0 || >=20.5.0}
|
||||
@@ -16021,6 +15972,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-core': 47.4.0
|
||||
'@ckeditor/ckeditor5-upload': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-ai@47.4.0(bufferutil@4.0.9)(utf-8-validate@6.0.5)':
|
||||
dependencies:
|
||||
@@ -16161,12 +16114,16 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
'@ckeditor/ckeditor5-widget': 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-cloud-services@47.4.0':
|
||||
dependencies:
|
||||
'@ckeditor/ckeditor5-core': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-code-block@47.4.0(patch_hash=2361d8caad7d6b5bddacc3a3b4aa37dbfba260b1c1b22a450413a79c1bb1ce95)':
|
||||
dependencies:
|
||||
@@ -16359,6 +16316,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-classic@47.4.0':
|
||||
dependencies:
|
||||
@@ -16368,6 +16327,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-decoupled@47.4.0':
|
||||
dependencies:
|
||||
@@ -16377,6 +16338,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-editor-inline@47.4.0':
|
||||
dependencies:
|
||||
@@ -16410,8 +16373,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-table': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-emoji@47.4.0':
|
||||
dependencies:
|
||||
@@ -16468,8 +16429,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-export-word@47.4.0':
|
||||
dependencies:
|
||||
@@ -16494,6 +16453,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-font@47.4.0':
|
||||
dependencies:
|
||||
@@ -16568,6 +16529,8 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
'@ckeditor/ckeditor5-widget': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-html-embed@47.4.0':
|
||||
dependencies:
|
||||
@@ -16627,8 +16590,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-indent@47.4.0':
|
||||
dependencies:
|
||||
@@ -16752,8 +16713,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-merge-fields@47.4.0':
|
||||
dependencies:
|
||||
@@ -16766,8 +16725,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-widget': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-minimap@47.4.0':
|
||||
dependencies:
|
||||
@@ -16776,8 +16733,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-operations-compressor@47.4.0':
|
||||
dependencies:
|
||||
@@ -16832,8 +16787,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
'@ckeditor/ckeditor5-widget': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-pagination@47.4.0':
|
||||
dependencies:
|
||||
@@ -16941,8 +16894,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-slash-command@47.4.0':
|
||||
dependencies:
|
||||
@@ -16955,8 +16906,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-ui': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-source-editing-enhanced@47.4.0':
|
||||
dependencies:
|
||||
@@ -17004,8 +16953,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-table@47.4.0':
|
||||
dependencies:
|
||||
@@ -17018,8 +16965,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-widget': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-template@47.4.0':
|
||||
dependencies:
|
||||
@@ -17130,8 +17075,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-engine': 47.4.0
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@ckeditor/ckeditor5-widget@47.4.0':
|
||||
dependencies:
|
||||
@@ -17151,8 +17094,6 @@ snapshots:
|
||||
'@ckeditor/ckeditor5-utils': 47.4.0
|
||||
ckeditor5: 47.4.0
|
||||
es-toolkit: 1.39.5
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@codemirror/autocomplete@6.18.6':
|
||||
dependencies:
|
||||
@@ -19160,25 +19101,23 @@ snapshots:
|
||||
|
||||
'@mapbox/jsonlint-lines-primitives@2.0.2': {}
|
||||
|
||||
'@mapbox/point-geometry@0.1.0': {}
|
||||
'@mapbox/point-geometry@1.1.0': {}
|
||||
|
||||
'@mapbox/tiny-sdf@2.0.7': {}
|
||||
|
||||
'@mapbox/unitbezier@0.0.1': {}
|
||||
|
||||
'@mapbox/vector-tile@1.3.1':
|
||||
'@mapbox/vector-tile@2.0.4':
|
||||
dependencies:
|
||||
'@mapbox/point-geometry': 0.1.0
|
||||
'@mapbox/point-geometry': 1.1.0
|
||||
'@types/geojson': 7946.0.16
|
||||
pbf: 4.0.1
|
||||
|
||||
'@mapbox/whoots-js@3.1.0': {}
|
||||
|
||||
'@maplibre/maplibre-gl-leaflet@0.1.3(@types/leaflet@1.9.21)(leaflet@1.9.4)(maplibre-gl@5.6.1)':
|
||||
dependencies:
|
||||
'@types/leaflet': 1.9.21
|
||||
leaflet: 1.9.4
|
||||
maplibre-gl: 5.6.1
|
||||
'@maplibre/geojson-vt@5.0.4': {}
|
||||
|
||||
'@maplibre/maplibre-gl-style-spec@23.3.0':
|
||||
'@maplibre/maplibre-gl-style-spec@24.4.1':
|
||||
dependencies:
|
||||
'@mapbox/jsonlint-lines-primitives': 2.0.2
|
||||
'@mapbox/unitbezier': 0.0.1
|
||||
@@ -19188,6 +19127,20 @@ snapshots:
|
||||
rw: 1.3.3
|
||||
tinyqueue: 3.0.0
|
||||
|
||||
'@maplibre/mlt@1.1.6':
|
||||
dependencies:
|
||||
'@mapbox/point-geometry': 1.1.0
|
||||
|
||||
'@maplibre/vt-pbf@4.2.1':
|
||||
dependencies:
|
||||
'@mapbox/point-geometry': 1.1.0
|
||||
'@mapbox/vector-tile': 2.0.4
|
||||
'@maplibre/geojson-vt': 5.0.4
|
||||
'@types/geojson': 7946.0.16
|
||||
'@types/supercluster': 7.1.3
|
||||
pbf: 4.0.1
|
||||
supercluster: 8.0.1
|
||||
|
||||
'@marijn/find-cluster-break@1.0.2': {}
|
||||
|
||||
'@mdi/font@7.4.47': {}
|
||||
@@ -21171,10 +21124,6 @@ snapshots:
|
||||
'@types/node': 24.10.13
|
||||
optional: true
|
||||
|
||||
'@types/geojson-vt@3.2.5':
|
||||
dependencies:
|
||||
'@types/geojson': 7946.0.16
|
||||
|
||||
'@types/geojson@7946.0.16': {}
|
||||
|
||||
'@types/har-format@1.2.16': {}
|
||||
@@ -21211,14 +21160,6 @@ snapshots:
|
||||
dependencies:
|
||||
'@types/node': 24.10.13
|
||||
|
||||
'@types/leaflet-gpx@1.3.8':
|
||||
dependencies:
|
||||
'@types/leaflet': 1.9.21
|
||||
|
||||
'@types/leaflet@1.9.21':
|
||||
dependencies:
|
||||
'@types/geojson': 7946.0.16
|
||||
|
||||
'@types/lodash-es@4.17.12':
|
||||
dependencies:
|
||||
'@types/lodash': 4.17.16
|
||||
@@ -21227,14 +21168,6 @@ snapshots:
|
||||
|
||||
'@types/luxon@3.6.2': {}
|
||||
|
||||
'@types/mapbox__point-geometry@0.1.4': {}
|
||||
|
||||
'@types/mapbox__vector-tile@1.3.4':
|
||||
dependencies:
|
||||
'@types/geojson': 7946.0.16
|
||||
'@types/mapbox__point-geometry': 0.1.4
|
||||
'@types/pbf': 3.0.5
|
||||
|
||||
'@types/mark.js@8.11.12':
|
||||
dependencies:
|
||||
'@types/jquery': 3.5.33
|
||||
@@ -21301,8 +21234,6 @@ snapshots:
|
||||
|
||||
'@types/parse-json@4.0.2': {}
|
||||
|
||||
'@types/pbf@3.0.5': {}
|
||||
|
||||
'@types/postcss-import@14.0.3':
|
||||
dependencies:
|
||||
postcss: 8.5.6
|
||||
@@ -25532,8 +25463,6 @@ snapshots:
|
||||
|
||||
gensync@1.0.0-beta.2: {}
|
||||
|
||||
geojson-vt@4.0.2: {}
|
||||
|
||||
get-caller-file@2.0.5: {}
|
||||
|
||||
get-east-asian-width@1.4.0: {}
|
||||
@@ -25701,12 +25630,6 @@ snapshots:
|
||||
kind-of: 6.0.3
|
||||
which: 1.3.1
|
||||
|
||||
global-prefix@4.0.0:
|
||||
dependencies:
|
||||
ini: 4.1.3
|
||||
kind-of: 6.0.3
|
||||
which: 4.0.0
|
||||
|
||||
globals@15.15.0: {}
|
||||
|
||||
globals@16.5.0: {}
|
||||
@@ -26964,10 +26887,6 @@ snapshots:
|
||||
|
||||
leac@0.6.0: {}
|
||||
|
||||
leaflet-gpx@2.2.0: {}
|
||||
|
||||
leaflet@1.9.4: {}
|
||||
|
||||
less@4.1.3:
|
||||
dependencies:
|
||||
copy-anything: 2.0.6
|
||||
@@ -27384,34 +27303,30 @@ snapshots:
|
||||
dependencies:
|
||||
p-defer: 1.0.0
|
||||
|
||||
maplibre-gl@5.6.1:
|
||||
maplibre-gl@5.18.0:
|
||||
dependencies:
|
||||
'@mapbox/geojson-rewind': 0.5.2
|
||||
'@mapbox/jsonlint-lines-primitives': 2.0.2
|
||||
'@mapbox/point-geometry': 0.1.0
|
||||
'@mapbox/point-geometry': 1.1.0
|
||||
'@mapbox/tiny-sdf': 2.0.7
|
||||
'@mapbox/unitbezier': 0.0.1
|
||||
'@mapbox/vector-tile': 1.3.1
|
||||
'@mapbox/vector-tile': 2.0.4
|
||||
'@mapbox/whoots-js': 3.1.0
|
||||
'@maplibre/maplibre-gl-style-spec': 23.3.0
|
||||
'@maplibre/geojson-vt': 5.0.4
|
||||
'@maplibre/maplibre-gl-style-spec': 24.4.1
|
||||
'@maplibre/mlt': 1.1.6
|
||||
'@maplibre/vt-pbf': 4.2.1
|
||||
'@types/geojson': 7946.0.16
|
||||
'@types/geojson-vt': 3.2.5
|
||||
'@types/mapbox__point-geometry': 0.1.4
|
||||
'@types/mapbox__vector-tile': 1.3.4
|
||||
'@types/pbf': 3.0.5
|
||||
'@types/supercluster': 7.1.3
|
||||
earcut: 3.0.2
|
||||
geojson-vt: 4.0.2
|
||||
gl-matrix: 3.4.4
|
||||
global-prefix: 4.0.0
|
||||
kdbush: 4.0.2
|
||||
murmurhash-js: 1.0.0
|
||||
pbf: 3.3.0
|
||||
pbf: 4.0.1
|
||||
potpack: 2.1.0
|
||||
quickselect: 3.0.0
|
||||
supercluster: 8.0.1
|
||||
tinyqueue: 3.0.0
|
||||
vt-pbf: 3.1.3
|
||||
|
||||
mark.js@8.11.1: {}
|
||||
|
||||
@@ -28830,9 +28745,8 @@ snapshots:
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
pbf@3.3.0:
|
||||
pbf@4.0.1:
|
||||
dependencies:
|
||||
ieee754: 1.2.1
|
||||
resolve-protobuf-schema: 2.1.0
|
||||
|
||||
pdfjs-dist@5.4.530:
|
||||
@@ -32296,12 +32210,6 @@ snapshots:
|
||||
|
||||
vscode-uri@3.1.0: {}
|
||||
|
||||
vt-pbf@3.1.3:
|
||||
dependencies:
|
||||
'@mapbox/point-geometry': 0.1.0
|
||||
'@mapbox/vector-tile': 1.3.1
|
||||
pbf: 3.3.0
|
||||
|
||||
w3c-hr-time@1.0.2:
|
||||
dependencies:
|
||||
browser-process-hrtime: 1.0.0
|
||||
@@ -32665,10 +32573,6 @@ snapshots:
|
||||
dependencies:
|
||||
isexe: 2.0.0
|
||||
|
||||
which@4.0.0:
|
||||
dependencies:
|
||||
isexe: 3.1.1
|
||||
|
||||
which@5.0.0:
|
||||
dependencies:
|
||||
isexe: 3.1.1
|
||||
|
||||
Reference in New Issue
Block a user