@inkbar/vue 0.0.0-stage → 0.1.1
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/LICENSE +21 -0
- package/README.md +111 -2
- package/dist/components/InkAnnotation.js +7 -0
- package/dist/components/InkAnnotation.js.map +1 -0
- package/dist/components/InkAnnotation.vue.d.ts +22 -0
- package/dist/components/InkAnnotation.vue.d.ts.map +1 -0
- package/dist/components/InkAnnotation.vue_vue_type_script_setup_true_lang.js +48 -0
- package/dist/components/InkAnnotation.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InkNotePopover.js +7 -0
- package/dist/components/InkNotePopover.js.map +1 -0
- package/dist/components/InkNotePopover.vue.d.ts +15 -0
- package/dist/components/InkNotePopover.vue.d.ts.map +1 -0
- package/dist/components/InkNotePopover.vue_vue_type_script_setup_true_lang.js +132 -0
- package/dist/components/InkNotePopover.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InkPage.js +7 -0
- package/dist/components/InkPage.js.map +1 -0
- package/dist/components/InkPage.vue.d.ts +32 -0
- package/dist/components/InkPage.vue.d.ts.map +1 -0
- package/dist/components/InkPage.vue_vue_type_script_setup_true_lang.js +68 -0
- package/dist/components/InkPage.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InkPageCount.js +7 -0
- package/dist/components/InkPageCount.js.map +1 -0
- package/dist/components/InkPageCount.vue.d.ts +4 -0
- package/dist/components/InkPageCount.vue.d.ts.map +1 -0
- package/dist/components/InkPageCount.vue_vue_type_script_setup_true_lang.js +18 -0
- package/dist/components/InkPageCount.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InkPageInput.js +7 -0
- package/dist/components/InkPageInput.js.map +1 -0
- package/dist/components/InkPageInput.vue.d.ts +4 -0
- package/dist/components/InkPageInput.vue.d.ts.map +1 -0
- package/dist/components/InkPageInput.vue_vue_type_script_setup_true_lang.js +30 -0
- package/dist/components/InkPageInput.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InkRoot.js +7 -0
- package/dist/components/InkRoot.js.map +1 -0
- package/dist/components/InkRoot.vue.d.ts +784 -0
- package/dist/components/InkRoot.vue.d.ts.map +1 -0
- package/dist/components/InkRoot.vue_vue_type_script_setup_true_lang.js +162 -0
- package/dist/components/InkRoot.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InkSuggestionCard.js +7 -0
- package/dist/components/InkSuggestionCard.js.map +1 -0
- package/dist/components/InkSuggestionCard.vue.d.ts +16 -0
- package/dist/components/InkSuggestionCard.vue.d.ts.map +1 -0
- package/dist/components/InkSuggestionCard.vue_vue_type_script_setup_true_lang.js +85 -0
- package/dist/components/InkSuggestionCard.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InkToolButton.js +7 -0
- package/dist/components/InkToolButton.js.map +1 -0
- package/dist/components/InkToolButton.vue.d.ts +26 -0
- package/dist/components/InkToolButton.vue.d.ts.map +1 -0
- package/dist/components/InkToolButton.vue_vue_type_script_setup_true_lang.js +45 -0
- package/dist/components/InkToolButton.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InkToolbar.js +7 -0
- package/dist/components/InkToolbar.js.map +1 -0
- package/dist/components/InkToolbar.vue.d.ts +21 -0
- package/dist/components/InkToolbar.vue.d.ts.map +1 -0
- package/dist/components/InkToolbar.vue_vue_type_script_setup_true_lang.js +29 -0
- package/dist/components/InkToolbar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InkViewport.js +7 -0
- package/dist/components/InkViewport.js.map +1 -0
- package/dist/components/InkViewport.vue.d.ts +39 -0
- package/dist/components/InkViewport.vue.d.ts.map +1 -0
- package/dist/components/InkViewport.vue_vue_type_script_setup_true_lang.js +148 -0
- package/dist/components/InkViewport.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InkZoomIn.js +7 -0
- package/dist/components/InkZoomIn.js.map +1 -0
- package/dist/components/InkZoomIn.vue.d.ts +19 -0
- package/dist/components/InkZoomIn.vue.d.ts.map +1 -0
- package/dist/components/InkZoomIn.vue_vue_type_script_setup_true_lang.js +30 -0
- package/dist/components/InkZoomIn.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InkZoomOut.js +7 -0
- package/dist/components/InkZoomOut.js.map +1 -0
- package/dist/components/InkZoomOut.vue.d.ts +19 -0
- package/dist/components/InkZoomOut.vue.d.ts.map +1 -0
- package/dist/components/InkZoomOut.vue_vue_type_script_setup_true_lang.js +30 -0
- package/dist/components/InkZoomOut.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/InkZoomSelect.js +7 -0
- package/dist/components/InkZoomSelect.js.map +1 -0
- package/dist/components/InkZoomSelect.vue.d.ts +4 -0
- package/dist/components/InkZoomSelect.vue.d.ts.map +1 -0
- package/dist/components/InkZoomSelect.vue_vue_type_script_setup_true_lang.js +39 -0
- package/dist/components/InkZoomSelect.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/components/annotation-element.d.ts +5 -0
- package/dist/components/annotation-element.d.ts.map +1 -0
- package/dist/components/annotation-element.js +18 -0
- package/dist/components/annotation-element.js.map +1 -0
- package/dist/components/annotation-slot.d.ts +2 -0
- package/dist/components/annotation-slot.d.ts.map +1 -0
- package/dist/components/popover-focus.d.ts +9 -0
- package/dist/components/popover-focus.d.ts.map +1 -0
- package/dist/components/popover-focus.js +15 -0
- package/dist/components/popover-focus.js.map +1 -0
- package/dist/composables/useAnnotations.d.ts +1921 -0
- package/dist/composables/useAnnotations.d.ts.map +1 -0
- package/dist/composables/useAnnotations.js +27 -0
- package/dist/composables/useAnnotations.js.map +1 -0
- package/dist/composables/useDocument.d.ts +12 -0
- package/dist/composables/useDocument.d.ts.map +1 -0
- package/dist/composables/useDocument.js +20 -0
- package/dist/composables/useDocument.js.map +1 -0
- package/dist/composables/useEmbedded.d.ts +6 -0
- package/dist/composables/useEmbedded.d.ts.map +1 -0
- package/dist/composables/useEmbedded.js +15 -0
- package/dist/composables/useEmbedded.js.map +1 -0
- package/dist/composables/useHistory.d.ts +7 -0
- package/dist/composables/useHistory.d.ts.map +1 -0
- package/dist/composables/useHistory.js +9 -0
- package/dist/composables/useHistory.js.map +1 -0
- package/dist/composables/usePage.d.ts +10 -0
- package/dist/composables/usePage.d.ts.map +1 -0
- package/dist/composables/usePage.js +22 -0
- package/dist/composables/usePage.js.map +1 -0
- package/dist/composables/useSelection.d.ts +6 -0
- package/dist/composables/useSelection.d.ts.map +1 -0
- package/dist/composables/useSelection.js +14 -0
- package/dist/composables/useSelection.js.map +1 -0
- package/dist/composables/useTool.d.ts +6 -0
- package/dist/composables/useTool.d.ts.map +1 -0
- package/dist/composables/useTool.js +13 -0
- package/dist/composables/useTool.js.map +1 -0
- package/dist/composables/useZoom.d.ts +9 -0
- package/dist/composables/useZoom.d.ts.map +1 -0
- package/dist/composables/useZoom.js +17 -0
- package/dist/composables/useZoom.js.map +1 -0
- package/dist/compositions/DefaultToolbar.js +7 -0
- package/dist/compositions/DefaultToolbar.js.map +1 -0
- package/dist/compositions/DefaultToolbar.vue.d.ts +19 -0
- package/dist/compositions/DefaultToolbar.vue.d.ts.map +1 -0
- package/dist/compositions/DefaultToolbar.vue_vue_type_script_setup_true_lang.js +100 -0
- package/dist/compositions/DefaultToolbar.vue_vue_type_script_setup_true_lang.js.map +1 -0
- package/dist/context.d.ts +50 -0
- package/dist/context.d.ts.map +1 -0
- package/dist/context.js +13 -0
- package/dist/context.js.map +1 -0
- package/dist/index.d.ts +29 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +25 -0
- package/dist/ref-history.d.ts +18 -0
- package/dist/ref-history.d.ts.map +1 -0
- package/dist/ref-history.js +25 -0
- package/dist/ref-history.js.map +1 -0
- package/dist/use-readable.d.ts +5 -0
- package/dist/use-readable.d.ts.map +1 -0
- package/dist/use-readable.js +16 -0
- package/dist/use-readable.js.map +1 -0
- package/package.json +52 -3
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Redactr Platforms Ltd
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -1,3 +1,112 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<picture>
|
|
3
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/inkbar/inkbar/main/.github/assets/inkbar-mark-on-dark.svg">
|
|
4
|
+
<img src="https://raw.githubusercontent.com/inkbar/inkbar/main/.github/assets/inkbar-mark-on-light.svg" alt="Inkbar" width="72" height="72">
|
|
5
|
+
</picture>
|
|
6
|
+
</p>
|
|
2
7
|
|
|
3
|
-
|
|
8
|
+
<h1 align="center">@inkbar/vue</h1>
|
|
9
|
+
|
|
10
|
+
<p align="center">
|
|
11
|
+
A PDF viewer and editor for Vue 3. No licence fee.
|
|
12
|
+
</p>
|
|
13
|
+
|
|
14
|
+
<p align="center">
|
|
15
|
+
<a href="https://inkbar.dev">Website</a> ·
|
|
16
|
+
<a href="https://inkbar.dev/docs/">Docs</a> ·
|
|
17
|
+
<a href="https://sandbox.inkbar.dev">Sandbox</a> ·
|
|
18
|
+
<a href="https://github.com/inkbar/inkbar">GitHub</a>
|
|
19
|
+
</p>
|
|
20
|
+
|
|
21
|
+
---
|
|
22
|
+
|
|
23
|
+
Drop a PDF viewer into your Vue app and let people highlight text, leave notes with replies and review redactions. `@inkbar/vue` gives you a default toolbar and popovers styled by `@inkbar/theme`, plus the unstyled components and composables they are made from, so you can keep the defaults or build your own UI.
|
|
24
|
+
|
|
25
|
+
Annotations are plain typed objects. Bind them with `v-model` to keep them in your own state, or let Inkbar hold them with undo and redo.
|
|
26
|
+
|
|
27
|
+
## Install
|
|
28
|
+
|
|
29
|
+
```sh
|
|
30
|
+
pnpm add @inkbar/vue @inkbar/core @inkbar/theme
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
Vue 3.5 or later is a peer dependency. Add the Vite plugin from `@inkbar/core/vite` so PDF.js can load its worker and fonts; the [`@inkbar/core` README](https://github.com/inkbar/inkbar/tree/main/packages/core#serving-pdfjs-assets) covers it.
|
|
34
|
+
|
|
35
|
+
## Usage
|
|
36
|
+
|
|
37
|
+
```vue
|
|
38
|
+
<script setup lang="ts">
|
|
39
|
+
import { ref } from 'vue'
|
|
40
|
+
import {
|
|
41
|
+
InkRoot,
|
|
42
|
+
InkViewport,
|
|
43
|
+
DefaultToolbar,
|
|
44
|
+
InkNotePopover,
|
|
45
|
+
InkSuggestionCard,
|
|
46
|
+
} from '@inkbar/vue'
|
|
47
|
+
import type { Annotation } from '@inkbar/core'
|
|
48
|
+
import { assets } from 'virtual:inkbar-assets'
|
|
49
|
+
import '@inkbar/theme/theme.css'
|
|
50
|
+
|
|
51
|
+
defineProps<{ signedUrl: string; documentId: string }>()
|
|
52
|
+
|
|
53
|
+
const annotations = ref<Annotation[]>([])
|
|
54
|
+
</script>
|
|
55
|
+
|
|
56
|
+
<template>
|
|
57
|
+
<InkRoot
|
|
58
|
+
:src="{ url: signedUrl, key: documentId }"
|
|
59
|
+
:assets="assets"
|
|
60
|
+
v-model:annotations="annotations"
|
|
61
|
+
>
|
|
62
|
+
<DefaultToolbar />
|
|
63
|
+
<InkViewport />
|
|
64
|
+
<InkNotePopover />
|
|
65
|
+
<InkSuggestionCard />
|
|
66
|
+
</InkRoot>
|
|
67
|
+
</template>
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`v-model:annotations` makes your list the source of truth. Leave it off and Inkbar keeps the state itself, with undo and redo.
|
|
71
|
+
|
|
72
|
+
## Controlled mode
|
|
73
|
+
|
|
74
|
+
With `v-model:annotations`, Inkbar proposes each change through `update:annotations` and shows whatever list you pass back. To change annotations yourself, replace the array: `annotations.value = [...annotations.value, next]`. In-place mutation (`push`, `splice`, editing an item) is not observed, so the viewer never sees it.
|
|
75
|
+
|
|
76
|
+
Undo and redo are off in controlled mode unless you pass `:history="true"`. Inkbar then keeps a stack of your list's past values and applies undo and redo through `v-model`. Every change to the list is a step, including the list you load from your server when the document opens. Clear the history once that load has reached the viewer, so the first undo does not empty the list:
|
|
77
|
+
|
|
78
|
+
```ts
|
|
79
|
+
const root = useTemplateRef<InstanceType<typeof InkRoot>>('root')
|
|
80
|
+
|
|
81
|
+
async function onReady(doc: InkDocument) {
|
|
82
|
+
annotations.value = await loadAnnotations(doc.key)
|
|
83
|
+
await nextTick()
|
|
84
|
+
root.value?.history.clear()
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Inside `InkRoot`, `useHistory()` returns the same object. Its `batch(fn)` runs `fn` and records what it changes as one undo step. With `:history="true"` on a controlled list, you still get one `update:annotations` event per add. The step lands when the current task ends, and `canUndo` updates then. Any other change you apply to the list in that window joins the step. In both modes, `fn` must be synchronous.
|
|
89
|
+
|
|
90
|
+
`key` identifies the document. Reuse the same key when only the signed URL changes and the document is not reloaded.
|
|
91
|
+
|
|
92
|
+
## Note popover
|
|
93
|
+
|
|
94
|
+
`InkNotePopover` opens the thread of the activated note. A new note has no text yet, so the form first writes the note's own text (labelled "Comment"); after that it adds replies.
|
|
95
|
+
|
|
96
|
+
A root note with no text and no replies is removed when its popover closes, or when activation moves to another annotation, so abandoned notes do not stay on the page. Locked notes are never removed. If you add notes yourself (`add({ type: 'note', ... })`), give them `contents`, or they will be removed the first time someone opens and closes them.
|
|
97
|
+
|
|
98
|
+
## Keyboard
|
|
99
|
+
|
|
100
|
+
The scroll element takes focus. With the highlight or redact tool active, `Enter` turns the text selection into a mark; with the note tool, `Enter` places a note in the middle of the current page and opens it. On a focused annotation, `Enter` or `Space` selects and activates it (a note opens its thread and a suggested redaction its card; other types are only selected), `ArrowUp` and `ArrowDown` move between annotations, `Shift` + arrow moves it by 1 pt (add `Alt` for 10 pt), `Ctrl` or `Cmd` + `Shift` + arrow resizes an area mark, and `Delete` removes it. The full table is in the [`@inkbar/core` README](https://github.com/inkbar/inkbar/tree/main/packages/core#tools-and-keyboard).
|
|
101
|
+
|
|
102
|
+
## Styling
|
|
103
|
+
|
|
104
|
+
Components carry state on data attributes, for example `[data-annotation][data-state="selected"]` and `[data-tool="highlight"][data-state="on"]`. `@inkbar/theme` gives a default look. Your own CSS overrides it without `!important`.
|
|
105
|
+
|
|
106
|
+
## Composables
|
|
107
|
+
|
|
108
|
+
`useDocument`, `useAnnotations`, `useTool`, `useSelection`, `useZoom`, `usePage`, `useHistory` and `useEmbedded` read the state of the nearest `InkRoot`. Build your own toolbar or side panel from them.
|
|
109
|
+
|
|
110
|
+
## Licence
|
|
111
|
+
|
|
112
|
+
[MIT](https://github.com/inkbar/inkbar/blob/main/LICENSE). From the team behind [Redactr](https://redactr.io).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkAnnotation.js","names":["_sfc_main","InkAnnotation_vue_vue_type_script_setup_true_lang_default","InkAnnotation_default"],"sources":["../../src/components/InkAnnotation.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * Slot props for one annotation. It renders only its default slot: the overlay already draws\n * the default visual for every annotation that is not custom, so this never draws a second\n * one. InkPage teleports it into the annotation's overlay element.\n */\nimport { acceptSuggestion } from '@inkbar/core'\nimport { computed, onBeforeUnmount, shallowRef, watch } from 'vue'\n\nimport { injectInkContext } from '../context'\n\nimport type { AnnotationSlotProps, AnnotationState } from './annotation-slot'\nimport type { Annotation } from '@inkbar/core'\n\nconst props = withDefaults(\n defineProps<{\n annotation: Annotation\n /** The overlay element; its `data-state` (which includes hover) drives `state`. */\n element?: HTMLElement | undefined\n }>(),\n { element: undefined }\n)\n\ndefineSlots<{ default?(props: AnnotationSlotProps): unknown }>()\n\nconst ctx = injectInkContext()\n\nconst observed = shallowRef<AnnotationState | null>(null)\nlet observer: MutationObserver | undefined\nfunction readState(el: HTMLElement): AnnotationState | null {\n const s = el.dataset.state\n return s === 'idle' || s === 'hover' || s === 'selected' ? s : null\n}\nwatch(\n () => props.element,\n (el) => {\n observer?.disconnect()\n observer = undefined\n observed.value = el ? readState(el) : null\n if (!el) return\n observer = new MutationObserver(() => {\n observed.value = readState(el)\n })\n observer.observe(el, { attributes: true, attributeFilter: ['data-state'] })\n },\n { immediate: true }\n)\nonBeforeUnmount(() => observer?.disconnect())\n\nconst isSuggestion = computed(\n () => props.annotation.type === 'redact' && props.annotation.origin === 'suggested'\n)\n\nconst slotProps = computed<AnnotationSlotProps>(() => {\n const { id } = props.annotation\n const store = () => ctx.document.value?.annotations\n return {\n annotation: props.annotation,\n state: observed.value ?? (ctx.selection.value.includes(id) ? 'selected' : 'idle'),\n locked: props.annotation.locked === true,\n select: () => store()?.select([id]),\n remove: () => void store()?.remove(id),\n accept: () => {\n const s = store()\n if (s && isSuggestion.value) acceptSuggestion(s, id)\n },\n dismiss: () => {\n if (isSuggestion.value) store()?.remove(id)\n },\n }\n})\n</script>\n\n<template>\n <slot v-bind=\"slotProps\" />\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AnnotationSlotProps } from './annotation-slot';
|
|
2
|
+
import { Annotation } from '@inkbar/core';
|
|
3
|
+
type __VLS_Props = {
|
|
4
|
+
annotation: Annotation;
|
|
5
|
+
/** The overlay element; its `data-state` (which includes hover) drives `state`. */
|
|
6
|
+
element?: HTMLElement | undefined;
|
|
7
|
+
};
|
|
8
|
+
type __VLS_Slots = {
|
|
9
|
+
default?(props: AnnotationSlotProps): unknown;
|
|
10
|
+
};
|
|
11
|
+
declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
12
|
+
element: HTMLElement | undefined;
|
|
13
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
14
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
15
|
+
declare const _default: typeof __VLS_export;
|
|
16
|
+
export default _default;
|
|
17
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
18
|
+
new (): {
|
|
19
|
+
$slots: S;
|
|
20
|
+
};
|
|
21
|
+
};
|
|
22
|
+
//# sourceMappingURL=InkAnnotation.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkAnnotation.vue.d.ts","sourceRoot":"","sources":["../../src/components/InkAnnotation.vue"],"names":[],"mappings":"AAwFA,OAAO,KAAK,EAAE,mBAAmB,EAAmB,MAAM,mBAAmB,CAAA;AAC7E,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAA;AAG9C,KAAK,WAAW,GAAG;IACf,UAAU,EAAE,UAAU,CAAA;IACtB,mFAAmF;IACnF,OAAO,CAAC,EAAE,WAAW,GAAG,SAAS,CAAA;CAClC,CAAC;AAMJ,KAAK,WAAW,GAAG;IAAE,OAAO,CAAC,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAA;CAAE,CAAC;AAuErE,QAAA,MAAM,UAAU;;6EAGd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AAWzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { injectInkContext as e } from "../context.js";
|
|
2
|
+
import { computed as t, defineComponent as n, guardReactiveProps as r, normalizeProps as i, onBeforeUnmount as a, renderSlot as o, shallowRef as s, watch as c } from "vue";
|
|
3
|
+
import { acceptSuggestion as l } from "@inkbar/core";
|
|
4
|
+
//#region src/components/InkAnnotation.vue?vue&type=script&setup=true&lang.ts
|
|
5
|
+
var u = /*@__PURE__*/ n({
|
|
6
|
+
__name: "InkAnnotation",
|
|
7
|
+
props: {
|
|
8
|
+
annotation: {},
|
|
9
|
+
element: { default: void 0 }
|
|
10
|
+
},
|
|
11
|
+
setup(n) {
|
|
12
|
+
let u = n, d = e(), f = s(null), p;
|
|
13
|
+
function m(e) {
|
|
14
|
+
let t = e.dataset.state;
|
|
15
|
+
return t === "idle" || t === "hover" || t === "selected" ? t : null;
|
|
16
|
+
}
|
|
17
|
+
c(() => u.element, (e) => {
|
|
18
|
+
p?.disconnect(), p = void 0, f.value = e ? m(e) : null, e && (p = new MutationObserver(() => {
|
|
19
|
+
f.value = m(e);
|
|
20
|
+
}), p.observe(e, {
|
|
21
|
+
attributes: !0,
|
|
22
|
+
attributeFilter: ["data-state"]
|
|
23
|
+
}));
|
|
24
|
+
}, { immediate: !0 }), a(() => p?.disconnect());
|
|
25
|
+
let h = t(() => u.annotation.type === "redact" && u.annotation.origin === "suggested"), g = t(() => {
|
|
26
|
+
let { id: e } = u.annotation, t = () => d.document.value?.annotations;
|
|
27
|
+
return {
|
|
28
|
+
annotation: u.annotation,
|
|
29
|
+
state: f.value ?? (d.selection.value.includes(e) ? "selected" : "idle"),
|
|
30
|
+
locked: u.annotation.locked === !0,
|
|
31
|
+
select: () => t()?.select([e]),
|
|
32
|
+
remove: () => void t()?.remove(e),
|
|
33
|
+
accept: () => {
|
|
34
|
+
let n = t();
|
|
35
|
+
n && h.value && l(n, e);
|
|
36
|
+
},
|
|
37
|
+
dismiss: () => {
|
|
38
|
+
h.value && t()?.remove(e);
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
});
|
|
42
|
+
return (e, t) => o(e.$slots, "default", i(r(g.value)));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
45
|
+
//#endregion
|
|
46
|
+
export { u as default };
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=InkAnnotation.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkAnnotation.vue_vue_type_script_setup_true_lang.js","names":["_renderSlot","_normalizeProps","_guardReactiveProps","InkAnnotation_vue_vue_type_script_setup_true_lang_default","props","__props","ctx","injectInkContext","observed","shallowRef","observer","readState","s","el","watch","onBeforeUnmount","isSuggestion","computed","slotProps","store","id","acceptSuggestion","renderSlot","_ctx","normalizeProps","guardReactiveProps"],"sources":["../../src/components/InkAnnotation.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * Slot props for one annotation. It renders only its default slot: the overlay already draws\n * the default visual for every annotation that is not custom, so this never draws a second\n * one. InkPage teleports it into the annotation's overlay element.\n */\nimport { acceptSuggestion } from '@inkbar/core'\nimport { computed, onBeforeUnmount, shallowRef, watch } from 'vue'\n\nimport { injectInkContext } from '../context'\n\nimport type { AnnotationSlotProps, AnnotationState } from './annotation-slot'\nimport type { Annotation } from '@inkbar/core'\n\nconst props = withDefaults(\n defineProps<{\n annotation: Annotation\n /** The overlay element; its `data-state` (which includes hover) drives `state`. */\n element?: HTMLElement | undefined\n }>(),\n { element: undefined }\n)\n\ndefineSlots<{ default?(props: AnnotationSlotProps): unknown }>()\n\nconst ctx = injectInkContext()\n\nconst observed = shallowRef<AnnotationState | null>(null)\nlet observer: MutationObserver | undefined\nfunction readState(el: HTMLElement): AnnotationState | null {\n const s = el.dataset.state\n return s === 'idle' || s === 'hover' || s === 'selected' ? s : null\n}\nwatch(\n () => props.element,\n (el) => {\n observer?.disconnect()\n observer = undefined\n observed.value = el ? readState(el) : null\n if (!el) return\n observer = new MutationObserver(() => {\n observed.value = readState(el)\n })\n observer.observe(el, { attributes: true, attributeFilter: ['data-state'] })\n },\n { immediate: true }\n)\nonBeforeUnmount(() => observer?.disconnect())\n\nconst isSuggestion = computed(\n () => props.annotation.type === 'redact' && props.annotation.origin === 'suggested'\n)\n\nconst slotProps = computed<AnnotationSlotProps>(() => {\n const { id } = props.annotation\n const store = () => ctx.document.value?.annotations\n return {\n annotation: props.annotation,\n state: observed.value ?? (ctx.selection.value.includes(id) ? 'selected' : 'idle'),\n locked: props.annotation.locked === true,\n select: () => store()?.select([id]),\n remove: () => void store()?.remove(id),\n accept: () => {\n const s = store()\n if (s && isSuggestion.value) acceptSuggestion(s, id)\n },\n dismiss: () => {\n if (isSuggestion.value) store()?.remove(id)\n },\n }\n})\n</script>\n\n<template>\n <slot v-bind=\"slotProps\" />\n</template>\n"],"mappings":";;;;;;;;;;;EAcA,IAAMI,IAAQC,GAWRC,IAAMC,EAAiB,GAEvBC,IAAWC,EAAmC,IAAI,GACpDC;EACJ,SAASC,EAAU,GAAyC;GAC1D,IAAMC,IAAIC,EAAG,QAAQ;GACrB,OAAOD,MAAM,UAAUA,MAAM,WAAWA,MAAM,aAAaA,IAAI;EACjE;EAeA,AAdAE,QACQV,EAAM,UACX,MAAO;GAIN,AAHAM,GAAU,WAAW,GACrB,IAAW,KAAA,GACX,EAAS,QAAQG,IAAKF,EAAUE,CAAE,IAAI,MACjCA,MACL,IAAW,IAAI,uBAAuB;IACpC,EAAS,QAAQF,EAAUE,CAAE;GAC/B,CAAC,GACDH,EAAS,QAAQG,GAAI;IAAE,YAAY;IAAM,iBAAiB,CAAC,YAAY;GAAE,CAAC;EAC5E,GACA,EAAE,WAAW,GAAK,CACpB,GACAE,QAAsBL,GAAU,WAAW,CAAC;EAE5C,IAAMM,IAAeC,QACbb,EAAM,WAAW,SAAS,YAAYA,EAAM,WAAW,WAAW,WAC1E,GAEMc,IAAYD,QAAoC;GACpD,IAAM,EAAE,UAAOb,EAAM,YACfe,UAAcb,EAAI,SAAS,OAAO;GACxC,OAAO;IACL,YAAYF,EAAM;IAClB,OAAOI,EAAS,UAAUF,EAAI,UAAU,MAAM,SAASc,CAAE,IAAI,aAAa;IAC1E,QAAQhB,EAAM,WAAW,WAAW;IACpC,cAAce,EAAM,CAAC,EAAE,OAAO,CAACC,CAAE,CAAC;IAClC,cAAc,KAAKD,EAAM,CAAC,EAAE,OAAOC,CAAE;IACrC,cAAc;KACZ,IAAMR,IAAIO,EAAM;KAChB,AAAIP,KAAKI,EAAa,SAAOK,EAAiBT,GAAGQ,CAAE;IACrD;IACA,eAAe;KACb,AAAIJ,EAAa,SAAOG,EAAM,CAAC,EAAE,OAAOC,CAAE;IAC5C;GACF;EACF,CAAC;mBAICpB,EAA2BuB,EAAA,QAAA,WAAAtB,EAAAC,EAAbgB,EAAA,KAAS,CAAA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkNotePopover.js","names":["_sfc_main","InkNotePopover_vue_vue_type_script_setup_true_lang_default","InkNotePopover_default"],"sources":["../../src/components/InkNotePopover.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * The thread for the activated note, anchored to the note's overlay element. Activating a\n * reply opens the whole thread from its root. Closing (Close, Escape, a press outside) clears `activated`.\n *\n * A new note has no text. While its root is empty (and unlocked) the default form writes the\n * root's own `contents`, labelled \"Comment\", and the thread list stays hidden until there is\n * something in it; after that the form adds replies. Closing a root that is still empty and\n * has no replies removes it, so an abandoned note does not stay on the page.\n */\nimport { isAbandonedNote, threadOf, threadRoot } from '@inkbar/core'\nimport { PopoverContent, PopoverPortal, PopoverRoot } from 'reka-ui'\nimport { computed, ref, watch } from 'vue'\n\nimport { useAnnotations } from '../composables/useAnnotations'\nimport { injectInkContext } from '../context'\n\nimport { useAnnotationElement } from './annotation-element'\nimport { keepOpenOnAnnotation, useFocusReturn } from './popover-focus'\n\nimport type { Annotation, NoteAnnotation } from '@inkbar/core'\nimport type { NotePopoverSlotProps } from '@inkbar/core/dom'\n\nexport type { NotePopoverSlotProps } from '@inkbar/core/dom'\n\ndefineSlots<{ default?(props: NotePopoverSlotProps): unknown }>()\n\nconst ctx = injectInkContext()\nconst { annotations, add, update, remove } = useAnnotations()\n\nconst find = (id: string | undefined) =>\n id ? annotations.value.find((a) => a.id === id) : undefined\n\nconst note = computed<NoteAnnotation | null>(() => {\n const a = find(ctx.activated.value ?? undefined)\n return a?.type === 'note' ? threadRoot(annotations.value, a) : null\n})\nconst anchor = useAnnotationElement(\n ctx,\n computed(() => note.value?.id ?? null)\n)\nconst focusReturn = useFocusReturn(ctx)\nconst open = computed(() => note.value !== null && anchor.value !== null)\nconst thread = computed<Annotation[]>(() =>\n note.value ? threadOf(annotations.value, note.value) : []\n)\n\n/** True while the default form writes the root note's own text rather than a reply. */\nconst editingRoot = computed(() => {\n const root = note.value\n return root !== null && !root.locked && !root.contents?.trim()\n})\n\nconst draft = ref('')\nconst noteId = () => note.value?.id\n// Synchronous, so a render for another note never shows the old draft.\nwatch(noteId, () => (draft.value = ''), { flush: 'sync' })\nwatch(noteId, (_next, previous) => {\n // Activation moved on from a note that never got any text: drop it.\n if (previous !== undefined) dropIfEmpty(previous)\n})\n\n/**\n * Notes this popover wrote text or a reply for. In controlled mode the write is a proposal\n * the host may not have applied yet, so the applied list still shows the note empty; these\n * are never dropped as abandoned.\n */\nconst written = new Set<string>()\n/** Notes this popover has already asked to remove, so a slow host is asked once. */\nconst dropped = new Set<string>()\n\n/** Removes an unlocked root note with no text and no replies. True when it did. */\nfunction dropIfEmpty(id: string): boolean {\n if (dropped.has(id) || written.has(id) || !isAbandonedNote(annotations.value, id))\n return false\n dropped.add(id)\n remove(id)\n return true\n}\n\nfunction reply(text: string): void {\n const root = note.value\n const contents = text.trim()\n if (!root || !contents) return\n written.add(root.id)\n add({ type: 'note', page: root.page, rect: root.rect, contents, inReplyTo: root.id })\n draft.value = ''\n}\nfunction close(): void {\n const root = note.value\n if (root && find(root.id) && isEmptyRoot(root)) {\n focusReturn.beforeRemove(anchor.value)\n dropIfEmpty(root.id)\n }\n ctx.setActivated(null)\n}\nfunction isEmptyRoot(root: NoteAnnotation): boolean {\n return (\n !written.has(root.id) &&\n !root.locked &&\n !root.contents?.trim() &&\n thread.value.length === 1\n )\n}\nfunction submit(): void {\n const text = draft.value.trim()\n if (!text) return\n if (editingRoot.value) {\n updateContents(text)\n draft.value = ''\n } else reply(text)\n}\n/** Locked notes cannot be removed: removing one does nothing and keeps the popover. */\nfunction removeThread(): void {\n const root = note.value\n if (!root || root.locked) return\n focusReturn.beforeRemove(anchor.value)\n // Under a slow controlled host the note stays listed for a while; it is not asked for again.\n dropped.add(root.id)\n remove(root.id)\n ctx.setActivated(null)\n}\nfunction updateContents(contents: string): void {\n if (!note.value || note.value.locked) return\n if (contents.trim()) written.add(note.value.id)\n update(note.value.id, { contents })\n}\nfunction onOpenChange(next: boolean): void {\n if (!next) close()\n}\n</script>\n\n<template>\n <PopoverRoot :open=\"open\" @update:open=\"onOpenChange\">\n <PopoverPortal>\n <PopoverContent\n v-if=\"note && anchor\"\n :reference=\"anchor\"\n class=\"ink-note-popover\"\n data-ink-note-popover\n side=\"right\"\n :side-offset=\"8\"\n :collision-padding=\"8\"\n v-bind=\"{ 'aria-label': 'Note thread' }\"\n @interact-outside=\"keepOpenOnAnnotation\"\n @close-auto-focus=\"focusReturn.onCloseAutoFocus($event, anchor)\"\n >\n <slot\n :note=\"note\"\n :replies=\"thread.slice(1)\"\n :reply=\"reply\"\n :update=\"updateContents\"\n :remove=\"removeThread\"\n :close=\"close\"\n >\n <ol v-if=\"!editingRoot || thread.length > 1\" class=\"ink-thread\" data-ink-thread>\n <li\n v-for=\"item in thread\"\n :key=\"item.id\"\n class=\"ink-thread-item\"\n data-ink-thread-item\n :data-reply=\"item.inReplyTo ? '' : undefined\"\n >\n <span class=\"ink-thread-author\">{{ item.author ?? 'Anonymous' }}</span>\n <time class=\"ink-thread-time\" :datetime=\"item.createdAt\">\n {{ new Date(item.createdAt).toLocaleString() }}\n </time>\n <p class=\"ink-thread-text\">{{ item.contents }}</p>\n </li>\n </ol>\n <form class=\"ink-thread-reply\" data-ink-thread-reply @submit.prevent=\"submit\">\n <textarea\n v-model=\"draft\"\n :aria-label=\"editingRoot ? 'Comment' : 'Reply'\"\n rows=\"2\"\n :placeholder=\"editingRoot ? 'Comment' : 'Reply'\"\n />\n <div class=\"ink-thread-actions\">\n <button type=\"submit\" :disabled=\"!draft.trim()\">\n {{ editingRoot ? 'Comment' : 'Reply' }}\n </button>\n <button v-if=\"!note.locked\" type=\"button\" @click=\"removeThread\">\n Delete\n </button>\n <button type=\"button\" @click=\"close\">Close</button>\n </div>\n </form>\n </slot>\n </PopoverContent>\n </PopoverPortal>\n </PopoverRoot>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { NotePopoverSlotProps } from '@inkbar/core/dom';
|
|
2
|
+
export type { NotePopoverSlotProps } from '@inkbar/core/dom';
|
|
3
|
+
type __VLS_Slots = {
|
|
4
|
+
default?(props: NotePopoverSlotProps): unknown;
|
|
5
|
+
};
|
|
6
|
+
declare const __VLS_base: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
7
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
8
|
+
declare const _default: typeof __VLS_export;
|
|
9
|
+
export default _default;
|
|
10
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
11
|
+
new (): {
|
|
12
|
+
$slots: S;
|
|
13
|
+
};
|
|
14
|
+
};
|
|
15
|
+
//# sourceMappingURL=InkNotePopover.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkNotePopover.vue.d.ts","sourceRoot":"","sources":["../../src/components/InkNotePopover.vue"],"names":[],"mappings":"AAsNA,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAE5D,YAAY,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAA;AAG5D,KAAK,WAAW,GAAG;IAAE,OAAO,CAAC,CAAC,KAAK,EAAE,oBAAoB,GAAG,OAAO,CAAA;CAAE,CAAC;AA2RtE,QAAA,MAAM,UAAU,+QACd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AACzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { injectInkContext as e } from "../context.js";
|
|
2
|
+
import { useAnnotations as t } from "../composables/useAnnotations.js";
|
|
3
|
+
import { useAnnotationElement as n } from "./annotation-element.js";
|
|
4
|
+
import { keepOpenOnAnnotation as r, useFocusReturn as i } from "./popover-focus.js";
|
|
5
|
+
import { Fragment as a, computed as o, createBlock as s, createCommentVNode as c, createElementBlock as l, createElementVNode as u, createVNode as d, defineComponent as f, mergeProps as p, openBlock as m, ref as h, renderList as g, renderSlot as _, toDisplayString as v, unref as y, vModelText as b, watch as x, withCtx as S, withDirectives as C, withModifiers as w } from "vue";
|
|
6
|
+
import { isAbandonedNote as T, threadOf as E, threadRoot as ee } from "@inkbar/core";
|
|
7
|
+
import { PopoverContent as te, PopoverPortal as ne, PopoverRoot as re } from "reka-ui";
|
|
8
|
+
//#region src/components/InkNotePopover.vue?vue&type=script&setup=true&lang.ts
|
|
9
|
+
var ie = {
|
|
10
|
+
key: 0,
|
|
11
|
+
class: "ink-thread",
|
|
12
|
+
"data-ink-thread": ""
|
|
13
|
+
}, ae = ["data-reply"], D = { class: "ink-thread-author" }, O = ["datetime"], k = { class: "ink-thread-text" }, A = ["aria-label", "placeholder"], j = { class: "ink-thread-actions" }, M = ["disabled"], N = /*@__PURE__*/ f({
|
|
14
|
+
__name: "InkNotePopover",
|
|
15
|
+
setup(f) {
|
|
16
|
+
let N = e(), { annotations: P, add: F, update: I, remove: L } = t(), R = (e) => e ? P.value.find((t) => t.id === e) : void 0, z = o(() => {
|
|
17
|
+
let e = R(N.activated.value ?? void 0);
|
|
18
|
+
return e?.type === "note" ? ee(P.value, e) : null;
|
|
19
|
+
}), B = n(N, o(() => z.value?.id ?? null)), V = i(N), H = o(() => z.value !== null && B.value !== null), U = o(() => z.value ? E(P.value, z.value) : []), W = o(() => {
|
|
20
|
+
let e = z.value;
|
|
21
|
+
return e !== null && !e.locked && !e.contents?.trim();
|
|
22
|
+
}), G = h(""), K = () => z.value?.id;
|
|
23
|
+
x(K, () => G.value = "", { flush: "sync" }), x(K, (e, t) => {
|
|
24
|
+
t !== void 0 && Y(t);
|
|
25
|
+
});
|
|
26
|
+
let q = /* @__PURE__ */ new Set(), J = /* @__PURE__ */ new Set();
|
|
27
|
+
function Y(e) {
|
|
28
|
+
return J.has(e) || q.has(e) || !T(P.value, e) ? !1 : (J.add(e), L(e), !0);
|
|
29
|
+
}
|
|
30
|
+
function X(e) {
|
|
31
|
+
let t = z.value, n = e.trim();
|
|
32
|
+
t && n && (q.add(t.id), F({
|
|
33
|
+
type: "note",
|
|
34
|
+
page: t.page,
|
|
35
|
+
rect: t.rect,
|
|
36
|
+
contents: n,
|
|
37
|
+
inReplyTo: t.id
|
|
38
|
+
}), G.value = "");
|
|
39
|
+
}
|
|
40
|
+
function Z() {
|
|
41
|
+
let e = z.value;
|
|
42
|
+
e && R(e.id) && oe(e) && (V.beforeRemove(B.value), Y(e.id)), N.setActivated(null);
|
|
43
|
+
}
|
|
44
|
+
function oe(e) {
|
|
45
|
+
return !q.has(e.id) && !e.locked && !e.contents?.trim() && U.value.length === 1;
|
|
46
|
+
}
|
|
47
|
+
function se() {
|
|
48
|
+
let e = G.value.trim();
|
|
49
|
+
e && (W.value ? ($(e), G.value = "") : X(e));
|
|
50
|
+
}
|
|
51
|
+
function Q() {
|
|
52
|
+
let e = z.value;
|
|
53
|
+
e && !e.locked && (V.beforeRemove(B.value), J.add(e.id), L(e.id), N.setActivated(null));
|
|
54
|
+
}
|
|
55
|
+
function $(e) {
|
|
56
|
+
z.value && !z.value.locked && (e.trim() && q.add(z.value.id), I(z.value.id, { contents: e }));
|
|
57
|
+
}
|
|
58
|
+
function ce(e) {
|
|
59
|
+
e || Z();
|
|
60
|
+
}
|
|
61
|
+
return (e, t) => (m(), s(y(re), {
|
|
62
|
+
open: H.value,
|
|
63
|
+
"onUpdate:open": ce
|
|
64
|
+
}, {
|
|
65
|
+
default: S(() => [d(y(ne), null, {
|
|
66
|
+
default: S(() => [z.value && y(B) ? (m(), s(y(te), p({
|
|
67
|
+
key: 0,
|
|
68
|
+
reference: y(B),
|
|
69
|
+
class: "ink-note-popover",
|
|
70
|
+
"data-ink-note-popover": "",
|
|
71
|
+
side: "right",
|
|
72
|
+
"side-offset": 8,
|
|
73
|
+
"collision-padding": 8
|
|
74
|
+
}, { "aria-label": "Note thread" }, {
|
|
75
|
+
onInteractOutside: y(r),
|
|
76
|
+
onCloseAutoFocus: t[1] ||= (e) => y(V).onCloseAutoFocus(e, y(B))
|
|
77
|
+
}), {
|
|
78
|
+
default: S(() => [_(e.$slots, "default", {
|
|
79
|
+
note: z.value,
|
|
80
|
+
replies: U.value.slice(1),
|
|
81
|
+
reply: X,
|
|
82
|
+
update: $,
|
|
83
|
+
remove: Q,
|
|
84
|
+
close: Z
|
|
85
|
+
}, () => [!W.value || U.value.length > 1 ? (m(), l("ol", ie, [(m(!0), l(a, null, g(U.value, (e) => (m(), l("li", {
|
|
86
|
+
key: e.id,
|
|
87
|
+
class: "ink-thread-item",
|
|
88
|
+
"data-ink-thread-item": "",
|
|
89
|
+
"data-reply": e.inReplyTo ? "" : void 0
|
|
90
|
+
}, [
|
|
91
|
+
u("span", D, v(e.author ?? "Anonymous"), 1),
|
|
92
|
+
u("time", {
|
|
93
|
+
class: "ink-thread-time",
|
|
94
|
+
datetime: e.createdAt
|
|
95
|
+
}, v(new Date(e.createdAt).toLocaleString()), 9, O),
|
|
96
|
+
u("p", k, v(e.contents), 1)
|
|
97
|
+
], 8, ae))), 128))])) : c("", !0), u("form", {
|
|
98
|
+
class: "ink-thread-reply",
|
|
99
|
+
"data-ink-thread-reply": "",
|
|
100
|
+
onSubmit: w(se, ["prevent"])
|
|
101
|
+
}, [C(u("textarea", {
|
|
102
|
+
"onUpdate:modelValue": t[0] ||= (e) => G.value = e,
|
|
103
|
+
"aria-label": W.value ? "Comment" : "Reply",
|
|
104
|
+
rows: "2",
|
|
105
|
+
placeholder: W.value ? "Comment" : "Reply"
|
|
106
|
+
}, null, 8, A), [[b, G.value]]), u("div", j, [
|
|
107
|
+
u("button", {
|
|
108
|
+
type: "submit",
|
|
109
|
+
disabled: !G.value.trim()
|
|
110
|
+
}, v(W.value ? "Comment" : "Reply"), 9, M),
|
|
111
|
+
z.value.locked ? c("", !0) : (m(), l("button", {
|
|
112
|
+
key: 0,
|
|
113
|
+
type: "button",
|
|
114
|
+
onClick: Q
|
|
115
|
+
}, " Delete ")),
|
|
116
|
+
u("button", {
|
|
117
|
+
type: "button",
|
|
118
|
+
onClick: Z
|
|
119
|
+
}, "Close")
|
|
120
|
+
])], 32)])]),
|
|
121
|
+
_: 3
|
|
122
|
+
}, 16, ["reference", "onInteractOutside"])) : c("", !0)]),
|
|
123
|
+
_: 3
|
|
124
|
+
})]),
|
|
125
|
+
_: 3
|
|
126
|
+
}, 8, ["open"]));
|
|
127
|
+
}
|
|
128
|
+
});
|
|
129
|
+
//#endregion
|
|
130
|
+
export { N as default };
|
|
131
|
+
|
|
132
|
+
//# sourceMappingURL=InkNotePopover.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkNotePopover.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createBlock","_unref","_withCtx","_createVNode","_mergeProps","_renderSlot","_createElementBlock","_Fragment","_renderList","_createElementVNode","_toDisplayString","_createCommentVNode","_withModifiers","_withDirectives","_vModelText","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","InkNotePopover_vue_vue_type_script_setup_true_lang_default","ctx","injectInkContext","useAnnotations","find","id","annotations","a","note","computed","threadRoot","anchor","useAnnotationElement","focusReturn","useFocusReturn","open","thread","threadOf","editingRoot","root","draft","ref","noteId","watch","previous","dropIfEmpty","written","dropped","isAbandonedNote","remove","reply","contents","text","add","close","isEmptyRoot","submit","updateContents","removeThread","update","onOpenChange","next","openBlock","createBlock","unref","PopoverRoot","withCtx","createVNode","PopoverPortal","PopoverContent","mergeProps","keepOpenOnAnnotation","_cache","$event","renderSlot","_ctx","createElementBlock","Fragment","renderList","item","createElementVNode","toDisplayString","createCommentVNode","withModifiers","withDirectives","vModelText"],"sources":["../../src/components/InkNotePopover.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * The thread for the activated note, anchored to the note's overlay element. Activating a\n * reply opens the whole thread from its root. Closing (Close, Escape, a press outside) clears `activated`.\n *\n * A new note has no text. While its root is empty (and unlocked) the default form writes the\n * root's own `contents`, labelled \"Comment\", and the thread list stays hidden until there is\n * something in it; after that the form adds replies. Closing a root that is still empty and\n * has no replies removes it, so an abandoned note does not stay on the page.\n */\nimport { isAbandonedNote, threadOf, threadRoot } from '@inkbar/core'\nimport { PopoverContent, PopoverPortal, PopoverRoot } from 'reka-ui'\nimport { computed, ref, watch } from 'vue'\n\nimport { useAnnotations } from '../composables/useAnnotations'\nimport { injectInkContext } from '../context'\n\nimport { useAnnotationElement } from './annotation-element'\nimport { keepOpenOnAnnotation, useFocusReturn } from './popover-focus'\n\nimport type { Annotation, NoteAnnotation } from '@inkbar/core'\nimport type { NotePopoverSlotProps } from '@inkbar/core/dom'\n\nexport type { NotePopoverSlotProps } from '@inkbar/core/dom'\n\ndefineSlots<{ default?(props: NotePopoverSlotProps): unknown }>()\n\nconst ctx = injectInkContext()\nconst { annotations, add, update, remove } = useAnnotations()\n\nconst find = (id: string | undefined) =>\n id ? annotations.value.find((a) => a.id === id) : undefined\n\nconst note = computed<NoteAnnotation | null>(() => {\n const a = find(ctx.activated.value ?? undefined)\n return a?.type === 'note' ? threadRoot(annotations.value, a) : null\n})\nconst anchor = useAnnotationElement(\n ctx,\n computed(() => note.value?.id ?? null)\n)\nconst focusReturn = useFocusReturn(ctx)\nconst open = computed(() => note.value !== null && anchor.value !== null)\nconst thread = computed<Annotation[]>(() =>\n note.value ? threadOf(annotations.value, note.value) : []\n)\n\n/** True while the default form writes the root note's own text rather than a reply. */\nconst editingRoot = computed(() => {\n const root = note.value\n return root !== null && !root.locked && !root.contents?.trim()\n})\n\nconst draft = ref('')\nconst noteId = () => note.value?.id\n// Synchronous, so a render for another note never shows the old draft.\nwatch(noteId, () => (draft.value = ''), { flush: 'sync' })\nwatch(noteId, (_next, previous) => {\n // Activation moved on from a note that never got any text: drop it.\n if (previous !== undefined) dropIfEmpty(previous)\n})\n\n/**\n * Notes this popover wrote text or a reply for. In controlled mode the write is a proposal\n * the host may not have applied yet, so the applied list still shows the note empty; these\n * are never dropped as abandoned.\n */\nconst written = new Set<string>()\n/** Notes this popover has already asked to remove, so a slow host is asked once. */\nconst dropped = new Set<string>()\n\n/** Removes an unlocked root note with no text and no replies. True when it did. */\nfunction dropIfEmpty(id: string): boolean {\n if (dropped.has(id) || written.has(id) || !isAbandonedNote(annotations.value, id))\n return false\n dropped.add(id)\n remove(id)\n return true\n}\n\nfunction reply(text: string): void {\n const root = note.value\n const contents = text.trim()\n if (!root || !contents) return\n written.add(root.id)\n add({ type: 'note', page: root.page, rect: root.rect, contents, inReplyTo: root.id })\n draft.value = ''\n}\nfunction close(): void {\n const root = note.value\n if (root && find(root.id) && isEmptyRoot(root)) {\n focusReturn.beforeRemove(anchor.value)\n dropIfEmpty(root.id)\n }\n ctx.setActivated(null)\n}\nfunction isEmptyRoot(root: NoteAnnotation): boolean {\n return (\n !written.has(root.id) &&\n !root.locked &&\n !root.contents?.trim() &&\n thread.value.length === 1\n )\n}\nfunction submit(): void {\n const text = draft.value.trim()\n if (!text) return\n if (editingRoot.value) {\n updateContents(text)\n draft.value = ''\n } else reply(text)\n}\n/** Locked notes cannot be removed: removing one does nothing and keeps the popover. */\nfunction removeThread(): void {\n const root = note.value\n if (!root || root.locked) return\n focusReturn.beforeRemove(anchor.value)\n // Under a slow controlled host the note stays listed for a while; it is not asked for again.\n dropped.add(root.id)\n remove(root.id)\n ctx.setActivated(null)\n}\nfunction updateContents(contents: string): void {\n if (!note.value || note.value.locked) return\n if (contents.trim()) written.add(note.value.id)\n update(note.value.id, { contents })\n}\nfunction onOpenChange(next: boolean): void {\n if (!next) close()\n}\n</script>\n\n<template>\n <PopoverRoot :open=\"open\" @update:open=\"onOpenChange\">\n <PopoverPortal>\n <PopoverContent\n v-if=\"note && anchor\"\n :reference=\"anchor\"\n class=\"ink-note-popover\"\n data-ink-note-popover\n side=\"right\"\n :side-offset=\"8\"\n :collision-padding=\"8\"\n v-bind=\"{ 'aria-label': 'Note thread' }\"\n @interact-outside=\"keepOpenOnAnnotation\"\n @close-auto-focus=\"focusReturn.onCloseAutoFocus($event, anchor)\"\n >\n <slot\n :note=\"note\"\n :replies=\"thread.slice(1)\"\n :reply=\"reply\"\n :update=\"updateContents\"\n :remove=\"removeThread\"\n :close=\"close\"\n >\n <ol v-if=\"!editingRoot || thread.length > 1\" class=\"ink-thread\" data-ink-thread>\n <li\n v-for=\"item in thread\"\n :key=\"item.id\"\n class=\"ink-thread-item\"\n data-ink-thread-item\n :data-reply=\"item.inReplyTo ? '' : undefined\"\n >\n <span class=\"ink-thread-author\">{{ item.author ?? 'Anonymous' }}</span>\n <time class=\"ink-thread-time\" :datetime=\"item.createdAt\">\n {{ new Date(item.createdAt).toLocaleString() }}\n </time>\n <p class=\"ink-thread-text\">{{ item.contents }}</p>\n </li>\n </ol>\n <form class=\"ink-thread-reply\" data-ink-thread-reply @submit.prevent=\"submit\">\n <textarea\n v-model=\"draft\"\n :aria-label=\"editingRoot ? 'Comment' : 'Reply'\"\n rows=\"2\"\n :placeholder=\"editingRoot ? 'Comment' : 'Reply'\"\n />\n <div class=\"ink-thread-actions\">\n <button type=\"submit\" :disabled=\"!draft.trim()\">\n {{ editingRoot ? 'Comment' : 'Reply' }}\n </button>\n <button v-if=\"!note.locked\" type=\"button\" @click=\"removeThread\">\n Delete\n </button>\n <button type=\"button\" @click=\"close\">Close</button>\n </div>\n </form>\n </slot>\n </PopoverContent>\n </PopoverPortal>\n </PopoverRoot>\n</template>\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkPage.js","names":["_sfc_main","InkPage_vue_vue_type_script_setup_true_lang_default","InkPage_default"],"sources":["../../src/components/InkPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * One page the viewer engine mounted. Renders nothing in place; Teleports go into the host\n * (page error) and, when the host provides the `annotation` slot, into each annotation's\n * overlay element.\n *\n * The overlay creates and removes those elements as the annotation list changes, outside\n * Vue. So the teleport targets come from a list read after every overlay render: this\n * component subscribes to the store after the overlay does (the engine created the mount\n * first), and core notifies subscribers in order, so the elements already exist when the list\n * arrives here.\n *\n * The engine tracks page errors and sets `data-state=\"error\"` on the host; this component\n * teleports the `page-error` slot into it while `error` is set.\n */\nimport { onBeforeUnmount, onMounted, shallowRef, watch } from 'vue'\n\nimport { injectInkContext } from '../context'\n\nimport InkAnnotation from './InkAnnotation.vue'\n\nimport type { AnnotationSlotProps } from './annotation-slot'\nimport type { Annotation, InkError } from '@inkbar/core'\nimport type { PageMount } from '@inkbar/core/dom'\n\nconst props = withDefaults(\n defineProps<{\n page: number\n host: HTMLElement\n /** The engine's page mount in `host`. */\n mount: PageMount\n /** The engine's error for this page, `null` while it renders. */\n error: InkError | null\n /** Fixed for the life of the mount: the engine rebuilds when it changes. */\n customAnnotations?: boolean\n }>(),\n { customAnnotations: false }\n)\n\ndefineSlots<{\n annotation?(props: AnnotationSlotProps): unknown\n 'page-error'?(props: { error: InkError; page: number }): unknown\n}>()\n\nconst ctx = injectInkContext()\nconst targets = shallowRef<{ annotation: Annotation; el: HTMLElement }[]>([])\nlet stopList: (() => void) | undefined\n\nfunction stop(): void {\n stopList?.()\n stopList = undefined\n targets.value = []\n}\n\n// Targets are read from the current mount, and read again when InkViewport hands this page a\n// new one (the page left and came back within one tick, under the same key), as React and\n// Svelte do. The watch fires only when one of these values changes, not on an error change.\nfunction start(): void {\n stop()\n const doc = ctx.document.value\n if (!doc || !props.customAnnotations) return\n const mount = props.mount\n const read = (): void => {\n targets.value = doc.annotations.byPage(props.page).flatMap((annotation) => {\n const el = mount.overlay.elementFor(annotation.id)\n return el ? [{ annotation, el }] : []\n })\n }\n stopList = doc.annotations.list.subscribe(read)\n read()\n}\n\nonMounted(() => {\n start()\n watch([() => props.mount, () => props.page, () => props.customAnnotations], start)\n})\n\nonBeforeUnmount(stop)\n</script>\n\n<template>\n <Teleport v-if=\"error\" :to=\"host\">\n <div class=\"ink-page-error\" data-ink-page-error role=\"alert\">\n <slot name=\"page-error\" :error=\"error\" :page=\"page\">\n <p>Page {{ page + 1 }} could not be shown.</p>\n <code class=\"ink-error-code\">{{ error.code }}</code>\n </slot>\n </div>\n </Teleport>\n <Teleport v-for=\"t in targets\" :key=\"t.annotation.id\" :to=\"t.el\">\n <InkAnnotation :annotation=\"t.annotation\" :element=\"t.el\">\n <template #default=\"slotProps\">\n <slot name=\"annotation\" v-bind=\"slotProps\" />\n </template>\n </InkAnnotation>\n </Teleport>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { AnnotationSlotProps } from './annotation-slot';
|
|
2
|
+
import { InkError } from '@inkbar/core';
|
|
3
|
+
import { PageMount } from '@inkbar/core/dom';
|
|
4
|
+
type __VLS_Props = {
|
|
5
|
+
page: number;
|
|
6
|
+
host: HTMLElement;
|
|
7
|
+
/** The engine's page mount in `host`. */
|
|
8
|
+
mount: PageMount;
|
|
9
|
+
/** The engine's error for this page, `null` while it renders. */
|
|
10
|
+
error: InkError | null;
|
|
11
|
+
/** Fixed for the life of the mount: the engine rebuilds when it changes. */
|
|
12
|
+
customAnnotations?: boolean;
|
|
13
|
+
};
|
|
14
|
+
type __VLS_Slots = {
|
|
15
|
+
annotation?(props: AnnotationSlotProps): unknown;
|
|
16
|
+
'page-error'?(props: {
|
|
17
|
+
error: InkError;
|
|
18
|
+
page: number;
|
|
19
|
+
}): unknown;
|
|
20
|
+
};
|
|
21
|
+
declare const __VLS_base: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{}>, {
|
|
22
|
+
customAnnotations: boolean;
|
|
23
|
+
}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
|
|
24
|
+
declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
|
|
25
|
+
declare const _default: typeof __VLS_export;
|
|
26
|
+
export default _default;
|
|
27
|
+
type __VLS_WithSlots<T, S> = T & {
|
|
28
|
+
new (): {
|
|
29
|
+
$slots: S;
|
|
30
|
+
};
|
|
31
|
+
};
|
|
32
|
+
//# sourceMappingURL=InkPage.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkPage.vue.d.ts","sourceRoot":"","sources":["../../src/components/InkPage.vue"],"names":[],"mappings":"AAuHA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,KAAK,EAAc,QAAQ,EAAE,MAAM,cAAc,CAAA;AACxD,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAA;AAGjD,KAAK,WAAW,GAAG;IACf,IAAI,EAAE,MAAM,CAAA;IACZ,IAAI,EAAE,WAAW,CAAA;IACjB,yCAAyC;IACzC,KAAK,EAAE,SAAS,CAAA;IAChB,iEAAiE;IACjE,KAAK,EAAE,QAAQ,GAAG,IAAI,CAAA;IACtB,4EAA4E;IAC5E,iBAAiB,CAAC,EAAE,OAAO,CAAA;CAC5B,CAAC;AAMJ,KAAK,WAAW,GAAG;IACjB,UAAU,CAAC,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAA;IAChD,YAAY,CAAC,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAA;CACjE,CAAC;AAqIF,QAAA,MAAM,UAAU;;6EAGd,CAAC;AACH,QAAA,MAAM,YAAY,EAAS,eAAe,CAAC,OAAO,UAAU,EAAE,WAAW,CAAC,CAAC;wBACtD,OAAO,YAAY;AAAxC,wBAAyC;AAWzC,KAAK,eAAe,CAAC,CAAC,EAAE,CAAC,IAAI,CAAC,GAAG;IAChC,QAAO;QACN,MAAM,EAAE,CAAC,CAAC;KACV,CAAA;CACD,CAAC"}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { injectInkContext as e } from "../context.js";
|
|
2
|
+
import t from "./InkAnnotation.js";
|
|
3
|
+
import { Fragment as n, Teleport as r, createBlock as i, createCommentVNode as a, createElementBlock as o, createElementVNode as s, createVNode as c, defineComponent as l, mergeProps as u, onBeforeUnmount as d, onMounted as f, openBlock as p, renderList as m, renderSlot as h, shallowRef as g, toDisplayString as _, watch as v, withCtx as y } from "vue";
|
|
4
|
+
//#region src/components/InkPage.vue?vue&type=script&setup=true&lang.ts
|
|
5
|
+
var b = {
|
|
6
|
+
class: "ink-page-error",
|
|
7
|
+
"data-ink-page-error": "",
|
|
8
|
+
role: "alert"
|
|
9
|
+
}, x = { class: "ink-error-code" }, S = /*@__PURE__*/ l({
|
|
10
|
+
__name: "InkPage",
|
|
11
|
+
props: {
|
|
12
|
+
page: {},
|
|
13
|
+
host: {},
|
|
14
|
+
mount: {},
|
|
15
|
+
error: {},
|
|
16
|
+
customAnnotations: {
|
|
17
|
+
type: Boolean,
|
|
18
|
+
default: !1
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
setup(l) {
|
|
22
|
+
let S = l, C = e(), w = g([]), T;
|
|
23
|
+
function E() {
|
|
24
|
+
T?.(), T = void 0, w.value = [];
|
|
25
|
+
}
|
|
26
|
+
function D() {
|
|
27
|
+
E();
|
|
28
|
+
let e = C.document.value;
|
|
29
|
+
if (!e || !S.customAnnotations) return;
|
|
30
|
+
let t = S.mount, n = () => {
|
|
31
|
+
w.value = e.annotations.byPage(S.page).flatMap((e) => {
|
|
32
|
+
let n = t.overlay.elementFor(e.id);
|
|
33
|
+
return n ? [{
|
|
34
|
+
annotation: e,
|
|
35
|
+
el: n
|
|
36
|
+
}] : [];
|
|
37
|
+
});
|
|
38
|
+
};
|
|
39
|
+
T = e.annotations.list.subscribe(n), n();
|
|
40
|
+
}
|
|
41
|
+
return f(() => {
|
|
42
|
+
D(), v([
|
|
43
|
+
() => S.mount,
|
|
44
|
+
() => S.page,
|
|
45
|
+
() => S.customAnnotations
|
|
46
|
+
], D);
|
|
47
|
+
}), d(E), (e, d) => (p(), o(n, null, [l.error ? (p(), i(r, {
|
|
48
|
+
key: 0,
|
|
49
|
+
to: l.host
|
|
50
|
+
}, [s("div", b, [h(e.$slots, "page-error", {
|
|
51
|
+
error: l.error,
|
|
52
|
+
page: l.page
|
|
53
|
+
}, () => [s("p", null, "Page " + _(l.page + 1) + " could not be shown.", 1), s("code", x, _(l.error.code), 1)])])], 8, ["to"])) : a("", !0), (p(!0), o(n, null, m(w.value, (n) => (p(), i(r, {
|
|
54
|
+
key: n.annotation.id,
|
|
55
|
+
to: n.el
|
|
56
|
+
}, [c(t, {
|
|
57
|
+
annotation: n.annotation,
|
|
58
|
+
element: n.el
|
|
59
|
+
}, {
|
|
60
|
+
default: y((t) => [h(e.$slots, "annotation", u({ ref_for: !0 }, t))]),
|
|
61
|
+
_: 3
|
|
62
|
+
}, 8, ["annotation", "element"])], 8, ["to"]))), 128))], 64));
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
//#endregion
|
|
66
|
+
export { S as default };
|
|
67
|
+
|
|
68
|
+
//# sourceMappingURL=InkPage.vue_vue_type_script_setup_true_lang.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkPage.vue_vue_type_script_setup_true_lang.js","names":["_openBlock","_createElementBlock","_Fragment","_createBlock","_Teleport","_createElementVNode","_renderSlot","_toDisplayString","_createCommentVNode","_renderList","_createVNode","InkAnnotation","_withCtx","_mergeProps","InkAnnotation_default","_hoisted_1","_hoisted_2","InkPage_vue_vue_type_script_setup_true_lang_default","props","__props","ctx","injectInkContext","targets","shallowRef","stopList","stop","start","doc","mount","read","el","annotation","onMounted","watch","onBeforeUnmount","openBlock","createElementBlock","Fragment","createBlock","Teleport","createElementVNode","renderSlot","_ctx","toDisplayString","createCommentVNode","renderList","t","createVNode","withCtx","mergeProps","slotProps"],"sources":["../../src/components/InkPage.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * One page the viewer engine mounted. Renders nothing in place; Teleports go into the host\n * (page error) and, when the host provides the `annotation` slot, into each annotation's\n * overlay element.\n *\n * The overlay creates and removes those elements as the annotation list changes, outside\n * Vue. So the teleport targets come from a list read after every overlay render: this\n * component subscribes to the store after the overlay does (the engine created the mount\n * first), and core notifies subscribers in order, so the elements already exist when the list\n * arrives here.\n *\n * The engine tracks page errors and sets `data-state=\"error\"` on the host; this component\n * teleports the `page-error` slot into it while `error` is set.\n */\nimport { onBeforeUnmount, onMounted, shallowRef, watch } from 'vue'\n\nimport { injectInkContext } from '../context'\n\nimport InkAnnotation from './InkAnnotation.vue'\n\nimport type { AnnotationSlotProps } from './annotation-slot'\nimport type { Annotation, InkError } from '@inkbar/core'\nimport type { PageMount } from '@inkbar/core/dom'\n\nconst props = withDefaults(\n defineProps<{\n page: number\n host: HTMLElement\n /** The engine's page mount in `host`. */\n mount: PageMount\n /** The engine's error for this page, `null` while it renders. */\n error: InkError | null\n /** Fixed for the life of the mount: the engine rebuilds when it changes. */\n customAnnotations?: boolean\n }>(),\n { customAnnotations: false }\n)\n\ndefineSlots<{\n annotation?(props: AnnotationSlotProps): unknown\n 'page-error'?(props: { error: InkError; page: number }): unknown\n}>()\n\nconst ctx = injectInkContext()\nconst targets = shallowRef<{ annotation: Annotation; el: HTMLElement }[]>([])\nlet stopList: (() => void) | undefined\n\nfunction stop(): void {\n stopList?.()\n stopList = undefined\n targets.value = []\n}\n\n// Targets are read from the current mount, and read again when InkViewport hands this page a\n// new one (the page left and came back within one tick, under the same key), as React and\n// Svelte do. The watch fires only when one of these values changes, not on an error change.\nfunction start(): void {\n stop()\n const doc = ctx.document.value\n if (!doc || !props.customAnnotations) return\n const mount = props.mount\n const read = (): void => {\n targets.value = doc.annotations.byPage(props.page).flatMap((annotation) => {\n const el = mount.overlay.elementFor(annotation.id)\n return el ? [{ annotation, el }] : []\n })\n }\n stopList = doc.annotations.list.subscribe(read)\n read()\n}\n\nonMounted(() => {\n start()\n watch([() => props.mount, () => props.page, () => props.customAnnotations], start)\n})\n\nonBeforeUnmount(stop)\n</script>\n\n<template>\n <Teleport v-if=\"error\" :to=\"host\">\n <div class=\"ink-page-error\" data-ink-page-error role=\"alert\">\n <slot name=\"page-error\" :error=\"error\" :page=\"page\">\n <p>Page {{ page + 1 }} could not be shown.</p>\n <code class=\"ink-error-code\">{{ error.code }}</code>\n </slot>\n </div>\n </Teleport>\n <Teleport v-for=\"t in targets\" :key=\"t.annotation.id\" :to=\"t.el\">\n <InkAnnotation :annotation=\"t.annotation\" :element=\"t.el\">\n <template #default=\"slotProps\">\n <slot name=\"annotation\" v-bind=\"slotProps\" />\n </template>\n </InkAnnotation>\n </Teleport>\n</template>\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;EAyBA,IAAMkB,IAAQC,GAmBRC,IAAMC,EAAiB,GACvBC,IAAUC,EAA0D,CAAC,CAAC,GACxEC;EAEJ,SAASC,IAAa;GAGpB,AAFAD,IAAW,GACX,IAAW,KAAA,GACX,EAAQ,QAAQ,CAAC;EACnB;EAKA,SAASE,IAAc;GACrB,EAAK;GACL,IAAMC,IAAMP,EAAI,SAAS;GACzB,IAAI,CAACO,KAAO,CAACT,EAAM,mBAAmB;GACtC,IAAMU,IAAQV,EAAM,OACdW,UAAmB;IACvB,EAAQ,QAAQF,EAAI,YAAY,OAAOT,EAAM,IAAI,CAAC,CAAC,SAAS,MAAe;KACzE,IAAMY,IAAKF,EAAM,QAAQ,WAAWG,EAAW,EAAE;KACjD,OAAOD,IAAK,CAAC;MAAE;MAAY;KAAG,CAAC,IAAI,CAAC;IACtC,CAAC;GACH;GAEA,AADA,IAAWH,EAAI,YAAY,KAAK,UAAUE,CAAI,GAC9CA,EAAK;EACP;SAEAG,QAAgB;GAEd,AADAN,EAAM,GACNO,EAAM;UAAOf,EAAM;UAAaA,EAAM;UAAYA,EAAM;GAAiB,GAAGQ,CAAK;EACnF,CAAC,GAEDQ,EAAgBT,CAAI,cAIFU,EAAA,GAAAC,EAAAC,GAAA,MAAA,CAAAlB,EAAA,SAAhBnB,EAAA,GAAAG,EAOWC,GAAA;;GAPa,IAAIe,EAAA;EAC1Bd,GAAAA,CAAAA,EAKM,OALNU,GAKM,CAJJT,EAGOoC,EAAA,QAAA,cAAA;GAHkB,OAAOvB,EAAA;GAAQ,MAAMA,EAAA;EAA9C,SAGO,CAFLd,EAA8C,KAAA,MAA3C,UAAKE,EAAGY,EAAA,OAAI,CAAA,IAAO,wBAAoB,CAAA,GAC1Cd,EAAoD,QAApDW,GAAoDT,EAApBY,EAAA,MAAM,IAAI,GAAA,CAAA,CAAA,CAAA,CAAA,CAAA,CAAA,GAAA,GAAA,CAAA,IAAA,CAAA,KAAAyB,EAAA,IAAA,EAAA,IAIhD5C,EAAA,EAAA,GAAAC,EAMWC,GAAA,MAAAO,EANWa,EAAA,QAAL,OAAjBtB,EAAA,GAAAG,EAMWC,GAAA;GANqB,KAAK0C,EAAE,WAAW;GAAK,IAAIA,EAAE;EAC3DpC,GAAAA,CAAAA,EAIgBC,GAAA;GAJA,YAAYmC,EAAE;GAAa,SAASA,EAAE;;GACzC,SAAOlC,GAAE,MAAS,CAC3BN,EAA6CoC,EAAA,QAAA,cAA7C7B,EAA6C,EAAA,SAAA,GAAA,GAAbqC,CAAS,CAAA,CAAA,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkPageCount.js","names":["_sfc_main","InkPageCount_vue_vue_type_script_setup_true_lang_default","InkPageCount_default"],"sources":["../../src/components/InkPageCount.vue"],"sourcesContent":["<script setup lang=\"ts\">\nimport { computed } from 'vue'\n\nimport { usePage } from '../composables/usePage'\n\nconst { count } = usePage()\nconst text = computed(() => `of ${count.value}`)\n</script>\n\n<template>\n <span class=\"ink-page-count\" data-ink-page-count aria-live=\"polite\">{{ text }}</span>\n</template>\n"],"mappings":""}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
declare const __VLS_export: import('vue').DefineComponent<{}, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {}, string, import('vue').PublicProps, Readonly<{}> & Readonly<{}>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, true, {}, any>;
|
|
2
|
+
declare const _default: typeof __VLS_export;
|
|
3
|
+
export default _default;
|
|
4
|
+
//# sourceMappingURL=InkPageCount.vue.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"InkPageCount.vue.d.ts","sourceRoot":"","sources":["../../src/components/InkPageCount.vue"],"names":[],"mappings":"AAoCA,QAAA,MAAM,YAAY,+QAChB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
|