@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,276 @@
1
+ <!-- Generated from apps/docs/components/markdown.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
2
+
3
+ # Markdown
4
+
5
+ Render markdown content with Cocoar Design System styling. The system is split into two packages: a framework-agnostic parser and a Vue component.
6
+
7
+ > **Info: Separate Packages**
8
+ >
9
+ ```bash
10
+ pnpm add @cocoar/vue-markdown @cocoar/vue-markdown-core
11
+ ```
12
+ >
13
+ > Then load the shared block stylesheet **once** at app entry, alongside `@cocoar/vue-ui/styles`:
14
+ >
15
+ ```css
16
+ /* app/main.css */
17
+ @import "@cocoar/vue-ui/styles";
18
+ @import "@cocoar/vue-markdown/styles";
19
+ ```
20
+ >
21
+ > The same stylesheet is consumed by `@cocoar/vue-markdown-editor` — viewer and editor render identical output for every node type.
22
+
23
+ ## Quick Start
24
+
25
+ Parse a markdown string, then render it:
26
+
27
+ ```vue
28
+ <template>
29
+ <CoarMarkdown :doc="doc" />
30
+ </template>
31
+
32
+ <script setup lang="ts">
33
+ import { parse } from '@cocoar/vue-markdown-core';
34
+ import { CoarMarkdown } from '@cocoar/vue-markdown';
35
+
36
+ const doc = parse(`
37
+ # Hello World
38
+
39
+ This is **bold** and *italic* text with a [link](https://example.com).
40
+
41
+ - Item one
42
+ - Item two
43
+ - Item three
44
+ `, { gfm: true });
45
+ </script>
46
+ ```
47
+
48
+ ## Parsing (`@cocoar/vue-markdown-core`)
49
+
50
+ ### `parse(markdown, options?)`
51
+
52
+ Converts a markdown string into a `MarkdownDocument` tree.
53
+
54
+ ```ts
55
+ import { parse } from '@cocoar/vue-markdown-core';
56
+
57
+ const doc = parse('# Title\n\nParagraph text.', { gfm: true });
58
+ ```
59
+
60
+ | Option | Type | Default | Description |
61
+ |--------|------|---------|-------------|
62
+ | `gfm` | `boolean` | `false` | Enable GitHub Flavored Markdown (tables, strikethrough, task lists) |
63
+
64
+ ### `serialize(doc, options?)`
65
+
66
+ Convert a document tree back to a markdown string:
67
+
68
+ ```ts
69
+ import { serialize } from '@cocoar/vue-markdown-core';
70
+
71
+ const markdown = serialize(doc, { gfm: true });
72
+ ```
73
+
74
+ ### `transform(doc, ...transforms)`
75
+
76
+ Apply transformations to the document tree:
77
+
78
+ ```ts
79
+ import { parse, transform, type MarkdownTransform } from '@cocoar/vue-markdown-core';
80
+
81
+ const addPrefix: MarkdownTransform = (doc) => ({
82
+ ...doc,
83
+ nodes: doc.nodes.map(node => {
84
+ if (node.type === 'heading') {
85
+ return { ...node, text: `[Docs] ${node.text}` };
86
+ }
87
+ return node;
88
+ }),
89
+ });
90
+
91
+ const doc = transform(parse(markdown), addPrefix);
92
+ ```
93
+
94
+ ## Rendering (`@cocoar/vue-markdown`)
95
+
96
+ ### `CoarMarkdown`
97
+
98
+ | Prop | Type | Description |
99
+ |------|------|-------------|
100
+ | `doc` | `MarkdownDocument` | Pre-parsed markdown document |
101
+ | `renderers` | `MarkdownViewerRenderers` | _(optional)_ Per-instance renderer override. See [Custom renderers](#custom-renderers-registry) below. |
102
+ | `embeds` | `EmbedRegistry` | _(optional)_ Custom-embed registry — renders `:::key{props}` directives via your components. See [Custom Embeds](./markdown-embeds.md). |
103
+ | `fenceRenderers` | `FenceRegistry` | _(optional)_ Fenced-code-block renderer registry — render a fence language (e.g. ` ```mermaid `) with a rich component instead of a plain code block. Unregistered languages stay code blocks. See [Diagrams](./markdown-diagrams.md). |
104
+
105
+ ### Custom renderers (registry)
106
+
107
+ Every node type — headings, paragraphs, code blocks, tables, lists, even inline marks like `<em>` — is rendered by a swappable Vue component. The package exports the full default registry so you can override **just one slot** while keeping the rest of the Cocoar look:
108
+
109
+ ```vue
110
+ <script setup lang="ts">
111
+ import { CoarMarkdown, defaultMarkdownRenderers } from '@cocoar/vue-markdown';
112
+ import MyHighlightedCodeBlock from './MyHighlightedCodeBlock.vue';
113
+
114
+ const renderers = {
115
+ ...defaultMarkdownRenderers,
116
+ codeBlock: MyHighlightedCodeBlock, // Swap just the code-block slot
117
+ };
118
+ </script>
119
+
120
+ <template>
121
+ <CoarMarkdown :doc="doc" :renderers="renderers" />
122
+ </template>
123
+ ```
124
+
125
+ For app-wide overrides, `provide` the registry once at startup:
126
+
127
+ ```ts
128
+ import { MARKDOWN_RENDERERS_KEY, defaultMarkdownRenderers } from '@cocoar/vue-markdown';
129
+ app.provide(MARKDOWN_RENDERERS_KEY, {
130
+ ...defaultMarkdownRenderers,
131
+ codeBlock: MyHighlightedCodeBlock,
132
+ });
133
+ ```
134
+
135
+ Resolution order: per-instance prop → app-level inject → built-in defaults.
136
+
137
+ #### Renderer contract
138
+
139
+ Each renderer receives:
140
+
141
+ ```ts
142
+ interface MarkdownRendererProps {
143
+ /** The AST node currently being rendered. */
144
+ node: MarkdownNode;
145
+ /** Recursive child renderer — call to render `node.children`. */
146
+ renderChildren: () => VNode[];
147
+ /** Render an arbitrary list of nodes through the registry. Used by `DefaultTable`
148
+ * to render each cell's inline content while keeping the `<thead>/<tbody>` shape
149
+ * under the renderer's control. */
150
+ renderNodes: (nodes: readonly MarkdownNode[]) => VNode[];
151
+ }
152
+ ```
153
+
154
+ A custom renderer is a regular Vue component that emits the right semantic HTML for its node type. Use `renderChildren()` for the typical "wrap children in a tag" case; reach for `renderNodes(...)` only when the rendered structure isn't a flat children list (the GFM table is the canonical example).
155
+
156
+ #### Why the registry matters
157
+
158
+ The same registry is consumed by `@cocoar/vue-markdown-editor`. Overriding `codeBlock` in your app's `provide` flips the rendering both in the viewer **and** in the editor's render mode (the cursor-out state of the in-editor code block). Output stays in sync without any duplicated wiring.
159
+
160
+ ### Supported Elements
161
+
162
+ | Markdown | HTML | Notes |
163
+ |----------|------|-------|
164
+ | `# Heading` | `<h1>` - `<h6>` | With anchor IDs |
165
+ | `**bold**` | `<strong>` | |
166
+ | `*italic*` | `<em>` | |
167
+ | `` `code` `` | `<code>` | Inline code |
168
+ | Code blocks | `<CoarCodeBlock>` | With language highlighting |
169
+ | `[text](url)` | `<a>` | External links open in new tab |
170
+ | `![alt](src)` | `<img>` | Lazy loaded |
171
+ | `> quote` | `<blockquote>` | Styled with left border |
172
+ | Lists | `<ul>` / `<ol>` | Including task lists |
173
+ | Tables | `<CoarTable>` | GFM tables with alignment |
174
+ | `~~strike~~` | `<del>` | GFM strikethrough |
175
+ | `---` | `<hr>` | Thematic break |
176
+
177
+ ### Links
178
+
179
+ Links are handled intelligently:
180
+ - **External** (`https://...`): Opens in new tab with `rel="noopener noreferrer"`
181
+ - **Hash anchors** (`#section`): Resolved relative to current page
182
+ - **Relative paths** (`./page`): Local navigation
183
+
184
+ ## Frontmatter
185
+
186
+ A leading YAML frontmatter block (`---` … `---`) is parsed into a single `frontmatter` node and rendered as muted, italic `key: value` lines (styled like disabled text, so it reads as metadata rather than body content). Without this, CommonMark mis-reads the block as a thematic break followed by a setext heading, collapsing the entire YAML onto one line.
187
+
188
+ ```ts
189
+ import { parse } from '@cocoar/vue-markdown-core';
190
+
191
+ const doc = parse(`---
192
+ title: Release notes
193
+ tags:
194
+ - editor
195
+ - markdown
196
+ ---
197
+
198
+ # Heading
199
+ `);
200
+
201
+ doc.nodes[0].type; // 'frontmatter'
202
+ doc.nodes[0].attrs.data; // { title: 'Release notes', tags: ['editor', 'markdown'] }
203
+ doc.nodes[0].attrs.entries; // [{ key: 'title', value: 'Release notes' }, { key: 'tags', value: 'editor, markdown' }]
204
+ doc.nodes[0].attrs.raw; // the original YAML text (used for round-trip via serialize())
205
+ ```
206
+
207
+ The node carries three attrs: `raw` (verbatim YAML, the round-trip source), `data` (the parsed object, or `null` for non-map/invalid YAML), and `entries` (flattened, display-ready key/value rows). The default `DefaultFrontmatter` renderer reads `entries`; on a parse failure it falls back to the raw text so nothing is hidden. Override it like any other slot via `MARKDOWN_RENDERERS_KEY` (key: `frontmatter`).
208
+
209
+ `@cocoar/vue-markdown-editor` renders it the **same** way and round-trips the block on save — see the [editor's Frontmatter section](./markdown-editor.md#frontmatter).
210
+
211
+ ## Custom Embeds
212
+
213
+ Render your own Vue components from a `:::key{props}` directive by passing an
214
+ `embeds` registry. The same registry works in `<CoarMarkdownEditor>`, where the
215
+ embed becomes editable — so a document looks consistent whether read or written.
216
+
217
+ ```vue
218
+ <CoarMarkdown :doc="doc" :embeds="embeds" />
219
+ ```
220
+
221
+ See the dedicated **[Custom Embeds](./markdown-embeds.md)** page for the
222
+ registry shape, security model, and a live editor + viewer demo.
223
+
224
+ ## Diagrams
225
+
226
+ Render a fenced code block — ` ```mermaid ` — as a diagram by passing a
227
+ `fenceRenderers` registry. An unregistered fence language stays a plain,
228
+ syntax-highlighted code block, so the markdown stays portable.
229
+
230
+ ```vue
231
+ <CoarMarkdown :doc="doc" :fence-renderers="fenceRenderers" />
232
+ ```
233
+
234
+ See the dedicated **[Diagrams](./markdown-diagrams.md)** page for the
235
+ opt-in `@cocoar/vue-markdown-mermaid` package, theming, zoom/pan and how to
236
+ register your own fence renderer.
237
+
238
+ ## Node Types
239
+
240
+ ```ts
241
+ type MarkdownNodeType =
242
+ | 'frontmatter'
243
+ | 'heading' | 'paragraph' | 'blockquote'
244
+ | 'list' | 'listItem'
245
+ | 'codeBlock' | 'table' | 'tableRow' | 'tableCell'
246
+ | 'thematicBreak' | 'lineBreak'
247
+ | 'text' | 'emphasis' | 'strong' | 'strikethrough'
248
+ | 'inlineCode' | 'link' | 'image'
249
+ | 'colorSpan' | 'embed';
250
+
251
+ interface MarkdownNode {
252
+ id: string;
253
+ type: MarkdownNodeType;
254
+ children?: readonly MarkdownNode[];
255
+ text?: string;
256
+ attrs?: Record<string, unknown>;
257
+ position?: MarkdownPosition;
258
+ }
259
+
260
+ interface MarkdownDocument {
261
+ nodes: readonly MarkdownNode[];
262
+ }
263
+ ```
264
+
265
+ ## Theming
266
+
267
+ The markdown component uses CSS custom properties for theming:
268
+
269
+ ```css
270
+ /* Override in your app */
271
+ .coar-markdown {
272
+ --coar-markdown-text: var(--coar-text-neutral-primary);
273
+ --coar-markdown-link: var(--coar-text-accent);
274
+ --coar-markdown-border: var(--coar-border-neutral);
275
+ }
276
+ ```
@@ -0,0 +1,380 @@
1
+ <!-- Generated from apps/docs/components/menu.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
2
+
3
+ # Menu
4
+
5
+ Build context menus, action lists, and navigation panels with full keyboard support. Menus group related actions together, making them easy to discover and interact with. They support nested submenus, icons, section headings, and danger variants for destructive actions.
6
+
7
+ ```ts
8
+ import { CoarMenu, CoarMenuItem, CoarMenuDivider, CoarMenuHeading, CoarSubExpand, CoarSubFlyout } from '@cocoar/vue-ui';
9
+ ```
10
+
11
+ ## Basic Menu
12
+
13
+ The simplest menu: a list of clickable items separated by dividers. Individual items can be disabled when an action is not available.
14
+
15
+ **Demo — `menu/demos/MenuBasic.vue`**
16
+
17
+ ```vue
18
+ <template>
19
+ <div>
20
+ <CoarMenu>
21
+ <CoarMenuItem @clicked="handleClick('New File')">New File</CoarMenuItem>
22
+ <CoarMenuItem @clicked="handleClick('Open...')">Open...</CoarMenuItem>
23
+ <CoarMenuDivider />
24
+ <CoarMenuItem @clicked="handleClick('Save')">Save</CoarMenuItem>
25
+ <CoarMenuItem @clicked="handleClick('Save As...')">Save As...</CoarMenuItem>
26
+ <CoarMenuDivider />
27
+ <CoarMenuItem :disabled="true">Export (disabled)</CoarMenuItem>
28
+ </CoarMenu>
29
+ <p style="margin-top: 8px; font-size: 13px; color: var(--coar-text-neutral-secondary);">Last clicked: {{ lastClicked || 'none' }}</p>
30
+ </div>
31
+ </template>
32
+
33
+ <script setup lang="ts">
34
+ import { ref } from 'vue';
35
+ import { CoarMenu, CoarMenuItem, CoarMenuDivider } from '@cocoar/vue-ui';
36
+
37
+ const lastClicked = ref('');
38
+
39
+ function handleClick(item: string) {
40
+ lastClicked.value = item;
41
+ }
42
+ </script>
43
+ ```
44
+
45
+ ## With Headings
46
+
47
+ Use `CoarMenuHeading` to organize a longer menu into labeled sections. This helps users scan for the action they need without reading every item.
48
+
49
+ **Demo — `menu/demos/MenuHeadings.vue`**
50
+
51
+ ```vue
52
+ <template>
53
+ <CoarMenu>
54
+ <CoarMenuHeading>File</CoarMenuHeading>
55
+ <CoarMenuItem>New</CoarMenuItem>
56
+ <CoarMenuItem>Open</CoarMenuItem>
57
+ <CoarMenuItem>Recent</CoarMenuItem>
58
+ <CoarMenuDivider />
59
+ <CoarMenuHeading>Edit</CoarMenuHeading>
60
+ <CoarMenuItem>Cut</CoarMenuItem>
61
+ <CoarMenuItem>Copy</CoarMenuItem>
62
+ <CoarMenuItem>Paste</CoarMenuItem>
63
+ </CoarMenu>
64
+ </template>
65
+
66
+ <script setup lang="ts">
67
+ import { CoarMenu, CoarMenuItem, CoarMenuDivider, CoarMenuHeading } from '@cocoar/vue-ui';
68
+ </script>
69
+ ```
70
+
71
+ ## With Icons
72
+
73
+ Leading icons give each item a visual anchor, making menus faster to scan. Use a trash icon on destructive actions like "Delete" to signal their intent.
74
+
75
+ **Demo — `menu/demos/MenuIcons.vue`**
76
+
77
+ ```vue
78
+ <template>
79
+ <CoarMenu>
80
+ <CoarMenuItem icon="plus">New File</CoarMenuItem>
81
+ <CoarMenuItem icon="copy">Duplicate</CoarMenuItem>
82
+ <CoarMenuItem icon="clipboard">Paste</CoarMenuItem>
83
+ <CoarMenuDivider />
84
+ <CoarMenuItem icon="settings">Settings</CoarMenuItem>
85
+ <CoarMenuItem icon="trash-2">Delete</CoarMenuItem>
86
+ </CoarMenu>
87
+ </template>
88
+
89
+ <script setup lang="ts">
90
+ import { CoarMenu, CoarMenuItem, CoarMenuDivider } from '@cocoar/vue-ui';
91
+ </script>
92
+ ```
93
+
94
+ ## Nested Submenus
95
+
96
+ When a menu item leads to a group of related options, wrap them in `CoarSubExpand`. Submenus expand inline, keeping the user in context without opening a separate overlay.
97
+
98
+ **Demo — `menu/demos/MenuSubmenus.vue`**
99
+
100
+ ```vue
101
+ <template>
102
+ <CoarMenu>
103
+ <CoarMenuItem @clicked="handleClick('Dashboard')">Dashboard</CoarMenuItem>
104
+ <CoarSubExpand label="Settings">
105
+ <CoarMenuItem @clicked="handleClick('Profile')">Profile</CoarMenuItem>
106
+ <CoarMenuItem @clicked="handleClick('Security')">Security</CoarMenuItem>
107
+ <CoarMenuItem @clicked="handleClick('Notifications')">Notifications</CoarMenuItem>
108
+ </CoarSubExpand>
109
+ <CoarSubExpand label="Reports">
110
+ <CoarMenuItem @clicked="handleClick('Sales')">Sales</CoarMenuItem>
111
+ <CoarMenuItem @clicked="handleClick('Traffic')">Traffic</CoarMenuItem>
112
+ </CoarSubExpand>
113
+ <CoarMenuDivider />
114
+ <CoarMenuItem @clicked="handleClick('Logout')">Logout</CoarMenuItem>
115
+ </CoarMenu>
116
+ </template>
117
+
118
+ <script setup lang="ts">
119
+ import { ref } from 'vue';
120
+ import { CoarMenu, CoarMenuItem, CoarMenuDivider, CoarSubExpand } from '@cocoar/vue-ui';
121
+
122
+ const lastClicked = ref('');
123
+
124
+ function handleClick(item: string) {
125
+ lastClicked.value = item;
126
+ }
127
+ </script>
128
+ ```
129
+
130
+ ## Flyout Submenus
131
+
132
+ Use `CoarSubFlyout` when the submenu should appear as a floating panel beside the trigger instead of expanding inline. The `label` prop sets the visible text; child items go in the default slot and render inside the flyout.
133
+
134
+ **Demo — `menu/demos/MenuFlyout.vue`**
135
+
136
+ ```vue
137
+ <template>
138
+ <CoarMenu>
139
+ <CoarMenuItem icon="home" @clicked="handleClick('Home')">Home</CoarMenuItem>
140
+ <CoarSubFlyout label="Account" icon="user">
141
+ <CoarMenu>
142
+ <CoarMenuItem icon="user" @clicked="handleClick('Profile')">Profile</CoarMenuItem>
143
+ <CoarMenuItem icon="shield" @clicked="handleClick('Security')">Security</CoarMenuItem>
144
+ <CoarMenuItem icon="bell" @clicked="handleClick('Notifications')">Notifications</CoarMenuItem>
145
+ </CoarMenu>
146
+ </CoarSubFlyout>
147
+ <CoarSubFlyout label="Reports" icon="chart-bar">
148
+ <CoarMenu>
149
+ <CoarMenuItem @clicked="handleClick('Sales')">Sales</CoarMenuItem>
150
+ <CoarMenuItem @clicked="handleClick('Traffic')">Traffic</CoarMenuItem>
151
+ </CoarMenu>
152
+ </CoarSubFlyout>
153
+ <CoarMenuDivider />
154
+ <CoarMenuItem icon="log-out" @clicked="handleClick('Logout')">Logout</CoarMenuItem>
155
+ </CoarMenu>
156
+ </template>
157
+
158
+ <script setup lang="ts">
159
+ import { ref } from 'vue';
160
+ import { CoarMenu, CoarMenuItem, CoarMenuDivider, CoarSubFlyout } from '@cocoar/vue-ui';
161
+
162
+ const lastClicked = ref('');
163
+
164
+ function handleClick(item: string) {
165
+ lastClicked.value = item;
166
+ }
167
+ </script>
168
+ ```
169
+
170
+ ## Borderless (Sidebar)
171
+
172
+ Pass the `borderless` prop when embedding a menu inside a sidebar, panel, or card. This removes the outer border and background so the menu blends into its container.
173
+
174
+ **Demo — `menu/demos/MenuBorderless.vue`**
175
+
176
+ ```vue
177
+ <template>
178
+ <div style="background: var(--coar-background-neutral-secondary); border-radius: 8px; padding: 8px;">
179
+ <CoarMenu borderless>
180
+ <CoarMenuHeading>Navigation</CoarMenuHeading>
181
+ <CoarMenuItem icon="home">Home</CoarMenuItem>
182
+ <CoarMenuItem icon="user">Profile</CoarMenuItem>
183
+ <CoarMenuItem icon="settings">Settings</CoarMenuItem>
184
+ </CoarMenu>
185
+ </div>
186
+ </template>
187
+
188
+ <script setup lang="ts">
189
+ import { CoarMenu, CoarMenuItem, CoarMenuHeading } from '@cocoar/vue-ui';
190
+ </script>
191
+ ```
192
+
193
+ ## Router-aware items
194
+
195
+ Pass `to` to `CoarMenuItem` to render it as a real `<a href>` link instead of `<div role="menuitem">`. Same payoff as on the sidebar: middle-click and Ctrl/Cmd-click open the destination in a new tab via the browser's native handling, right-click exposes "Open in new tab" / "Copy link address", and screenreaders announce "link to {label}". `role="menuitem"` is preserved on the `<a>` branch (the parent `CoarMenu` is `role="menu"`, so the role pairing is WAI-ARIA-correct).
196
+
197
+ ```vue
198
+ <CoarMenu>
199
+ <CoarMenuItem icon="user" label="Profile" to="/profile" />
200
+ <CoarMenuItem icon="settings" label="Settings" to="/settings" />
201
+ <CoarMenuDivider />
202
+ <CoarMenuItem icon="log-out" label="Logout" @clicked="auth.signOut()" />
203
+ </CoarMenu>
204
+ ```
205
+
206
+ **Modifier-clicks do NOT auto-close the menu.** When the user Ctrl/Cmd/Middle-clicks a link item the browser opens a new tab natively and the menu stays open — matches the macOS Finder / Chrome bookmarks bar pattern, lets the user fire several link items in a row without re-opening the menu. Plain click still triggers SPA navigation and auto-closes the menu (subject to `keepMenuOpen()` in your `@clicked` handler).
207
+
208
+ > **Info**
209
+ >
210
+ > `vue-router` is declared as an **optional `peerDependenciesMeta`** entry of `@cocoar/vue-ui` — install it for SPA routing, omit it for click-emit-only use. Items without `to` keep the original `<div role="menuitem">` rendering and the `@clicked` event pathway exactly as before.
211
+
212
+ > **Warning: Object `to` without router**
213
+ >
214
+ > Passing an object literal (`:to="{ name: 'profile' }"`) when no router is installed falls back to `String(to)`, producing `href="[object Object]"` — a broken link. The component logs a DEV-only `console.warn` once per component instance to make this loud at dev-time. Pass a string path for the no-router case.
215
+
216
+ ## Active state
217
+
218
+ Use `active` to mark a menu item as the current selection — view-mode toggles, settings checkmarks, sort-direction indicators. The library renders the `coar-menu-item--active` class + `aria-current="page"`. When `to` is set and `active` is omitted, the state follows `<RouterLink>`'s `isActive` automatically.
219
+
220
+ ```vue
221
+ <CoarMenu>
222
+ <CoarMenuItem icon="list" :active="view === 'list'" @clicked="view = 'list'">
223
+ List view
224
+ </CoarMenuItem>
225
+ <CoarMenuItem icon="grid" :active="view === 'grid'" @clicked="view = 'grid'">
226
+ Grid view
227
+ </CoarMenuItem>
228
+ </CoarMenu>
229
+ ```
230
+
231
+ The menu still auto-closes on click — active styling is meaningful while the menu is open (the user sees what's currently selected), then the menu closes and reopens later showing the new selection as active.
232
+
233
+ ### Keyboard support on link items
234
+
235
+ When `to` is set the menu item renders as `<a href>`, so the keyboard pathway differs slightly from action items:
236
+
237
+ - **Enter** — delegated to the browser, which fires a native click on the `<a>`. RouterLink navigation runs, the menu auto-closes.
238
+ - **Space** — synthesizes a click on the underlying anchor element (Space does not natively activate `<a>` in any browser). Same outcome as Enter / mouse click: navigation + auto-close.
239
+ - **Modifier+Enter** — browser opens a new tab natively; the menu stays open so the user can fire additional link items.
240
+
241
+ `@clicked` fires for all three pathways. `keepMenuOpen()` still suppresses auto-close on the link path.
242
+
243
+ ## Scrollable Menu
244
+
245
+ When a menu has many items, it scrolls automatically. Use `#header` and `#footer` slots for fixed content above and below the scrollable area. `CoarMenuHeading` supports a `sticky` prop to keep section headers visible while scrolling.
246
+
247
+ **Demo — `menu/demos/MenuScrollable.vue`**
248
+
249
+ ```vue
250
+ <template>
251
+ <div style="display: flex; flex-direction: column; gap: 12px;">
252
+ <CoarCheckbox v-model="sticky" label="sticky headings" />
253
+ <div style="height: 280px; width: 240px;">
254
+ <CoarMenu style="height: 100%; width: 100%;">
255
+ <template #header>
256
+ <div style="padding: 8px;">
257
+ <input
258
+ v-model="filter"
259
+ type="text"
260
+ placeholder="Filter..."
261
+ style="width: 100%; box-sizing: border-box; padding: 4px 8px; border: 1px solid var(--coar-border-input); border-radius: var(--coar-radius-xs); background: var(--coar-surface-input); font-size: 13px; outline: none;"
262
+ />
263
+ </div>
264
+ </template>
265
+
266
+ <template v-for="item in filteredItems" :key="item.label">
267
+ <CoarMenuDivider v-if="item.divider" />
268
+ <CoarMenuHeading v-else-if="item.heading" :label="item.label" :sticky="sticky" />
269
+ <CoarMenuItem v-else :icon="item.icon" :label="item.label" />
270
+ </template>
271
+
272
+ <template #footer>
273
+ <CoarMenuItem icon="plus" label="New project..." />
274
+ </template>
275
+ </CoarMenu>
276
+ </div>
277
+ </div>
278
+ </template>
279
+
280
+ <script setup lang="ts">
281
+ import { ref, computed } from 'vue';
282
+ import { CoarMenu, CoarMenuItem, CoarMenuDivider, CoarMenuHeading, CoarCheckbox } from '@cocoar/vue-ui';
283
+
284
+ const filter = ref('');
285
+ const sticky = ref(false);
286
+
287
+ const items = [
288
+ { heading: true, label: 'Navigation' },
289
+ { icon: 'home', label: 'Dashboard' },
290
+ { icon: 'users', label: 'Users' },
291
+ { icon: 'settings', label: 'Settings' },
292
+ { icon: 'bell', label: 'Notifications' },
293
+ { divider: true, label: 'd1' },
294
+ { heading: true, label: 'Projects' },
295
+ { icon: 'folder', label: 'Frontend' },
296
+ { icon: 'folder', label: 'Backend' },
297
+ { icon: 'folder', label: 'Mobile App' },
298
+ { icon: 'folder', label: 'Design System' },
299
+ { icon: 'folder', label: 'Documentation' },
300
+ { divider: true, label: 'd2' },
301
+ { heading: true, label: 'Admin' },
302
+ { icon: 'database', label: 'Database' },
303
+ { icon: 'code', label: 'API Keys' },
304
+ { icon: 'clipboard', label: 'Audit Log' },
305
+ { icon: 'download', label: 'Exports' },
306
+ ];
307
+
308
+ const filteredItems = computed(() => {
309
+ const q = filter.value.toLowerCase().trim();
310
+ if (!q) return items;
311
+ return items.filter((item) =>
312
+ item.heading || item.divider || item.label.toLowerCase().includes(q),
313
+ );
314
+ });
315
+ </script>
316
+ ```
317
+
318
+ ## Accessibility
319
+
320
+ ### Keyboard Navigation
321
+
322
+ | Key | Action |
323
+ |-----|--------|
324
+ | `Arrow Up` / `Arrow Down` | Move focus between items |
325
+ | `Enter` / `Space` | Activate focused item |
326
+ | `Escape` | Close nested submenus |
327
+ | `Tab` | Move focus out of the menu |
328
+
329
+ ## API
330
+
331
+ ### CoarMenu Props
332
+
333
+ | Prop | Type | Default | Description |
334
+ |------|------|---------|-------------|
335
+ | `showIconColumn` | `boolean` | `true` | Reserve icon column to prevent layout shift |
336
+ | `borderless` | `boolean` | `false` | Remove outer border/background |
337
+
338
+ ### CoarMenu Slots
339
+
340
+ | Slot | Description |
341
+ |------|-------------|
342
+ | `default` | Menu items (scrollable area) |
343
+ | `header` | Fixed content above the scrollable area |
344
+ | `footer` | Fixed content below the scrollable area |
345
+
346
+ ### CoarMenuHeading Props
347
+
348
+ | Prop | Type | Default | Description |
349
+ |------|------|---------|-------------|
350
+ | `label` | `string` | `undefined` | Heading text (alternative to default slot) |
351
+ | `sticky` | `boolean` | `false` | Stick to top of scroll container |
352
+
353
+ ### CoarMenuItem Props
354
+
355
+ | Prop | Type | Default | Description |
356
+ |------|------|---------|-------------|
357
+ | `label` | `string` | `undefined` | Item label text (alternative to default slot) |
358
+ | `icon` | `string` | `undefined` | Leading icon name |
359
+ | `to` | `RouteLocationRaw \| string` | `undefined` | Vue Router target. When set, renders as `<a href>` via `<RouterLink>` (or plain `<a>` if no router is installed). Modifier-clicks open a new tab without closing the menu. See [Router-aware items](#router-aware-items). |
360
+ | `active` | `boolean` | `undefined` | Mark the item as the current selection (view-mode toggle, settings checkmark, sort indicator). Renders `coar-menu-item--active` + `aria-current="page"`. Defaults to `<RouterLink>`'s `isActive` when `to` is set; explicit value always wins. The menu still auto-closes on click — active state is meaningful WHILE the menu is open. |
361
+ | `disabled` | `boolean` | `false` | Disable the item |
362
+
363
+ ### CoarMenuItem Slots
364
+
365
+ | Slot | Description |
366
+ |------|-------------|
367
+ | `default` | Item label content |
368
+
369
+ ### CoarMenuItem Events
370
+
371
+ | Event | Payload | Description |
372
+ |-------|---------|-------------|
373
+ | `clicked` | `MenuItemClickEvent` | Emitted when item is clicked |
374
+
375
+ ```ts
376
+ interface MenuItemClickEvent {
377
+ event: MouseEvent;
378
+ keepMenuOpen(): void; // Call to prevent auto-close of the menu tree
379
+ }
380
+ ```