Porównaj commity

...

4 Commity

Autor SHA1 Wiadomość Data
Candid Dauth 9aa954c3c4 Update documentation to reflect new features 2024-03-29 01:44:57 +01:00
Candid Dauth 29ef631fc4 Remove id from update object types 2024-03-29 01:44:39 +01:00
Candid Dauth 326332eedf Enable high accuracy for locate control 2024-03-29 00:20:34 +01:00
Candid Dauth c43878c991 Fix colours for locate control 2024-03-29 00:19:17 +01:00
20 zmienionych plików z 93 dodań i 66 usunięć

Wyświetl plik

@ -440,7 +440,7 @@ export default class Client {
return marker; return marker;
} }
async editMarker(data: Marker<CRU.UPDATE>): Promise<Marker> { async editMarker(data: Marker<CRU.UPDATE> & { id: ID }): Promise<Marker> {
return await this._emit("editMarker", data); return await this._emit("editMarker", data);
} }
@ -456,7 +456,7 @@ export default class Client {
return await this._emit("addLine", data); return await this._emit("addLine", data);
} }
async editLine(data: Line<CRU.UPDATE>): Promise<Line> { async editLine(data: Line<CRU.UPDATE> & { id: ID }): Promise<Line> {
return await this._emit("editLine", data); return await this._emit("editLine", data);
} }
@ -542,7 +542,7 @@ export default class Client {
return await this._emit("addType", data); return await this._emit("addType", data);
} }
async editType(data: Type<CRU.UPDATE>): Promise<Type> { async editType(data: Type<CRU.UPDATE> & { id: ID }): Promise<Type> {
return await this._emit("editType", data); return await this._emit("editType", data);
} }
@ -554,7 +554,7 @@ export default class Client {
return await this._emit("addView", data); return await this._emit("addView", data);
} }
async editView(data: View<CRU.UPDATE>): Promise<View> { async editView(data: View<CRU.UPDATE> & { id: ID }): Promise<View> {
return await this._emit("editView", data); return await this._emit("editView", data);
} }

Wyświetl plik

@ -2,26 +2,39 @@
A map can be exported as a file, in order to use it in another application or to create a backup. A map can be exported as a file, in order to use it in another application or to create a backup.
To export a map, in the [toolbox](../ui/#toolbox), click “Tools” and then one of the “Export” options. Note that when a [filter](../filter/) is active, only the objects that match the filter are exported. To export a map, in the [toolbox](../ui/#toolbox), click “Tools” and then “Export”. This opens a dialog where you can configure in what format to export the map.
The exports are available under their own URL. In the context menu (right click) of the export links, you can copy the URL to use it elsewhere. ## Formats
## GeoJSON ### GPX
When exporting a map as GeoJSON, all markers and lines (or, if a [filter](../filter/) is active, those that match the filter) including their data fields, all [saved views](../views/), and all [types](../types/) that represent the exported markers/lines are exported. This means that if no filter is active, this is suitable to make a backup of the whole map (apart from the [map settings](../map-settings/) and the [edit history](../history/)). Such a file can also be [imported](../import/) again into a map to restore the backup. GPX is a format that is understood by many geographic apps and navigation devices. Exporting a map as GPX will export all markers and lines on the map.
## GPX When exporting to GPX, one of three “Route type” options needs to be selected:
* **Track points:** Lines that are calculated routes will be exported as GPX tracks. This means that the whole course of the route is saved in the file.
* **Track points, one file per line (ZIP file):** Like “Track points”, but rather than creating one GPX file containing all markers/lines of the map, a ZIP file will be generated that contains one GPX file with all markers and a folder with one GPX file per line. This is useful for importing the file into OsmAnd, which only supports one track per file.
* **Route points:** Lines that are calculated routes will be exported as GPX routes. This means that only the route destinations are saved in the file, and the app or device that opens the file is responsible for calculating the best route.
GPX is a format that is understood by many geographic apps and navigation devices. Exporting a map as GPX will export all markers and lines on the map (or, if a [filter](../filter/) is active, those that match the filter). There are two options: The marker/line description and any [custom fields](../types/) will be saved in the description of the GPX objects. The marker/line styles are not exported, with the exception of some basic style settings supported by OsmAnd.
* **Export as GPX (tracks):** Lines that are calculated routes will be exported as GPX tracks. This means that the whole course of the route is saved in the file.
* **Export as GPX (routes):** Lines that are calculated routes will be exported as GPX routes. This means that only the route destinations are saved in the file, and the app or device that opens the file is responsible for calculating the best route.
The marker/line description and any [custom fields](../types/) will be saved in the description of the GPX objects. ### GeoJSON
## Table When exporting a map as GeoJSON, all markers and lines including their data fields, all [saved views](../views/), and all [types](../types/) that represent the exported markers/lines are exported. This means that if no filter is active, this is suitable to make a backup of the whole map (apart from the [map settings](../map-settings/) and the [edit history](../history/)). Such a file can also be [imported](../import/) again into a map to restore the backup.
The table export is a static HTML export of all the markers and lines (or, if a [filter](../filter/) is active, those that match the filter) in table form. All the field values of the markers/lines are shown, along with some metadata (name, coordinates, distance, time). ### HTML
A separate table for each [type](../types/) is shown. Individual types can be hidden by clicking the down arrow next to their heading. The HTML export will render a web page that contains a table for each [type](../types/) that lists each marker/line of that type along with all field values and some metadata (name, coordinates, distance, time). Types can be shown/hidden by clicking on the down arrow next to their heading, and the table can be sorted by an individual data attribute by clicking on its column header.
Table columns can be sorted by clicking on their header. For HTML exports, there additional “Copy to clipboard” export method is available. This will copy the table for a single type into the clipboard. Such a table can be pasted directly into a spreadsheet application such as EtherCalc or LibreOffice Calc.
### CSV
CSV files can be opened by most spreadsheet applications. A CSV export will contain all the markers/lines of a single type, along with their field values and some metadata (name, coordinates, distance, time). Note that CSV only supports plain text, so any rich text formatting will be lost.
## Generate a link
By default, the export dialog will create a file and download or open it. By selecting “Generate link” as the export method, you can copy a URL to the exported file instead. This URL will always generate the file with the lastest map data according to the export settings that you have defined, so you can use it to link to the export from a website or a browser bookmark or to integrate the export with another tool that should periodically create copies of your map data.
## Apply a filter
If you have an active [filter](../filter/), the export dialog will show an additional “Apply filter” option. When this option is enabled, the exported file will only contain the map objects that match the filter.

Wyświetl plik

@ -20,6 +20,7 @@ Base layers set the main style of the map. Only one base layer can be shown at t
| Base layer | Source | Purpose | | Base layer | Source | Purpose |
|------------|--------|---------| |------------|--------|---------|
| Lima Labs | [Lima Labs](https://maps.lima-labs.com/) | Good for a general geographic overview. Has a focus on car infrastructure and political borders. Supports high resolution displays. |
| Mapnik | [OpenStreetMap](https://www.openstreetmap.org/) | Good for a general geographic overview. Has a focus on car infrastructure and political borders. | | Mapnik | [OpenStreetMap](https://www.openstreetmap.org/) | Good for a general geographic overview. Has a focus on car infrastructure and political borders. |
| TopPlus | [German state](https://gdz.bkg.bund.de/index.php/default/wms-topplusopen-wms-topplus-open.html) | Good for a general geographic overview, with more details than Mapnik. Has a focus on car infrastructure, political borders and topography. Unfortunately all labels are in German. | | TopPlus | [German state](https://gdz.bkg.bund.de/index.php/default/wms-topplusopen-wms-topplus-open.html) | Good for a general geographic overview, with more details than Mapnik. Has a focus on car infrastructure, political borders and topography. Unfortunately all labels are in German. |
| Map1.eu | [Map1.eu](https://www.map1.eu/) | Has a focus on cities/towns, car infrastructure and political borders. Aims to have the same level of detail as a paper map. Only available for Europe. | | Map1.eu | [Map1.eu](https://www.map1.eu/) | Has a focus on cities/towns, car infrastructure and political borders. Aims to have the same level of detail as a paper map. Only available for Europe. |

Wyświetl plik

@ -27,7 +27,7 @@ The zoom buttons allow you to zoom in and out of the map. Alternatively, you can
The search box allows you to [search for places](../search/) and to [calculate a route](../route/). On small screens, it also contains the [legend](../legend/) if enabled. The search box allows you to [search for places](../search/) and to [calculate a route](../route/). On small screens, it also contains the [legend](../legend/) if enabled.
On top of the search box, you can click the different tabs to switch between different functions. By default, the search box contains two tabs, the search form and the route form. Different functions of the map may temporarily or permanently add additional tabs. On top of the search box, you can click the different tabs to switch between different functions. By default, the search box contains three tabs, the search form, the route form and the POI tab. Different functions of the map may temporarily or permanently add additional tabs.
On big screens, you can drag the resize handle on the bottom right of the search box to resize it. Click the resize handle to bring it back to its original size.\ On big screens, you can drag the resize handle on the bottom right of the search box to resize it. Click the resize handle to bring it back to its original size.\
On small screens, the search box appears at the bottom of the screen. You can drag it into and out of view as it fits by dragging the tab bar on top of the search box. On small screens, the search box appears at the bottom of the screen. You can drag it into and out of view as it fits by dragging the tab bar on top of the search box.

Wyświetl plik

@ -126,7 +126,17 @@ function useLinesLayer(map: Ref<Map>, client: Ref<ClientContext>): Ref<Raw<Lines
function useLocateControl(map: Ref<Map>): Ref<Raw<Control.Locate>> { function useLocateControl(map: Ref<Map>): Ref<Raw<Control.Locate>> {
return useMapComponent( return useMapComponent(
map, map,
() => markRaw(control.locate({ flyTo: true, icon: "a", iconLoading: "a", markerStyle: { pane: "fm-raised-marker", zIndexOffset: 10000 } })), () => (
markRaw(control.locate({
flyTo: true,
icon: "a",
iconLoading: "a",
markerStyle: { pane: "fm-raised-marker", zIndexOffset: 10000 },
locateOptions: {
enableHighAccuracy: true
}
}))
),
(locateControl, onCleanup) => { (locateControl, onCleanup) => {
locateControl.addTo(map.value); locateControl.addTo(map.value);

Wyświetl plik

@ -1,12 +1,5 @@
<script setup lang="ts"> <script setup lang="ts">
import { type Ref, computed, onMounted, ref } from "vue"; import { type Ref, computed, onMounted, ref } from "vue";
import "leaflet/dist/leaflet.css";
import "leaflet.locatecontrol";
import "leaflet.locatecontrol/dist/L.Control.Locate.css";
import "leaflet-graphicscale";
import "leaflet-graphicscale/src/Leaflet.GraphicScale.scss";
import "leaflet-mouse-position";
import "leaflet-mouse-position/src/L.Control.MousePosition.css";
import { useMapContext } from "./leaflet-map-components"; import { useMapContext } from "./leaflet-map-components";
import vTooltip from "../../utils/tooltip"; import vTooltip from "../../utils/tooltip";
import type { WritableMapContext } from "../facil-map-context-provider/map-context"; import type { WritableMapContext } from "../facil-map-context-provider/map-context";
@ -142,11 +135,21 @@
} }
} }
.leaflet-control-locate.leaflet-control-locate a { .leaflet-control-locate.leaflet-control-locate {
font-size: inherit; a {
display: inline-flex; font-size: inherit;
align-items: center; display: inline-flex;
justify-content: center; align-items: center;
justify-content: center;
}
&.active a {
color: rgb(32, 116, 182);
}
&.following a {
color: rgb(252, 132, 40);
}
} }
path.leaflet-interactive { path.leaflet-interactive {

Wyświetl plik

@ -1,6 +1,6 @@
import { expect, test, vi } from "vitest"; import { expect, test, vi } from "vitest";
import { createTemporaryPad, emit, getTemporaryPadData, openClient, openSocket, retry } from "./utils"; import { createTemporaryPad, emit, getTemporaryPadData, openClient, openSocket, retry } from "./utils";
import { SocketVersion, CRU, type Line, type LinePointsEvent, type FindOnMapLine } from "facilmap-types"; import { SocketVersion, CRU, type Line, type LinePointsEvent, type FindOnMapLine, type ID } from "facilmap-types";
import type { LineWithTrackPoints } from "facilmap-client"; import type { LineWithTrackPoints } from "facilmap-client";
import { cloneDeep, omit } from "lodash-es"; import { cloneDeep, omit } from "lodash-es";
@ -241,7 +241,7 @@ test("Edit line", async () => {
{ lat: 14, lon: 14 }, { lat: 14, lon: 14 },
{ lat: 12, lon: 12 } { lat: 12, lon: 12 }
] ]
} satisfies Line<CRU.UPDATE>; } satisfies Line<CRU.UPDATE> & { id: ID };
const line = await client1.editLine(newData); const line = await client1.editLine(newData);
const expectedLine = { const expectedLine = {

Wyświetl plik

@ -1,6 +1,6 @@
import { expect, test, vi } from "vitest"; import { expect, test, vi } from "vitest";
import { createTemporaryPad, emit, getTemporaryPadData, openClient, openSocket, retry } from "./utils"; import { createTemporaryPad, emit, getTemporaryPadData, openClient, openSocket, retry } from "./utils";
import { SocketVersion, CRU, type Marker, type FindOnMapMarker } from "facilmap-types"; import { SocketVersion, CRU, type Marker, type FindOnMapMarker, type ID } from "facilmap-types";
import { cloneDeep } from "lodash-es"; import { cloneDeep } from "lodash-es";
test("Create marker (using default values)", async () => { test("Create marker (using default values)", async () => {
@ -136,7 +136,7 @@ test("Edit marker", async () => {
const onMarker3 = vi.fn(); const onMarker3 = vi.fn();
client3.on("marker", onMarker3); client3.on("marker", onMarker3);
const newData: Marker<CRU.UPDATE> = { const newData: Marker<CRU.UPDATE> & { id: ID } = {
id: createdMarker.id, id: createdMarker.id,
lat: 10, lat: 10,
lon: 10, lon: 10,

Wyświetl plik

@ -1,6 +1,6 @@
import { expect, test, vi } from "vitest"; import { expect, test, vi } from "vitest";
import { createTemporaryPad, openClient, retry } from "../utils"; import { createTemporaryPad, openClient, retry } from "../utils";
import { CRU, type Type } from "facilmap-types"; import { CRU, type ID, type Type } from "facilmap-types";
import { cloneDeep } from "lodash-es"; import { cloneDeep } from "lodash-es";
test("Default types are added", async () => { test("Default types are added", async () => {
@ -321,7 +321,7 @@ test("Update type", async () => {
fields: [ fields: [
{ name: "Test field", type: "input" } { name: "Test field", type: "input" }
] ]
} satisfies Type<CRU.UPDATE>; } satisfies Type<CRU.UPDATE> & { id: ID };
const typeResult = await client1.editType(update); const typeResult = await client1.editType(update);

Wyświetl plik

@ -1,6 +1,6 @@
import { expect, test, vi } from "vitest"; import { expect, test, vi } from "vitest";
import { createTemporaryPad, openClient, retry } from "./utils"; import { createTemporaryPad, openClient, retry } from "./utils";
import { type CRU, type View } from "facilmap-types"; import { type CRU, type ID, type View } from "facilmap-types";
import { cloneDeep } from "lodash-es"; import { cloneDeep } from "lodash-es";
test("Create view (default values)", async () => { test("Create view (default values)", async () => {
@ -129,7 +129,7 @@ test("Update view", async () => {
layers: ["grid"], layers: ["grid"],
idx: 2, idx: 2,
filter: "name == 'Test'" filter: "name == 'Test'"
} satisfies View<CRU.UPDATE>; } satisfies View<CRU.UPDATE> & { id: ID };
const view = await client1.editView(update); const view = await client1.editView(update);
const expectedView: View = { const expectedView: View = {

Wyświetl plik

@ -223,13 +223,13 @@ export default class DatabaseLines {
return createdLine; return createdLine;
} }
async updateLine(padId: PadId, lineId: ID, data: Omit<Line<CRU.UPDATE_VALIDATED>, "id">, noHistory?: boolean, trackPointsFromRoute?: Route): Promise<Line> { async updateLine(padId: PadId, lineId: ID, data: Line<CRU.UPDATE_VALIDATED>, noHistory?: boolean, trackPointsFromRoute?: Route): Promise<Line> {
const originalLine = await this.getLine(padId, lineId); const originalLine = await this.getLine(padId, lineId);
const newType = await this._db.types.getType(padId, data.typeId ?? originalLine.typeId); const newType = await this._db.types.getType(padId, data.typeId ?? originalLine.typeId);
return await this._updateLine(originalLine, data, newType, noHistory, trackPointsFromRoute); return await this._updateLine(originalLine, data, newType, noHistory, trackPointsFromRoute);
} }
async _updateLine(originalLine: Line, data: Omit<Line<CRU.UPDATE_VALIDATED>, "id">, newType: Type, noHistory?: boolean, trackPointsFromRoute?: Route): Promise<Line> { async _updateLine(originalLine: Line, data: Line<CRU.UPDATE_VALIDATED>, newType: Type, noHistory?: boolean, trackPointsFromRoute?: Route): Promise<Line> {
if (newType.type !== "line") { if (newType.type !== "line") {
throw new Error(`Cannot use ${newType.type} type for line.`); throw new Error(`Cannot use ${newType.type} type for line.`);
} }

Wyświetl plik

@ -110,13 +110,13 @@ export default class DatabaseMarkers {
return result; return result;
} }
async updateMarker(padId: PadId, markerId: ID, data: Omit<Marker<CRU.UPDATE_VALIDATED>, "id">, noHistory = false): Promise<Marker> { async updateMarker(padId: PadId, markerId: ID, data: Marker<CRU.UPDATE_VALIDATED>, noHistory = false): Promise<Marker> {
const originalMarker = await this.getMarker(padId, markerId); const originalMarker = await this.getMarker(padId, markerId);
const newType = await this._db.types.getType(padId, data.typeId ?? originalMarker.typeId); const newType = await this._db.types.getType(padId, data.typeId ?? originalMarker.typeId);
return await this._updateMarker(originalMarker, data, newType, noHistory); return await this._updateMarker(originalMarker, data, newType, noHistory);
} }
async _updateMarker(originalMarker: Marker, data: Omit<Marker<CRU.UPDATE_VALIDATED>, "id">, newType: Type, noHistory = false): Promise<Marker> { async _updateMarker(originalMarker: Marker, data: Marker<CRU.UPDATE_VALIDATED>, newType: Type, noHistory = false): Promise<Marker> {
if (newType.type !== "marker") { if (newType.type !== "marker") {
throw new Error(`Cannot use ${newType.type} type for marker.`); throw new Error(`Cannot use ${newType.type} type for marker.`);
} }

Wyświetl plik

@ -125,7 +125,7 @@ export default class DatabaseTypes {
return createdType; return createdType;
} }
async updateType(padId: PadId, typeId: ID, data: Omit<Type<CRU.UPDATE_VALIDATED>, "id">): Promise<Type> { async updateType(padId: PadId, typeId: ID, data: Type<CRU.UPDATE_VALIDATED>): Promise<Type> {
const rename: Record<string, { name?: string, values?: Record<string, string> }> = {}; const rename: Record<string, { name?: string, values?: Record<string, string> }> = {};
for(const field of (data.fields || [])) { for(const field of (data.fields || [])) {
if(field.oldName && field.oldName != field.name) if(field.oldName && field.oldName != field.name)

Wyświetl plik

@ -102,7 +102,7 @@ export default class DatabaseViews {
return newData; return newData;
} }
async updateView(padId: PadId, viewId: ID, data: Omit<View<CRU.UPDATE_VALIDATED>, "id">): Promise<View> { async updateView(padId: PadId, viewId: ID, data: View<CRU.UPDATE_VALIDATED>): Promise<View> {
if (data.idx != null) { if (data.idx != null) {
await this._freeViewIdx(padId, viewId, data.idx); await this._freeViewIdx(padId, viewId, data.idx);
} }

Wyświetl plik

@ -1,5 +1,5 @@
import { bboxValidator, colourValidator, idValidator, padIdValidator, pointValidator, routeModeValidator, strokeValidator, zoomLevelValidator } from "./base.js"; import { bboxValidator, colourValidator, idValidator, padIdValidator, pointValidator, routeModeValidator, strokeValidator, zoomLevelValidator } from "./base.js";
import { CRU, type CRUType, cruValidator, optionalCreate, onlyRead, exceptCreate, optionalUpdate, mapValues, exceptRead } from "./cru"; import { CRU, type CRUType, cruValidator, optionalCreate, onlyRead, optionalUpdate, mapValues, exceptRead } from "./cru";
import * as z from "zod"; import * as z from "zod";
export const extraInfoValidator = z.record(z.array(z.tuple([z.number(), z.number(), z.number()]))); export const extraInfoValidator = z.record(z.array(z.tuple([z.number(), z.number(), z.number()])));
@ -14,7 +14,7 @@ export const trackPointValidator = cruValidator({
export type TrackPoint<Mode extends CRU = CRU.READ> = CRUType<Mode, typeof trackPointValidator>; export type TrackPoint<Mode extends CRU = CRU.READ> = CRUType<Mode, typeof trackPointValidator>;
export const lineValidator = cruValidator({ export const lineValidator = cruValidator({
id: exceptCreate(idValidator), id: onlyRead(idValidator),
routePoints: optionalUpdate(z.array(pointValidator).min(2)), routePoints: optionalUpdate(z.array(pointValidator).min(2)),
typeId: optionalUpdate(idValidator), typeId: optionalUpdate(idValidator),
name: optionalCreate(z.string().trim().max(100), ""), name: optionalCreate(z.string().trim().max(100), ""),

Wyświetl plik

@ -1,9 +1,9 @@
import { colourValidator, idValidator, padIdValidator, pointValidator, shapeValidator, sizeValidator, symbolValidator } from "./base.js"; import { colourValidator, idValidator, padIdValidator, pointValidator, shapeValidator, sizeValidator, symbolValidator } from "./base.js";
import { CRU, type CRUType, cruValidator, exceptCreate, onlyRead, optionalUpdate, mapValues, optionalCreate } from "./cru"; import { CRU, type CRUType, cruValidator, onlyRead, optionalUpdate, mapValues, optionalCreate } from "./cru";
import * as z from "zod"; import * as z from "zod";
export const markerValidator = cruValidator({ export const markerValidator = cruValidator({
id: exceptCreate(idValidator), id: onlyRead(idValidator),
padId: onlyRead(padIdValidator), padId: onlyRead(padIdValidator),
...mapValues(pointValidator.shape, optionalUpdate), ...mapValues(pointValidator.shape, optionalUpdate),
typeId: optionalUpdate(idValidator), typeId: optionalUpdate(idValidator),

Wyświetl plik

@ -1,4 +1,4 @@
import { type Bbox, bboxWithZoomValidator, objectWithIdValidator, type ObjectWithId } from "../base.js"; import { type Bbox, bboxWithZoomValidator, objectWithIdValidator, type ObjectWithId, idValidator } from "../base.js";
import { type PadData, padDataValidator, Writable } from "../padData.js"; import { type PadData, padDataValidator, Writable } from "../padData.js";
import { type Marker, markerValidator } from "../marker.js"; import { type Marker, markerValidator } from "../marker.js";
import { type Line, lineValidator, type TrackPoint } from "../line.js"; import { type Line, lineValidator, type TrackPoint } from "../line.js";
@ -23,11 +23,11 @@ export const requestDataValidatorsV2 = {
revertHistoryEntry: objectWithIdValidator, revertHistoryEntry: objectWithIdValidator,
getMarker: objectWithIdValidator, getMarker: objectWithIdValidator,
addMarker: markerValidator.create, addMarker: markerValidator.create,
editMarker: markerValidator.update, editMarker: markerValidator.update.extend({ id: idValidator }),
deleteMarker: objectWithIdValidator, deleteMarker: objectWithIdValidator,
getLineTemplate: lineTemplateRequestValidator, getLineTemplate: lineTemplateRequestValidator,
addLine: lineValidator.create, addLine: lineValidator.create,
editLine: lineValidator.update, editLine: lineValidator.update.extend({ id: idValidator }),
deleteLine: objectWithIdValidator, deleteLine: objectWithIdValidator,
exportLine: lineExportRequestValidator, exportLine: lineExportRequestValidator,
find: findQueryValidator, find: findQueryValidator,
@ -38,10 +38,10 @@ export const requestDataValidatorsV2 = {
lineToRoute: lineToRouteCreateValidator, lineToRoute: lineToRouteCreateValidator,
exportRoute: routeExportRequestValidator, exportRoute: routeExportRequestValidator,
addType: typeValidator.create, addType: typeValidator.create,
editType: typeValidator.update, editType: typeValidator.update.extend({ id: idValidator }),
deleteType: objectWithIdValidator, deleteType: objectWithIdValidator,
addView: viewValidator.create, addView: viewValidator.create,
editView: viewValidator.update, editView: viewValidator.update.extend({ id: idValidator }),
deleteView: objectWithIdValidator, deleteView: objectWithIdValidator,
geoip: nullOrUndefinedValidator, geoip: nullOrUndefinedValidator,
setPadId: z.string() setPadId: z.string()

Wyświetl plik

@ -1,5 +1,5 @@
import { colourValidator, idValidator, padIdValidator, routeModeValidator, shapeValidator, sizeValidator, strokeValidator, symbolValidator, widthValidator } from "./base.js"; import { colourValidator, idValidator, padIdValidator, routeModeValidator, shapeValidator, sizeValidator, strokeValidator, symbolValidator, widthValidator } from "./base.js";
import { CRU, type CRUType, cruValidator, onlyUpdate, optionalCreate, exceptCreate, exceptUpdate, optionalUpdate, onlyRead } from "./cru"; import { CRU, type CRUType, cruValidator, onlyUpdate, optionalCreate, exceptUpdate, optionalUpdate, onlyRead } from "./cru";
import * as z from "zod"; import * as z from "zod";
export const objectTypeValidator = z.enum(["marker", "line"]); export const objectTypeValidator = z.enum(["marker", "line"]);
@ -90,7 +90,7 @@ export const fieldsValidator = {
}; };
const rawTypeValidator = cruValidator({ const rawTypeValidator = cruValidator({
id: exceptCreate(idValidator), id: onlyRead(idValidator),
type: exceptUpdate(objectTypeValidator), type: exceptUpdate(objectTypeValidator),
padId: onlyRead(padIdValidator), padId: onlyRead(padIdValidator),

Wyświetl plik

@ -1,9 +1,9 @@
import { bboxValidator, idValidator, layerValidator, padIdValidator } from "./base.js"; import { bboxValidator, idValidator, layerValidator, padIdValidator } from "./base.js";
import { CRU, type CRUType, cruValidator, exceptCreate, onlyRead, optionalUpdate, mapValues, optionalCreate } from "./cru.js"; import { CRU, type CRUType, cruValidator, onlyRead, optionalUpdate, mapValues, optionalCreate } from "./cru.js";
import * as z from "zod"; import * as z from "zod";
export const viewValidator = cruValidator({ export const viewValidator = cruValidator({
id: exceptCreate(idValidator), id: onlyRead(idValidator),
padId: onlyRead(padIdValidator), padId: onlyRead(padIdValidator),
name: optionalUpdate(z.string().trim().min(1).max(100)), name: optionalUpdate(z.string().trim().min(1).max(100)),

Wyświetl plik

@ -46,8 +46,8 @@ export function normalizeFieldValue(field: Field, value: string | undefined, ign
} }
} }
export function applyMarkerStyles(marker: Marker<CRU.READ | CRU.CREATE_VALIDATED>, type: Type): Omit<Marker<CRU.UPDATE_VALIDATED>, "id"> { export function applyMarkerStyles(marker: Marker<CRU.READ | CRU.CREATE_VALIDATED>, type: Type): Marker<CRU.UPDATE_VALIDATED> {
const update: Omit<Marker<CRU.UPDATE_VALIDATED>, "id"> = {}; const update: Marker<CRU.UPDATE_VALIDATED> = {};
if(type.colourFixed && marker.colour != type.defaultColour) if(type.colourFixed && marker.colour != type.defaultColour)
update.colour = type.defaultColour; update.colour = type.defaultColour;
@ -91,16 +91,16 @@ export function resolveCreateMarker(marker: Marker<CRU.CREATE>, type: Type): Mar
return result; return result;
} }
export function resolveUpdateMarker(marker: Marker, update: Omit<Marker<CRU.UPDATE>, "id">, newType: Type): Omit<Marker<CRU.UPDATE_VALIDATED>, "id"> { export function resolveUpdateMarker(marker: Marker, update: Marker<CRU.UPDATE>, newType: Type): Marker<CRU.UPDATE_VALIDATED> {
const resolvedUpdate = markerValidator.update.omit({ id: true }).parse(update); const resolvedUpdate = markerValidator.update.parse(update);
return { return {
...resolvedUpdate, ...resolvedUpdate,
...applyMarkerStyles({ ...marker, ...resolvedUpdate }, newType) ...applyMarkerStyles({ ...marker, ...resolvedUpdate }, newType)
}; };
} }
export function applyLineStyles(line: Line<CRU.READ | CRU.CREATE_VALIDATED>, type: Type): Omit<Line<CRU.UPDATE_VALIDATED>, "id"> { export function applyLineStyles(line: Line<CRU.READ | CRU.CREATE_VALIDATED>, type: Type): Line<CRU.UPDATE_VALIDATED> {
const update: Omit<Line<CRU.UPDATE_VALIDATED>, "id"> = {}; const update: Line<CRU.UPDATE_VALIDATED> = {};
if(type.colourFixed && line.colour != type.defaultColour) { if(type.colourFixed && line.colour != type.defaultColour) {
update.colour = type.defaultColour; update.colour = type.defaultColour;
@ -149,8 +149,8 @@ export function resolveCreateLine(line: Line<CRU.CREATE>, type: Type): Line<CRU.
return result; return result;
} }
export function resolveUpdateLine(line: Line, update: Omit<Line<CRU.UPDATE>, "id">, newType: Type): Omit<Line<CRU.UPDATE_VALIDATED>, "id"> { export function resolveUpdateLine(line: Line, update: Line<CRU.UPDATE>, newType: Type): Line<CRU.UPDATE_VALIDATED> {
const resolvedUpdate = lineValidator.update.omit({ id: true }).parse(update); const resolvedUpdate = lineValidator.update.parse(update);
return { return {
...resolvedUpdate, ...resolvedUpdate,
...applyLineStyles({ ...line, ...resolvedUpdate }, newType) ...applyLineStyles({ ...line, ...resolvedUpdate }, newType)