@cocoar/vue-ui 3.1.0 → 3.2.0-beta.11

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.
Files changed (180) hide show
  1. package/bin/cli.mjs +53 -0
  2. package/dist/components/checkbox/CoarCheckbox.vue.d.ts +1 -1
  3. package/dist/components/checkbox/CoarCheckbox.vue.d.ts.map +1 -1
  4. package/dist/components/code-block/CoarCodeBlock.vue.d.ts +1 -1
  5. package/dist/components/code-block/CoarCodeBlock.vue.d.ts.map +1 -1
  6. package/dist/components/data-list/CoarDataList.vue.d.ts +158 -0
  7. package/dist/components/data-list/CoarDataList.vue.d.ts.map +1 -0
  8. package/dist/components/data-list/CoarDataListToolbar.vue.d.ts +30 -0
  9. package/dist/components/data-list/CoarDataListToolbar.vue.d.ts.map +1 -0
  10. package/dist/components/data-list/data-list-builder.d.ts +288 -0
  11. package/dist/components/data-list/data-list-builder.d.ts.map +1 -0
  12. package/dist/components/data-list/index.d.ts +12 -0
  13. package/dist/components/data-list/index.d.ts.map +1 -0
  14. package/dist/components/data-list/internal/compare.d.ts +14 -0
  15. package/dist/components/data-list/internal/compare.d.ts.map +1 -0
  16. package/dist/components/data-list/internal/reorder-core.d.ts +46 -0
  17. package/dist/components/data-list/internal/reorder-core.d.ts.map +1 -0
  18. package/dist/components/data-list/internal/search.d.ts +16 -0
  19. package/dist/components/data-list/internal/search.d.ts.map +1 -0
  20. package/dist/components/data-list/internal/useDataListLines.d.ts +92 -0
  21. package/dist/components/data-list/internal/useDataListLines.d.ts.map +1 -0
  22. package/dist/components/data-list/internal/useDataListReorder.d.ts +68 -0
  23. package/dist/components/data-list/internal/useDataListReorder.d.ts.map +1 -0
  24. package/dist/components/data-list/internal/useSearchHighlight.d.ts +19 -0
  25. package/dist/components/data-list/internal/useSearchHighlight.d.ts.map +1 -0
  26. package/dist/components/data-list/types.d.ts +185 -0
  27. package/dist/components/data-list/types.d.ts.map +1 -0
  28. package/dist/components/data-list/useDataListModel.d.ts +79 -0
  29. package/dist/components/data-list/useDataListModel.d.ts.map +1 -0
  30. package/dist/components/date-time/_shared/maskito-config.d.ts +7 -2
  31. package/dist/components/date-time/_shared/maskito-config.d.ts.map +1 -1
  32. package/dist/components/date-time/_shared/time-helpers.d.ts +11 -4
  33. package/dist/components/date-time/_shared/time-helpers.d.ts.map +1 -1
  34. package/dist/components/date-time/_shared/use-date-picker-base.d.ts +1 -1
  35. package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts +1 -1
  36. package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts.map +1 -1
  37. package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts +1 -1
  38. package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts.map +1 -1
  39. package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts +1 -1
  40. package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts.map +1 -1
  41. package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts +3 -0
  42. package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts.map +1 -1
  43. package/dist/components/form-field/CoarFormField.vue.d.ts +1 -1
  44. package/dist/components/form-field/CoarFormField.vue.d.ts.map +1 -1
  45. package/dist/components/icon/CoarIcon.vue.d.ts +2 -2
  46. package/dist/components/icon/CoarIcon.vue.d.ts.map +1 -1
  47. package/dist/components/listbox/CoarListbox.vue.d.ts +7 -0
  48. package/dist/components/listbox/CoarListbox.vue.d.ts.map +1 -1
  49. package/dist/components/number-input/CoarNumberInput.vue.d.ts +1 -1
  50. package/dist/components/number-input/CoarNumberInput.vue.d.ts.map +1 -1
  51. package/dist/components/otp-input/CoarOtpInput.vue.d.ts +1 -1
  52. package/dist/components/otp-input/CoarOtpInput.vue.d.ts.map +1 -1
  53. package/dist/components/password-input/CoarPasswordInput.vue.d.ts +1 -1
  54. package/dist/components/password-input/CoarPasswordInput.vue.d.ts.map +1 -1
  55. package/dist/components/switch/CoarSwitch.vue.d.ts +1 -1
  56. package/dist/components/switch/CoarSwitch.vue.d.ts.map +1 -1
  57. package/dist/components/text-input/CoarTextInput.vue.d.ts +1 -1
  58. package/dist/components/text-input/CoarTextInput.vue.d.ts.map +1 -1
  59. package/dist/composables/dragRegistry.d.ts +12 -0
  60. package/dist/composables/dragRegistry.d.ts.map +1 -1
  61. package/dist/composables/index.d.ts +2 -2
  62. package/dist/composables/index.d.ts.map +1 -1
  63. package/dist/composables/useDragDrop.d.ts +62 -6
  64. package/dist/composables/useDragDrop.d.ts.map +1 -1
  65. package/dist/composables/useVirtualList.d.ts +21 -1
  66. package/dist/composables/useVirtualList.d.ts.map +1 -1
  67. package/dist/index.css +1 -1
  68. package/dist/index.d.ts +4 -2
  69. package/dist/index.d.ts.map +1 -1
  70. package/dist/index.js +4508 -2039
  71. package/package.json +9 -3
  72. package/skills/cocoar-vue-ui/SKILL.md +244 -0
  73. package/skills/cocoar-vue-ui/references/components/avatar.md +178 -0
  74. package/skills/cocoar-vue-ui/references/components/badge.md +290 -0
  75. package/skills/cocoar-vue-ui/references/components/breadcrumb.md +238 -0
  76. package/skills/cocoar-vue-ui/references/components/button.md +252 -0
  77. package/skills/cocoar-vue-ui/references/components/calendar/agenda-view.md +372 -0
  78. package/skills/cocoar-vue-ui/references/components/calendar/coar-calendar.md +1647 -0
  79. package/skills/cocoar-vue-ui/references/components/calendar/day-view.md +361 -0
  80. package/skills/cocoar-vue-ui/references/components/calendar/index.md +312 -0
  81. package/skills/cocoar-vue-ui/references/components/calendar/month-view.md +486 -0
  82. package/skills/cocoar-vue-ui/references/components/calendar/performance.md +186 -0
  83. package/skills/cocoar-vue-ui/references/components/calendar/timeline-view.md +301 -0
  84. package/skills/cocoar-vue-ui/references/components/calendar/week-view.md +382 -0
  85. package/skills/cocoar-vue-ui/references/components/calendar/work-week-view.md +145 -0
  86. package/skills/cocoar-vue-ui/references/components/calendar/year-view.md +63 -0
  87. package/skills/cocoar-vue-ui/references/components/card.md +151 -0
  88. package/skills/cocoar-vue-ui/references/components/checkbox-group.md +103 -0
  89. package/skills/cocoar-vue-ui/references/components/checkbox.md +279 -0
  90. package/skills/cocoar-vue-ui/references/components/code-block.md +241 -0
  91. package/skills/cocoar-vue-ui/references/components/context-menu.md +355 -0
  92. package/skills/cocoar-vue-ui/references/components/data-grid/checkbox.md +213 -0
  93. package/skills/cocoar-vue-ui/references/components/data-grid/date-columns.md +272 -0
  94. package/skills/cocoar-vue-ui/references/components/data-grid/editing.md +236 -0
  95. package/skills/cocoar-vue-ui/references/components/data-grid/multi-select.md +193 -0
  96. package/skills/cocoar-vue-ui/references/components/data-grid/number.md +147 -0
  97. package/skills/cocoar-vue-ui/references/components/data-grid/select.md +202 -0
  98. package/skills/cocoar-vue-ui/references/components/data-grid/text.md +127 -0
  99. package/skills/cocoar-vue-ui/references/components/data-grid.md +1225 -0
  100. package/skills/cocoar-vue-ui/references/components/data-list.md +2121 -0
  101. package/skills/cocoar-vue-ui/references/components/date-or-time-picker.md +200 -0
  102. package/skills/cocoar-vue-ui/references/components/date-picker.md +203 -0
  103. package/skills/cocoar-vue-ui/references/components/date-time-picker.md +154 -0
  104. package/skills/cocoar-vue-ui/references/components/date-views.md +212 -0
  105. package/skills/cocoar-vue-ui/references/components/dialog.md +148 -0
  106. package/skills/cocoar-vue-ui/references/components/divider.md +157 -0
  107. package/skills/cocoar-vue-ui/references/components/document-viewer/annotations.md +400 -0
  108. package/skills/cocoar-vue-ui/references/components/document-viewer/coar-document-viewer.md +327 -0
  109. package/skills/cocoar-vue-ui/references/components/document-viewer/index.md +234 -0
  110. package/skills/cocoar-vue-ui/references/components/document-viewer/toolbar.md +221 -0
  111. package/skills/cocoar-vue-ui/references/components/drag-drop.md +267 -0
  112. package/skills/cocoar-vue-ui/references/components/dual-listbox.md +449 -0
  113. package/skills/cocoar-vue-ui/references/components/file-explorer/asset-store.md +327 -0
  114. package/skills/cocoar-vue-ui/references/components/file-explorer/in-memory-store.md +176 -0
  115. package/skills/cocoar-vue-ui/references/components/file-explorer/index.md +311 -0
  116. package/skills/cocoar-vue-ui/references/components/file-explorer/use-file-explorer.md +300 -0
  117. package/skills/cocoar-vue-ui/references/components/form-field.md +1025 -0
  118. package/skills/cocoar-vue-ui/references/components/fragment-parser.md +298 -0
  119. package/skills/cocoar-vue-ui/references/components/link.md +235 -0
  120. package/skills/cocoar-vue-ui/references/components/listbox.md +575 -0
  121. package/skills/cocoar-vue-ui/references/components/map/editor.md +448 -0
  122. package/skills/cocoar-vue-ui/references/components/map/index.md +351 -0
  123. package/skills/cocoar-vue-ui/references/components/markdown-diagrams.md +210 -0
  124. package/skills/cocoar-vue-ui/references/components/markdown-editor.md +1478 -0
  125. package/skills/cocoar-vue-ui/references/components/markdown-embeds.md +387 -0
  126. package/skills/cocoar-vue-ui/references/components/markdown-form.md +447 -0
  127. package/skills/cocoar-vue-ui/references/components/markdown.md +276 -0
  128. package/skills/cocoar-vue-ui/references/components/menu.md +380 -0
  129. package/skills/cocoar-vue-ui/references/components/mermaid.md +172 -0
  130. package/skills/cocoar-vue-ui/references/components/navbar.md +147 -0
  131. package/skills/cocoar-vue-ui/references/components/note.md +96 -0
  132. package/skills/cocoar-vue-ui/references/components/notice.md +131 -0
  133. package/skills/cocoar-vue-ui/references/components/number-input.md +215 -0
  134. package/skills/cocoar-vue-ui/references/components/otp-input.md +324 -0
  135. package/skills/cocoar-vue-ui/references/components/page-builder/authoring-contract.md +271 -0
  136. package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-builder.md +476 -0
  137. package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-renderer.md +677 -0
  138. package/skills/cocoar-vue-ui/references/components/page-builder/custom-elements.md +441 -0
  139. package/skills/cocoar-vue-ui/references/components/page-builder/idp-integration.md +122 -0
  140. package/skills/cocoar-vue-ui/references/components/page-builder/index.md +573 -0
  141. package/skills/cocoar-vue-ui/references/components/pagination.md +150 -0
  142. package/skills/cocoar-vue-ui/references/components/panel-layout.md +372 -0
  143. package/skills/cocoar-vue-ui/references/components/password-input.md +175 -0
  144. package/skills/cocoar-vue-ui/references/components/popconfirm.md +186 -0
  145. package/skills/cocoar-vue-ui/references/components/popover.md +173 -0
  146. package/skills/cocoar-vue-ui/references/components/progress-bar.md +178 -0
  147. package/skills/cocoar-vue-ui/references/components/radio-group.md +225 -0
  148. package/skills/cocoar-vue-ui/references/components/script-editor.md +1271 -0
  149. package/skills/cocoar-vue-ui/references/components/segmented-control.md +238 -0
  150. package/skills/cocoar-vue-ui/references/components/select.md +463 -0
  151. package/skills/cocoar-vue-ui/references/components/sidebar.md +421 -0
  152. package/skills/cocoar-vue-ui/references/components/spinner.md +132 -0
  153. package/skills/cocoar-vue-ui/references/components/switch.md +195 -0
  154. package/skills/cocoar-vue-ui/references/components/table.md +170 -0
  155. package/skills/cocoar-vue-ui/references/components/tabs.md +382 -0
  156. package/skills/cocoar-vue-ui/references/components/tag.md +178 -0
  157. package/skills/cocoar-vue-ui/references/components/text-input.md +256 -0
  158. package/skills/cocoar-vue-ui/references/components/toast.md +160 -0
  159. package/skills/cocoar-vue-ui/references/components/tooltip.md +121 -0
  160. package/skills/cocoar-vue-ui/references/components/transitions.md +193 -0
  161. package/skills/cocoar-vue-ui/references/components/tree.md +2388 -0
  162. package/skills/cocoar-vue-ui/references/components/virtual-list.md +212 -0
  163. package/skills/cocoar-vue-ui/references/components/wizard.md +251 -0
  164. package/skills/cocoar-vue-ui/references/components/zoned-date-time-picker.md +177 -0
  165. package/skills/cocoar-vue-ui/references/foundations/colors.md +708 -0
  166. package/skills/cocoar-vue-ui/references/foundations/design-principles.md +115 -0
  167. package/skills/cocoar-vue-ui/references/foundations/icons.md +381 -0
  168. package/skills/cocoar-vue-ui/references/foundations/localization/formatting.md +530 -0
  169. package/skills/cocoar-vue-ui/references/foundations/localization/setup.md +86 -0
  170. package/skills/cocoar-vue-ui/references/foundations/localization/timezones.md +182 -0
  171. package/skills/cocoar-vue-ui/references/foundations/localization/translations.md +305 -0
  172. package/skills/cocoar-vue-ui/references/foundations/motion.md +549 -0
  173. package/skills/cocoar-vue-ui/references/foundations/spacing.md +330 -0
  174. package/skills/cocoar-vue-ui/references/foundations/theming.md +140 -0
  175. package/skills/cocoar-vue-ui/references/foundations/typography.md +206 -0
  176. package/skills/cocoar-vue-ui/references/guide/error-handling.md +162 -0
  177. package/skills/cocoar-vue-ui/references/guide/getting-started.md +116 -0
  178. package/skills/cocoar-vue-ui/references/guide/migration-page-builder-3.md +207 -0
  179. package/skills/cocoar-vue-ui/references/guide/migration.md +140 -0
  180. 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.