@cocoar/vue-ui 3.1.0 → 3.2.0-beta.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/checkbox/CoarCheckbox.vue.d.ts +1 -1
- package/dist/components/checkbox/CoarCheckbox.vue.d.ts.map +1 -1
- package/dist/components/code-block/CoarCodeBlock.vue.d.ts +1 -1
- package/dist/components/code-block/CoarCodeBlock.vue.d.ts.map +1 -1
- package/dist/components/data-list/CoarDataList.vue.d.ts +158 -0
- package/dist/components/data-list/CoarDataList.vue.d.ts.map +1 -0
- package/dist/components/data-list/CoarDataListToolbar.vue.d.ts +30 -0
- package/dist/components/data-list/CoarDataListToolbar.vue.d.ts.map +1 -0
- package/dist/components/data-list/data-list-builder.d.ts +288 -0
- package/dist/components/data-list/data-list-builder.d.ts.map +1 -0
- package/dist/components/data-list/index.d.ts +12 -0
- package/dist/components/data-list/index.d.ts.map +1 -0
- package/dist/components/data-list/internal/compare.d.ts +14 -0
- package/dist/components/data-list/internal/compare.d.ts.map +1 -0
- package/dist/components/data-list/internal/reorder-core.d.ts +46 -0
- package/dist/components/data-list/internal/reorder-core.d.ts.map +1 -0
- package/dist/components/data-list/internal/search.d.ts +16 -0
- package/dist/components/data-list/internal/search.d.ts.map +1 -0
- package/dist/components/data-list/internal/useDataListLines.d.ts +92 -0
- package/dist/components/data-list/internal/useDataListLines.d.ts.map +1 -0
- package/dist/components/data-list/internal/useDataListReorder.d.ts +68 -0
- package/dist/components/data-list/internal/useDataListReorder.d.ts.map +1 -0
- package/dist/components/data-list/internal/useSearchHighlight.d.ts +19 -0
- package/dist/components/data-list/internal/useSearchHighlight.d.ts.map +1 -0
- package/dist/components/data-list/types.d.ts +185 -0
- package/dist/components/data-list/types.d.ts.map +1 -0
- package/dist/components/data-list/useDataListModel.d.ts +79 -0
- package/dist/components/data-list/useDataListModel.d.ts.map +1 -0
- package/dist/components/date-time/_shared/maskito-config.d.ts +7 -2
- package/dist/components/date-time/_shared/maskito-config.d.ts.map +1 -1
- package/dist/components/date-time/_shared/time-helpers.d.ts +11 -4
- package/dist/components/date-time/_shared/time-helpers.d.ts.map +1 -1
- package/dist/components/date-time/_shared/use-date-picker-base.d.ts +1 -1
- package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts +1 -1
- package/dist/components/date-time/plain-date-picker/CoarPlainDatePicker.vue.d.ts.map +1 -1
- package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts +1 -1
- package/dist/components/date-time/plain-date-time-picker/CoarPlainDateTimePicker.vue.d.ts.map +1 -1
- package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts +1 -1
- package/dist/components/date-time/zoned-date-time-picker/CoarZonedDateTimePicker.vue.d.ts.map +1 -1
- package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts +3 -0
- package/dist/components/dual-listbox/CoarDualListbox.vue.d.ts.map +1 -1
- package/dist/components/form-field/CoarFormField.vue.d.ts +1 -1
- package/dist/components/form-field/CoarFormField.vue.d.ts.map +1 -1
- package/dist/components/icon/CoarIcon.vue.d.ts +2 -2
- package/dist/components/icon/CoarIcon.vue.d.ts.map +1 -1
- package/dist/components/listbox/CoarListbox.vue.d.ts +7 -0
- package/dist/components/listbox/CoarListbox.vue.d.ts.map +1 -1
- package/dist/components/number-input/CoarNumberInput.vue.d.ts +1 -1
- package/dist/components/number-input/CoarNumberInput.vue.d.ts.map +1 -1
- package/dist/components/otp-input/CoarOtpInput.vue.d.ts +1 -1
- package/dist/components/otp-input/CoarOtpInput.vue.d.ts.map +1 -1
- package/dist/components/password-input/CoarPasswordInput.vue.d.ts +1 -1
- package/dist/components/password-input/CoarPasswordInput.vue.d.ts.map +1 -1
- package/dist/components/switch/CoarSwitch.vue.d.ts +1 -1
- package/dist/components/switch/CoarSwitch.vue.d.ts.map +1 -1
- package/dist/components/text-input/CoarTextInput.vue.d.ts +1 -1
- package/dist/components/text-input/CoarTextInput.vue.d.ts.map +1 -1
- package/dist/composables/dragRegistry.d.ts +12 -0
- package/dist/composables/dragRegistry.d.ts.map +1 -1
- package/dist/composables/index.d.ts +2 -2
- package/dist/composables/index.d.ts.map +1 -1
- package/dist/composables/useDragDrop.d.ts +62 -6
- package/dist/composables/useDragDrop.d.ts.map +1 -1
- package/dist/composables/useVirtualList.d.ts +21 -1
- package/dist/composables/useVirtualList.d.ts.map +1 -1
- package/dist/index.css +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4508 -2039
- package/package.json +5 -3
- package/skills/cocoar-vue-ui/SKILL.md +244 -0
- package/skills/cocoar-vue-ui/references/components/avatar.md +178 -0
- package/skills/cocoar-vue-ui/references/components/badge.md +290 -0
- package/skills/cocoar-vue-ui/references/components/breadcrumb.md +238 -0
- package/skills/cocoar-vue-ui/references/components/button.md +252 -0
- package/skills/cocoar-vue-ui/references/components/calendar/agenda-view.md +353 -0
- package/skills/cocoar-vue-ui/references/components/calendar/coar-calendar.md +1645 -0
- package/skills/cocoar-vue-ui/references/components/calendar/day-view.md +361 -0
- package/skills/cocoar-vue-ui/references/components/calendar/index.md +312 -0
- package/skills/cocoar-vue-ui/references/components/calendar/month-view.md +475 -0
- package/skills/cocoar-vue-ui/references/components/calendar/performance.md +186 -0
- package/skills/cocoar-vue-ui/references/components/calendar/timeline-view.md +301 -0
- package/skills/cocoar-vue-ui/references/components/calendar/week-view.md +382 -0
- package/skills/cocoar-vue-ui/references/components/calendar/work-week-view.md +145 -0
- package/skills/cocoar-vue-ui/references/components/calendar/year-view.md +63 -0
- package/skills/cocoar-vue-ui/references/components/card.md +151 -0
- package/skills/cocoar-vue-ui/references/components/checkbox-group.md +103 -0
- package/skills/cocoar-vue-ui/references/components/checkbox.md +279 -0
- package/skills/cocoar-vue-ui/references/components/code-block.md +241 -0
- package/skills/cocoar-vue-ui/references/components/context-menu.md +355 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/checkbox.md +213 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/date-columns.md +272 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/editing.md +236 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/multi-select.md +193 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/number.md +147 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/select.md +202 -0
- package/skills/cocoar-vue-ui/references/components/data-grid/text.md +127 -0
- package/skills/cocoar-vue-ui/references/components/data-grid.md +1225 -0
- package/skills/cocoar-vue-ui/references/components/data-list.md +2121 -0
- package/skills/cocoar-vue-ui/references/components/date-or-time-picker.md +200 -0
- package/skills/cocoar-vue-ui/references/components/date-picker.md +203 -0
- package/skills/cocoar-vue-ui/references/components/date-time-picker.md +154 -0
- package/skills/cocoar-vue-ui/references/components/date-views.md +212 -0
- package/skills/cocoar-vue-ui/references/components/dialog.md +148 -0
- package/skills/cocoar-vue-ui/references/components/divider.md +157 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/annotations.md +400 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/coar-document-viewer.md +327 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/index.md +234 -0
- package/skills/cocoar-vue-ui/references/components/document-viewer/toolbar.md +221 -0
- package/skills/cocoar-vue-ui/references/components/drag-drop.md +267 -0
- package/skills/cocoar-vue-ui/references/components/dual-listbox.md +449 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/asset-store.md +327 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/in-memory-store.md +176 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/index.md +311 -0
- package/skills/cocoar-vue-ui/references/components/file-explorer/use-file-explorer.md +300 -0
- package/skills/cocoar-vue-ui/references/components/form-field.md +1025 -0
- package/skills/cocoar-vue-ui/references/components/fragment-parser.md +298 -0
- package/skills/cocoar-vue-ui/references/components/link.md +235 -0
- package/skills/cocoar-vue-ui/references/components/listbox.md +575 -0
- package/skills/cocoar-vue-ui/references/components/map/editor.md +448 -0
- package/skills/cocoar-vue-ui/references/components/map/index.md +351 -0
- package/skills/cocoar-vue-ui/references/components/markdown-diagrams.md +210 -0
- package/skills/cocoar-vue-ui/references/components/markdown-editor.md +1478 -0
- package/skills/cocoar-vue-ui/references/components/markdown-embeds.md +387 -0
- package/skills/cocoar-vue-ui/references/components/markdown-form.md +447 -0
- package/skills/cocoar-vue-ui/references/components/markdown.md +276 -0
- package/skills/cocoar-vue-ui/references/components/menu.md +380 -0
- package/skills/cocoar-vue-ui/references/components/mermaid.md +172 -0
- package/skills/cocoar-vue-ui/references/components/navbar.md +147 -0
- package/skills/cocoar-vue-ui/references/components/note.md +96 -0
- package/skills/cocoar-vue-ui/references/components/notice.md +131 -0
- package/skills/cocoar-vue-ui/references/components/number-input.md +215 -0
- package/skills/cocoar-vue-ui/references/components/otp-input.md +324 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/authoring-contract.md +271 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-builder.md +476 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/coar-page-renderer.md +677 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/custom-elements.md +441 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/idp-integration.md +122 -0
- package/skills/cocoar-vue-ui/references/components/page-builder/index.md +573 -0
- package/skills/cocoar-vue-ui/references/components/pagination.md +150 -0
- package/skills/cocoar-vue-ui/references/components/panel-layout.md +372 -0
- package/skills/cocoar-vue-ui/references/components/password-input.md +175 -0
- package/skills/cocoar-vue-ui/references/components/popconfirm.md +186 -0
- package/skills/cocoar-vue-ui/references/components/popover.md +173 -0
- package/skills/cocoar-vue-ui/references/components/progress-bar.md +178 -0
- package/skills/cocoar-vue-ui/references/components/radio-group.md +225 -0
- package/skills/cocoar-vue-ui/references/components/script-editor.md +1271 -0
- package/skills/cocoar-vue-ui/references/components/segmented-control.md +238 -0
- package/skills/cocoar-vue-ui/references/components/select.md +463 -0
- package/skills/cocoar-vue-ui/references/components/sidebar.md +421 -0
- package/skills/cocoar-vue-ui/references/components/spinner.md +132 -0
- package/skills/cocoar-vue-ui/references/components/switch.md +195 -0
- package/skills/cocoar-vue-ui/references/components/table.md +170 -0
- package/skills/cocoar-vue-ui/references/components/tabs.md +382 -0
- package/skills/cocoar-vue-ui/references/components/tag.md +178 -0
- package/skills/cocoar-vue-ui/references/components/text-input.md +256 -0
- package/skills/cocoar-vue-ui/references/components/toast.md +160 -0
- package/skills/cocoar-vue-ui/references/components/tooltip.md +121 -0
- package/skills/cocoar-vue-ui/references/components/transitions.md +193 -0
- package/skills/cocoar-vue-ui/references/components/tree.md +2388 -0
- package/skills/cocoar-vue-ui/references/components/virtual-list.md +212 -0
- package/skills/cocoar-vue-ui/references/components/wizard.md +251 -0
- package/skills/cocoar-vue-ui/references/components/zoned-date-time-picker.md +177 -0
- package/skills/cocoar-vue-ui/references/foundations/colors.md +708 -0
- package/skills/cocoar-vue-ui/references/foundations/design-principles.md +115 -0
- package/skills/cocoar-vue-ui/references/foundations/icons.md +381 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/formatting.md +530 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/setup.md +86 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/timezones.md +182 -0
- package/skills/cocoar-vue-ui/references/foundations/localization/translations.md +305 -0
- package/skills/cocoar-vue-ui/references/foundations/motion.md +549 -0
- package/skills/cocoar-vue-ui/references/foundations/spacing.md +330 -0
- package/skills/cocoar-vue-ui/references/foundations/theming.md +140 -0
- package/skills/cocoar-vue-ui/references/foundations/typography.md +206 -0
- package/skills/cocoar-vue-ui/references/guide/error-handling.md +162 -0
- package/skills/cocoar-vue-ui/references/guide/getting-started.md +119 -0
- package/skills/cocoar-vue-ui/references/guide/migration-page-builder-3.md +207 -0
- package/skills/cocoar-vue-ui/references/guide/migration.md +140 -0
- package/skills/cocoar-vue-ui/references/guide/theming.md +98 -0
|
@@ -0,0 +1,327 @@
|
|
|
1
|
+
<!-- Generated from apps/docs/components/document-viewer/coar-document-viewer.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
|
|
2
|
+
|
|
3
|
+
# CoarDocumentViewer
|
|
4
|
+
|
|
5
|
+
The all-in-one viewer component. One required prop — `source` — plus a handful of toggles for chrome, panels, and the annotation surface.
|
|
6
|
+
|
|
7
|
+
```ts
|
|
8
|
+
import { CoarDocumentViewer } from '@cocoar/vue-document-viewer';
|
|
9
|
+
import '@cocoar/vue-document-viewer/styles';
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
For PDFs, also pull `pdfSource` from the [`/pdf` subpath](./index.md#worker-setup-pdf-consumers-only) and wire the worker once at app bootstrap.
|
|
13
|
+
|
|
14
|
+
## Minimal usage
|
|
15
|
+
|
|
16
|
+
```vue
|
|
17
|
+
<script setup lang="ts">
|
|
18
|
+
import { computed } from 'vue';
|
|
19
|
+
import { CoarDocumentViewer, imageSource } from '@cocoar/vue-document-viewer';
|
|
20
|
+
import '@cocoar/vue-document-viewer/styles';
|
|
21
|
+
|
|
22
|
+
const source = computed(() => imageSource({ url: '/attachments/diagram.png' }));
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<CoarDocumentViewer :source="source" />
|
|
27
|
+
</template>
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
That's it. Toolbar on, sidebars and annotations panel off, default 'view' annotation mode. Wrap the viewer in a sized container — the component fills 100% of its parent.
|
|
31
|
+
|
|
32
|
+
## Props
|
|
33
|
+
|
|
34
|
+
### Source
|
|
35
|
+
|
|
36
|
+
| Prop | Type | Default | Notes |
|
|
37
|
+
|---|---|---|---|
|
|
38
|
+
| `source` | `DocumentSource` | _required_ | Build via `pdfSource()`, `imageSource()`, or `imageGallerySource()`. Returns a frozen object — build it inside `computed` to avoid unnecessary rebinds. |
|
|
39
|
+
|
|
40
|
+
Switching the `source` keeps the surrounding chrome mounted; only the inner page renderer rebinds, so users see toolbar / panels stay still across document changes.
|
|
41
|
+
|
|
42
|
+
### Chrome toggles
|
|
43
|
+
|
|
44
|
+
| Prop | Type | Default | Notes |
|
|
45
|
+
|---|---|---|---|
|
|
46
|
+
| `showToolbar` | `boolean` | `true` | Top/side/bottom toolbar — see `toolbarPosition`. |
|
|
47
|
+
| `toolbarPosition` | `'top' \| 'right' \| 'bottom' \| 'left'` | `'top'` | Where the toolbar sits. Horizontal at top/bottom, vertical at sides. |
|
|
48
|
+
| `showThumbnails` | `boolean` | `false` | Left-rail Thumbnails tab. Works for every source (per-page mini canvas). |
|
|
49
|
+
| `showOutline` | `boolean` | `false` | Left-rail Outline (TOC) tab. PDF-only; auto-hides when the doc has no outline. |
|
|
50
|
+
| `showAnnotationsPanel` | `boolean` | `false` | Right-rail panel: Info section + annotation list with filter/sort/search. |
|
|
51
|
+
| `showInfoSection` | `boolean` | `true` | Top section of the annotations panel surfacing source metadata. Only active when the panel is open. |
|
|
52
|
+
| `showSearch` | `boolean` | `true` | Search input (button + bar). Disabled when the source can't search (e.g. images). |
|
|
53
|
+
| `showPrintDownload` | `boolean` | `false` | Print + Download buttons in the toolbar. |
|
|
54
|
+
| `showAnnotationModes` | `boolean` | `true` | The drawing-mode button group (marker / note / draw / text). |
|
|
55
|
+
|
|
56
|
+
Two convenience props — `showThumbnails || showOutline` controls the left-rail toggle button; `showAnnotationsPanel` controls the right-rail toggle. The user can collapse either rail from inside the viewer; these props gate whether the toggle is even present in the toolbar.
|
|
57
|
+
|
|
58
|
+
#### Panel open state (`v-model`)
|
|
59
|
+
|
|
60
|
+
By default, the left and right rails start closed and their open/closed state lives inside the component — fine for stand-alone viewers. If you embed the viewer inside a parent that mounts/unmounts it (e.g. a tab bar in a file explorer where the user switches between a Markdown file and a PDF and back), use `v-model` to hold the state on the parent so it **persists across remounts**.
|
|
61
|
+
|
|
62
|
+
| Prop / Event | Type | Notes |
|
|
63
|
+
|---|---|---|
|
|
64
|
+
| `:sidebar-open` + `@update:sidebar-open` (`v-model:sidebar-open`) | `boolean` | Left rail (thumbnails / outline). Default `false`. |
|
|
65
|
+
| `:annotations-panel-open` + `@update:annotations-panel-open` (`v-model:annotations-panel-open`) | `boolean` | Right rail (info section + annotations list). Default `false`. |
|
|
66
|
+
|
|
67
|
+
```vue
|
|
68
|
+
<script setup lang="ts">
|
|
69
|
+
import { ref } from 'vue';
|
|
70
|
+
const sidebarOpen = ref(false);
|
|
71
|
+
const panelOpen = ref(false);
|
|
72
|
+
</script>
|
|
73
|
+
|
|
74
|
+
<template>
|
|
75
|
+
<CoarDocumentViewer
|
|
76
|
+
:source="source"
|
|
77
|
+
:show-thumbnails="true"
|
|
78
|
+
:show-annotations-panel="true"
|
|
79
|
+
v-model:sidebar-open="sidebarOpen"
|
|
80
|
+
v-model:annotations-panel-open="panelOpen"
|
|
81
|
+
/>
|
|
82
|
+
</template>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Without `v-model`, the state is purely internal — same behavior as before, no breaking change.
|
|
86
|
+
|
|
87
|
+
### Position memory
|
|
88
|
+
|
|
89
|
+
```ts
|
|
90
|
+
interface CoarDocumentViewerPosition {
|
|
91
|
+
page: number; // 0-based page index in view
|
|
92
|
+
pageOffset: number; // fractional scroll inside the page, 0..1
|
|
93
|
+
zoom: number; // 1 = 100%
|
|
94
|
+
rotation: 0 | 90 | 180 | 270;
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
Two compatible mechanisms:
|
|
99
|
+
|
|
100
|
+
| Prop / Event | Use when |
|
|
101
|
+
|---|---|
|
|
102
|
+
| `storageKey: string` | You want the viewer to persist position in `localStorage` automatically. Different keys per document (e.g. include the file ID) so each document remembers its own view. |
|
|
103
|
+
| `:position` + `@update:position` (`v-model:position`) | You own persistence (server, IndexedDB, your existing state manager). Two-way binding — the viewer reads on mount, writes on every change. |
|
|
104
|
+
|
|
105
|
+
Both mechanisms can coexist; when both are present, the bound `position` wins on mount.
|
|
106
|
+
|
|
107
|
+
### Toolbar layout
|
|
108
|
+
|
|
109
|
+
| Prop | Type | Default | Notes |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| `tools` | `CoarDocumentViewerTool[]` | `undefined` (= `COAR_DOCUMENT_VIEWER_ALL_TOOLS`) | Array drives BOTH the visible set AND the order. See [Toolbar customization](./toolbar.md). |
|
|
112
|
+
|
|
113
|
+
### Annotations
|
|
114
|
+
|
|
115
|
+
| Prop / Event | Type | Notes |
|
|
116
|
+
|---|---|---|
|
|
117
|
+
| `annotations` | `CoarPdfAnnotation[]` | Consumer-owned. The viewer never mutates this array. |
|
|
118
|
+
| `:annotation-mode` + `@update:annotationMode` (`v-model:annotation-mode`) | `'view' \| 'select' \| 'eraser' \| 'marker' \| 'comment' \| 'ink' \| 'freetext'` | The active pointer mode. `'view'` is read-only (existing annotations clickable, no new ones created). |
|
|
119
|
+
| `annotationColors` | `string[]` | Palette for the color picker. Defaults to a 7-color pastel + neon set. |
|
|
120
|
+
| `@annotation:create` | `(payload: CoarPdfAnnotationCreatePayload) => void` | Consumer assigns `id` + `createdAt` (+ optionally `createdBy`), pushes to `annotations`. |
|
|
121
|
+
| `@annotation:update` | `(payload: { id: string; patch: Partial<CoarPdfAnnotation> }) => void` | Consumer merges the patch into the matching annotation. |
|
|
122
|
+
| `@annotation:delete` | `(id: string) => void` | Consumer removes by id. |
|
|
123
|
+
|
|
124
|
+
See [Annotations](./annotations.md) for the full lifecycle, schema, and a worked example.
|
|
125
|
+
|
|
126
|
+
### Labels
|
|
127
|
+
|
|
128
|
+
```ts
|
|
129
|
+
interface CoarDocumentViewerLabels {
|
|
130
|
+
// Loading / error overlays
|
|
131
|
+
loading?: string;
|
|
132
|
+
errorTitle?: string;
|
|
133
|
+
errorRetry?: string;
|
|
134
|
+
|
|
135
|
+
// Page navigation
|
|
136
|
+
pageOf?: string; // "Page {current} of {total}"
|
|
137
|
+
pageJumpAria?: string;
|
|
138
|
+
prevPage?: string;
|
|
139
|
+
nextPage?: string;
|
|
140
|
+
|
|
141
|
+
// Zoom
|
|
142
|
+
zoomIn?: string;
|
|
143
|
+
zoomOut?: string;
|
|
144
|
+
resetZoom?: string;
|
|
145
|
+
zoomLevel?: string;
|
|
146
|
+
fitWidth?: string;
|
|
147
|
+
fitPage?: string;
|
|
148
|
+
resetView?: string;
|
|
149
|
+
pan?: string;
|
|
150
|
+
|
|
151
|
+
// Rotation
|
|
152
|
+
rotateCw?: string;
|
|
153
|
+
rotateCcw?: string;
|
|
154
|
+
|
|
155
|
+
// Search
|
|
156
|
+
search?: string;
|
|
157
|
+
searchNext?: string;
|
|
158
|
+
searchPrev?: string;
|
|
159
|
+
searchMatchOf?: string; // "{current} of {total}"
|
|
160
|
+
|
|
161
|
+
// Panels + sidebar tabs
|
|
162
|
+
thumbnails?: string;
|
|
163
|
+
outline?: string;
|
|
164
|
+
annotationsPanel?: string;
|
|
165
|
+
|
|
166
|
+
// Document actions
|
|
167
|
+
print?: string;
|
|
168
|
+
download?: string;
|
|
169
|
+
|
|
170
|
+
// Annotation modes
|
|
171
|
+
modeView?: string;
|
|
172
|
+
modeSelect?: string;
|
|
173
|
+
modeEraser?: string;
|
|
174
|
+
modeMarker?: string;
|
|
175
|
+
modeNote?: string;
|
|
176
|
+
modeInk?: string;
|
|
177
|
+
modeFreetext?: string;
|
|
178
|
+
strokeWidth?: string;
|
|
179
|
+
|
|
180
|
+
// Annotation panel
|
|
181
|
+
noAnnotations?: string;
|
|
182
|
+
noMatchingAnnotations?: string;
|
|
183
|
+
searchAnnotations?: string;
|
|
184
|
+
filterBy?: string;
|
|
185
|
+
sortBy?: string;
|
|
186
|
+
sortByPage?: string;
|
|
187
|
+
sortChronological?: string;
|
|
188
|
+
pagePrefix?: string;
|
|
189
|
+
justNow?: string;
|
|
190
|
+
moreActions?: string;
|
|
191
|
+
annotationDelete?: string;
|
|
192
|
+
annotationEditComment?: string;
|
|
193
|
+
annotationColor?: string;
|
|
194
|
+
|
|
195
|
+
// Capability tooltip suffix — appended when a tool isn't supported by the source.
|
|
196
|
+
notAvailableForSource?: string;
|
|
197
|
+
|
|
198
|
+
// Info section
|
|
199
|
+
infoSection?: string; // "Info"
|
|
200
|
+
infoFormat?: string;
|
|
201
|
+
infoPages?: string;
|
|
202
|
+
infoPage?: string; // "Page {n}"
|
|
203
|
+
infoSize?: string;
|
|
204
|
+
infoTitle?: string;
|
|
205
|
+
infoAuthor?: string;
|
|
206
|
+
infoSubject?: string;
|
|
207
|
+
infoKeywords?: string;
|
|
208
|
+
infoCreator?: string;
|
|
209
|
+
infoProducer?: string;
|
|
210
|
+
infoCreated?: string;
|
|
211
|
+
infoModified?: string;
|
|
212
|
+
infoPdfVersion?: string;
|
|
213
|
+
}
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
English defaults are baked in. Pass any subset to override individual strings — keys you omit fall back to the default. `{current}`, `{total}`, `{n}` placeholders are substituted at render time.
|
|
217
|
+
|
|
218
|
+
## Events
|
|
219
|
+
|
|
220
|
+
| Event | Payload | When |
|
|
221
|
+
|---|---|---|
|
|
222
|
+
| `update:position` | `CoarDocumentViewerPosition` | Page / scroll / zoom / rotation change. Used for `v-model:position`. |
|
|
223
|
+
| `update:annotationMode` | `CoarPdfAnnotationMode` | User picks a different mode in the toolbar. Used for `v-model:annotation-mode`. |
|
|
224
|
+
| `update:sidebarOpen` | `boolean` | User toggles the left rail (thumbnails / outline). Used for `v-model:sidebar-open`. |
|
|
225
|
+
| `update:annotationsPanelOpen` | `boolean` | User toggles the right rail (annotations panel). Used for `v-model:annotations-panel-open`. |
|
|
226
|
+
| `annotation:create` | `CoarPdfAnnotationCreatePayload` | New annotation drawn. Consumer assigns id + timestamp. |
|
|
227
|
+
| `annotation:update` | `{ id, patch }` | Existing annotation edited (color / comment / position). |
|
|
228
|
+
| `annotation:delete` | `string` (id) | Existing annotation removed (via panel menu or eraser tool). |
|
|
229
|
+
| `error` | `CoarDocumentViewerErrorEvent` | Source failed to load. `{ error: unknown, src?: string }`. |
|
|
230
|
+
|
|
231
|
+
## Slots
|
|
232
|
+
|
|
233
|
+
| Slot | Props | When |
|
|
234
|
+
|---|---|---|
|
|
235
|
+
| `loading` | _none_ | Replaces the default `"Loading…"` text shown while the source is fetching/parsing. |
|
|
236
|
+
| `error` | `{ error: unknown; retry: () => void }` | Replaces the default error overlay. Call `retry()` to re-trigger the load. |
|
|
237
|
+
|
|
238
|
+
```vue
|
|
239
|
+
<CoarDocumentViewer :source="source">
|
|
240
|
+
<template #loading>
|
|
241
|
+
<CoarSpinner /> Fetching document…
|
|
242
|
+
</template>
|
|
243
|
+
<template #error="{ error, retry }">
|
|
244
|
+
<div>
|
|
245
|
+
<strong>{{ String(error) }}</strong>
|
|
246
|
+
<CoarButton @click="retry">Try again</CoarButton>
|
|
247
|
+
</div>
|
|
248
|
+
</template>
|
|
249
|
+
</CoarDocumentViewer>
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
## Sizing
|
|
253
|
+
|
|
254
|
+
The viewer fills 100% of its parent and uses internal flexbox to allocate space across toolbar, panels, and the page area. Put it inside a sized container:
|
|
255
|
+
|
|
256
|
+
```vue
|
|
257
|
+
<div style="height: 80vh">
|
|
258
|
+
<CoarDocumentViewer :source="source" />
|
|
259
|
+
</div>
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Splitters between the columns are draggable — users can resize the left rail (thumbnails) and right rail (annotations panel) on the fly.
|
|
263
|
+
|
|
264
|
+
## Position memory example
|
|
265
|
+
|
|
266
|
+
```vue
|
|
267
|
+
<script setup lang="ts">
|
|
268
|
+
import { ref } from 'vue';
|
|
269
|
+
import { CoarDocumentViewer, type CoarDocumentViewerPosition } from '@cocoar/vue-document-viewer';
|
|
270
|
+
import { pdfSource } from '@cocoar/vue-document-viewer/pdf';
|
|
271
|
+
|
|
272
|
+
const source = pdfSource({ url: '/files/manual.pdf' });
|
|
273
|
+
|
|
274
|
+
// v-model:position — server-side persistence
|
|
275
|
+
const position = ref<CoarDocumentViewerPosition>({
|
|
276
|
+
page: 0, pageOffset: 0, zoom: 1, rotation: 0,
|
|
277
|
+
});
|
|
278
|
+
watchDebounced(position, savePositionToServer, { debounce: 300 });
|
|
279
|
+
|
|
280
|
+
// OR — storageKey for automatic localStorage persistence
|
|
281
|
+
</script>
|
|
282
|
+
|
|
283
|
+
<template>
|
|
284
|
+
<CoarDocumentViewer
|
|
285
|
+
:source="source"
|
|
286
|
+
v-model:position="position"
|
|
287
|
+
storage-key="manual.pdf"
|
|
288
|
+
/>
|
|
289
|
+
</template>
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
Use one OR the other in practice — both is fine, just pick the priority. When both are present, the bound `position` wins on mount; afterwards both stay in sync.
|
|
293
|
+
|
|
294
|
+
## Common configurations
|
|
295
|
+
|
|
296
|
+
### Read-only PDF viewer with thumbnails
|
|
297
|
+
|
|
298
|
+
```vue
|
|
299
|
+
<CoarDocumentViewer
|
|
300
|
+
:source="source"
|
|
301
|
+
:show-thumbnails="true"
|
|
302
|
+
:show-outline="true"
|
|
303
|
+
:show-print-download="true"
|
|
304
|
+
:show-annotation-modes="false"
|
|
305
|
+
/>
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
### Annotation tool with persistence
|
|
309
|
+
|
|
310
|
+
```vue
|
|
311
|
+
<CoarDocumentViewer
|
|
312
|
+
:source="source"
|
|
313
|
+
:show-annotations-panel="true"
|
|
314
|
+
v-model:annotation-mode="mode"
|
|
315
|
+
:annotations="annotations"
|
|
316
|
+
storage-key="contract-2024"
|
|
317
|
+
@annotation:create="handleCreate"
|
|
318
|
+
@annotation:update="handleUpdate"
|
|
319
|
+
@annotation:delete="handleDelete"
|
|
320
|
+
/>
|
|
321
|
+
```
|
|
322
|
+
|
|
323
|
+
### Minimal embedded preview (no toolbar)
|
|
324
|
+
|
|
325
|
+
```vue
|
|
326
|
+
<CoarDocumentViewer :source="source" :show-toolbar="false" />
|
|
327
|
+
```
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
<!-- Generated from apps/docs/components/document-viewer/index.md by apps/docs/scripts/sync-skill.mjs. Do not edit; edit the docs page. -->
|
|
2
|
+
|
|
3
|
+
# Document Viewer (Preview)
|
|
4
|
+
|
|
5
|
+
`@cocoar/vue-document-viewer` is a generic, source-agnostic document viewer for Vue 3. One component — [`<CoarDocumentViewer>`](./coar-document-viewer.md) — renders **PDFs**, **single images**, and **multi-page image galleries**, with shared toolbar chrome, side panels, and an annotation layer.
|
|
6
|
+
|
|
7
|
+
You pick the source kind with a small factory (`pdfSource(...)`, `imageSource(...)`, `imageGallerySource(...)`); the viewer dispatches internally. The toolbar greys out tools the active source doesn't support — e.g. search and outline are PDF-only — rather than hiding them, so users don't see UI moving around when they switch documents.
|
|
8
|
+
|
|
9
|
+
```ts
|
|
10
|
+
import {
|
|
11
|
+
CoarDocumentViewer,
|
|
12
|
+
imageSource,
|
|
13
|
+
imageGallerySource,
|
|
14
|
+
} from '@cocoar/vue-document-viewer';
|
|
15
|
+
import { pdfSource } from '@cocoar/vue-document-viewer/pdf';
|
|
16
|
+
import '@cocoar/vue-document-viewer/styles';
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
`pdfjs-dist` is an **optional peer dependency**. PDF consumers import `pdfSource` from the `/pdf` subpath; image-only consumers never pay the pdfjs bundle cost.
|
|
20
|
+
|
|
21
|
+
## Three source factories
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
// PDF — pdfjs subpath
|
|
25
|
+
pdfSource({ url, headers?, withCredentials? })
|
|
26
|
+
|
|
27
|
+
// Single-page raster / vector image (JPG / PNG / SVG / WebP / AVIF / GIF / blob: / data:)
|
|
28
|
+
imageSource({ url })
|
|
29
|
+
|
|
30
|
+
// Multi-page image document — pages may mix orientations
|
|
31
|
+
imageGallerySource({ urls })
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Each factory returns a frozen `DocumentSource`. Build it inside a `computed` so the viewer rebinds only when something actually changes — switching sources keeps the toolbar, panels, and viewport mounted; only the inner page renderer rebinds.
|
|
35
|
+
|
|
36
|
+
## Single image
|
|
37
|
+
|
|
38
|
+
**Demo — `document-viewer/demos/ImageSourceDemo.vue`**
|
|
39
|
+
|
|
40
|
+
```vue
|
|
41
|
+
<template>
|
|
42
|
+
<ClientOnly>
|
|
43
|
+
<div style="height: 520px; border: 1px solid var(--vp-c-divider); border-radius: 8px; overflow: hidden;">
|
|
44
|
+
<component
|
|
45
|
+
:is="Viewer"
|
|
46
|
+
v-if="Viewer && source"
|
|
47
|
+
:source="source"
|
|
48
|
+
:show-thumbnails="true"
|
|
49
|
+
:show-annotations-panel="true"
|
|
50
|
+
/>
|
|
51
|
+
<div v-else class="dv-loading">Loading viewer…</div>
|
|
52
|
+
</div>
|
|
53
|
+
</ClientOnly>
|
|
54
|
+
</template>
|
|
55
|
+
|
|
56
|
+
<script setup lang="ts">
|
|
57
|
+
import { computed, onMounted, ref, shallowRef, type Component } from 'vue';
|
|
58
|
+
import { SINGLE_IMAGE_URL } from './_shared';
|
|
59
|
+
|
|
60
|
+
const Viewer = shallowRef<Component | null>(null);
|
|
61
|
+
const imageSource = shallowRef<((opts: { url: string }) => unknown) | null>(null);
|
|
62
|
+
|
|
63
|
+
const source = computed(() => imageSource.value?.({ url: SINGLE_IMAGE_URL }) ?? null);
|
|
64
|
+
|
|
65
|
+
onMounted(async () => {
|
|
66
|
+
const mod = await import('@cocoar/vue-document-viewer');
|
|
67
|
+
await import('@cocoar/vue-document-viewer/styles');
|
|
68
|
+
imageSource.value = mod.imageSource;
|
|
69
|
+
Viewer.value = mod.CoarDocumentViewer;
|
|
70
|
+
});
|
|
71
|
+
</script>
|
|
72
|
+
|
|
73
|
+
<style scoped>
|
|
74
|
+
.dv-loading {
|
|
75
|
+
height: 100%;
|
|
76
|
+
display: flex;
|
|
77
|
+
align-items: center;
|
|
78
|
+
justify-content: center;
|
|
79
|
+
color: var(--coar-text-neutral-tertiary, #6b7280);
|
|
80
|
+
font-size: 13px;
|
|
81
|
+
}
|
|
82
|
+
</style>
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
Toolbar tools the source doesn't support stay visible but disabled — e.g. **Search**, **Previous/Next page**, and **Outline** are off for a single-page image. Their tooltips append the `notAvailableForSource` label so the user understands the state.
|
|
86
|
+
|
|
87
|
+
## Image gallery
|
|
88
|
+
|
|
89
|
+
**Demo — `document-viewer/demos/GallerySourceDemo.vue`**
|
|
90
|
+
|
|
91
|
+
```vue
|
|
92
|
+
<template>
|
|
93
|
+
<ClientOnly>
|
|
94
|
+
<div style="height: 520px; border: 1px solid var(--vp-c-divider); border-radius: 8px; overflow: hidden;">
|
|
95
|
+
<component
|
|
96
|
+
:is="Viewer"
|
|
97
|
+
v-if="Viewer && source"
|
|
98
|
+
:source="source"
|
|
99
|
+
:show-thumbnails="true"
|
|
100
|
+
:show-annotations-panel="true"
|
|
101
|
+
/>
|
|
102
|
+
<div v-else class="dv-loading">Loading viewer…</div>
|
|
103
|
+
</div>
|
|
104
|
+
</ClientOnly>
|
|
105
|
+
</template>
|
|
106
|
+
|
|
107
|
+
<script setup lang="ts">
|
|
108
|
+
import { computed, onMounted, shallowRef, type Component } from 'vue';
|
|
109
|
+
import { GALLERY_URLS } from './_shared';
|
|
110
|
+
|
|
111
|
+
const Viewer = shallowRef<Component | null>(null);
|
|
112
|
+
const galleryFactory = shallowRef<((opts: { urls: readonly string[] }) => unknown) | null>(null);
|
|
113
|
+
|
|
114
|
+
const source = computed(() => galleryFactory.value?.({ urls: GALLERY_URLS }) ?? null);
|
|
115
|
+
|
|
116
|
+
onMounted(async () => {
|
|
117
|
+
const mod = await import('@cocoar/vue-document-viewer');
|
|
118
|
+
await import('@cocoar/vue-document-viewer/styles');
|
|
119
|
+
galleryFactory.value = mod.imageGallerySource;
|
|
120
|
+
Viewer.value = mod.CoarDocumentViewer;
|
|
121
|
+
});
|
|
122
|
+
</script>
|
|
123
|
+
|
|
124
|
+
<style scoped>
|
|
125
|
+
.dv-loading {
|
|
126
|
+
height: 100%;
|
|
127
|
+
display: flex;
|
|
128
|
+
align-items: center;
|
|
129
|
+
justify-content: center;
|
|
130
|
+
color: var(--coar-text-neutral-tertiary, #6b7280);
|
|
131
|
+
font-size: 13px;
|
|
132
|
+
}
|
|
133
|
+
</style>
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
Pages can mix orientations (landscape page 1, portrait page 2, wide page 3 above). Every page's intrinsic dimensions are read from its own image, so the viewer never letter-boxes or stretches. Sidebar thumbnails track the active page.
|
|
137
|
+
|
|
138
|
+
## PDF source
|
|
139
|
+
|
|
140
|
+
The PDF demo runs in the playground at [`localhost:5188/pdf-viewer`](http://localhost:5188/pdf-viewer) — it's omitted here because the pdfjs worker has to be configured by the consumer (one-line setup, below).
|
|
141
|
+
|
|
142
|
+
```vue
|
|
143
|
+
<script setup lang="ts">
|
|
144
|
+
import { computed } from 'vue';
|
|
145
|
+
import { CoarDocumentViewer } from '@cocoar/vue-document-viewer';
|
|
146
|
+
import { pdfSource } from '@cocoar/vue-document-viewer/pdf';
|
|
147
|
+
import '@cocoar/vue-document-viewer/styles';
|
|
148
|
+
|
|
149
|
+
const source = computed(() => pdfSource({
|
|
150
|
+
url: '/api/files/contract.pdf',
|
|
151
|
+
withCredentials: true, // forward cookies / HTTP auth
|
|
152
|
+
headers: { 'X-Tenant': 'acme' }, // arbitrary request headers
|
|
153
|
+
}));
|
|
154
|
+
</script>
|
|
155
|
+
|
|
156
|
+
<template>
|
|
157
|
+
<CoarDocumentViewer
|
|
158
|
+
:source="source"
|
|
159
|
+
:show-thumbnails="true"
|
|
160
|
+
:show-outline="true"
|
|
161
|
+
:show-annotations-panel="true"
|
|
162
|
+
:show-print-download="true"
|
|
163
|
+
/>
|
|
164
|
+
</template>
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Worker setup (PDF consumers only)
|
|
168
|
+
|
|
169
|
+
pdfjs needs a worker to parse the binary off the main thread. Wire it once at app bootstrap — Vite/webpack/Rollup all support the `?worker` query:
|
|
170
|
+
|
|
171
|
+
```ts
|
|
172
|
+
// main.ts
|
|
173
|
+
import * as pdfjs from 'pdfjs-dist';
|
|
174
|
+
import PdfWorker from 'pdfjs-dist/build/pdf.worker.min.mjs?worker';
|
|
175
|
+
|
|
176
|
+
pdfjs.GlobalWorkerOptions.workerPort = new PdfWorker();
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
If you skip this step, pdfjs will try to fetch the worker over the network and fall back to a slow inline mode — both viable, but not what you want in production.
|
|
180
|
+
|
|
181
|
+
## Source capabilities
|
|
182
|
+
|
|
183
|
+
Every source advertises what it can do. The toolbar reads these flags to enable/disable individual tools; consumers can inspect them too:
|
|
184
|
+
|
|
185
|
+
```ts
|
|
186
|
+
interface DocumentSourceCapabilities {
|
|
187
|
+
multiPage: boolean; // Prev / Next / page input
|
|
188
|
+
textLayer: boolean; // Text selection, Ctrl+C copy
|
|
189
|
+
search: boolean; // Search button
|
|
190
|
+
outline: boolean; // Outline (TOC) sidebar tab
|
|
191
|
+
print: boolean; // Print button
|
|
192
|
+
}
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
The factories pre-populate the right values: PDFs get all-true, images get a single-page no-text profile, galleries get the same with `multiPage: true`. Adding a future source kind (e.g. OCR'd images) is a matter of providing a new factory that flips the relevant flags.
|
|
196
|
+
|
|
197
|
+
> **Tip: Why disabled, not hidden?**
|
|
198
|
+
>
|
|
199
|
+
> Tools that vanish when the source changes make the toolbar layout shift — buttons jump positions, muscle memory breaks. Greying out keeps positions stable and surfaces the capability constraint to the user via tooltip.
|
|
200
|
+
|
|
201
|
+
## Info panel
|
|
202
|
+
|
|
203
|
+
When the right-side annotations panel is open, a collapsible **Info** section at the top surfaces source metadata:
|
|
204
|
+
|
|
205
|
+
- Format string (`"PDF · v1.7"`, `"Image · PNG"`, `"Image gallery · SVG"`)
|
|
206
|
+
- Total page count
|
|
207
|
+
- Current page dimensions (live — updates as the user flips pages)
|
|
208
|
+
- PDF-only fields: title / author / subject / keywords / creator / producer / created / modified / PDF version (empty fields are skipped)
|
|
209
|
+
- File size in bytes (PDFs only — pdfjs exposes `contentLength`)
|
|
210
|
+
|
|
211
|
+
Disable with `:show-info-section="false"` if you want a minimal annotations-only panel.
|
|
212
|
+
|
|
213
|
+
## Architecture
|
|
214
|
+
|
|
215
|
+
```
|
|
216
|
+
<CoarDocumentViewer :source="…">
|
|
217
|
+
useDocumentLoader(sourceRef) ← internal dispatcher, watches source.kind
|
|
218
|
+
usePdfDocumentAdapter(pdfSourceRef)
|
|
219
|
+
useImageDocumentAdapter(imgSourceRef)
|
|
220
|
+
useImageGalleryAdapter(gallerySourceRef)
|
|
221
|
+
each publishes { status, pageProviders, info, error, retry, destroy }
|
|
222
|
+
usePageRenderer({ pageProviders, … }) ← source-agnostic, owns canvas + textLayer DOM
|
|
223
|
+
DocumentToolbar, DocumentSidebar, DocumentAnnotationPanel, DocumentSearchBar
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
The seam between formats is `PageProvider` — every page (PDF page proxy, image element, future kinds) materializes through the same interface (`render(canvas, opts)`, `cancel()`, optional `getTextLayer()`). The renderer never imports `pdfjs-dist`.
|
|
227
|
+
|
|
228
|
+
## What's next
|
|
229
|
+
|
|
230
|
+
| Page | Covers |
|
|
231
|
+
|------|--------|
|
|
232
|
+
| [CoarDocumentViewer](./coar-document-viewer.md) | Full props / emits / slots reference, labels, position memory |
|
|
233
|
+
| [Toolbar customization](./toolbar.md) | Order-driven `tools` array, separator pseudo-tool, section toggles |
|
|
234
|
+
| [Annotations](./annotations.md) | Modes, types, lifecycle events, schema, color picker |
|