@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.
Files changed (179) hide show
  1. package/dist/components/checkbox/CoarCheckbox.vue.d.ts +1 -1
  2. package/dist/components/checkbox/CoarCheckbox.vue.d.ts.map +1 -1
  3. package/dist/components/code-block/CoarCodeBlock.vue.d.ts +1 -1
  4. package/dist/components/code-block/CoarCodeBlock.vue.d.ts.map +1 -1
  5. package/dist/components/data-list/CoarDataList.vue.d.ts +158 -0
  6. package/dist/components/data-list/CoarDataList.vue.d.ts.map +1 -0
  7. package/dist/components/data-list/CoarDataListToolbar.vue.d.ts +30 -0
  8. package/dist/components/data-list/CoarDataListToolbar.vue.d.ts.map +1 -0
  9. package/dist/components/data-list/data-list-builder.d.ts +288 -0
  10. package/dist/components/data-list/data-list-builder.d.ts.map +1 -0
  11. package/dist/components/data-list/index.d.ts +12 -0
  12. package/dist/components/data-list/index.d.ts.map +1 -0
  13. package/dist/components/data-list/internal/compare.d.ts +14 -0
  14. package/dist/components/data-list/internal/compare.d.ts.map +1 -0
  15. package/dist/components/data-list/internal/reorder-core.d.ts +46 -0
  16. package/dist/components/data-list/internal/reorder-core.d.ts.map +1 -0
  17. package/dist/components/data-list/internal/search.d.ts +16 -0
  18. package/dist/components/data-list/internal/search.d.ts.map +1 -0
  19. package/dist/components/data-list/internal/useDataListLines.d.ts +92 -0
  20. package/dist/components/data-list/internal/useDataListLines.d.ts.map +1 -0
  21. package/dist/components/data-list/internal/useDataListReorder.d.ts +68 -0
  22. package/dist/components/data-list/internal/useDataListReorder.d.ts.map +1 -0
  23. package/dist/components/data-list/internal/useSearchHighlight.d.ts +19 -0
  24. package/dist/components/data-list/internal/useSearchHighlight.d.ts.map +1 -0
  25. package/dist/components/data-list/types.d.ts +185 -0
  26. package/dist/components/data-list/types.d.ts.map +1 -0
  27. package/dist/components/data-list/useDataListModel.d.ts +79 -0
  28. package/dist/components/data-list/useDataListModel.d.ts.map +1 -0
  29. package/dist/components/date-time/_shared/maskito-config.d.ts +7 -2
  30. package/dist/components/date-time/_shared/maskito-config.d.ts.map +1 -1
  31. package/dist/components/date-time/_shared/time-helpers.d.ts +11 -4
  32. package/dist/components/date-time/_shared/time-helpers.d.ts.map +1 -1
  33. package/dist/components/date-time/_shared/use-date-picker-base.d.ts +1 -1
  34. package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts +1 -1
  35. package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts.map +1 -1
  36. package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts +1 -1
  37. package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts.map +1 -1
  38. package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts +1 -1
  39. package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts.map +1 -1
  40. package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts +3 -0
  41. package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts.map +1 -1
  42. package/dist/components/form-field/CoarFormField.vue.d.ts +1 -1
  43. package/dist/components/form-field/CoarFormField.vue.d.ts.map +1 -1
  44. package/dist/components/icon/CoarIcon.vue.d.ts +2 -2
  45. package/dist/components/icon/CoarIcon.vue.d.ts.map +1 -1
  46. package/dist/components/listbox/CoarListbox.vue.d.ts +7 -0
  47. package/dist/components/listbox/CoarListbox.vue.d.ts.map +1 -1
  48. package/dist/components/number-input/CoarNumberInput.vue.d.ts +1 -1
  49. package/dist/components/number-input/CoarNumberInput.vue.d.ts.map +1 -1
  50. package/dist/components/otp-input/CoarOtpInput.vue.d.ts +1 -1
  51. package/dist/components/otp-input/CoarOtpInput.vue.d.ts.map +1 -1
  52. package/dist/components/password-input/CoarPasswordInput.vue.d.ts +1 -1
  53. package/dist/components/password-input/CoarPasswordInput.vue.d.ts.map +1 -1
  54. package/dist/components/switch/CoarSwitch.vue.d.ts +1 -1
  55. package/dist/components/switch/CoarSwitch.vue.d.ts.map +1 -1
  56. package/dist/components/text-input/CoarTextInput.vue.d.ts +1 -1
  57. package/dist/components/text-input/CoarTextInput.vue.d.ts.map +1 -1
  58. package/dist/composables/dragRegistry.d.ts +12 -0
  59. package/dist/composables/dragRegistry.d.ts.map +1 -1
  60. package/dist/composables/index.d.ts +2 -2
  61. package/dist/composables/index.d.ts.map +1 -1
  62. package/dist/composables/useDragDrop.d.ts +62 -6
  63. package/dist/composables/useDragDrop.d.ts.map +1 -1
  64. package/dist/composables/useVirtualList.d.ts +21 -1
  65. package/dist/composables/useVirtualList.d.ts.map +1 -1
  66. package/dist/index.css +1 -1
  67. package/dist/index.d.ts +4 -2
  68. package/dist/index.d.ts.map +1 -1
  69. package/dist/index.js +4508 -2039
  70. package/package.json +5 -3
  71. package/skills/cocoar-vue-ui/SKILL.md +244 -0
  72. package/skills/cocoar-vue-ui/references/components/avatar.md +178 -0
  73. package/skills/cocoar-vue-ui/references/components/badge.md +290 -0
  74. package/skills/cocoar-vue-ui/references/components/breadcrumb.md +238 -0
  75. package/skills/cocoar-vue-ui/references/components/button.md +252 -0
  76. package/skills/cocoar-vue-ui/references/components/calendar/agenda-view.md +353 -0
  77. package/skills/cocoar-vue-ui/references/components/calendar/coar-calendar.md +1645 -0
  78. package/skills/cocoar-vue-ui/references/components/calendar/day-view.md +361 -0
  79. package/skills/cocoar-vue-ui/references/components/calendar/index.md +312 -0
  80. package/skills/cocoar-vue-ui/references/components/calendar/month-view.md +475 -0
  81. package/skills/cocoar-vue-ui/references/components/calendar/performance.md +186 -0
  82. package/skills/cocoar-vue-ui/references/components/calendar/timeline-view.md +301 -0
  83. package/skills/cocoar-vue-ui/references/components/calendar/week-view.md +382 -0
  84. package/skills/cocoar-vue-ui/references/components/calendar/work-week-view.md +145 -0
  85. package/skills/cocoar-vue-ui/references/components/calendar/year-view.md +63 -0
  86. package/skills/cocoar-vue-ui/references/components/card.md +151 -0
  87. package/skills/cocoar-vue-ui/references/components/checkbox-group.md +103 -0
  88. package/skills/cocoar-vue-ui/references/components/checkbox.md +279 -0
  89. package/skills/cocoar-vue-ui/references/components/code-block.md +241 -0
  90. package/skills/cocoar-vue-ui/references/components/context-menu.md +355 -0
  91. package/skills/cocoar-vue-ui/references/components/data-grid/checkbox.md +213 -0
  92. package/skills/cocoar-vue-ui/references/components/data-grid/date-columns.md +272 -0
  93. package/skills/cocoar-vue-ui/references/components/data-grid/editing.md +236 -0
  94. package/skills/cocoar-vue-ui/references/components/data-grid/multi-select.md +193 -0
  95. package/skills/cocoar-vue-ui/references/components/data-grid/number.md +147 -0
  96. package/skills/cocoar-vue-ui/references/components/data-grid/select.md +202 -0
  97. package/skills/cocoar-vue-ui/references/components/data-grid/text.md +127 -0
  98. package/skills/cocoar-vue-ui/references/components/data-grid.md +1225 -0
  99. package/skills/cocoar-vue-ui/references/components/data-list.md +2121 -0
  100. package/skills/cocoar-vue-ui/references/components/date-or-time-picker.md +200 -0
  101. package/skills/cocoar-vue-ui/references/components/date-picker.md +203 -0
  102. package/skills/cocoar-vue-ui/references/components/date-time-picker.md +154 -0
  103. package/skills/cocoar-vue-ui/references/components/date-views.md +212 -0
  104. package/skills/cocoar-vue-ui/references/components/dialog.md +148 -0
  105. package/skills/cocoar-vue-ui/references/components/divider.md +157 -0
  106. package/skills/cocoar-vue-ui/references/components/document-viewer/annotations.md +400 -0
  107. package/skills/cocoar-vue-ui/references/components/document-viewer/coar-document-viewer.md +327 -0
  108. package/skills/cocoar-vue-ui/references/components/document-viewer/index.md +234 -0
  109. package/skills/cocoar-vue-ui/references/components/document-viewer/toolbar.md +221 -0
  110. package/skills/cocoar-vue-ui/references/components/drag-drop.md +267 -0
  111. package/skills/cocoar-vue-ui/references/components/dual-listbox.md +449 -0
  112. package/skills/cocoar-vue-ui/references/components/file-explorer/asset-store.md +327 -0
  113. package/skills/cocoar-vue-ui/references/components/file-explorer/in-memory-store.md +176 -0
  114. package/skills/cocoar-vue-ui/references/components/file-explorer/index.md +311 -0
  115. package/skills/cocoar-vue-ui/references/components/file-explorer/use-file-explorer.md +300 -0
  116. package/skills/cocoar-vue-ui/references/components/form-field.md +1025 -0
  117. package/skills/cocoar-vue-ui/references/components/fragment-parser.md +298 -0
  118. package/skills/cocoar-vue-ui/references/components/link.md +235 -0
  119. package/skills/cocoar-vue-ui/references/components/listbox.md +575 -0
  120. package/skills/cocoar-vue-ui/references/components/map/editor.md +448 -0
  121. package/skills/cocoar-vue-ui/references/components/map/index.md +351 -0
  122. package/skills/cocoar-vue-ui/references/components/markdown-diagrams.md +210 -0
  123. package/skills/cocoar-vue-ui/references/components/markdown-editor.md +1478 -0
  124. package/skills/cocoar-vue-ui/references/components/markdown-embeds.md +387 -0
  125. package/skills/cocoar-vue-ui/references/components/markdown-form.md +447 -0
  126. package/skills/cocoar-vue-ui/references/components/markdown.md +276 -0
  127. package/skills/cocoar-vue-ui/references/components/menu.md +380 -0
  128. package/skills/cocoar-vue-ui/references/components/mermaid.md +172 -0
  129. package/skills/cocoar-vue-ui/references/components/navbar.md +147 -0
  130. package/skills/cocoar-vue-ui/references/components/note.md +96 -0
  131. package/skills/cocoar-vue-ui/references/components/notice.md +131 -0
  132. package/skills/cocoar-vue-ui/references/components/number-input.md +215 -0
  133. package/skills/cocoar-vue-ui/references/components/otp-input.md +324 -0
  134. package/skills/cocoar-vue-ui/references/components/page-builder/authoring-contract.md +271 -0
  135. package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-builder.md +476 -0
  136. package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-renderer.md +677 -0
  137. package/skills/cocoar-vue-ui/references/components/page-builder/custom-elements.md +441 -0
  138. package/skills/cocoar-vue-ui/references/components/page-builder/idp-integration.md +122 -0
  139. package/skills/cocoar-vue-ui/references/components/page-builder/index.md +573 -0
  140. package/skills/cocoar-vue-ui/references/components/pagination.md +150 -0
  141. package/skills/cocoar-vue-ui/references/components/panel-layout.md +372 -0
  142. package/skills/cocoar-vue-ui/references/components/password-input.md +175 -0
  143. package/skills/cocoar-vue-ui/references/components/popconfirm.md +186 -0
  144. package/skills/cocoar-vue-ui/references/components/popover.md +173 -0
  145. package/skills/cocoar-vue-ui/references/components/progress-bar.md +178 -0
  146. package/skills/cocoar-vue-ui/references/components/radio-group.md +225 -0
  147. package/skills/cocoar-vue-ui/references/components/script-editor.md +1271 -0
  148. package/skills/cocoar-vue-ui/references/components/segmented-control.md +238 -0
  149. package/skills/cocoar-vue-ui/references/components/select.md +463 -0
  150. package/skills/cocoar-vue-ui/references/components/sidebar.md +421 -0
  151. package/skills/cocoar-vue-ui/references/components/spinner.md +132 -0
  152. package/skills/cocoar-vue-ui/references/components/switch.md +195 -0
  153. package/skills/cocoar-vue-ui/references/components/table.md +170 -0
  154. package/skills/cocoar-vue-ui/references/components/tabs.md +382 -0
  155. package/skills/cocoar-vue-ui/references/components/tag.md +178 -0
  156. package/skills/cocoar-vue-ui/references/components/text-input.md +256 -0
  157. package/skills/cocoar-vue-ui/references/components/toast.md +160 -0
  158. package/skills/cocoar-vue-ui/references/components/tooltip.md +121 -0
  159. package/skills/cocoar-vue-ui/references/components/transitions.md +193 -0
  160. package/skills/cocoar-vue-ui/references/components/tree.md +2388 -0
  161. package/skills/cocoar-vue-ui/references/components/virtual-list.md +212 -0
  162. package/skills/cocoar-vue-ui/references/components/wizard.md +251 -0
  163. package/skills/cocoar-vue-ui/references/components/zoned-date-time-picker.md +177 -0
  164. package/skills/cocoar-vue-ui/references/foundations/colors.md +708 -0
  165. package/skills/cocoar-vue-ui/references/foundations/design-principles.md +115 -0
  166. package/skills/cocoar-vue-ui/references/foundations/icons.md +381 -0
  167. package/skills/cocoar-vue-ui/references/foundations/localization/formatting.md +530 -0
  168. package/skills/cocoar-vue-ui/references/foundations/localization/setup.md +86 -0
  169. package/skills/cocoar-vue-ui/references/foundations/localization/timezones.md +182 -0
  170. package/skills/cocoar-vue-ui/references/foundations/localization/translations.md +305 -0
  171. package/skills/cocoar-vue-ui/references/foundations/motion.md +549 -0
  172. package/skills/cocoar-vue-ui/references/foundations/spacing.md +330 -0
  173. package/skills/cocoar-vue-ui/references/foundations/theming.md +140 -0
  174. package/skills/cocoar-vue-ui/references/foundations/typography.md +206 -0
  175. package/skills/cocoar-vue-ui/references/guide/error-handling.md +162 -0
  176. package/skills/cocoar-vue-ui/references/guide/getting-started.md +119 -0
  177. package/skills/cocoar-vue-ui/references/guide/migration-page-builder-3.md +207 -0
  178. package/skills/cocoar-vue-ui/references/guide/migration.md +140 -0
  179. package/skills/cocoar-vue-ui/references/guide/theming.md +98 -0
@@ -0,0 +1,351 @@
1
+ <!-- Generated from apps/docs/components/map/index.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
2
+
3
+ # Map (Preview)
4
+
5
+ An interactive [Leaflet](https://leafletjs.com/) map for Vue 3. `<CoarMap>` is
6
+ **standalone and data-driven**: you feed it resolved `MapData` + `MapConfig` and
7
+ it renders pins, a route, popups and an optional legend. It has **no dependency
8
+ on — and no knowledge of — markdown or any embedding layer**; resolving a source
9
+ (a store, an API, a `:::map{id}` directive) into `MapData` is the consumer's job.
10
+
11
+ > **Info: Separate package**
12
+ >
13
+ ```bash
14
+ pnpm add @cocoar/vue-map
15
+ ```
16
+ > `leaflet` is a regular dependency, imported **lazily** at runtime — `vue` is the
17
+ > only peer. Import the component stylesheet once (it carries the pin, legend and
18
+ > layout styles); Leaflet's own CSS is still loaded lazily by the component:
19
+ >
20
+ ```ts
21
+ import '@cocoar/vue-map/styles';
22
+ ```
23
+
24
+ > **Tip: Authoring maps?**
25
+ >
26
+ > This page is the **viewer**. To place, move and edit points visually, see
27
+ > [**CoarMapEditor**](./editor.md) — the write counterpart, same data.
28
+
29
+ **Demo — `map/demos/MapBasic.vue`**
30
+
31
+ ```vue
32
+ <script setup lang="ts">
33
+ /**
34
+ * `<CoarMap>` with a consumer-built point list wired to it via the interactive
35
+ * bridge — no markdown anywhere. The list/layout is the consumer's job (built
36
+ * here from the exported `fallbackEntries` helper); the map provides the hooks
37
+ * (`v-model:selected`, `focusPoint`, `highlightPoint`).
38
+ */
39
+ import { computed, ref } from 'vue';
40
+ import {
41
+ CoarMap,
42
+ fallbackEntries,
43
+ type MapConfig,
44
+ type MapData,
45
+ } from '@cocoar/vue-map';
46
+
47
+ const config: MapConfig = {
48
+ defaultBasemap: 'voyager',
49
+ basemaps: [
50
+ {
51
+ id: 'voyager',
52
+ url: 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',
53
+ subdomains: 'abcd',
54
+ attribution: '© OpenStreetMap, © CARTO',
55
+ maxZoom: 20,
56
+ },
57
+ ],
58
+ categories: [
59
+ { id: 'sight', label: 'Sights', emoji: '🏛️', color: '#a855f7' },
60
+ { id: 'food', label: 'Food', emoji: '🍽️', color: '#f97316' },
61
+ { id: 'nature', label: 'Nature', emoji: '🌲', color: '#16a34a' },
62
+ ],
63
+ };
64
+
65
+ const data: MapData = {
66
+ type: 'route',
67
+ caption: 'A sample London walk.',
68
+ points: [
69
+ { lat: 51.4995, lng: -0.1248, kind: 'stop', label: 'Westminster', note: 'Start', category: 'sight' },
70
+ { lat: 51.5076, lng: -0.118, kind: 'shape', label: 'Thames bend' },
71
+ { lat: 51.5129, lng: -0.1243, kind: 'stop', label: 'Dishoom', note: 'Lunch stop', category: 'food', icon: '🥘' },
72
+ { lat: 51.5194, lng: -0.127, kind: 'stop', label: 'British Museum', category: 'sight' },
73
+ { lat: 51.5074, lng: -0.1657, kind: 'stop', label: 'Hyde Park', category: 'nature' },
74
+ ],
75
+ };
76
+
77
+ const entries = computed(() => fallbackEntries(data, config));
78
+ const selected = ref<number | null>(null);
79
+ const showLegend = ref(true);
80
+ const mapRef = ref<{
81
+ focusPoint: (index: number) => void;
82
+ highlightPoint: (index: number | null) => void;
83
+ } | null>(null);
84
+ </script>
85
+
86
+ <template>
87
+ <ClientOnly>
88
+ <label class="map-d__toggle"><input v-model="showLegend" type="checkbox" /> show legend</label>
89
+ <div class="map-d">
90
+ <div class="map-d__map">
91
+ <CoarMap
92
+ ref="mapRef"
93
+ v-model:selected="selected"
94
+ :data="data"
95
+ :config="config"
96
+ :show-legend="showLegend"
97
+ @point-click="(e) => (selected = e.index)"
98
+ />
99
+ </div>
100
+ <aside class="map-d__list">
101
+ <div class="map-d__list-title">Points</div>
102
+ <ul>
103
+ <li
104
+ v-for="entry in entries"
105
+ :key="entry.index"
106
+ :class="{ 'is-active': selected === entry.index }"
107
+ @click="mapRef?.focusPoint(entry.index)"
108
+ @mouseenter="mapRef?.highlightPoint(entry.index)"
109
+ @mouseleave="mapRef?.highlightPoint(null)"
110
+ >
111
+ <span>{{ entry.emoji }}</span> {{ entry.label || '(unnamed)' }}
112
+ </li>
113
+ </ul>
114
+ </aside>
115
+ </div>
116
+ </ClientOnly>
117
+ </template>
118
+
119
+ <style scoped>
120
+ .map-d__toggle {
121
+ display: inline-flex;
122
+ align-items: center;
123
+ gap: 6px;
124
+ margin-bottom: 8px;
125
+ font-size: 13px;
126
+ font-weight: 600;
127
+ }
128
+ .map-d {
129
+ display: flex;
130
+ gap: 12px;
131
+ align-items: stretch;
132
+ }
133
+ .map-d__map {
134
+ flex: 1;
135
+ min-width: 0;
136
+ --coar-map-height: 360px;
137
+ }
138
+ .map-d__list {
139
+ width: 200px;
140
+ flex-shrink: 0;
141
+ border: 1px solid var(--vp-c-divider);
142
+ border-radius: 10px;
143
+ overflow: hidden;
144
+ }
145
+ .map-d__list-title {
146
+ padding: 8px 12px;
147
+ font-size: 11px;
148
+ font-weight: 700;
149
+ text-transform: uppercase;
150
+ letter-spacing: 0.04em;
151
+ color: var(--vp-c-text-2);
152
+ border-bottom: 1px solid var(--vp-c-divider);
153
+ }
154
+ .map-d__list ul {
155
+ margin: 0;
156
+ padding: 4px;
157
+ list-style: none;
158
+ overflow: auto;
159
+ max-height: 320px;
160
+ }
161
+ .map-d__list li {
162
+ padding: 6px 8px;
163
+ border-radius: 6px;
164
+ font-size: 13px;
165
+ cursor: pointer;
166
+ }
167
+ .map-d__list li:hover {
168
+ background: var(--vp-c-bg-soft);
169
+ }
170
+ .map-d__list li.is-active {
171
+ background: var(--vp-c-brand-soft);
172
+ }
173
+ @media (max-width: 720px) {
174
+ .map-d { flex-direction: column; }
175
+ .map-d__list { width: auto; }
176
+ }
177
+ </style>
178
+ ```
179
+
180
+ The list on the right is **not** part of `<CoarMap>` — it's built by the demo from
181
+ the exported `fallbackEntries` helper and wired to the map via the
182
+ [interactive bridge](#interactive-bridge). Click a row to fly to it, hover to
183
+ highlight, click a marker to select it back.
184
+
185
+ ## Quick start
186
+
187
+ ```vue
188
+ <template>
189
+ <CoarMap :data="data" :config="config" />
190
+ </template>
191
+
192
+ <script setup lang="ts">
193
+ import { CoarMap, type MapData, type MapConfig } from '@cocoar/vue-map';
194
+
195
+ const config: MapConfig = {
196
+ defaultBasemap: 'osm',
197
+ basemaps: [
198
+ { id: 'osm', url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', attribution: '© OpenStreetMap' },
199
+ ],
200
+ categories: [
201
+ { id: 'sight', label: 'Sights', emoji: '🏛️', color: '#a855f7' },
202
+ ],
203
+ };
204
+
205
+ const data: MapData = {
206
+ type: 'route',
207
+ points: [
208
+ { lat: 51.4995, lng: -0.1248, kind: 'stop', label: 'Westminster', category: 'sight' },
209
+ { lat: 51.5194, lng: -0.1270, kind: 'stop', label: 'British Museum', category: 'sight' },
210
+ ],
211
+ };
212
+ </script>
213
+ ```
214
+
215
+ ## Data
216
+
217
+ | Field | Type | Description |
218
+ |---|---|---|
219
+ | `type` | `'single' \| 'multi' \| 'route'` | `route` draws a polyline through all points; others just place markers. |
220
+ | `caption` | `string?` | Shown under the map. |
221
+ | `basemap` | `string?` | Basemap id; falls back to `config.defaultBasemap`. |
222
+ | `viewport` | `{ centerLat, centerLng, zoom }?` | Fixed view; when omitted the map **auto-fits** all points. |
223
+ | `points` | `MapPoint[]` | The markers + route vertices. |
224
+
225
+ A **`MapPoint`**:
226
+
227
+ | Field | Type | Description |
228
+ |---|---|---|
229
+ | `lat` / `lng` | `number` | Position. |
230
+ | `kind` | `'stop' \| 'shape'` | `stop` = a marker; `shape` = a route/line vertex. |
231
+ | `label` | `string?` | Hover tooltip + popup title. |
232
+ | `note` | `string?` | Extra line in the click popup. |
233
+ | `category` | `string?` | _(stop only)_ category id → marker color + legend. |
234
+ | `icon` | `string?` | _(stop only)_ emoji override (else the category's emoji). |
235
+
236
+ Markers: a **stop** is a category-colored pin (emoji = `icon` or the category's);
237
+ a **named shape** point is a small dot; an **unnamed shape** point draws no
238
+ marker (it only contributes to the route line + auto-fit bounds).
239
+
240
+ ## Config
241
+
242
+ | Field | Type | Description |
243
+ |---|---|---|
244
+ | `defaultBasemap` | `string` | Basemap id used when a map specifies none. |
245
+ | `basemaps` | `MapBasemap[]` | Tile sources (`{ id, url, subdomains?, attribution?, maxZoom? }`). |
246
+ | `categories` | `MapCategory[]?` | `{ id, label, emoji?, color }` — drives stop colors + the legend. |
247
+
248
+ Pass it per-instance via `config`, or app-wide:
249
+
250
+ ```ts
251
+ import { COAR_MAP_CONFIG_KEY } from '@cocoar/vue-map';
252
+ app.provide(COAR_MAP_CONFIG_KEY, config); // a per-instance `config` prop still wins
253
+ ```
254
+
255
+ > **Info: Google basemaps**
256
+ >
257
+ > Out of scope for now. A basemap flagged `google: true` is **skipped** in favour
258
+ > of the default. The `google` / `googleType` fields are kept on `MapBasemap` so a
259
+ > registry can carry them for a future release.
260
+
261
+ ## Interactive bridge
262
+
263
+ The component renders the map; **the list/layout around it is the consumer's
264
+ job** (the data is yours, and `fallbackEntries(data, config)` gives ready-made
265
+ rows — each carrying its `index` into `data.points`). What a consumer *can't* do
266
+ alone is drive the map from outside, so `<CoarMap>` exposes exactly that:
267
+
268
+ | API | Direction | Description |
269
+ |---|---|---|
270
+ | `v-model:selected` | both | Selected point index. Set it to ring a marker; reads back when a marker is clicked. |
271
+ | `@point-click` | map → out | `{ point, index }` when a marker is clicked. |
272
+ | `focusPoint(index)` | out → map | Pan to the point, open its popup, select it. _(exposed method)_ |
273
+ | `highlightPoint(index \| null)` | out → map | Transient hover emphasis on a marker. _(exposed method)_ |
274
+
275
+ ```vue
276
+ <script setup lang="ts">
277
+ import { ref, computed } from 'vue';
278
+ import { CoarMap, fallbackEntries } from '@cocoar/vue-map';
279
+
280
+ const mapRef = ref<InstanceType<typeof CoarMap>>();
281
+ const selected = ref<number | null>(null);
282
+ const rows = computed(() => fallbackEntries(data, config));
283
+ </script>
284
+
285
+ <template>
286
+ <CoarMap ref="mapRef" v-model:selected="selected" :data="data" :config="config" />
287
+ <ul>
288
+ <li
289
+ v-for="row in rows"
290
+ :key="row.index"
291
+ :class="{ active: selected === row.index }"
292
+ @click="mapRef?.focusPoint(row.index)"
293
+ @mouseenter="mapRef?.highlightPoint(row.index)"
294
+ @mouseleave="mapRef?.highlightPoint(null)"
295
+ >{{ row.emoji }} {{ row.label }}</li>
296
+ </ul>
297
+ </template>
298
+ ```
299
+
300
+ ## Legend & caption
301
+
302
+ The legend is **off by default**. Set `show-legend` to enable it — it then
303
+ appears when **≥ 2 categories** are present among the stops, listing each
304
+ category's swatch + label. The `caption` from `MapData` renders below the map.
305
+
306
+ ```vue
307
+ <CoarMap :data="data" :config="config" show-legend />
308
+ ```
309
+
310
+ ## Lazy loading & fallback
311
+
312
+ Leaflet (JS **and** CSS) is imported in `onMounted`, so it loads only on pages
313
+ that actually render a map (your bundler code-splits it into its own chunk).
314
+ Until the map hydrates — and if Leaflet ever fails to load, or with JS off — a
315
+ crawlable `<ol>` of the named points is shown and then hidden once the map is
316
+ live. The map container's height is `--coar-map-height` (default `360px`).
317
+
318
+ ## Security
319
+
320
+ Point text (`label`, `note`, `icon`) is treated as untrusted: popups are built
321
+ from DOM **text nodes** (never an HTML string), tooltips and the marker emoji are
322
+ HTML-escaped, and a category color is guarded before it reaches an inline style.
323
+ A hostile label can't break out into markup.
324
+
325
+ ## Props
326
+
327
+ | Prop | Type | Default | Description |
328
+ |---|---|---|---|
329
+ | `data` | `MapData` | _required_ | The resolved map data. |
330
+ | `config` | `MapConfig` | _provide_ | Basemaps + categories. Falls back to `COAR_MAP_CONFIG_KEY`. |
331
+ | `selected` | `number \| null` | `null` | Selected point index (`v-model:selected`). |
332
+ | `showLegend` | `boolean` | `false` | Show the legend (then needs ≥ 2 categories). |
333
+
334
+ ## Using it as a markdown embed
335
+
336
+ `<CoarMap>` and the [markdown custom-embed](../markdown-embeds.md) system are
337
+ fully independent. To show a map inside markdown, a consumer registers a small
338
+ wrapper that resolves the directive's id into `MapData` and renders `<CoarMap>`:
339
+
340
+ ```ts
341
+ // the wrapper is the CONSUMER's — it bridges markdown ↔ map; neither package
342
+ // depends on the other.
343
+ const embeds = {
344
+ map: {
345
+ viewer: defineComponent({
346
+ props: { id: { type: String, required: true } },
347
+ setup: (props) => () => h(CoarMap, { data: resolveMap(props.id), config }),
348
+ }),
349
+ },
350
+ };
351
+ ```
@@ -0,0 +1,210 @@
1
+ <!-- Generated from apps/docs/components/markdown-diagrams.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
2
+
3
+ # Diagrams (Preview)
4
+
5
+ Render diagrams inside markdown from a fenced code block — <code>```mermaid</code>
6
+ — using [`@cocoar/vue-markdown-mermaid`](https://www.npmjs.com/package/@cocoar/vue-markdown-mermaid).
7
+ The diagram source lives in the markdown as a normal code fence, so it
8
+ round-trips losslessly and **degrades to a readable code block** anywhere the
9
+ renderer isn't installed (strict CommonMark, a viewer-only build, a native mobile
10
+ renderer).
11
+
12
+ > **Info: Two mechanisms, two shapes**
13
+ >
14
+ > A **fenced code block** is for content whose *body is authored text in a DSL* —
15
+ > diagrams, code. A [custom embed](./markdown-embeds.md) (`:::key{props}`) is
16
+ > for a *single-line reference + a few props* rendered by a rich, visually-edited
17
+ > component (e.g. a map). Diagrams belong in a fence; the map belongs in an embed.
18
+
19
+ > **Tip: Two packages**
20
+ >
21
+ > The renderer lives in the standalone, markdown-free **`@cocoar/vue-mermaid`**
22
+ > ([`<CoarMermaidDiagram :code>`](./mermaid.md) — usable anywhere).
23
+ > **`@cocoar/vue-markdown-mermaid`** is the thin adapter that registers it as a
24
+ > fence renderer. Rendering a diagram **outside** markdown? See the
25
+ > [**Mermaid Diagram**](./mermaid.md) page.
26
+
27
+ **Demo — `markdown-diagrams/demos/MarkdownDiagrams.vue`**
28
+
29
+ ````vue
30
+ <script setup lang="ts">
31
+ /**
32
+ * Live demo of the opt-in Mermaid fence renderer. The viewer is handed
33
+ * `mermaidFenceRenderers`, so the ` ```mermaid ` fenced code block in the source
34
+ * renders as a diagram. The plain ` ```ts ` block stays a normal code block — an
35
+ * unregistered fence language is untouched.
36
+ */
37
+ import { computed, ref } from 'vue';
38
+ import { CoarMarkdown } from '@cocoar/vue-markdown';
39
+ import { mermaidFenceRenderers } from '@cocoar/vue-markdown-mermaid';
40
+ import { parse } from '@cocoar/vue-markdown-core';
41
+
42
+ // Note: assembled from parts so the ```mermaid fences here are never seen by the
43
+ // docs' own vitepress-plugin-mermaid (which only scans the page's own markdown).
44
+ const source = [
45
+ '# Release flow',
46
+ '',
47
+ 'A `mermaid` fenced block renders as a diagram:',
48
+ '',
49
+ '```mermaid',
50
+ 'flowchart LR',
51
+ ' A[Author writes markdown] --> B{Renderer registered?}',
52
+ ' B -->|yes| C[Diagram]',
53
+ ' B -->|no| D[Readable code block]',
54
+ '```',
55
+ '',
56
+ 'Other code blocks are untouched:',
57
+ '',
58
+ '```ts',
59
+ 'const shipped: boolean = true;',
60
+ '```',
61
+ ].join('\n');
62
+
63
+ const doc = computed(() => parse(source));
64
+ </script>
65
+
66
+ <template>
67
+ <ClientOnly>
68
+ <div class="mmd-demo">
69
+ <CoarMarkdown :doc="doc" :fence-renderers="mermaidFenceRenderers" />
70
+ </div>
71
+ </ClientOnly>
72
+ </template>
73
+
74
+ <style scoped>
75
+ .mmd-demo {
76
+ padding: 8px 4px;
77
+ }
78
+ </style>
79
+ ````
80
+
81
+ ## How it works
82
+
83
+ The markdown packages have **no dependency on Mermaid**. `<CoarMarkdown>` exposes
84
+ an open, language-keyed **fence-renderer registry**: register a component for a
85
+ fence language and that language renders through it instead of as a plain code
86
+ block. Installing `@cocoar/vue-markdown-mermaid` and registering it is the opt-in.
87
+
88
+ - **On disk** a diagram is a fenced code block with the `mermaid` info string.
89
+ It's ordinary CommonMark — nothing custom to parse.
90
+ - **Rendering** is client-only (Mermaid needs a DOM) and lazy — Mermaid is
91
+ dynamically imported on the first diagram, so viewer pages without one never
92
+ pay for it.
93
+ - **Theming** maps Cocoar design tokens onto Mermaid's `themeVariables`, so
94
+ diagrams match the app's fonts and palette.
95
+ - **Security**: Mermaid runs with `securityLevel: 'strict'` — author diagram text
96
+ is treated as untrusted (HTML in labels is sanitized).
97
+ - **Invalid diagrams** degrade to an error box that still shows the raw source;
98
+ they never throw up to the app.
99
+
100
+ ## Install
101
+
102
+ ```bash
103
+ pnpm add @cocoar/vue-markdown-mermaid
104
+ ```
105
+
106
+ `vue` and `@cocoar/vue-markdown` are peer dependencies; `@cocoar/vue-mermaid`
107
+ (which carries Mermaid, dynamically imported on first render) comes along as a
108
+ regular dependency. Import its stylesheet once (diagram wrapper, error box, zoom
109
+ viewport):
110
+
111
+ ```ts
112
+ import '@cocoar/vue-mermaid/styles';
113
+ ```
114
+
115
+ ## Usage
116
+
117
+ Pass the ready-made registry fragment to the viewer's `fenceRenderers` prop:
118
+
119
+ ```vue
120
+ <template>
121
+ <CoarMarkdown :doc="doc" :fence-renderers="mermaidFenceRenderers" />
122
+ </template>
123
+
124
+ <script setup lang="ts">
125
+ import { parse } from '@cocoar/vue-markdown-core';
126
+ import { CoarMarkdown } from '@cocoar/vue-markdown';
127
+ import { mermaidFenceRenderers } from '@cocoar/vue-markdown-mermaid';
128
+
129
+ const doc = parse(source);
130
+ </script>
131
+ ```
132
+
133
+ A diagram in the source is just a fenced code block (shown here inside a wider
134
+ fence so it isn't rendered):
135
+
136
+ ````text
137
+ ```mermaid
138
+ flowchart LR
139
+ A[Start] --> B{Choice}
140
+ B -->|yes| C[Do it]
141
+ B -->|no| D[Skip]
142
+ ```
143
+ ````
144
+
145
+ An app-wide default works too — `app.provide(MARKDOWN_FENCE_RENDERERS_KEY, mermaidFenceRenderers)`.
146
+ A per-instance `fence-renderers` prop wins over the provided value.
147
+
148
+ ## Zoom & pan
149
+
150
+ Per-diagram options are configured on the **registry** (the fence contract only
151
+ passes `{ code, language }` to a component), via `createMermaidFenceRenderers`:
152
+
153
+ ```ts
154
+ import { createMermaidFenceRenderers } from '@cocoar/vue-markdown-mermaid';
155
+
156
+ const fenceRenderers = createMermaidFenceRenderers({ zoomable: true });
157
+ ```
158
+
159
+ Each diagram then sits in a fixed-height viewport with **+ / − / ⤢** controls,
160
+ **Ctrl/⌘ + wheel** zoom, **drag** to pan and **double-click** to reset. Plain
161
+ wheel and one-finger touch scrolling are left to the page, so a diagram never
162
+ traps the scroll. Set the height with the `--coar-mermaid-height` CSS variable
163
+ (default `420px`).
164
+
165
+ ## Registering your own fence renderer
166
+
167
+ The registry is open — any language can be mapped to any component. Register your
168
+ own (e.g. a Graphviz renderer) alongside Mermaid, or replace Mermaid entirely:
169
+
170
+ ```ts
171
+ import type { FenceRegistry } from '@cocoar/vue-markdown';
172
+ import { mermaidFenceRenderers } from '@cocoar/vue-markdown-mermaid';
173
+ import MyGraphviz from './MyGraphviz.vue';
174
+
175
+ // Component receives `{ code, language }` (FenceRendererProps).
176
+ const fenceRenderers: FenceRegistry = {
177
+ ...mermaidFenceRenderers,
178
+ dot: MyGraphviz,
179
+ };
180
+ ```
181
+
182
+ A registered component receives the fence's raw text as `code` and the info
183
+ string as `language`. This is the same seam a future BPMN or PlantUML renderer
184
+ would plug into — no change to the markdown core.
185
+
186
+ ## API reference
187
+
188
+ ### `@cocoar/vue-markdown`
189
+
190
+ | Export | Description |
191
+ |---|---|
192
+ | `FenceRegistry` | `Record<string, Component>` — fence language → renderer component. Keys match case-insensitively. |
193
+ | `FenceRendererProps` | `{ code: string; language: string }` — props a registered renderer receives. |
194
+ | `MARKDOWN_FENCE_RENDERERS_KEY` | Inject key for an app-wide registry. |
195
+ | `resolveFenceRenderer(registry, language)` | The case-insensitive lookup used by `DefaultCodeBlock`. |
196
+
197
+ ### `@cocoar/vue-markdown-mermaid` (the fence adapter)
198
+
199
+ | Export | Description |
200
+ |---|---|
201
+ | `mermaidFenceRenderers` | Ready-to-spread `FenceRegistry` fragment (`{ mermaid }`), no zoom. |
202
+ | `createMermaidFenceRenderers(options?)` | Build a registry with options baked in — `{ zoomable }`. |
203
+
204
+ ### `@cocoar/vue-mermaid` (the standalone renderer)
205
+
206
+ | Export | Description |
207
+ |---|---|
208
+ | `CoarMermaidDiagram` | The renderer component (`{ code, language, zoomable }` props). Use directly to render diagrams outside markdown. |
209
+ | `buildMermaidThemeVariables(getToken, resolveColor?)` | Pure Cocoar-token → Mermaid-theme mapping. |
210
+ | `makeCssColorResolver()` / `readCssTokens(el?)` | Browser-backed color/token resolvers for the bridge. |