@cocoar/vue-ui 3.1.0 → 3.2.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/checkbox/CoarCheckbox.vue.d.ts +1 -1
- package/dist/components/checkbox/CoarCheckbox.vue.d.ts.map +1 -1
- package/dist/components/code-block/CoarCodeBlock.vue.d.ts +1 -1
- package/dist/components/code-block/CoarCodeBlock.vue.d.ts.map +1 -1
- package/dist/components/data-list/CoarDataList.vue.d.ts +158 -0
- package/dist/components/data-list/CoarDataList.vue.d.ts.map +1 -0
- package/dist/components/data-list/CoarDataListToolbar.vue.d.ts +30 -0
- package/dist/components/data-list/CoarDataListToolbar.vue.d.ts.map +1 -0
- package/dist/components/data-list/data-list-builder.d.ts +288 -0
- package/dist/components/data-list/data-list-builder.d.ts.map +1 -0
- package/dist/components/data-list/index.d.ts +12 -0
- package/dist/components/data-list/index.d.ts.map +1 -0
- package/dist/components/data-list/internal/compare.d.ts +14 -0
- package/dist/components/data-list/internal/compare.d.ts.map +1 -0
- package/dist/components/data-list/internal/reorder-core.d.ts +46 -0
- package/dist/components/data-list/internal/reorder-core.d.ts.map +1 -0
- package/dist/components/data-list/internal/search.d.ts +16 -0
- package/dist/components/data-list/internal/search.d.ts.map +1 -0
- package/dist/components/data-list/internal/useDataListLines.d.ts +92 -0
- package/dist/components/data-list/internal/useDataListLines.d.ts.map +1 -0
- package/dist/components/data-list/internal/useDataListReorder.d.ts +68 -0
- package/dist/components/data-list/internal/useDataListReorder.d.ts.map +1 -0
- package/dist/components/data-list/internal/useSearchHighlight.d.ts +19 -0
- package/dist/components/data-list/internal/useSearchHighlight.d.ts.map +1 -0
- package/dist/components/data-list/types.d.ts +185 -0
- package/dist/components/data-list/types.d.ts.map +1 -0
- package/dist/components/data-list/useDataListModel.d.ts +79 -0
- package/dist/components/data-list/useDataListModel.d.ts.map +1 -0
- package/dist/components/date-time/_shared/maskito-config.d.ts +7 -2
- package/dist/components/date-time/_shared/maskito-config.d.ts.map +1 -1
- package/dist/components/date-time/_shared/time-helpers.d.ts +11 -4
- package/dist/components/date-time/_shared/time-helpers.d.ts.map +1 -1
- package/dist/components/date-time/_shared/use-date-picker-base.d.ts +1 -1
- package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts +1 -1
- package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts.map +1 -1
- package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts +1 -1
- package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts.map +1 -1
- package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts +1 -1
- package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts.map +1 -1
- package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts +3 -0
- package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts.map +1 -1
- package/dist/components/form-field/CoarFormField.vue.d.ts +1 -1
- package/dist/components/form-field/CoarFormField.vue.d.ts.map +1 -1
- package/dist/components/icon/CoarIcon.vue.d.ts +2 -2
- package/dist/components/icon/CoarIcon.vue.d.ts.map +1 -1
- package/dist/components/listbox/CoarListbox.vue.d.ts +7 -0
- package/dist/components/listbox/CoarListbox.vue.d.ts.map +1 -1
- package/dist/components/number-input/CoarNumberInput.vue.d.ts +1 -1
- package/dist/components/number-input/CoarNumberInput.vue.d.ts.map +1 -1
- package/dist/components/otp-input/CoarOtpInput.vue.d.ts +1 -1
- package/dist/components/otp-input/CoarOtpInput.vue.d.ts.map +1 -1
- package/dist/components/password-input/CoarPasswordInput.vue.d.ts +1 -1
- package/dist/components/password-input/CoarPasswordInput.vue.d.ts.map +1 -1
- package/dist/components/switch/CoarSwitch.vue.d.ts +1 -1
- package/dist/components/switch/CoarSwitch.vue.d.ts.map +1 -1
- package/dist/components/text-input/CoarTextInput.vue.d.ts +1 -1
- package/dist/components/text-input/CoarTextInput.vue.d.ts.map +1 -1
- package/dist/composables/dragRegistry.d.ts +12 -0
- package/dist/composables/dragRegistry.d.ts.map +1 -1
- package/dist/composables/index.d.ts +2 -2
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useDragDrop.d.ts +62 -6
- package/dist/composables/useDragDrop.d.ts.map +1 -1
- package/dist/composables/useVirtualList.d.ts +21 -1
- package/dist/composables/useVirtualList.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4508 -2039
- package/package.json +5 -3
- package/skills/cocoar-vue-ui/SKILL.md +244 -0
- package/skills/cocoar-vue-ui/references/components/avatar.md +178 -0
- package/skills/cocoar-vue-ui/references/components/badge.md +290 -0
- package/skills/cocoar-vue-ui/references/components/breadcrumb.md +238 -0
- package/skills/cocoar-vue-ui/references/components/button.md +252 -0
- package/skills/cocoar-vue-ui/references/components/calendar/agenda-view.md +353 -0
- package/skills/cocoar-vue-ui/references/components/calendar/coar-calendar.md +1645 -0
- package/skills/cocoar-vue-ui/references/components/calendar/day-view.md +361 -0
- package/skills/cocoar-vue-ui/references/components/calendar/index.md +312 -0
- package/skills/cocoar-vue-ui/references/components/calendar/month-view.md +475 -0
- package/skills/cocoar-vue-ui/references/components/calendar/performance.md +186 -0
- package/skills/cocoar-vue-ui/references/components/calendar/timeline-view.md +301 -0
- package/skills/cocoar-vue-ui/references/components/calendar/week-view.md +382 -0
- package/skills/cocoar-vue-ui/references/components/calendar/work-week-view.md +145 -0
- package/skills/cocoar-vue-ui/references/components/calendar/year-view.md +63 -0
- package/skills/cocoar-vue-ui/references/components/card.md +151 -0
- package/skills/cocoar-vue-ui/references/components/checkbox-group.md +103 -0
- package/skills/cocoar-vue-ui/references/components/checkbox.md +279 -0
- package/skills/cocoar-vue-ui/references/components/code-block.md +241 -0
- package/skills/cocoar-vue-ui/references/components/context-menu.md +355 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/checkbox.md +213 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/date-columns.md +272 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/editing.md +236 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/multi-select.md +193 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/number.md +147 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/select.md +202 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/text.md +127 -0
- package/skills/cocoar-vue-ui/references/components/data-grid.md +1225 -0
- package/skills/cocoar-vue-ui/references/components/data-list.md +2121 -0
- package/skills/cocoar-vue-ui/references/components/date-or-time-picker.md +200 -0
- package/skills/cocoar-vue-ui/references/components/date-picker.md +203 -0
- package/skills/cocoar-vue-ui/references/components/date-time-picker.md +154 -0
- package/skills/cocoar-vue-ui/references/components/date-views.md +212 -0
- package/skills/cocoar-vue-ui/references/components/dialog.md +148 -0
- package/skills/cocoar-vue-ui/references/components/divider.md +157 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/annotations.md +400 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/coar-document-viewer.md +327 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/index.md +234 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/toolbar.md +221 -0
- package/skills/cocoar-vue-ui/references/components/drag-drop.md +267 -0
- package/skills/cocoar-vue-ui/references/components/dual-listbox.md +449 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/asset-store.md +327 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/in-memory-store.md +176 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/index.md +311 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/use-file-explorer.md +300 -0
- package/skills/cocoar-vue-ui/references/components/form-field.md +1025 -0
- package/skills/cocoar-vue-ui/references/components/fragment-parser.md +298 -0
- package/skills/cocoar-vue-ui/references/components/link.md +235 -0
- package/skills/cocoar-vue-ui/references/components/listbox.md +575 -0
- package/skills/cocoar-vue-ui/references/components/map/editor.md +448 -0
- package/skills/cocoar-vue-ui/references/components/map/index.md +351 -0
- package/skills/cocoar-vue-ui/references/components/markdown-diagrams.md +210 -0
- package/skills/cocoar-vue-ui/references/components/markdown-editor.md +1478 -0
- package/skills/cocoar-vue-ui/references/components/markdown-embeds.md +387 -0
- package/skills/cocoar-vue-ui/references/components/markdown-form.md +447 -0
- package/skills/cocoar-vue-ui/references/components/markdown.md +276 -0
- package/skills/cocoar-vue-ui/references/components/menu.md +380 -0
- package/skills/cocoar-vue-ui/references/components/mermaid.md +172 -0
- package/skills/cocoar-vue-ui/references/components/navbar.md +147 -0
- package/skills/cocoar-vue-ui/references/components/note.md +96 -0
- package/skills/cocoar-vue-ui/references/components/notice.md +131 -0
- package/skills/cocoar-vue-ui/references/components/number-input.md +215 -0
- package/skills/cocoar-vue-ui/references/components/otp-input.md +324 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/authoring-contract.md +271 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-builder.md +476 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-renderer.md +677 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/custom-elements.md +441 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/idp-integration.md +122 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/index.md +573 -0
- package/skills/cocoar-vue-ui/references/components/pagination.md +150 -0
- package/skills/cocoar-vue-ui/references/components/panel-layout.md +372 -0
- package/skills/cocoar-vue-ui/references/components/password-input.md +175 -0
- package/skills/cocoar-vue-ui/references/components/popconfirm.md +186 -0
- package/skills/cocoar-vue-ui/references/components/popover.md +173 -0
- package/skills/cocoar-vue-ui/references/components/progress-bar.md +178 -0
- package/skills/cocoar-vue-ui/references/components/radio-group.md +225 -0
- package/skills/cocoar-vue-ui/references/components/script-editor.md +1271 -0
- package/skills/cocoar-vue-ui/references/components/segmented-control.md +238 -0
- package/skills/cocoar-vue-ui/references/components/select.md +463 -0
- package/skills/cocoar-vue-ui/references/components/sidebar.md +421 -0
- package/skills/cocoar-vue-ui/references/components/spinner.md +132 -0
- package/skills/cocoar-vue-ui/references/components/switch.md +195 -0
- package/skills/cocoar-vue-ui/references/components/table.md +170 -0
- package/skills/cocoar-vue-ui/references/components/tabs.md +382 -0
- package/skills/cocoar-vue-ui/references/components/tag.md +178 -0
- package/skills/cocoar-vue-ui/references/components/text-input.md +256 -0
- package/skills/cocoar-vue-ui/references/components/toast.md +160 -0
- package/skills/cocoar-vue-ui/references/components/tooltip.md +121 -0
- package/skills/cocoar-vue-ui/references/components/transitions.md +193 -0
- package/skills/cocoar-vue-ui/references/components/tree.md +2388 -0
- package/skills/cocoar-vue-ui/references/components/virtual-list.md +212 -0
- package/skills/cocoar-vue-ui/references/components/wizard.md +251 -0
- package/skills/cocoar-vue-ui/references/components/zoned-date-time-picker.md +177 -0
- package/skills/cocoar-vue-ui/references/foundations/colors.md +708 -0
- package/skills/cocoar-vue-ui/references/foundations/design-principles.md +115 -0
- package/skills/cocoar-vue-ui/references/foundations/icons.md +381 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/formatting.md +530 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/setup.md +86 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/timezones.md +182 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/translations.md +305 -0
- package/skills/cocoar-vue-ui/references/foundations/motion.md +549 -0
- package/skills/cocoar-vue-ui/references/foundations/spacing.md +330 -0
- package/skills/cocoar-vue-ui/references/foundations/theming.md +140 -0
- package/skills/cocoar-vue-ui/references/foundations/typography.md +206 -0
- package/skills/cocoar-vue-ui/references/guide/error-handling.md +162 -0
- package/skills/cocoar-vue-ui/references/guide/getting-started.md +119 -0
- package/skills/cocoar-vue-ui/references/guide/migration-page-builder-3.md +207 -0
- package/skills/cocoar-vue-ui/references/guide/migration.md +140 -0
- package/skills/cocoar-vue-ui/references/guide/theming.md +98 -0
|
@@ -0,0 +1,448 @@
|
|
|
1
|
+
<!-- Generated from apps/docs/components/map/editor.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
|
|
2
|
+
|
|
3
|
+
# Map Editor (Preview)
|
|
4
|
+
|
|
5
|
+
A visual editor for [`<CoarMap>`](./index.md) data. `<CoarMapEditor>` is the
|
|
6
|
+
**write counterpart** of `<CoarMap>`: same `MapData` + `MapConfig`, but you can
|
|
7
|
+
**place, move, edit, reorder and delete** points directly on the map, with every
|
|
8
|
+
change flowing out through `v-model:data`. Like `<CoarMap>` it is **standalone** —
|
|
9
|
+
no dependency on, or knowledge of, markdown or any embedding layer.
|
|
10
|
+
|
|
11
|
+
> **Info: Same package as the map**
|
|
12
|
+
>
|
|
13
|
+
```bash
|
|
14
|
+
pnpm add @cocoar/vue-map
|
|
15
|
+
```
|
|
16
|
+
> `CoarMapEditor` ships alongside `CoarMap` and is tree-shakeable — pages that only
|
|
17
|
+
> render `<CoarMap>` don't pull in the editing weight. Leaflet is imported
|
|
18
|
+
> **lazily** at runtime. Import the component styles once with
|
|
19
|
+
> `import '@cocoar/vue-map/styles'` (as for the viewer). The editor's built-in form
|
|
20
|
+
> uses the Cocoar UI controls, so `@cocoar/vue-ui` is an **optional peer** — install
|
|
21
|
+
> it when you use the editor; viewer-only `<CoarMap>` consumers don't need it.
|
|
22
|
+
|
|
23
|
+
**Demo — `map/demos/MapEditorBasic.vue`**
|
|
24
|
+
|
|
25
|
+
```vue
|
|
26
|
+
<script setup lang="ts">
|
|
27
|
+
/**
|
|
28
|
+
* `<CoarMapEditor>` — the write counterpart of `<CoarMap>`. Click the map to
|
|
29
|
+
* add points, drag markers to move them, click a marker to edit its properties,
|
|
30
|
+
* click the route line to insert a waypoint. Everything flows through
|
|
31
|
+
* `v-model:data`; no markdown anywhere.
|
|
32
|
+
*/
|
|
33
|
+
import { ref } from 'vue';
|
|
34
|
+
import { CoarMapEditor, type MapConfig, type MapData, type MapType } from '@cocoar/vue-map';
|
|
35
|
+
|
|
36
|
+
const config: MapConfig = {
|
|
37
|
+
defaultBasemap: 'voyager',
|
|
38
|
+
basemaps: [
|
|
39
|
+
{
|
|
40
|
+
id: 'voyager',
|
|
41
|
+
url: 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',
|
|
42
|
+
subdomains: 'abcd',
|
|
43
|
+
attribution: '© OpenStreetMap, © CARTO',
|
|
44
|
+
maxZoom: 20,
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
categories: [
|
|
48
|
+
{ id: 'sight', label: 'Sights', emoji: '🏛️', color: '#a855f7' },
|
|
49
|
+
{ id: 'food', label: 'Food', emoji: '🍽️', color: '#f97316' },
|
|
50
|
+
{ id: 'nature', label: 'Nature', emoji: '🌲', color: '#16a34a' },
|
|
51
|
+
],
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const data = ref<MapData>({
|
|
55
|
+
type: 'route',
|
|
56
|
+
caption: 'Click to add · drag to move · click a marker to edit · click the line to insert · Esc / click-away to close.',
|
|
57
|
+
points: [
|
|
58
|
+
{ lat: 51.4995, lng: -0.1248, kind: 'stop', label: 'Westminster', category: 'sight' },
|
|
59
|
+
{ lat: 51.5076, lng: -0.118, kind: 'shape' },
|
|
60
|
+
{ lat: 51.5129, lng: -0.1243, kind: 'stop', label: 'Dishoom', category: 'food', icon: '🥘' },
|
|
61
|
+
{ lat: 51.5194, lng: -0.127, kind: 'stop', label: 'British Museum', category: 'sight' },
|
|
62
|
+
],
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
const selected = ref<number | null>(null);
|
|
66
|
+
const editor = ref<{ captureViewport: () => void } | null>(null);
|
|
67
|
+
|
|
68
|
+
function setType(t: MapType): void {
|
|
69
|
+
const points = t === 'single' ? data.value.points.slice(0, 1) : data.value.points;
|
|
70
|
+
data.value = { ...data.value, type: t, points };
|
|
71
|
+
}
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<template>
|
|
75
|
+
<ClientOnly>
|
|
76
|
+
<div class="mape-d__controls">
|
|
77
|
+
<span class="mape-d__label">Type:</span>
|
|
78
|
+
<button
|
|
79
|
+
v-for="t in (['single', 'multi', 'route'] as MapType[])"
|
|
80
|
+
:key="t"
|
|
81
|
+
:class="['mape-d__btn', { 'is-active': data.type === t }]"
|
|
82
|
+
@click="setType(t)"
|
|
83
|
+
>{{ t }}</button>
|
|
84
|
+
<button class="mape-d__btn" @click="editor?.captureViewport()">Save view</button>
|
|
85
|
+
<span class="mape-d__hint">{{ data.points.length }} points</span>
|
|
86
|
+
</div>
|
|
87
|
+
|
|
88
|
+
<div class="mape-d__map">
|
|
89
|
+
<CoarMapEditor ref="editor" v-model:data="data" v-model:selected="selected" :config="config" />
|
|
90
|
+
</div>
|
|
91
|
+
|
|
92
|
+
<details class="mape-d__data">
|
|
93
|
+
<summary>Live data (v-model:data)</summary>
|
|
94
|
+
<pre>{{ JSON.stringify(data, null, 2) }}</pre>
|
|
95
|
+
</details>
|
|
96
|
+
</ClientOnly>
|
|
97
|
+
</template>
|
|
98
|
+
|
|
99
|
+
<style scoped>
|
|
100
|
+
.mape-d__controls {
|
|
101
|
+
display: flex;
|
|
102
|
+
align-items: center;
|
|
103
|
+
flex-wrap: wrap;
|
|
104
|
+
gap: 6px;
|
|
105
|
+
margin-bottom: 10px;
|
|
106
|
+
}
|
|
107
|
+
.mape-d__label {
|
|
108
|
+
font-size: 13px;
|
|
109
|
+
font-weight: 600;
|
|
110
|
+
}
|
|
111
|
+
.mape-d__btn {
|
|
112
|
+
padding: 4px 12px;
|
|
113
|
+
border-radius: 5px;
|
|
114
|
+
border: 1px solid var(--vp-c-divider);
|
|
115
|
+
background: transparent;
|
|
116
|
+
color: inherit;
|
|
117
|
+
cursor: pointer;
|
|
118
|
+
font-size: 13px;
|
|
119
|
+
}
|
|
120
|
+
.mape-d__btn.is-active {
|
|
121
|
+
background: var(--vp-c-brand-1);
|
|
122
|
+
color: #fff;
|
|
123
|
+
border-color: transparent;
|
|
124
|
+
}
|
|
125
|
+
.mape-d__hint {
|
|
126
|
+
margin-left: 8px;
|
|
127
|
+
font-size: 13px;
|
|
128
|
+
color: var(--vp-c-text-2);
|
|
129
|
+
}
|
|
130
|
+
.mape-d__map {
|
|
131
|
+
--coar-map-height: 420px;
|
|
132
|
+
}
|
|
133
|
+
.mape-d__data {
|
|
134
|
+
margin-top: 10px;
|
|
135
|
+
}
|
|
136
|
+
.mape-d__data pre {
|
|
137
|
+
font-size: 12px;
|
|
138
|
+
max-height: 220px;
|
|
139
|
+
overflow: auto;
|
|
140
|
+
}
|
|
141
|
+
</style>
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Quick start
|
|
145
|
+
|
|
146
|
+
```vue
|
|
147
|
+
<template>
|
|
148
|
+
<CoarMapEditor v-model:data="data" :config="config" />
|
|
149
|
+
</template>
|
|
150
|
+
|
|
151
|
+
<script setup lang="ts">
|
|
152
|
+
import { ref } from 'vue';
|
|
153
|
+
import { CoarMapEditor, type MapData, type MapConfig } from '@cocoar/vue-map';
|
|
154
|
+
|
|
155
|
+
const config: MapConfig = {
|
|
156
|
+
defaultBasemap: 'osm',
|
|
157
|
+
basemaps: [
|
|
158
|
+
{ id: 'osm', url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', attribution: '© OpenStreetMap' },
|
|
159
|
+
],
|
|
160
|
+
categories: [{ id: 'sight', label: 'Sights', emoji: '🏛️', color: '#a855f7' }],
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
const data = ref<MapData>({
|
|
164
|
+
type: 'route',
|
|
165
|
+
points: [
|
|
166
|
+
{ lat: 51.4995, lng: -0.1248, kind: 'stop', label: 'Westminster', category: 'sight' },
|
|
167
|
+
{ lat: 51.5194, lng: -0.127, kind: 'stop', label: 'British Museum', category: 'sight' },
|
|
168
|
+
],
|
|
169
|
+
});
|
|
170
|
+
</script>
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
`data` is **controlled**: the editor never mutates your object, it emits a fresh
|
|
174
|
+
`MapData` on every edit. The data contract (`MapData`, `MapPoint`, `MapConfig`)
|
|
175
|
+
is identical to [`<CoarMap>`](./index.md#data) — read that page for the
|
|
176
|
+
field-level reference.
|
|
177
|
+
|
|
178
|
+
## Editing
|
|
179
|
+
|
|
180
|
+
| Gesture | Result |
|
|
181
|
+
|---|---|
|
|
182
|
+
| **Click an empty spot** | Adds a point — type-aware: `route` / `multi` **append** a stop, `single` **moves** the one point. The new point is **not** auto-opened, so dropping several in a row stays fluid. |
|
|
183
|
+
| **Drag a marker** | Moves that point; the route polyline follows **live**, `update:data` fires on drop. |
|
|
184
|
+
| **Click a marker** | Selects it and opens the **property popup** (see below). |
|
|
185
|
+
| **Click the route line** | Inserts a waypoint **exactly there**, snapped to the nearest segment. |
|
|
186
|
+
| **Click away · `Esc` · popup ×** | Closes the popup (deselects). Clicking empty space **while a point is selected dismisses it** — it does *not* add a point. |
|
|
187
|
+
|
|
188
|
+
Every point gets a draggable handle — including the unnamed `shape` vertices that
|
|
189
|
+
`<CoarMap>` draws as pure line geometry — so route shapes are editable too. Set
|
|
190
|
+
`readonly` to freeze all of this (the editor then behaves like `<CoarMap>`).
|
|
191
|
+
|
|
192
|
+
## Property popup
|
|
193
|
+
|
|
194
|
+
Selecting a point opens a small popup anchored over its marker. The built-in form
|
|
195
|
+
is rendered with the Cocoar UI controls (`CoarTextInput`, `CoarSelect`,
|
|
196
|
+
`CoarButton`) so it matches the design system; it edits `label`, `note`,
|
|
197
|
+
`category` and `icon` (for stops), toggles a point between **stop** and
|
|
198
|
+
**vertex**, **reorders** route waypoints (↑ / ↓) and **deletes** the point. It
|
|
199
|
+
flips below the marker automatically when there isn't room above. Close it with
|
|
200
|
+
the **×**, **`Esc`**, or by clicking empty map space (which dismisses rather than
|
|
201
|
+
adding a point).
|
|
202
|
+
|
|
203
|
+
Override it entirely with the **`#point-form`** slot to drop in your own
|
|
204
|
+
controls (e.g. design-system inputs):
|
|
205
|
+
|
|
206
|
+
```vue
|
|
207
|
+
<CoarMapEditor v-model:data="data" :config="config">
|
|
208
|
+
<template #point-form="{ point, index, update, remove, moveUp, moveDown }">
|
|
209
|
+
<CoarTextInput :model-value="point.label" @update:model-value="(v) => update({ label: v })" />
|
|
210
|
+
<button @click="moveUp">Up</button>
|
|
211
|
+
<button @click="moveDown">Down</button>
|
|
212
|
+
<button @click="remove">Delete</button>
|
|
213
|
+
</template>
|
|
214
|
+
</CoarMapEditor>
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
| Slot prop | Type | Description |
|
|
218
|
+
|---|---|---|
|
|
219
|
+
| `point` | `MapPoint` | The selected point. |
|
|
220
|
+
| `index` | `number` | Its index in `data.points`. |
|
|
221
|
+
| `update` | `(patch: Partial<MapPoint>) => void` | Apply a partial change to the point. |
|
|
222
|
+
| `remove` | `() => void` | Delete the point. |
|
|
223
|
+
| `moveUp` / `moveDown` | `() => void` | Reorder the point earlier / later. |
|
|
224
|
+
|
|
225
|
+
## Imperative bridge
|
|
226
|
+
|
|
227
|
+
A consumer-built toolbar or point list can drive edits through exposed methods —
|
|
228
|
+
the same hooks as `<CoarMap>` plus the editing operations:
|
|
229
|
+
|
|
230
|
+
| API | Description |
|
|
231
|
+
|---|---|
|
|
232
|
+
| `v-model:selected` | Selected point index (rings the marker, opens the popup). |
|
|
233
|
+
| `@point-click` | `{ point, index }` when a marker is clicked. |
|
|
234
|
+
| `addPoint(lat, lng, init?)` | Add a point (type-aware), like a map click. |
|
|
235
|
+
| `updatePoint(index, patch)` | Patch a point's fields. |
|
|
236
|
+
| `removePoint(index)` | Delete a point. Selection follows (stays on the same point, or clears). |
|
|
237
|
+
| `reorder(from, to)` | Move a point within the order. Selection tracks the same point. |
|
|
238
|
+
| `captureViewport()` | Save the current center + zoom into `data.viewport`. |
|
|
239
|
+
| `focusPoint(index)` / `highlightPoint(index \| null)` | Pan-to-and-select / transient hover emphasis. |
|
|
240
|
+
|
|
241
|
+
```vue
|
|
242
|
+
<script setup lang="ts">
|
|
243
|
+
import { ref } from 'vue';
|
|
244
|
+
import { CoarMapEditor } from '@cocoar/vue-map';
|
|
245
|
+
|
|
246
|
+
const editor = ref<InstanceType<typeof CoarMapEditor>>();
|
|
247
|
+
</script>
|
|
248
|
+
|
|
249
|
+
<template>
|
|
250
|
+
<button @click="editor?.captureViewport()">Save current view</button>
|
|
251
|
+
<CoarMapEditor ref="editor" v-model:data="data" :config="config" />
|
|
252
|
+
</template>
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
## Point list (`CoarMapPointList`)
|
|
256
|
+
|
|
257
|
+
A ready-made, drop-anywhere point list ships in the package so you don't rebuild
|
|
258
|
+
it per app. It's **controlled** (`v-model:data` / `v-model:selected`) and stays
|
|
259
|
+
decoupled from the map — bind both to the same refs as the editor, and wire its
|
|
260
|
+
`focus` / `highlight` events to the map's exposed methods for fly-to + hover.
|
|
261
|
+
|
|
262
|
+
**Demo — `map/demos/MapEditorList.vue`**
|
|
263
|
+
|
|
264
|
+
```vue
|
|
265
|
+
<script setup lang="ts">
|
|
266
|
+
/**
|
|
267
|
+
* `<CoarMapEditor>` + the ready-made `<CoarMapPointList>` beside it. Both bind
|
|
268
|
+
* to the same `data` + `selected` (no coupling — just shared v-model). The list
|
|
269
|
+
* is `reorderable` (drag the handle) + `removable`; its `focus` / `highlight`
|
|
270
|
+
* events are wired to the editor's exposed methods for fly-to + hover emphasis.
|
|
271
|
+
*/
|
|
272
|
+
import { ref } from 'vue';
|
|
273
|
+
import {
|
|
274
|
+
CoarMapEditor,
|
|
275
|
+
CoarMapPointList,
|
|
276
|
+
type MapConfig,
|
|
277
|
+
type MapData,
|
|
278
|
+
} from '@cocoar/vue-map';
|
|
279
|
+
|
|
280
|
+
const config: MapConfig = {
|
|
281
|
+
defaultBasemap: 'voyager',
|
|
282
|
+
basemaps: [
|
|
283
|
+
{
|
|
284
|
+
id: 'voyager',
|
|
285
|
+
url: 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',
|
|
286
|
+
subdomains: 'abcd',
|
|
287
|
+
attribution: '© OpenStreetMap, © CARTO',
|
|
288
|
+
maxZoom: 20,
|
|
289
|
+
},
|
|
290
|
+
],
|
|
291
|
+
categories: [
|
|
292
|
+
{ id: 'sight', label: 'Sights', emoji: '🏛️', color: '#a855f7' },
|
|
293
|
+
{ id: 'food', label: 'Food', emoji: '🍽️', color: '#f97316' },
|
|
294
|
+
{ id: 'nature', label: 'Nature', emoji: '🌲', color: '#16a34a' },
|
|
295
|
+
],
|
|
296
|
+
};
|
|
297
|
+
|
|
298
|
+
const data = ref<MapData>({
|
|
299
|
+
type: 'route',
|
|
300
|
+
points: [
|
|
301
|
+
{ lat: 51.4995, lng: -0.1248, kind: 'stop', label: 'Westminster', category: 'sight' },
|
|
302
|
+
{ lat: 51.5076, lng: -0.118, kind: 'shape', label: 'Thames bend' },
|
|
303
|
+
{ lat: 51.5129, lng: -0.1243, kind: 'stop', label: 'Dishoom', category: 'food', icon: '🥘' },
|
|
304
|
+
{ lat: 51.5194, lng: -0.127, kind: 'stop', label: 'British Museum', category: 'sight' },
|
|
305
|
+
{ lat: 51.5074, lng: -0.1657, kind: 'stop', label: 'Hyde Park', category: 'nature' },
|
|
306
|
+
],
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
const selected = ref<number | null>(null);
|
|
310
|
+
const editor = ref<{
|
|
311
|
+
focusPoint: (index: number) => void;
|
|
312
|
+
highlightPoint: (index: number | null) => void;
|
|
313
|
+
} | null>(null);
|
|
314
|
+
</script>
|
|
315
|
+
|
|
316
|
+
<template>
|
|
317
|
+
<ClientOnly>
|
|
318
|
+
<div class="mel">
|
|
319
|
+
<div class="mel__map">
|
|
320
|
+
<CoarMapEditor ref="editor" v-model:data="data" v-model:selected="selected" :config="config" />
|
|
321
|
+
</div>
|
|
322
|
+
|
|
323
|
+
<aside class="mel__list">
|
|
324
|
+
<div class="mel__list-title">Points <span>({{ data.points.length }})</span></div>
|
|
325
|
+
<CoarMapPointList
|
|
326
|
+
v-model:data="data"
|
|
327
|
+
v-model:selected="selected"
|
|
328
|
+
:config="config"
|
|
329
|
+
reorderable
|
|
330
|
+
removable
|
|
331
|
+
@focus="editor?.focusPoint($event)"
|
|
332
|
+
@highlight="editor?.highlightPoint($event)"
|
|
333
|
+
/>
|
|
334
|
+
</aside>
|
|
335
|
+
</div>
|
|
336
|
+
</ClientOnly>
|
|
337
|
+
</template>
|
|
338
|
+
|
|
339
|
+
<style scoped>
|
|
340
|
+
.mel {
|
|
341
|
+
display: flex;
|
|
342
|
+
gap: 12px;
|
|
343
|
+
align-items: stretch;
|
|
344
|
+
}
|
|
345
|
+
.mel__map {
|
|
346
|
+
flex: 1;
|
|
347
|
+
min-width: 0;
|
|
348
|
+
--coar-map-height: 400px;
|
|
349
|
+
}
|
|
350
|
+
.mel__list {
|
|
351
|
+
width: 250px;
|
|
352
|
+
flex-shrink: 0;
|
|
353
|
+
border: 1px solid var(--vp-c-divider);
|
|
354
|
+
border-radius: 10px;
|
|
355
|
+
overflow: hidden;
|
|
356
|
+
}
|
|
357
|
+
.mel__list-title {
|
|
358
|
+
padding: 8px 12px;
|
|
359
|
+
font-size: 11px;
|
|
360
|
+
font-weight: 700;
|
|
361
|
+
text-transform: uppercase;
|
|
362
|
+
letter-spacing: 0.04em;
|
|
363
|
+
color: var(--vp-c-text-2);
|
|
364
|
+
border-bottom: 1px solid var(--vp-c-divider);
|
|
365
|
+
}
|
|
366
|
+
@media (max-width: 720px) {
|
|
367
|
+
.mel { flex-direction: column; }
|
|
368
|
+
.mel__list { width: auto; }
|
|
369
|
+
}
|
|
370
|
+
</style>
|
|
371
|
+
```
|
|
372
|
+
|
|
373
|
+
```vue
|
|
374
|
+
<CoarMapEditor ref="editor" v-model:data="data" v-model:selected="sel" :config="config" />
|
|
375
|
+
|
|
376
|
+
<CoarMapPointList
|
|
377
|
+
v-model:data="data"
|
|
378
|
+
v-model:selected="sel"
|
|
379
|
+
:config="config"
|
|
380
|
+
reorderable
|
|
381
|
+
removable
|
|
382
|
+
@focus="editor.focusPoint($event)"
|
|
383
|
+
@highlight="editor.highlightPoint($event)"
|
|
384
|
+
/>
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
| Prop / event | Description |
|
|
388
|
+
|---|---|
|
|
389
|
+
| `v-model:data` | The map data; reorder + delete emit a fresh `MapData`. |
|
|
390
|
+
| `v-model:selected` | Active row ↔ selected index. |
|
|
391
|
+
| `config` | Resolves stop emojis/labels (falls back to a point's own `icon`). |
|
|
392
|
+
| `reorderable` | Drag-and-drop sorting via a per-row handle (default `false`). |
|
|
393
|
+
| `removable` | Per-row delete button (default `false`). |
|
|
394
|
+
| `@focus(index)` | Row activated — wire to `focusPoint`. |
|
|
395
|
+
| `@highlight(index \| null)` | Row hover — wire to `highlightPoint`. |
|
|
396
|
+
| `#row="{ point, index, selected }"` | Override a row's content. |
|
|
397
|
+
|
|
398
|
+
It lists **every** point — including the unnamed `shape` vertices the viewer
|
|
399
|
+
draws as bare geometry — so the whole route order stays editable. Reordering and
|
|
400
|
+
deleting keep the selection on the **same point**, so the active row never
|
|
401
|
+
drifts. Drag-and-drop is built on Cocoar UI's `useDragDrop`. The list works next
|
|
402
|
+
to a read-only [`<CoarMap>`](./index.md) too (omit `reorderable` /
|
|
403
|
+
`removable` for a pure navigator). Prefer your own layout? It's still the
|
|
404
|
+
consumer's call — the same edits are available as exposed methods (above) and as
|
|
405
|
+
[pure operations](#pure-editing-operations).
|
|
406
|
+
|
|
407
|
+
## Pure editing operations
|
|
408
|
+
|
|
409
|
+
The same immutable, Leaflet-free helpers the editor uses are exported, for
|
|
410
|
+
consumers building edits outside a map (validation, undo stacks, server sync):
|
|
411
|
+
|
|
412
|
+
```ts
|
|
413
|
+
import {
|
|
414
|
+
addPointForType, movePoint, updatePoint, removePoint,
|
|
415
|
+
reorderPoint, insertOnSegment, setViewport, nearestSegment, normalizeLatLng,
|
|
416
|
+
} from '@cocoar/vue-map';
|
|
417
|
+
|
|
418
|
+
const next = addPointForType(data, 51.5, -0.12); // returns a fresh MapData
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Each takes a `MapData` and returns a new one — your input is never mutated.
|
|
422
|
+
|
|
423
|
+
## Props
|
|
424
|
+
|
|
425
|
+
| Prop | Type | Default | Description |
|
|
426
|
+
|---|---|---|---|
|
|
427
|
+
| `data` | `MapData` | _required_ | The map data (`v-model:data`). |
|
|
428
|
+
| `config` | `MapConfig` | _provide_ | Basemaps + categories. Falls back to `COAR_MAP_CONFIG_KEY`. |
|
|
429
|
+
| `selected` | `number \| null` | `null` | Selected point index (`v-model:selected`). |
|
|
430
|
+
| `readonly` | `boolean` | `false` | Freeze editing — behaves like `<CoarMap>`. |
|
|
431
|
+
|
|
432
|
+
## Events
|
|
433
|
+
|
|
434
|
+
| Event | Payload | Description |
|
|
435
|
+
|---|---|---|
|
|
436
|
+
| `update:data` | `MapData` | Emitted on every edit. |
|
|
437
|
+
| `update:selected` | `number \| null` | Selection changed. |
|
|
438
|
+
| `point-click` | `{ point, index }` | A marker was clicked. |
|
|
439
|
+
|
|
440
|
+
## Relationship to `<CoarMap>`
|
|
441
|
+
|
|
442
|
+
Use `<CoarMap>` to **display** a map and `<CoarMapEditor>` to **author** one —
|
|
443
|
+
they share the exact same `MapData`/`MapConfig`, so editor output drops straight
|
|
444
|
+
into the viewer. The editor keeps the same discipline as the viewer: Leaflet is
|
|
445
|
+
lazy-loaded, custom pins avoid Leaflet's broken default-icon asset path, point
|
|
446
|
+
text is treated as untrusted (escaped on render), and the package stays
|
|
447
|
+
**markdown-agnostic** — bridging a map into markdown is the consumer's job, not
|
|
448
|
+
the package's.
|