@inkbar/vue 0.6.0 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (69) hide show
  1. package/README.md +12 -1
  2. package/dist/components/InkFindBar.js +7 -0
  3. package/dist/components/InkFindBar.js.map +1 -0
  4. package/dist/components/InkFindBar.vue.d.ts +4 -0
  5. package/dist/components/InkFindBar.vue.d.ts.map +1 -0
  6. package/dist/components/InkFindBar.vue_vue_type_script_setup_true_lang.js +133 -0
  7. package/dist/components/InkFindBar.vue_vue_type_script_setup_true_lang.js.map +1 -0
  8. package/dist/components/InkFormAlert.js +7 -0
  9. package/dist/components/InkFormAlert.js.map +1 -0
  10. package/dist/components/InkFormAlert.vue.d.ts +16 -0
  11. package/dist/components/InkFormAlert.vue.d.ts.map +1 -0
  12. package/dist/components/InkFormAlert.vue_vue_type_script_setup_true_lang.js +90 -0
  13. package/dist/components/InkFormAlert.vue_vue_type_script_setup_true_lang.js.map +1 -0
  14. package/dist/components/InkRoot.js.map +1 -1
  15. package/dist/components/InkRoot.vue.d.ts +15 -1
  16. package/dist/components/InkRoot.vue.d.ts.map +1 -1
  17. package/dist/components/InkRoot.vue_vue_type_script_setup_true_lang.js +179 -132
  18. package/dist/components/InkRoot.vue_vue_type_script_setup_true_lang.js.map +1 -1
  19. package/dist/components/InkSearchButton.js +7 -0
  20. package/dist/components/InkSearchButton.js.map +1 -0
  21. package/dist/components/InkSearchButton.vue.d.ts +19 -0
  22. package/dist/components/InkSearchButton.vue.d.ts.map +1 -0
  23. package/dist/components/InkSearchButton.vue_vue_type_script_setup_true_lang.js +43 -0
  24. package/dist/components/InkSearchButton.vue_vue_type_script_setup_true_lang.js.map +1 -0
  25. package/dist/components/InkSignFieldPopover.js +7 -0
  26. package/dist/components/InkSignFieldPopover.js.map +1 -0
  27. package/dist/components/InkSignFieldPopover.vue.d.ts +13 -0
  28. package/dist/components/InkSignFieldPopover.vue.d.ts.map +1 -0
  29. package/dist/components/InkSignFieldPopover.vue_vue_type_script_setup_true_lang.js +116 -0
  30. package/dist/components/InkSignFieldPopover.vue_vue_type_script_setup_true_lang.js.map +1 -0
  31. package/dist/components/InkSignatureDialog.js.map +1 -1
  32. package/dist/components/InkSignatureDialog.vue.d.ts +9 -1
  33. package/dist/components/InkSignatureDialog.vue.d.ts.map +1 -1
  34. package/dist/components/InkSignatureDialog.vue_vue_type_script_setup_true_lang.js +9 -2
  35. package/dist/components/InkSignatureDialog.vue_vue_type_script_setup_true_lang.js.map +1 -1
  36. package/dist/components/InkSignatureMenu.js.map +1 -1
  37. package/dist/components/InkSignatureMenu.vue.d.ts.map +1 -1
  38. package/dist/components/InkSignatureMenu.vue_vue_type_script_setup_true_lang.js +72 -159
  39. package/dist/components/InkSignatureMenu.vue_vue_type_script_setup_true_lang.js.map +1 -1
  40. package/dist/components/InkViewport.js.map +1 -1
  41. package/dist/components/InkViewport.vue.d.ts.map +1 -1
  42. package/dist/components/InkViewport.vue_vue_type_script_setup_true_lang.js +117 -85
  43. package/dist/components/InkViewport.vue_vue_type_script_setup_true_lang.js.map +1 -1
  44. package/dist/components/SignatureForm.js.map +1 -1
  45. package/dist/components/SignatureForm.vue.d.ts +7 -3
  46. package/dist/components/SignatureForm.vue.d.ts.map +1 -1
  47. package/dist/components/SignatureForm.vue_vue_type_script_setup_true_lang.js +27 -21
  48. package/dist/components/SignatureForm.vue_vue_type_script_setup_true_lang.js.map +1 -1
  49. package/dist/components/SignatureMenuPanel.js +7 -0
  50. package/dist/components/SignatureMenuPanel.js.map +1 -0
  51. package/dist/components/SignatureMenuPanel.vue.d.ts +15 -0
  52. package/dist/components/SignatureMenuPanel.vue.d.ts.map +1 -0
  53. package/dist/components/SignatureMenuPanel.vue_vue_type_script_setup_true_lang.js +116 -0
  54. package/dist/components/SignatureMenuPanel.vue_vue_type_script_setup_true_lang.js.map +1 -0
  55. package/dist/composables/useForm.d.ts +18 -0
  56. package/dist/composables/useForm.d.ts.map +1 -0
  57. package/dist/composables/useForm.js +17 -0
  58. package/dist/composables/useForm.js.map +1 -0
  59. package/dist/composables/useSearch.d.ts +12 -0
  60. package/dist/composables/useSearch.d.ts.map +1 -0
  61. package/dist/composables/useSearch.js +27 -0
  62. package/dist/composables/useSearch.js.map +1 -0
  63. package/dist/context.d.ts +14 -2
  64. package/dist/context.d.ts.map +1 -1
  65. package/dist/context.js.map +1 -1
  66. package/dist/index.d.ts +5 -0
  67. package/dist/index.d.ts.map +1 -1
  68. package/dist/index.js +31 -27
  69. package/package.json +6 -5
@@ -1 +1 @@
1
- {"version":3,"file":"InkSignatureMenu.vue_vue_type_script_setup_true_lang.js","names":["$attrs","_openBlock","_createBlock","_unref","_withCtx","_createVNode","_createElementVNode","_mergeProps","_renderSlot","InkIcon","_normalizeProps","_guardReactiveProps","_createElementBlock","_Fragment","_renderList","_createCommentVNode","_createTextVNode","_toDisplayString","InkSignatureDialog","InkIcon_default","InkSignatureDialog_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","_hoisted_6","_hoisted_7","_hoisted_8","_hoisted_9","_hoisted_10","InkSignatureMenu_vue_vue_type_script_setup_true_lang_default","props","__props","NO_TEMPLATES","useTool","useToolOptions","ctx","useInkContext","templates","useReadable","removeError","model","computed","signatureMenuModel","options","active","tool","open","ref","creating","isOpen","main","useTemplateRef","previews","item","signaturePreview","focusFirst","toDialog","onTriggerKeydown","panel","onOpenAutoFocus","menu","menuItems","focusInitialMenuItem","onCloseAutoFocus","onMain","setTool","choose","setOptions","id","remove","nextTick","rows","row","create","onDialogCloseAutoFocus","mainAttrs","SIGNATURE_LABELS","triggerAttrs","menuAttrs","innerAttrs","handleMenuKeys","openBlock","createBlock","unref","PopoverRoot","_cache","$event","withCtx","createVNode","PopoverAnchor","createElementVNode","mergeProps","_ctx","ToolbarButton","renderSlot","Signature","PopoverTrigger","ChevronDown","PopoverPortal","PopoverContent","normalizeProps","guardReactiveProps","createElementBlock","Fragment","renderList","i","Trash2","createCommentVNode","Plus","createTextVNode","toDisplayString"],"sources":["../../src/components/InkSignatureMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * A split button for the signature tool. The main part activates the tool with the chosen\n * signature, or opens the menu when none is chosen; the chevron opens the saved signatures as\n * previews, each with a remove button, and \"Create signature\", which opens\n * `InkSignatureDialog`. Choosing a signature switches to the tool with it.\n */\nimport {\n focusInitialMenuItem,\n handleMenuKeys,\n menuItems,\n SIGNATURE_LABELS,\n signatureMenuModel,\n signaturePreview,\n} from '@inkbar/core/dom'\nimport { ChevronDown, Plus, Signature, Trash2 } from '@inkbar/icons'\nimport {\n PopoverAnchor,\n PopoverContent,\n PopoverPortal,\n PopoverRoot,\n PopoverTrigger,\n ToolbarButton,\n} from 'reka-ui'\nimport { computed, nextTick, ref, useTemplateRef, watch } from 'vue'\n\nimport { useTool } from '../composables/useTool'\nimport { useToolOptions } from '../composables/useToolOptions'\nimport { useInkContext } from '../context'\nimport { useReadable } from '../use-readable'\n\nimport InkIcon from './InkIcon.vue'\nimport InkSignatureDialog from './InkSignatureDialog.vue'\n\nimport type { SignatureOptions, SignatureTemplate } from '@inkbar/core'\n\ndefineOptions({ inheritAttrs: false })\nconst props = defineProps<{\n /** The fonts the dialog's Type tab offers. Default: InkRoot's `signatureFonts`. */\n fonts?: readonly string[] | undefined\n}>()\n\nconst NO_TEMPLATES: readonly SignatureTemplate[] = []\n\nconst { tool, setTool } = useTool()\nconst { options, setOptions } = useToolOptions<SignatureOptions>('signature')\nconst ctx = useInkContext('InkSignatureMenu')\nconst templates = useReadable(() => ctx.signatures.value.templates, NO_TEMPLATES)\nconst removeError = useReadable(() => ctx.signatures.value.removeError, undefined)\nconst model = computed(() => signatureMenuModel(templates.value, options.value))\nconst active = computed(() => tool.value === 'signature')\nconst open = ref(false)\nconst creating = ref(false)\n// An old removal error is not news when the menu opens again.\nwatch(open, (isOpen) => {\n if (isOpen) ctx.signatures.value.clearRemoveError()\n})\nconst main = useTemplateRef<{ $el: HTMLElement }>('main')\n\n/** A saved signature drawn small: ink as filled outlines. */\nconst previews = computed(() =>\n model.value.items.map((item) =>\n item.template.kind === 'ink' ? signaturePreview(item.template) : null\n )\n)\n\n/** ArrowDown opens on the first item; a click opens on the checked one. */\nlet focusFirst = false\n/** Set while the menu closes for the dialog, which takes focus instead of the trigger. */\nlet toDialog = false\n\nfunction onTriggerKeydown(e: KeyboardEvent): void {\n if (e.key !== 'ArrowDown' || e.defaultPrevented || open.value) return\n e.preventDefault()\n focusFirst = true\n open.value = true\n}\n\nconst panel = (): HTMLElement | null =>\n document.querySelector<HTMLElement>('[data-ink-menu=\"signature\"] [role=\"menu\"]')\n\nfunction onOpenAutoFocus(e: Event): void {\n e.preventDefault()\n const menu = panel()\n if (menu) {\n if (focusFirst) menuItems(menu)[0]?.focus({ preventScroll: true })\n else focusInitialMenuItem(menu)\n }\n focusFirst = false\n}\n\nfunction onCloseAutoFocus(e: Event): void {\n if (toDialog) e.preventDefault()\n toDialog = false\n}\n\nfunction onMain(): void {\n if (model.value.selected) setTool('signature')\n else open.value = true\n}\n\nfunction choose(id: string): void {\n setOptions({ templateId: id })\n setTool('signature')\n open.value = false\n}\n\n/** Removes a template; focus moves to the signature now in its row, else \"Create signature\". */\nfunction remove(id: string, row: number): void {\n ctx.signatures.value.remove(id).then(\n async () => {\n await nextTick()\n const menu = panel()\n if (!menu) return\n const rows = menu.querySelectorAll<HTMLElement>('.ink-signature-item')\n const next =\n rows[Math.min(row, rows.length - 1)] ??\n menu.querySelector<HTMLElement>('[data-ink-signature-create]')\n next?.focus({ preventScroll: true })\n },\n () => {\n // The host's store refused: the signature stays.\n }\n )\n}\n\nfunction create(): void {\n toDialog = true\n open.value = false\n creating.value = true\n}\n\nfunction onDialogCloseAutoFocus(e: Event): void {\n e.preventDefault()\n main.value?.$el.focus({ preventScroll: true })\n}\n\n// Reka's typed props stop at its own; ARIA and listeners go through bound objects.\nconst mainAttrs = computed(() => ({\n 'aria-pressed': String(active.value),\n 'aria-label': SIGNATURE_LABELS.sign,\n title: SIGNATURE_LABELS.sign,\n onClick: onMain,\n}))\nconst triggerAttrs = {\n 'aria-label': SIGNATURE_LABELS.trigger,\n title: SIGNATURE_LABELS.trigger,\n 'aria-haspopup': 'menu' as const,\n onKeydown: onTriggerKeydown,\n}\nconst menuAttrs = { 'aria-label': SIGNATURE_LABELS.menu }\nconst innerAttrs = {\n role: 'menu',\n 'aria-label': SIGNATURE_LABELS.menu,\n onKeydown: (e: KeyboardEvent) => handleMenuKeys(e, e.currentTarget as HTMLElement),\n}\n</script>\n\n<template>\n <PopoverRoot v-model:open=\"open\">\n <PopoverAnchor as-child>\n <div\n role=\"group\"\n :aria-label=\"SIGNATURE_LABELS.sign\"\n class=\"ink-split-button ink-signature-menu\"\n data-ink-signature-menu\n v-bind=\"$attrs\"\n >\n <ToolbarButton\n ref=\"main\"\n class=\"ink-tool-button ink-signature-button\"\n data-ink-tool-button\n data-ink-signature-button\n data-tool=\"signature\"\n :data-state=\"active ? 'on' : 'off'\"\n v-bind=\"mainAttrs\"\n >\n <slot :active=\"active\"><InkIcon :icon=\"Signature\" /></slot>\n </ToolbarButton>\n <ToolbarButton as-child>\n <PopoverTrigger as-child>\n <button\n type=\"button\"\n class=\"ink-menu-trigger ink-signature-menu-trigger\"\n data-ink-signature-menu-trigger\n :data-state=\"open ? 'open' : 'closed'\"\n v-bind=\"triggerAttrs\"\n >\n <InkIcon :icon=\"ChevronDown\" />\n </button>\n </PopoverTrigger>\n </ToolbarButton>\n <PopoverPortal>\n <PopoverContent\n class=\"ink-select-content ink-menu-content\"\n data-ink-select-content\n data-ink-menu=\"signature\"\n side=\"bottom\"\n align=\"start\"\n :side-offset=\"4\"\n :collision-padding=\"8\"\n v-bind=\"menuAttrs\"\n @open-auto-focus=\"onOpenAutoFocus\"\n @close-auto-focus=\"onCloseAutoFocus\"\n >\n <div v-bind=\"innerAttrs\">\n <template v-if=\"model.items.length > 0\">\n <div\n role=\"group\"\n :aria-label=\"SIGNATURE_LABELS.saved\"\n class=\"ink-signature-list\"\n data-ink-menu-columns=\"2\"\n >\n <template v-for=\"(item, row) in model.items\" :key=\"item.id\">\n <button\n type=\"button\"\n role=\"menuitemradio\"\n :aria-checked=\"item.checked\"\n :aria-label=\"item.label\"\n tabindex=\"-1\"\n class=\"ink-select-item ink-menu-item ink-signature-item\"\n data-ink-select-item\n :data-key=\"item.id\"\n :data-state=\"item.checked ? 'checked' : 'unchecked'\"\n @click=\"choose(item.id)\"\n >\n <span class=\"ink-signature-preview\" :data-kind=\"item.template.kind\">\n <img\n v-if=\"item.template.kind === 'image'\"\n :src=\"item.template.imagedata\"\n alt=\"\"\n draggable=\"false\"\n />\n <svg v-else :viewBox=\"previews[row]?.viewBox\" aria-hidden=\"true\">\n <path\n v-for=\"(d, i) in previews[row]?.paths ?? []\"\n :key=\"i\"\n :d=\"d\"\n fill=\"currentColor\"\n />\n </svg>\n </span>\n </button>\n <button\n type=\"button\"\n role=\"menuitem\"\n :aria-label=\"item.removeLabel\"\n :title=\"item.removeLabel\"\n tabindex=\"-1\"\n class=\"ink-menu-item ink-signature-remove\"\n data-ink-signature-remove\n :data-key=\"item.id\"\n @click=\"remove(item.id, row)\"\n >\n <InkIcon :icon=\"Trash2\" :size=\"16\" />\n </button>\n </template>\n </div>\n <div role=\"separator\" class=\"ink-select-separator\" />\n </template>\n <button\n type=\"button\"\n role=\"menuitem\"\n tabindex=\"-1\"\n class=\"ink-select-item ink-menu-item ink-signature-create\"\n data-ink-select-item\n data-ink-signature-create\n @click=\"create\"\n >\n <InkIcon :icon=\"Plus\" :size=\"16\" />\n {{ SIGNATURE_LABELS.create }}\n </button>\n <p\n v-if=\"removeError\"\n class=\"ink-field-error ink-signature-remove-error\"\n role=\"alert\"\n data-ink-signature-remove-error\n >\n {{ removeError }}\n </p>\n </div>\n </PopoverContent>\n </PopoverPortal>\n <InkSignatureDialog\n v-model:open=\"creating\"\n :fonts=\"props.fonts\"\n @close-auto-focus=\"onDialogCloseAutoFocus\"\n />\n </div>\n </PopoverAnchor>\n </PopoverRoot>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"InkSignatureMenu.vue_vue_type_script_setup_true_lang.js","names":["$attrs","_openBlock","_createBlock","_unref","_withCtx","_createVNode","_createElementVNode","_mergeProps","_renderSlot","InkIcon","SignatureMenuPanel","InkSignatureDialog","InkSignatureDialog_default","InkIcon_default","SignatureMenuPanel_default","_hoisted_1","_hoisted_2","InkSignatureMenu_vue_vue_type_script_setup_true_lang_default","props","__props","NO_TEMPLATES","useTool","useToolOptions","ctx","useInkContext","templates","useReadable","model","computed","signatureMenuModel","options","active","tool","open","ref","creating","isOpen","main","useTemplateRef","focusFirst","toDialog","onTriggerKeydown","panel","onOpenAutoFocus","menu","menuItems","focusInitialMenuItem","onCloseAutoFocus","onMain","setTool","choose","setOptions","id","create","onDialogCloseAutoFocus","mainAttrs","SIGNATURE_LABELS","triggerAttrs","menuAttrs","openBlock","createBlock","unref","PopoverRoot","_cache","$event","withCtx","createVNode","PopoverAnchor","createElementVNode","mergeProps","_ctx","ToolbarButton","renderSlot","Signature","PopoverTrigger","ChevronDown","PopoverPortal","PopoverContent"],"sources":["../../src/components/InkSignatureMenu.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * A split button for the signature tool. The main part activates the tool with the chosen\n * signature, or opens the menu when none is chosen; the chevron opens the saved signatures as\n * previews, each with a remove button, and \"Create signature\", which opens\n * `InkSignatureDialog`. Choosing a signature switches to the tool with it.\n */\nimport {\n focusInitialMenuItem,\n menuItems,\n SIGNATURE_LABELS,\n signatureMenuModel,\n} from '@inkbar/core/dom'\nimport { ChevronDown, Signature } from '@inkbar/icons'\nimport {\n PopoverAnchor,\n PopoverContent,\n PopoverPortal,\n PopoverRoot,\n PopoverTrigger,\n ToolbarButton,\n} from 'reka-ui'\nimport { computed, ref, useTemplateRef, watch } from 'vue'\n\nimport { useTool } from '../composables/useTool'\nimport { useToolOptions } from '../composables/useToolOptions'\nimport { useInkContext } from '../context'\nimport { useReadable } from '../use-readable'\n\nimport InkIcon from './InkIcon.vue'\nimport InkSignatureDialog from './InkSignatureDialog.vue'\nimport SignatureMenuPanel from './SignatureMenuPanel.vue'\n\nimport type { SignatureOptions, SignatureTemplate } from '@inkbar/core'\n\ndefineOptions({ inheritAttrs: false })\nconst props = defineProps<{\n /** The fonts the dialog's Type tab offers. Default: InkRoot's `signatureFonts`. */\n fonts?: readonly string[] | undefined\n}>()\n\nconst NO_TEMPLATES: readonly SignatureTemplate[] = []\n\nconst { tool, setTool } = useTool()\nconst { options, setOptions } = useToolOptions<SignatureOptions>('signature')\nconst ctx = useInkContext('InkSignatureMenu')\nconst templates = useReadable(() => ctx.signatures.value.templates, NO_TEMPLATES)\nconst model = computed(() => signatureMenuModel(templates.value, options.value))\nconst active = computed(() => tool.value === 'signature')\nconst open = ref(false)\nconst creating = ref(false)\n// An old removal error is not news when the menu opens again.\nwatch(open, (isOpen) => {\n if (isOpen) ctx.signatures.value.clearRemoveError()\n})\nconst main = useTemplateRef<{ $el: HTMLElement }>('main')\n\n/** ArrowDown opens on the first item; a click opens on the checked one. */\nlet focusFirst = false\n/** Set while the menu closes for the dialog, which takes focus instead of the trigger. */\nlet toDialog = false\n\nfunction onTriggerKeydown(e: KeyboardEvent): void {\n if (e.key !== 'ArrowDown' || e.defaultPrevented || open.value) return\n e.preventDefault()\n focusFirst = true\n open.value = true\n}\n\n// Not a signature field's menu, which shares the attribute.\nconst panel = (): HTMLElement | null =>\n document.querySelector<HTMLElement>(\n '[data-ink-menu=\"signature\"]:not([data-ink-sign-field-menu]) [role=\"menu\"]'\n )\n\nfunction onOpenAutoFocus(e: Event): void {\n e.preventDefault()\n const menu = panel()\n if (menu) {\n if (focusFirst) menuItems(menu)[0]?.focus({ preventScroll: true })\n else focusInitialMenuItem(menu)\n }\n focusFirst = false\n}\n\nfunction onCloseAutoFocus(e: Event): void {\n if (toDialog) e.preventDefault()\n toDialog = false\n}\n\nfunction onMain(): void {\n if (model.value.selected) setTool('signature')\n else open.value = true\n}\n\nfunction choose(id: string): void {\n setOptions({ templateId: id })\n setTool('signature')\n open.value = false\n}\n\nfunction create(): void {\n toDialog = true\n open.value = false\n creating.value = true\n}\n\nfunction onDialogCloseAutoFocus(e: Event): void {\n e.preventDefault()\n main.value?.$el.focus({ preventScroll: true })\n}\n\n// Reka's typed props stop at its own; ARIA and listeners go through bound objects.\nconst mainAttrs = computed(() => ({\n 'aria-pressed': String(active.value),\n 'aria-label': SIGNATURE_LABELS.sign,\n title: SIGNATURE_LABELS.sign,\n onClick: onMain,\n}))\nconst triggerAttrs = {\n 'aria-label': SIGNATURE_LABELS.trigger,\n title: SIGNATURE_LABELS.trigger,\n 'aria-haspopup': 'menu' as const,\n onKeydown: onTriggerKeydown,\n}\nconst menuAttrs = { 'aria-label': SIGNATURE_LABELS.menu }\n</script>\n\n<template>\n <PopoverRoot v-model:open=\"open\">\n <PopoverAnchor as-child>\n <div\n role=\"group\"\n :aria-label=\"SIGNATURE_LABELS.sign\"\n class=\"ink-split-button ink-signature-menu\"\n data-ink-signature-menu\n v-bind=\"$attrs\"\n >\n <ToolbarButton\n ref=\"main\"\n class=\"ink-tool-button ink-signature-button\"\n data-ink-tool-button\n data-ink-signature-button\n data-tool=\"signature\"\n :data-state=\"active ? 'on' : 'off'\"\n v-bind=\"mainAttrs\"\n >\n <slot :active=\"active\"><InkIcon :icon=\"Signature\" /></slot>\n </ToolbarButton>\n <ToolbarButton as-child>\n <PopoverTrigger as-child>\n <button\n type=\"button\"\n class=\"ink-menu-trigger ink-signature-menu-trigger\"\n data-ink-signature-menu-trigger\n :data-state=\"open ? 'open' : 'closed'\"\n v-bind=\"triggerAttrs\"\n >\n <InkIcon :icon=\"ChevronDown\" />\n </button>\n </PopoverTrigger>\n </ToolbarButton>\n <PopoverPortal>\n <PopoverContent\n class=\"ink-select-content ink-menu-content\"\n data-ink-select-content\n data-ink-menu=\"signature\"\n side=\"bottom\"\n align=\"start\"\n :side-offset=\"4\"\n :collision-padding=\"8\"\n v-bind=\"menuAttrs\"\n @open-auto-focus=\"onOpenAutoFocus\"\n @close-auto-focus=\"onCloseAutoFocus\"\n >\n <SignatureMenuPanel\n :items=\"model.items\"\n @choose=\"choose($event.id)\"\n @create=\"create\"\n />\n </PopoverContent>\n </PopoverPortal>\n <InkSignatureDialog\n v-model:open=\"creating\"\n :fonts=\"props.fonts\"\n @close-auto-focus=\"onDialogCloseAutoFocus\"\n />\n </div>\n </PopoverAnchor>\n </PopoverRoot>\n</template>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"InkViewport.js","names":["_sfc_main","InkViewport_vue_vue_type_script_setup_true_lang_default","InkViewport_default"],"sources":["../../src/components/InkViewport.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * The scroll element is handed to core's `mountViewer`, which builds the viewport, the page\n * layers, the tool controller and the keyboard shortcuts in it; Vue never renders children\n * into it. The engine reports each page host it mounts, and InkPage components rendered here\n * (they render nothing in place) teleport the page-error and custom-annotation slots into them.\n *\n * Two-way zoom and page: the viewport's own changes are reported through the context, and\n * the host's v-model values are applied only when they differ from the viewport's state, so\n * the echo of a reported value does nothing.\n */\nimport { mountViewer } from '@inkbar/core/dom'\nimport {\n computed,\n onBeforeUnmount,\n onBeforeUpdate,\n onMounted,\n shallowRef,\n useTemplateRef,\n watch,\n} from 'vue'\n\nimport { injectInkContext } from '../context'\n\nimport InkPage from './InkPage.vue'\n\nimport type { AnnotationSlotProps } from './annotation-slot'\nimport type { InkError } from '@inkbar/core'\nimport type {\n LayoutMode,\n PageMount,\n ToolId,\n ViewerEngine,\n ViewRotation,\n ZoomValue,\n} from '@inkbar/core/dom'\n\nconst props = withDefaults(\n defineProps<{\n mode?: LayoutMode\n rotation?: ViewRotation\n /** Gap between pages, CSS px. */\n gap?: number\n /** Pages kept mounted either side of the visible ones. */\n buffer?: number\n }>(),\n { mode: 'continuous', rotation: 0, gap: 16, buffer: 2 }\n)\n\nconst slots = defineSlots<{\n loading?(): unknown\n error?(props: { error: InkError }): unknown\n empty?(): unknown\n annotation?(props: AnnotationSlotProps): unknown\n /** Shown inside a page that failed to render. The rest of the document stays usable. */\n 'page-error'?(props: { error: InkError; page: number }): unknown\n}>()\n\nconst ctx = injectInkContext()\nconst scroll = useTemplateRef<HTMLElement>('scroll')\n\ninterface MountedPage {\n /** The engine build the host belongs to; part of the InkPage key. */\n build: number\n page: number\n host: HTMLElement\n mount: PageMount\n error: InkError | null\n /** Whether the build that mounted the page renders the annotation slot. */\n custom: boolean\n}\n/** Page hosts, mounts and errors the engine reported; InkPage renders Teleports for them. */\nconst pages = shallowRef<MountedPage[]>([])\n\nconst state = computed(() => ctx.status.value.state)\nconst error = computed<InkError | null>(() =>\n ctx.status.value.state === 'error' ? ctx.status.value.error : null\n)\n\nconst engine = shallowRef<ViewerEngine | null>(null)\n/**\n * Numbered before each build starts, so the pages a build mounts carry its own number. A\n * rebuild gives each page a new host and mount, so pages from an earlier build must remount,\n * not be reused.\n */\nlet build = 0\n/** Whether the current build renders the annotation slot. */\nlet custom = false\nlet stops: (() => void)[] = []\n/** The zoom and page last reported to the host. */\nlet lastReportedZoom: ZoomValue | undefined\nlet lastReportedPage: number | undefined\n\nfunction teardown(): void {\n for (const stop of stops) stop()\n stops = []\n // Unmounts every page, which empties `pages` through `onPageUnmount`.\n engine.value?.destroy()\n engine.value = null\n if (ctx.viewport.value) ctx.viewport.value = null\n if (ctx.tools.value) ctx.tools.value = null\n pages.value = []\n}\n\nfunction rebuild(opts: Parameters<ViewerEngine['rebuild']>[0]): void {\n const e = engine.value\n if (!e) return\n build += 1\n if (opts.customAnnotations !== undefined) custom = opts.customAnnotations\n e.rebuild(opts)\n}\n\n// Build for a new document or status; the previous engine is torn down first.\nfunction setup(): void {\n teardown()\n lastReportedZoom = undefined\n lastReportedPage = undefined\n const doc = ctx.document.value\n const el = scroll.value\n if (!doc || !el || ctx.status.value.state !== 'ready') return\n let first = true\n build += 1\n custom = !!slots.annotation\n engine.value = mountViewer({\n container: el,\n document: doc,\n zoom: ctx.zoomModel.value ?? 'fit-width',\n ...(ctx.pageModel.value === undefined ? {} : { page: ctx.pageModel.value }),\n mode: props.mode,\n rotation: props.rotation,\n gap: props.gap,\n buffer: props.buffer,\n tool: ctx.tool.value,\n ...(ctx.hostTools.value ? { tools: ctx.hostTools.value } : {}),\n // The engine adds the plugins' tools and layers, and decorates through the host, which\n // reads `decorateAnnotation` first.\n ...(ctx.pluginHost.value ? { plugins: ctx.pluginHost.value } : {}),\n // Read through, so the watch below can re-apply a new function without a rebuild.\n decorateAnnotation: (a) => ctx.decorateAnnotation.value?.(a),\n ...(ctx.toolStyle.value ? { toolStyle: ctx.toolStyle.value } : {}),\n toolHistory: { batch: (fn) => ctx.history.batch(fn) },\n toolOptions: ctx.toolOptions,\n fonts: [...ctx.fonts.value],\n // Read through, so a new `signatures` store reaches the tool as well as the menus.\n signatureLibrary: () => ctx.signatures.value,\n customAnnotations: custom,\n onActivate: (id) => ctx.setActivated(id),\n onBuild: (vp, tc) => {\n ctx.viewport.value = vp\n ctx.tools.value = tc\n if (first) {\n // A new document starts from where the viewport opens (its zoom, on the first page:\n // `createViewport` always starts at page 0); only later changes are reported, the\n // restored page among them, since the engine has already scrolled to it.\n first = false\n lastReportedZoom = vp.zoom.get()\n lastReportedPage = 0\n }\n for (const stop of stops) stop()\n const reportZoom = (z: ZoomValue): void => {\n if (z === lastReportedZoom) return\n lastReportedZoom = z\n ctx.reportZoom(z)\n }\n const reportPage = (p: number): void => {\n if (p === lastReportedPage) return\n lastReportedPage = p\n ctx.reportPage(p)\n }\n // A tool that switches itself (a one-shot tool once used) is reported as a pick.\n const followTool = (id: ToolId): void => {\n if (id !== ctx.tool.value) ctx.setTool(id)\n }\n stops = [\n vp.zoom.subscribe(reportZoom),\n vp.currentPage.subscribe(reportPage),\n tc.active.subscribe(followTool),\n ]\n // Whatever the build restored is reported once; nothing is when it matched.\n reportZoom(vp.zoom.get())\n reportPage(vp.currentPage.get())\n },\n onPageMount: (page, mount, host) => {\n pages.value = [...pages.value, { build, page, host, mount, error: null, custom }]\n },\n onPageUnmount: (page) => {\n pages.value = pages.value.filter((p) => p.page !== page)\n },\n onPageError: (page, err) => {\n pages.value = pages.value.map((p) => (p.page === page ? { ...p, error: err } : p))\n },\n })\n}\n\nonMounted(() => {\n ctx.viewportEl.value = scroll.value\n watch([() => ctx.document.value, () => ctx.status.value.state], setup, {\n immediate: true,\n })\n})\n\n// A new decorateAnnotation re-applies to the mounted annotation elements at once.\nwatch(\n () => ctx.decorateAnnotation.value,\n () => engine.value?.redecorate()\n)\nwatch(\n () => ctx.zoomModel.value,\n (z: ZoomValue | undefined) => {\n const vp = engine.value?.viewport\n if (vp && z !== undefined && z !== vp.zoom.get()) vp.setZoom(z)\n }\n)\nwatch(\n () => ctx.pageModel.value,\n (p) => {\n const vp = engine.value?.viewport\n if (!vp || p === undefined || p === lastReportedPage) return\n if (p !== vp.currentPage.get()) vp.scrollToPage(p)\n }\n)\nwatch(\n () => ctx.tool.value,\n (t) => {\n const tc = engine.value?.tools\n if (tc && tc.active.get() !== t) tc.set(t)\n }\n)\nwatch(\n () => props.mode,\n (m) => engine.value?.viewport.setMode(m)\n)\nwatch(\n () => props.rotation,\n (r) => engine.value?.viewport.setRotation(r)\n)\n// Gap and buffer are construction options: rebuild for the same document, keeping zoom and page.\n// The slots are already updated when this runs, so it carries the annotation slot too: a slot\n// toggle in the same tick is then recorded here and `onBeforeUpdate` finds nothing left to do.\nwatch([() => props.gap, () => props.buffer], ([gap, buffer]) =>\n rebuild({ gap, buffer, customAnnotations: !!slots.annotation })\n)\n\n// Slots are not reactive, so a change to whether the annotation slot is given is caught here.\n// `onBeforeUpdate` runs after the new slots are in place and before this render: the rebuild\n// swaps `pages` for the new build's pages synchronously, so the render that follows never pairs\n// the annotation slot with an overlay built without it (or the reverse). The rebuild keeps the\n// scroll position.\nonBeforeUpdate(() => {\n const next = !!slots.annotation\n if (engine.value && next !== custom) rebuild({ customAnnotations: next })\n})\n\nonBeforeUnmount(() => {\n teardown()\n if (ctx.viewportEl.value === scroll.value) ctx.viewportEl.value = null\n})\n</script>\n\n<template>\n <div class=\"ink-viewport-root\" data-ink-viewport-root :data-state=\"state\">\n <div v-if=\"state === 'loading'\" class=\"ink-status\" role=\"status\" aria-live=\"polite\">\n <slot name=\"loading\">Loading document…</slot>\n </div>\n <div v-else-if=\"error\" class=\"ink-status ink-error\" role=\"alert\">\n <slot name=\"error\" :error=\"error\">\n <p class=\"ink-error-title\">This document could not be opened.</p>\n <!-- The message comes from the PDF engine and is not written for readers: it stays\n off the page, on the code's tooltip. -->\n <p>\n <code class=\"ink-error-code\" :title=\"error.message\">{{ error.code }}</code>\n </p>\n </slot>\n </div>\n <div v-else-if=\"state === 'idle'\" class=\"ink-status ink-empty\">\n <slot name=\"empty\" />\n </div>\n <div\n ref=\"scroll\"\n class=\"ink-scroll\"\n data-ink-scroll\n tabindex=\"0\"\n role=\"document\"\n aria-label=\"Document\"\n />\n <InkPage\n v-for=\"p in pages\"\n :key=\"`${p.build}:${p.page}`\"\n :page=\"p.page\"\n :host=\"p.host\"\n :mount=\"p.mount\"\n :error=\"p.error\"\n :custom-annotations=\"p.custom\"\n >\n <template v-if=\"slots.annotation\" #annotation=\"slotProps\">\n <slot name=\"annotation\" v-bind=\"slotProps\" />\n </template>\n <template v-if=\"slots['page-error']\" #page-error=\"slotProps\">\n <slot name=\"page-error\" v-bind=\"slotProps\" />\n </template>\n </InkPage>\n </div>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"InkViewport.js","names":["_sfc_main","InkViewport_vue_vue_type_script_setup_true_lang_default","InkViewport_default"],"sources":["../../src/components/InkViewport.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * The scroll element is handed to core's `mountViewer`, which builds the viewport, the page\n * layers, the tool controller and the keyboard shortcuts in it; Vue never renders children\n * into it. The engine reports each page host it mounts, and InkPage components rendered here\n * (they render nothing in place) teleport the page-error and custom-annotation slots into them.\n *\n * Two-way zoom and page: the viewport's own changes are reported through the context, and\n * the host's v-model values are applied only when they differ from the viewport's state, so\n * the echo of a reported value does nothing.\n *\n * Form fields render in the engine's form layer. A signature field's \"Sign\" opens\n * `InkSignFieldPopover`, and a form script's alert shows in `InkFormAlert`; both are mounted\n * here, so hosts compose nothing for them.\n */\nimport { mountViewer } from '@inkbar/core/dom'\nimport {\n computed,\n onBeforeUnmount,\n onBeforeUpdate,\n onMounted,\n shallowRef,\n useTemplateRef,\n watch,\n} from 'vue'\n\nimport { injectInkContext } from '../context'\n\nimport InkFormAlert from './InkFormAlert.vue'\nimport InkPage from './InkPage.vue'\nimport InkSignFieldPopover from './InkSignFieldPopover.vue'\n\nimport type { AnnotationSlotProps } from './annotation-slot'\nimport type { InkDocument, InkError } from '@inkbar/core'\nimport type {\n LayoutMode,\n PageMount,\n SignFieldRequest,\n ToolId,\n ViewerEngine,\n ViewRotation,\n ZoomValue,\n} from '@inkbar/core/dom'\n\nconst props = withDefaults(\n defineProps<{\n mode?: LayoutMode\n rotation?: ViewRotation\n /** Gap between pages, CSS px. */\n gap?: number\n /** Pages kept mounted either side of the visible ones. */\n buffer?: number\n }>(),\n { mode: 'continuous', rotation: 0, gap: 16, buffer: 2 }\n)\n\nconst slots = defineSlots<{\n loading?(): unknown\n error?(props: { error: InkError }): unknown\n empty?(): unknown\n annotation?(props: AnnotationSlotProps): unknown\n /** Shown inside a page that failed to render. The rest of the document stays usable. */\n 'page-error'?(props: { error: InkError; page: number }): unknown\n}>()\n\nconst ctx = injectInkContext()\nconst scroll = useTemplateRef<HTMLElement>('scroll')\n\ninterface MountedPage {\n /** The engine build the host belongs to; part of the InkPage key. */\n build: number\n page: number\n host: HTMLElement\n mount: PageMount\n error: InkError | null\n /** Whether the build that mounted the page renders the annotation slot. */\n custom: boolean\n}\n/** A key per document, so the field menu and its dialog start afresh for each one. */\nconst documentKeys = new WeakMap<InkDocument, number>()\nlet documentKeyCount = 0\nfunction documentKey(doc: InkDocument | null): number {\n if (!doc) return 0\n let key = documentKeys.get(doc)\n if (key === undefined) {\n key = ++documentKeyCount\n documentKeys.set(doc, key)\n }\n return key\n}\n\n/** Page hosts, mounts and errors the engine reported; InkPage renders Teleports for them. */\nconst pages = shallowRef<MountedPage[]>([])\n\nconst state = computed(() => ctx.status.value.state)\nconst error = computed<InkError | null>(() =>\n ctx.status.value.state === 'error' ? ctx.status.value.error : null\n)\n\nconst engine = shallowRef<ViewerEngine | null>(null)\n/** The signature field whose menu is open. */\nconst signRequest = shallowRef<SignFieldRequest | null>(null)\n/** The form script's message on show, and how many have been shown. */\nconst alertMessage = shallowRef<string | null>(null)\nconst alertsShown = shallowRef(0)\n/**\n * Numbered before each build starts, so the pages a build mounts carry its own number. A\n * rebuild gives each page a new host and mount, so pages from an earlier build must remount,\n * not be reused.\n */\nlet build = 0\n/** Whether the current build renders the annotation slot. */\nlet custom = false\nlet stops: (() => void)[] = []\n/** The zoom and page last reported to the host. */\nlet lastReportedZoom: ZoomValue | undefined\nlet lastReportedPage: number | undefined\n\nfunction teardown(): void {\n for (const stop of stops) stop()\n stops = []\n // Unmounts every page, which empties `pages` through `onPageUnmount`.\n engine.value?.destroy()\n engine.value = null\n if (ctx.viewport.value) ctx.viewport.value = null\n if (ctx.tools.value) ctx.tools.value = null\n pages.value = []\n // Their fields and the document they came from are gone.\n signRequest.value = null\n alertMessage.value = null\n}\n\nfunction rebuild(opts: Parameters<ViewerEngine['rebuild']>[0]): void {\n const e = engine.value\n if (!e) return\n build += 1\n if (opts.customAnnotations !== undefined) custom = opts.customAnnotations\n e.rebuild(opts)\n}\n\n// Build for a new document or status; the previous engine is torn down first.\nfunction setup(): void {\n teardown()\n lastReportedZoom = undefined\n lastReportedPage = undefined\n const doc = ctx.document.value\n const el = scroll.value\n if (!doc || !el || ctx.status.value.state !== 'ready') return\n let first = true\n build += 1\n custom = !!slots.annotation\n engine.value = mountViewer({\n container: el,\n document: doc,\n zoom: ctx.zoomModel.value ?? 'fit-width',\n ...(ctx.pageModel.value === undefined ? {} : { page: ctx.pageModel.value }),\n mode: props.mode,\n rotation: props.rotation,\n gap: props.gap,\n buffer: props.buffer,\n tool: ctx.tool.value,\n ...(ctx.hostTools.value ? { tools: ctx.hostTools.value } : {}),\n // The engine adds the plugins' tools and layers, and decorates through the host, which\n // reads `decorateAnnotation` first.\n ...(ctx.pluginHost.value ? { plugins: ctx.pluginHost.value } : {}),\n // The engine draws the search matches on the pages.\n ...(ctx.search.value ? { search: ctx.search.value } : {}),\n // Read through, so the watch below can re-apply a new function without a rebuild.\n decorateAnnotation: (a) => ctx.decorateAnnotation.value?.(a),\n ...(ctx.toolStyle.value ? { toolStyle: ctx.toolStyle.value } : {}),\n toolHistory: { batch: (fn) => ctx.history.batch(fn) },\n toolOptions: ctx.toolOptions,\n fonts: [...ctx.fonts.value],\n // Read through, so a new `signatures` store reaches the tool as well as the menus.\n signatureLibrary: () => ctx.signatures.value,\n customAnnotations: custom,\n ...(ctx.formHighlight.value === undefined\n ? {}\n : { formHighlight: ctx.formHighlight.value }),\n // The field's own button again, while its menu is open, closes the menu.\n onSignField: (req) => {\n signRequest.value = signRequest.value?.widget === req.widget ? null : req\n },\n onFormAction: (a) => ctx.reportFormAction(a),\n onFormAlert: (message) => {\n alertMessage.value = message\n alertsShown.value += 1\n },\n onActivate: (id) => ctx.setActivated(id),\n onBuild: (vp, tc) => {\n ctx.viewport.value = vp\n ctx.tools.value = tc\n if (first) {\n // A new document starts from where the viewport opens (its zoom, on the first page:\n // `createViewport` always starts at page 0); only later changes are reported, the\n // restored page among them, since the engine has already scrolled to it.\n first = false\n lastReportedZoom = vp.zoom.get()\n lastReportedPage = 0\n }\n for (const stop of stops) stop()\n const reportZoom = (z: ZoomValue): void => {\n if (z === lastReportedZoom) return\n lastReportedZoom = z\n ctx.reportZoom(z)\n }\n const reportPage = (p: number): void => {\n if (p === lastReportedPage) return\n lastReportedPage = p\n ctx.reportPage(p)\n }\n // A tool that switches itself (a one-shot tool once used) is reported as a pick.\n const followTool = (id: ToolId): void => {\n if (id !== ctx.tool.value) ctx.setTool(id)\n }\n stops = [\n vp.zoom.subscribe(reportZoom),\n vp.currentPage.subscribe(reportPage),\n tc.active.subscribe(followTool),\n ]\n // Whatever the build restored is reported once; nothing is when it matched.\n reportZoom(vp.zoom.get())\n reportPage(vp.currentPage.get())\n },\n onPageMount: (page, mount, host) => {\n pages.value = [...pages.value, { build, page, host, mount, error: null, custom }]\n },\n onPageUnmount: (page) => {\n pages.value = pages.value.filter((p) => p.page !== page)\n },\n onPageError: (page, err) => {\n pages.value = pages.value.map((p) => (p.page === page ? { ...p, error: err } : p))\n },\n })\n}\n\nonMounted(() => {\n ctx.viewportEl.value = scroll.value\n watch([() => ctx.document.value, () => ctx.status.value.state], setup, {\n immediate: true,\n })\n})\n\n// A new decorateAnnotation re-applies to the mounted annotation elements at once.\nwatch(\n () => ctx.decorateAnnotation.value,\n () => engine.value?.redecorate()\n)\n// The tint follows `formHighlight` in place, without a new document.\nwatch(\n () => ctx.formHighlight.value,\n (on) => engine.value?.setFormHighlight(on ?? true)\n)\nwatch(\n () => ctx.zoomModel.value,\n (z: ZoomValue | undefined) => {\n const vp = engine.value?.viewport\n if (vp && z !== undefined && z !== vp.zoom.get()) vp.setZoom(z)\n }\n)\nwatch(\n () => ctx.pageModel.value,\n (p) => {\n const vp = engine.value?.viewport\n if (!vp || p === undefined || p === lastReportedPage) return\n if (p !== vp.currentPage.get()) vp.scrollToPage(p)\n }\n)\nwatch(\n () => ctx.tool.value,\n (t) => {\n const tc = engine.value?.tools\n if (tc && tc.active.get() !== t) tc.set(t)\n }\n)\nwatch(\n () => props.mode,\n (m) => engine.value?.viewport.setMode(m)\n)\nwatch(\n () => props.rotation,\n (r) => engine.value?.viewport.setRotation(r)\n)\n// Gap and buffer are construction options: rebuild for the same document, keeping zoom and page.\n// The slots are already updated when this runs, so it carries the annotation slot too: a slot\n// toggle in the same tick is then recorded here and `onBeforeUpdate` finds nothing left to do.\nwatch([() => props.gap, () => props.buffer], ([gap, buffer]) =>\n rebuild({ gap, buffer, customAnnotations: !!slots.annotation })\n)\n\n// Slots are not reactive, so a change to whether the annotation slot is given is caught here.\n// `onBeforeUpdate` runs after the new slots are in place and before this render: the rebuild\n// swaps `pages` for the new build's pages synchronously, so the render that follows never pairs\n// the annotation slot with an overlay built without it (or the reverse). The rebuild keeps the\n// scroll position.\nonBeforeUpdate(() => {\n const next = !!slots.annotation\n if (engine.value && next !== custom) rebuild({ customAnnotations: next })\n})\n\nonBeforeUnmount(() => {\n teardown()\n if (ctx.viewportEl.value === scroll.value) ctx.viewportEl.value = null\n})\n</script>\n\n<template>\n <div class=\"ink-viewport-root\" data-ink-viewport-root :data-state=\"state\">\n <div v-if=\"state === 'loading'\" class=\"ink-status\" role=\"status\" aria-live=\"polite\">\n <slot name=\"loading\">Loading document…</slot>\n </div>\n <div v-else-if=\"error\" class=\"ink-status ink-error\" role=\"alert\">\n <slot name=\"error\" :error=\"error\">\n <p class=\"ink-error-title\">This document could not be opened.</p>\n <!-- The message comes from the PDF engine and is not written for readers: it stays\n off the page, on the code's tooltip. -->\n <p>\n <code class=\"ink-error-code\" :title=\"error.message\">{{ error.code }}</code>\n </p>\n </slot>\n </div>\n <div v-else-if=\"state === 'idle'\" class=\"ink-status ink-empty\">\n <slot name=\"empty\" />\n </div>\n <div\n ref=\"scroll\"\n class=\"ink-scroll\"\n data-ink-scroll\n tabindex=\"0\"\n role=\"document\"\n aria-label=\"Document\"\n />\n <InkPage\n v-for=\"p in pages\"\n :key=\"`${p.build}:${p.page}`\"\n :page=\"p.page\"\n :host=\"p.host\"\n :mount=\"p.mount\"\n :error=\"p.error\"\n :custom-annotations=\"p.custom\"\n >\n <template v-if=\"slots.annotation\" #annotation=\"slotProps\">\n <slot name=\"annotation\" v-bind=\"slotProps\" />\n </template>\n <template v-if=\"slots['page-error']\" #page-error=\"slotProps\">\n <slot name=\"page-error\" v-bind=\"slotProps\" />\n </template>\n </InkPage>\n <!-- Keyed by document, so the menu and its dialog start afresh for each one. -->\n <InkSignFieldPopover\n :key=\"documentKey(ctx.document.value)\"\n :request=\"signRequest\"\n @close=\"signRequest = null\"\n />\n <InkFormAlert\n :message=\"alertMessage\"\n :shown=\"alertsShown\"\n :fallback=\"scroll\"\n @close=\"alertMessage = null\"\n />\n </div>\n</template>\n"],"mappings":""}
@@ -1 +1 @@
1
- {"version":3,"file":"InkViewport.vue.d.ts","sourceRoot":"","sources":["../../src/components/InkViewport.vue"],"names":[],"mappings":"AA0UA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,cAAc,CAAA;AAC5C,OAAO,KAAK,EACV,UAAU,EAIV,YAAY,EAEb,MAAM,kBAAkB,CAAA;AAGzB,KAAK,WAAW,GAAG;IACf,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAC;AAMJ,KAAK,WAAW,GAAG;IACjB,OAAO,CAAC,IAAI,OAAO,CAAA;IACnB,KAAK,CAAC,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAA;IAC3C,KAAK,CAAC,IAAI,OAAO,CAAA;IACjB,UAAU,CAAC,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAA;IAChD,wFAAwF;IACxF,YAAY,CAAC,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAA;CACjE,CAAC;AAoUF,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"}
1
+ {"version":3,"file":"InkViewport.vue.d.ts","sourceRoot":"","sources":["../../src/components/InkViewport.vue"],"names":[],"mappings":"AA2YA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,mBAAmB,CAAA;AAC5D,OAAO,KAAK,EAAe,QAAQ,EAAE,MAAM,cAAc,CAAA;AACzD,OAAO,KAAK,EACV,UAAU,EAKV,YAAY,EAEb,MAAM,kBAAkB,CAAA;AAGzB,KAAK,WAAW,GAAG;IACf,IAAI,CAAC,EAAE,UAAU,CAAA;IACjB,QAAQ,CAAC,EAAE,YAAY,CAAA;IACvB,iCAAiC;IACjC,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,0DAA0D;IAC1D,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB,CAAC;AAMJ,KAAK,WAAW,GAAG;IACjB,OAAO,CAAC,IAAI,OAAO,CAAA;IACnB,KAAK,CAAC,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,QAAQ,CAAA;KAAE,GAAG,OAAO,CAAA;IAC3C,KAAK,CAAC,IAAI,OAAO,CAAA;IACjB,UAAU,CAAC,CAAC,KAAK,EAAE,mBAAmB,GAAG,OAAO,CAAA;IAChD,wFAAwF;IACxF,YAAY,CAAC,CAAC,KAAK,EAAE;QAAE,KAAK,EAAE,QAAQ,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAA;CACjE,CAAC;AAqaF,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"}
@@ -1,21 +1,23 @@
1
1
  import { injectInkContext as e } from "../context.js";
2
- import t from "./InkPage.js";
3
- import { Fragment as n, computed as r, createBlock as i, createCommentVNode as a, createElementBlock as o, createElementVNode as s, createSlots as c, createTextVNode as l, defineComponent as u, mergeProps as d, onBeforeUnmount as f, onBeforeUpdate as p, onMounted as m, openBlock as h, renderList as g, renderSlot as _, shallowRef as v, toDisplayString as y, useSlots as b, useTemplateRef as x, watch as S, withCtx as C } from "vue";
4
- import { mountViewer as w } from "@inkbar/core/dom";
2
+ import t from "./InkFormAlert.js";
3
+ import n from "./InkPage.js";
4
+ import r from "./InkSignFieldPopover.js";
5
+ import { Fragment as i, computed as a, createBlock as o, createCommentVNode as ee, createElementBlock as s, createElementVNode as c, createSlots as l, createTextVNode as u, createVNode as d, defineComponent as f, mergeProps as p, onBeforeUnmount as m, onBeforeUpdate as h, onMounted as g, openBlock as _, renderList as v, renderSlot as y, shallowRef as b, toDisplayString as x, unref as S, useSlots as C, useTemplateRef as w, watch as T, withCtx as E } from "vue";
6
+ import { mountViewer as D } from "@inkbar/core/dom";
5
7
  //#region src/components/InkViewport.vue?vue&type=script&setup=true&lang.ts
6
- var T = ["data-state"], E = {
8
+ var O = ["data-state"], k = {
7
9
  key: 0,
8
10
  class: "ink-status",
9
11
  role: "status",
10
12
  "aria-live": "polite"
11
- }, D = {
13
+ }, A = {
12
14
  key: 1,
13
15
  class: "ink-status ink-error",
14
16
  role: "alert"
15
- }, O = ["title"], k = {
17
+ }, j = ["title"], M = {
16
18
  key: 2,
17
19
  class: "ink-status ink-empty"
18
- }, A = /*@__PURE__*/ u({
20
+ }, N = /*@__PURE__*/ f({
19
21
  __name: "InkViewport",
20
22
  props: {
21
23
  mode: { default: "continuous" },
@@ -23,130 +25,145 @@ var T = ["data-state"], E = {
23
25
  gap: { default: 16 },
24
26
  buffer: { default: 2 }
25
27
  },
26
- setup(u) {
27
- let A = u, j = b(), M = e(), N = x("scroll"), P = v([]), F = r(() => M.status.value.state), I = r(() => M.status.value.state === "error" ? M.status.value.error : null), L = v(null), R = 0, z = !1, B = [], V, H;
28
- function U() {
29
- for (let e of B) e();
30
- B = [], L.value?.destroy(), L.value = null, M.viewport.value && (M.viewport.value = null), M.tools.value && (M.tools.value = null), P.value = [];
28
+ setup(f) {
29
+ let N = f, P = C(), F = e(), I = w("scroll"), L = /* @__PURE__ */ new WeakMap(), R = 0;
30
+ function z(e) {
31
+ if (!e) return 0;
32
+ let t = L.get(e);
33
+ return t === void 0 && (t = ++R, L.set(e, t)), t;
31
34
  }
32
- function W(e) {
33
- let t = L.value;
34
- t && (R += 1, e.customAnnotations !== void 0 && (z = e.customAnnotations), t.rebuild(e));
35
+ let B = b([]), V = a(() => F.status.value.state), H = a(() => F.status.value.state === "error" ? F.status.value.error : null), U = b(null), W = b(null), G = b(null), K = b(0), q = 0, J = !1, Y = [], X, Z;
36
+ function Q() {
37
+ for (let e of Y) e();
38
+ Y = [], U.value?.destroy(), U.value = null, F.viewport.value && (F.viewport.value = null), F.tools.value && (F.tools.value = null), B.value = [], W.value = null, G.value = null;
35
39
  }
36
- function G() {
37
- U(), V = void 0, H = void 0;
38
- let e = M.document.value, t = N.value;
39
- if (!e || !t || M.status.value.state !== "ready") return;
40
+ function $(e) {
41
+ let t = U.value;
42
+ t && (q += 1, e.customAnnotations !== void 0 && (J = e.customAnnotations), t.rebuild(e));
43
+ }
44
+ function te() {
45
+ Q(), X = void 0, Z = void 0;
46
+ let e = F.document.value, t = I.value;
47
+ if (!e || !t || F.status.value.state !== "ready") return;
40
48
  let n = !0;
41
- R += 1, z = !!j.annotation, L.value = w({
49
+ q += 1, J = !!P.annotation, U.value = D({
42
50
  container: t,
43
51
  document: e,
44
- zoom: M.zoomModel.value ?? "fit-width",
45
- ...M.pageModel.value === void 0 ? {} : { page: M.pageModel.value },
46
- mode: A.mode,
47
- rotation: A.rotation,
48
- gap: A.gap,
49
- buffer: A.buffer,
50
- tool: M.tool.value,
51
- ...M.hostTools.value ? { tools: M.hostTools.value } : {},
52
- ...M.pluginHost.value ? { plugins: M.pluginHost.value } : {},
53
- decorateAnnotation: (e) => M.decorateAnnotation.value?.(e),
54
- ...M.toolStyle.value ? { toolStyle: M.toolStyle.value } : {},
55
- toolHistory: { batch: (e) => M.history.batch(e) },
56
- toolOptions: M.toolOptions,
57
- fonts: [...M.fonts.value],
58
- signatureLibrary: () => M.signatures.value,
59
- customAnnotations: z,
60
- onActivate: (e) => M.setActivated(e),
52
+ zoom: F.zoomModel.value ?? "fit-width",
53
+ ...F.pageModel.value === void 0 ? {} : { page: F.pageModel.value },
54
+ mode: N.mode,
55
+ rotation: N.rotation,
56
+ gap: N.gap,
57
+ buffer: N.buffer,
58
+ tool: F.tool.value,
59
+ ...F.hostTools.value ? { tools: F.hostTools.value } : {},
60
+ ...F.pluginHost.value ? { plugins: F.pluginHost.value } : {},
61
+ ...F.search.value ? { search: F.search.value } : {},
62
+ decorateAnnotation: (e) => F.decorateAnnotation.value?.(e),
63
+ ...F.toolStyle.value ? { toolStyle: F.toolStyle.value } : {},
64
+ toolHistory: { batch: (e) => F.history.batch(e) },
65
+ toolOptions: F.toolOptions,
66
+ fonts: [...F.fonts.value],
67
+ signatureLibrary: () => F.signatures.value,
68
+ customAnnotations: J,
69
+ ...F.formHighlight.value === void 0 ? {} : { formHighlight: F.formHighlight.value },
70
+ onSignField: (e) => {
71
+ W.value = W.value?.widget === e.widget ? null : e;
72
+ },
73
+ onFormAction: (e) => F.reportFormAction(e),
74
+ onFormAlert: (e) => {
75
+ G.value = e, K.value += 1;
76
+ },
77
+ onActivate: (e) => F.setActivated(e),
61
78
  onBuild: (e, t) => {
62
- M.viewport.value = e, M.tools.value = t, n && (n = !1, V = e.zoom.get(), H = 0);
63
- for (let e of B) e();
79
+ F.viewport.value = e, F.tools.value = t, n && (n = !1, X = e.zoom.get(), Z = 0);
80
+ for (let e of Y) e();
64
81
  let r = (e) => {
65
- e !== V && (V = e, M.reportZoom(e));
82
+ e !== X && (X = e, F.reportZoom(e));
66
83
  }, i = (e) => {
67
- e !== H && (H = e, M.reportPage(e));
84
+ e !== Z && (Z = e, F.reportPage(e));
68
85
  };
69
- B = [
86
+ Y = [
70
87
  e.zoom.subscribe(r),
71
88
  e.currentPage.subscribe(i),
72
89
  t.active.subscribe((e) => {
73
- e !== M.tool.value && M.setTool(e);
90
+ e !== F.tool.value && F.setTool(e);
74
91
  })
75
92
  ], r(e.zoom.get()), i(e.currentPage.get());
76
93
  },
77
94
  onPageMount: (e, t, n) => {
78
- P.value = [...P.value, {
79
- build: R,
95
+ B.value = [...B.value, {
96
+ build: q,
80
97
  page: e,
81
98
  host: n,
82
99
  mount: t,
83
100
  error: null,
84
- custom: z
101
+ custom: J
85
102
  }];
86
103
  },
87
104
  onPageUnmount: (e) => {
88
- P.value = P.value.filter((t) => t.page !== e);
105
+ B.value = B.value.filter((t) => t.page !== e);
89
106
  },
90
107
  onPageError: (e, t) => {
91
- P.value = P.value.map((n) => n.page === e ? {
108
+ B.value = B.value.map((n) => n.page === e ? {
92
109
  ...n,
93
110
  error: t
94
111
  } : n);
95
112
  }
96
113
  });
97
114
  }
98
- return m(() => {
99
- M.viewportEl.value = N.value, S([() => M.document.value, () => M.status.value.state], G, { immediate: !0 });
100
- }), S(() => M.decorateAnnotation.value, () => L.value?.redecorate()), S(() => M.zoomModel.value, (e) => {
101
- let t = L.value?.viewport;
115
+ return g(() => {
116
+ F.viewportEl.value = I.value, T([() => F.document.value, () => F.status.value.state], te, { immediate: !0 });
117
+ }), T(() => F.decorateAnnotation.value, () => U.value?.redecorate()), T(() => F.formHighlight.value, (e) => U.value?.setFormHighlight(e ?? !0)), T(() => F.zoomModel.value, (e) => {
118
+ let t = U.value?.viewport;
102
119
  t && e !== void 0 && e !== t.zoom.get() && t.setZoom(e);
103
- }), S(() => M.pageModel.value, (e) => {
104
- let t = L.value?.viewport;
105
- t && e !== void 0 && e !== H && e !== t.currentPage.get() && t.scrollToPage(e);
106
- }), S(() => M.tool.value, (e) => {
107
- let t = L.value?.tools;
120
+ }), T(() => F.pageModel.value, (e) => {
121
+ let t = U.value?.viewport;
122
+ t && e !== void 0 && e !== Z && e !== t.currentPage.get() && t.scrollToPage(e);
123
+ }), T(() => F.tool.value, (e) => {
124
+ let t = U.value?.tools;
108
125
  t && t.active.get() !== e && t.set(e);
109
- }), S(() => A.mode, (e) => L.value?.viewport.setMode(e)), S(() => A.rotation, (e) => L.value?.viewport.setRotation(e)), S([() => A.gap, () => A.buffer], ([e, t]) => W({
126
+ }), T(() => N.mode, (e) => U.value?.viewport.setMode(e)), T(() => N.rotation, (e) => U.value?.viewport.setRotation(e)), T([() => N.gap, () => N.buffer], ([e, t]) => $({
110
127
  gap: e,
111
128
  buffer: t,
112
- customAnnotations: !!j.annotation
113
- })), p(() => {
114
- let e = !!j.annotation;
115
- L.value && e !== z && W({ customAnnotations: e });
116
- }), f(() => {
117
- U(), M.viewportEl.value === N.value && (M.viewportEl.value = null);
118
- }), (e, r) => (h(), o("div", {
129
+ customAnnotations: !!P.annotation
130
+ })), h(() => {
131
+ let e = !!P.annotation;
132
+ U.value && e !== J && $({ customAnnotations: e });
133
+ }), m(() => {
134
+ Q(), F.viewportEl.value === I.value && (F.viewportEl.value = null);
135
+ }), (e, a) => (_(), s("div", {
119
136
  class: "ink-viewport-root",
120
137
  "data-ink-viewport-root": "",
121
- "data-state": F.value
138
+ "data-state": V.value
122
139
  }, [
123
- F.value === "loading" ? (h(), o("div", E, [_(e.$slots, "loading", {}, () => [r[0] ||= l("Loading document…", -1)])])) : I.value ? (h(), o("div", D, [_(e.$slots, "error", { error: I.value }, () => [r[1] ||= s("p", { class: "ink-error-title" }, "This document could not be opened.", -1), s("p", null, [s("code", {
140
+ V.value === "loading" ? (_(), s("div", k, [y(e.$slots, "loading", {}, () => [a[2] ||= u("Loading document…", -1)])])) : H.value ? (_(), s("div", A, [y(e.$slots, "error", { error: H.value }, () => [a[3] ||= c("p", { class: "ink-error-title" }, "This document could not be opened.", -1), c("p", null, [c("code", {
124
141
  class: "ink-error-code",
125
- title: I.value.message
126
- }, y(I.value.code), 9, O)])])])) : F.value === "idle" ? (h(), o("div", k, [_(e.$slots, "empty")])) : a("", !0),
127
- s("div", {
142
+ title: H.value.message
143
+ }, x(H.value.code), 9, j)])])])) : V.value === "idle" ? (_(), s("div", M, [y(e.$slots, "empty")])) : ee("", !0),
144
+ c("div", {
128
145
  ref_key: "scroll",
129
- ref: N,
146
+ ref: I,
130
147
  class: "ink-scroll",
131
148
  "data-ink-scroll": "",
132
149
  tabindex: "0",
133
150
  role: "document",
134
151
  "aria-label": "Document"
135
152
  }, null, 512),
136
- (h(!0), o(n, null, g(P.value, (n) => (h(), i(t, {
137
- key: `${n.build}:${n.page}`,
138
- page: n.page,
139
- host: n.host,
140
- mount: n.mount,
141
- error: n.error,
142
- "custom-annotations": n.custom
143
- }, c({ _: 2 }, [j.annotation ? {
153
+ (_(!0), s(i, null, v(B.value, (t) => (_(), o(n, {
154
+ key: `${t.build}:${t.page}`,
155
+ page: t.page,
156
+ host: t.host,
157
+ mount: t.mount,
158
+ error: t.error,
159
+ "custom-annotations": t.custom
160
+ }, l({ _: 2 }, [P.annotation ? {
144
161
  name: "annotation",
145
- fn: C((t) => [_(e.$slots, "annotation", d({ ref_for: !0 }, t))]),
162
+ fn: E((t) => [y(e.$slots, "annotation", p({ ref_for: !0 }, t))]),
146
163
  key: "0"
147
- } : void 0, j["page-error"] ? {
164
+ } : void 0, P["page-error"] ? {
148
165
  name: "page-error",
149
- fn: C((t) => [_(e.$slots, "page-error", d({ ref_for: !0 }, t))]),
166
+ fn: E((t) => [y(e.$slots, "page-error", p({ ref_for: !0 }, t))]),
150
167
  key: "1"
151
168
  } : void 0]), 1032, [
152
169
  "page",
@@ -154,11 +171,26 @@ var T = ["data-state"], E = {
154
171
  "mount",
155
172
  "error",
156
173
  "custom-annotations"
157
- ]))), 128))
158
- ], 8, T));
174
+ ]))), 128)),
175
+ (_(), o(r, {
176
+ key: z(S(F).document.value),
177
+ request: W.value,
178
+ onClose: a[0] ||= (e) => W.value = null
179
+ }, null, 8, ["request"])),
180
+ d(t, {
181
+ message: G.value,
182
+ shown: K.value,
183
+ fallback: I.value,
184
+ onClose: a[1] ||= (e) => G.value = null
185
+ }, null, 8, [
186
+ "message",
187
+ "shown",
188
+ "fallback"
189
+ ])
190
+ ], 8, O));
159
191
  }
160
192
  });
161
193
  //#endregion
162
- export { A as default };
194
+ export { N as default };
163
195
 
164
196
  //# sourceMappingURL=InkViewport.vue_vue_type_script_setup_true_lang.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"InkViewport.vue_vue_type_script_setup_true_lang.js","names":["_useSlots","_openBlock","_createElementBlock","_renderSlot","_createTextVNode","_createElementVNode","_toDisplayString","_createCommentVNode","_Fragment","_renderList","_createBlock","InkPage","_createSlots","_withCtx","_mergeProps","InkPage_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","InkViewport_vue_vue_type_script_setup_true_lang_default","props","__props","slots","useSlots","ctx","injectInkContext","scroll","useTemplateRef","pages","shallowRef","state","computed","error","engine","build","custom","stops","lastReportedZoom","lastReportedPage","teardown","stop","rebuild","e","opts","setup","doc","el","first","mountViewer","a","fn","id","vp","tc","reportZoom","z","reportPage","p","page","err","onMounted","watch","t","m","r","gap","buffer","onBeforeUpdate","next","onBeforeUnmount","openBlock","createElementBlock","renderSlot","_ctx","_cache","createTextVNode","createElementVNode","toDisplayString","createCommentVNode","Fragment","renderList","createBlock","createSlots","withCtx","mergeProps","slotProps"],"sources":["../../src/components/InkViewport.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * The scroll element is handed to core's `mountViewer`, which builds the viewport, the page\n * layers, the tool controller and the keyboard shortcuts in it; Vue never renders children\n * into it. The engine reports each page host it mounts, and InkPage components rendered here\n * (they render nothing in place) teleport the page-error and custom-annotation slots into them.\n *\n * Two-way zoom and page: the viewport's own changes are reported through the context, and\n * the host's v-model values are applied only when they differ from the viewport's state, so\n * the echo of a reported value does nothing.\n */\nimport { mountViewer } from '@inkbar/core/dom'\nimport {\n computed,\n onBeforeUnmount,\n onBeforeUpdate,\n onMounted,\n shallowRef,\n useTemplateRef,\n watch,\n} from 'vue'\n\nimport { injectInkContext } from '../context'\n\nimport InkPage from './InkPage.vue'\n\nimport type { AnnotationSlotProps } from './annotation-slot'\nimport type { InkError } from '@inkbar/core'\nimport type {\n LayoutMode,\n PageMount,\n ToolId,\n ViewerEngine,\n ViewRotation,\n ZoomValue,\n} from '@inkbar/core/dom'\n\nconst props = withDefaults(\n defineProps<{\n mode?: LayoutMode\n rotation?: ViewRotation\n /** Gap between pages, CSS px. */\n gap?: number\n /** Pages kept mounted either side of the visible ones. */\n buffer?: number\n }>(),\n { mode: 'continuous', rotation: 0, gap: 16, buffer: 2 }\n)\n\nconst slots = defineSlots<{\n loading?(): unknown\n error?(props: { error: InkError }): unknown\n empty?(): unknown\n annotation?(props: AnnotationSlotProps): unknown\n /** Shown inside a page that failed to render. The rest of the document stays usable. */\n 'page-error'?(props: { error: InkError; page: number }): unknown\n}>()\n\nconst ctx = injectInkContext()\nconst scroll = useTemplateRef<HTMLElement>('scroll')\n\ninterface MountedPage {\n /** The engine build the host belongs to; part of the InkPage key. */\n build: number\n page: number\n host: HTMLElement\n mount: PageMount\n error: InkError | null\n /** Whether the build that mounted the page renders the annotation slot. */\n custom: boolean\n}\n/** Page hosts, mounts and errors the engine reported; InkPage renders Teleports for them. */\nconst pages = shallowRef<MountedPage[]>([])\n\nconst state = computed(() => ctx.status.value.state)\nconst error = computed<InkError | null>(() =>\n ctx.status.value.state === 'error' ? ctx.status.value.error : null\n)\n\nconst engine = shallowRef<ViewerEngine | null>(null)\n/**\n * Numbered before each build starts, so the pages a build mounts carry its own number. A\n * rebuild gives each page a new host and mount, so pages from an earlier build must remount,\n * not be reused.\n */\nlet build = 0\n/** Whether the current build renders the annotation slot. */\nlet custom = false\nlet stops: (() => void)[] = []\n/** The zoom and page last reported to the host. */\nlet lastReportedZoom: ZoomValue | undefined\nlet lastReportedPage: number | undefined\n\nfunction teardown(): void {\n for (const stop of stops) stop()\n stops = []\n // Unmounts every page, which empties `pages` through `onPageUnmount`.\n engine.value?.destroy()\n engine.value = null\n if (ctx.viewport.value) ctx.viewport.value = null\n if (ctx.tools.value) ctx.tools.value = null\n pages.value = []\n}\n\nfunction rebuild(opts: Parameters<ViewerEngine['rebuild']>[0]): void {\n const e = engine.value\n if (!e) return\n build += 1\n if (opts.customAnnotations !== undefined) custom = opts.customAnnotations\n e.rebuild(opts)\n}\n\n// Build for a new document or status; the previous engine is torn down first.\nfunction setup(): void {\n teardown()\n lastReportedZoom = undefined\n lastReportedPage = undefined\n const doc = ctx.document.value\n const el = scroll.value\n if (!doc || !el || ctx.status.value.state !== 'ready') return\n let first = true\n build += 1\n custom = !!slots.annotation\n engine.value = mountViewer({\n container: el,\n document: doc,\n zoom: ctx.zoomModel.value ?? 'fit-width',\n ...(ctx.pageModel.value === undefined ? {} : { page: ctx.pageModel.value }),\n mode: props.mode,\n rotation: props.rotation,\n gap: props.gap,\n buffer: props.buffer,\n tool: ctx.tool.value,\n ...(ctx.hostTools.value ? { tools: ctx.hostTools.value } : {}),\n // The engine adds the plugins' tools and layers, and decorates through the host, which\n // reads `decorateAnnotation` first.\n ...(ctx.pluginHost.value ? { plugins: ctx.pluginHost.value } : {}),\n // Read through, so the watch below can re-apply a new function without a rebuild.\n decorateAnnotation: (a) => ctx.decorateAnnotation.value?.(a),\n ...(ctx.toolStyle.value ? { toolStyle: ctx.toolStyle.value } : {}),\n toolHistory: { batch: (fn) => ctx.history.batch(fn) },\n toolOptions: ctx.toolOptions,\n fonts: [...ctx.fonts.value],\n // Read through, so a new `signatures` store reaches the tool as well as the menus.\n signatureLibrary: () => ctx.signatures.value,\n customAnnotations: custom,\n onActivate: (id) => ctx.setActivated(id),\n onBuild: (vp, tc) => {\n ctx.viewport.value = vp\n ctx.tools.value = tc\n if (first) {\n // A new document starts from where the viewport opens (its zoom, on the first page:\n // `createViewport` always starts at page 0); only later changes are reported, the\n // restored page among them, since the engine has already scrolled to it.\n first = false\n lastReportedZoom = vp.zoom.get()\n lastReportedPage = 0\n }\n for (const stop of stops) stop()\n const reportZoom = (z: ZoomValue): void => {\n if (z === lastReportedZoom) return\n lastReportedZoom = z\n ctx.reportZoom(z)\n }\n const reportPage = (p: number): void => {\n if (p === lastReportedPage) return\n lastReportedPage = p\n ctx.reportPage(p)\n }\n // A tool that switches itself (a one-shot tool once used) is reported as a pick.\n const followTool = (id: ToolId): void => {\n if (id !== ctx.tool.value) ctx.setTool(id)\n }\n stops = [\n vp.zoom.subscribe(reportZoom),\n vp.currentPage.subscribe(reportPage),\n tc.active.subscribe(followTool),\n ]\n // Whatever the build restored is reported once; nothing is when it matched.\n reportZoom(vp.zoom.get())\n reportPage(vp.currentPage.get())\n },\n onPageMount: (page, mount, host) => {\n pages.value = [...pages.value, { build, page, host, mount, error: null, custom }]\n },\n onPageUnmount: (page) => {\n pages.value = pages.value.filter((p) => p.page !== page)\n },\n onPageError: (page, err) => {\n pages.value = pages.value.map((p) => (p.page === page ? { ...p, error: err } : p))\n },\n })\n}\n\nonMounted(() => {\n ctx.viewportEl.value = scroll.value\n watch([() => ctx.document.value, () => ctx.status.value.state], setup, {\n immediate: true,\n })\n})\n\n// A new decorateAnnotation re-applies to the mounted annotation elements at once.\nwatch(\n () => ctx.decorateAnnotation.value,\n () => engine.value?.redecorate()\n)\nwatch(\n () => ctx.zoomModel.value,\n (z: ZoomValue | undefined) => {\n const vp = engine.value?.viewport\n if (vp && z !== undefined && z !== vp.zoom.get()) vp.setZoom(z)\n }\n)\nwatch(\n () => ctx.pageModel.value,\n (p) => {\n const vp = engine.value?.viewport\n if (!vp || p === undefined || p === lastReportedPage) return\n if (p !== vp.currentPage.get()) vp.scrollToPage(p)\n }\n)\nwatch(\n () => ctx.tool.value,\n (t) => {\n const tc = engine.value?.tools\n if (tc && tc.active.get() !== t) tc.set(t)\n }\n)\nwatch(\n () => props.mode,\n (m) => engine.value?.viewport.setMode(m)\n)\nwatch(\n () => props.rotation,\n (r) => engine.value?.viewport.setRotation(r)\n)\n// Gap and buffer are construction options: rebuild for the same document, keeping zoom and page.\n// The slots are already updated when this runs, so it carries the annotation slot too: a slot\n// toggle in the same tick is then recorded here and `onBeforeUpdate` finds nothing left to do.\nwatch([() => props.gap, () => props.buffer], ([gap, buffer]) =>\n rebuild({ gap, buffer, customAnnotations: !!slots.annotation })\n)\n\n// Slots are not reactive, so a change to whether the annotation slot is given is caught here.\n// `onBeforeUpdate` runs after the new slots are in place and before this render: the rebuild\n// swaps `pages` for the new build's pages synchronously, so the render that follows never pairs\n// the annotation slot with an overlay built without it (or the reverse). The rebuild keeps the\n// scroll position.\nonBeforeUpdate(() => {\n const next = !!slots.annotation\n if (engine.value && next !== custom) rebuild({ customAnnotations: next })\n})\n\nonBeforeUnmount(() => {\n teardown()\n if (ctx.viewportEl.value === scroll.value) ctx.viewportEl.value = null\n})\n</script>\n\n<template>\n <div class=\"ink-viewport-root\" data-ink-viewport-root :data-state=\"state\">\n <div v-if=\"state === 'loading'\" class=\"ink-status\" role=\"status\" aria-live=\"polite\">\n <slot name=\"loading\">Loading document…</slot>\n </div>\n <div v-else-if=\"error\" class=\"ink-status ink-error\" role=\"alert\">\n <slot name=\"error\" :error=\"error\">\n <p class=\"ink-error-title\">This document could not be opened.</p>\n <!-- The message comes from the PDF engine and is not written for readers: it stays\n off the page, on the code's tooltip. -->\n <p>\n <code class=\"ink-error-code\" :title=\"error.message\">{{ error.code }}</code>\n </p>\n </slot>\n </div>\n <div v-else-if=\"state === 'idle'\" class=\"ink-status ink-empty\">\n <slot name=\"empty\" />\n </div>\n <div\n ref=\"scroll\"\n class=\"ink-scroll\"\n data-ink-scroll\n tabindex=\"0\"\n role=\"document\"\n aria-label=\"Document\"\n />\n <InkPage\n v-for=\"p in pages\"\n :key=\"`${p.build}:${p.page}`\"\n :page=\"p.page\"\n :host=\"p.host\"\n :mount=\"p.mount\"\n :error=\"p.error\"\n :custom-annotations=\"p.custom\"\n >\n <template v-if=\"slots.annotation\" #annotation=\"slotProps\">\n <slot name=\"annotation\" v-bind=\"slotProps\" />\n </template>\n <template v-if=\"slots['page-error']\" #page-error=\"slotProps\">\n <slot name=\"page-error\" v-bind=\"slotProps\" />\n </template>\n </InkPage>\n </div>\n</template>\n"],"mappings":"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"}
1
+ {"version":3,"file":"InkViewport.vue_vue_type_script_setup_true_lang.js","names":["_useSlots","_openBlock","_createElementBlock","_renderSlot","_createTextVNode","_createElementVNode","_toDisplayString","_createCommentVNode","_Fragment","_renderList","_createBlock","InkPage","_createSlots","_withCtx","_mergeProps","InkSignFieldPopover","_unref","_createVNode","InkFormAlert","InkFormAlert_default","InkPage_default","InkSignFieldPopover_default","_hoisted_1","_hoisted_2","_hoisted_3","_hoisted_4","_hoisted_5","InkViewport_vue_vue_type_script_setup_true_lang_default","props","__props","slots","useSlots","ctx","injectInkContext","scroll","useTemplateRef","documentKeys","documentKeyCount","documentKey","doc","key","pages","shallowRef","state","computed","error","engine","signRequest","alertMessage","alertsShown","build","custom","stops","lastReportedZoom","lastReportedPage","teardown","stop","rebuild","e","opts","setup","el","first","mountViewer","a","fn","req","message","id","vp","tc","reportZoom","z","reportPage","p","page","err","onMounted","watch","on","t","m","r","gap","buffer","onBeforeUpdate","next","onBeforeUnmount","openBlock","createElementBlock","renderSlot","_ctx","_cache","createTextVNode","createElementVNode","toDisplayString","createCommentVNode","Fragment","renderList","createBlock","createSlots","withCtx","mergeProps","slotProps","unref","createVNode"],"sources":["../../src/components/InkViewport.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * The scroll element is handed to core's `mountViewer`, which builds the viewport, the page\n * layers, the tool controller and the keyboard shortcuts in it; Vue never renders children\n * into it. The engine reports each page host it mounts, and InkPage components rendered here\n * (they render nothing in place) teleport the page-error and custom-annotation slots into them.\n *\n * Two-way zoom and page: the viewport's own changes are reported through the context, and\n * the host's v-model values are applied only when they differ from the viewport's state, so\n * the echo of a reported value does nothing.\n *\n * Form fields render in the engine's form layer. A signature field's \"Sign\" opens\n * `InkSignFieldPopover`, and a form script's alert shows in `InkFormAlert`; both are mounted\n * here, so hosts compose nothing for them.\n */\nimport { mountViewer } from '@inkbar/core/dom'\nimport {\n computed,\n onBeforeUnmount,\n onBeforeUpdate,\n onMounted,\n shallowRef,\n useTemplateRef,\n watch,\n} from 'vue'\n\nimport { injectInkContext } from '../context'\n\nimport InkFormAlert from './InkFormAlert.vue'\nimport InkPage from './InkPage.vue'\nimport InkSignFieldPopover from './InkSignFieldPopover.vue'\n\nimport type { AnnotationSlotProps } from './annotation-slot'\nimport type { InkDocument, InkError } from '@inkbar/core'\nimport type {\n LayoutMode,\n PageMount,\n SignFieldRequest,\n ToolId,\n ViewerEngine,\n ViewRotation,\n ZoomValue,\n} from '@inkbar/core/dom'\n\nconst props = withDefaults(\n defineProps<{\n mode?: LayoutMode\n rotation?: ViewRotation\n /** Gap between pages, CSS px. */\n gap?: number\n /** Pages kept mounted either side of the visible ones. */\n buffer?: number\n }>(),\n { mode: 'continuous', rotation: 0, gap: 16, buffer: 2 }\n)\n\nconst slots = defineSlots<{\n loading?(): unknown\n error?(props: { error: InkError }): unknown\n empty?(): unknown\n annotation?(props: AnnotationSlotProps): unknown\n /** Shown inside a page that failed to render. The rest of the document stays usable. */\n 'page-error'?(props: { error: InkError; page: number }): unknown\n}>()\n\nconst ctx = injectInkContext()\nconst scroll = useTemplateRef<HTMLElement>('scroll')\n\ninterface MountedPage {\n /** The engine build the host belongs to; part of the InkPage key. */\n build: number\n page: number\n host: HTMLElement\n mount: PageMount\n error: InkError | null\n /** Whether the build that mounted the page renders the annotation slot. */\n custom: boolean\n}\n/** A key per document, so the field menu and its dialog start afresh for each one. */\nconst documentKeys = new WeakMap<InkDocument, number>()\nlet documentKeyCount = 0\nfunction documentKey(doc: InkDocument | null): number {\n if (!doc) return 0\n let key = documentKeys.get(doc)\n if (key === undefined) {\n key = ++documentKeyCount\n documentKeys.set(doc, key)\n }\n return key\n}\n\n/** Page hosts, mounts and errors the engine reported; InkPage renders Teleports for them. */\nconst pages = shallowRef<MountedPage[]>([])\n\nconst state = computed(() => ctx.status.value.state)\nconst error = computed<InkError | null>(() =>\n ctx.status.value.state === 'error' ? ctx.status.value.error : null\n)\n\nconst engine = shallowRef<ViewerEngine | null>(null)\n/** The signature field whose menu is open. */\nconst signRequest = shallowRef<SignFieldRequest | null>(null)\n/** The form script's message on show, and how many have been shown. */\nconst alertMessage = shallowRef<string | null>(null)\nconst alertsShown = shallowRef(0)\n/**\n * Numbered before each build starts, so the pages a build mounts carry its own number. A\n * rebuild gives each page a new host and mount, so pages from an earlier build must remount,\n * not be reused.\n */\nlet build = 0\n/** Whether the current build renders the annotation slot. */\nlet custom = false\nlet stops: (() => void)[] = []\n/** The zoom and page last reported to the host. */\nlet lastReportedZoom: ZoomValue | undefined\nlet lastReportedPage: number | undefined\n\nfunction teardown(): void {\n for (const stop of stops) stop()\n stops = []\n // Unmounts every page, which empties `pages` through `onPageUnmount`.\n engine.value?.destroy()\n engine.value = null\n if (ctx.viewport.value) ctx.viewport.value = null\n if (ctx.tools.value) ctx.tools.value = null\n pages.value = []\n // Their fields and the document they came from are gone.\n signRequest.value = null\n alertMessage.value = null\n}\n\nfunction rebuild(opts: Parameters<ViewerEngine['rebuild']>[0]): void {\n const e = engine.value\n if (!e) return\n build += 1\n if (opts.customAnnotations !== undefined) custom = opts.customAnnotations\n e.rebuild(opts)\n}\n\n// Build for a new document or status; the previous engine is torn down first.\nfunction setup(): void {\n teardown()\n lastReportedZoom = undefined\n lastReportedPage = undefined\n const doc = ctx.document.value\n const el = scroll.value\n if (!doc || !el || ctx.status.value.state !== 'ready') return\n let first = true\n build += 1\n custom = !!slots.annotation\n engine.value = mountViewer({\n container: el,\n document: doc,\n zoom: ctx.zoomModel.value ?? 'fit-width',\n ...(ctx.pageModel.value === undefined ? {} : { page: ctx.pageModel.value }),\n mode: props.mode,\n rotation: props.rotation,\n gap: props.gap,\n buffer: props.buffer,\n tool: ctx.tool.value,\n ...(ctx.hostTools.value ? { tools: ctx.hostTools.value } : {}),\n // The engine adds the plugins' tools and layers, and decorates through the host, which\n // reads `decorateAnnotation` first.\n ...(ctx.pluginHost.value ? { plugins: ctx.pluginHost.value } : {}),\n // The engine draws the search matches on the pages.\n ...(ctx.search.value ? { search: ctx.search.value } : {}),\n // Read through, so the watch below can re-apply a new function without a rebuild.\n decorateAnnotation: (a) => ctx.decorateAnnotation.value?.(a),\n ...(ctx.toolStyle.value ? { toolStyle: ctx.toolStyle.value } : {}),\n toolHistory: { batch: (fn) => ctx.history.batch(fn) },\n toolOptions: ctx.toolOptions,\n fonts: [...ctx.fonts.value],\n // Read through, so a new `signatures` store reaches the tool as well as the menus.\n signatureLibrary: () => ctx.signatures.value,\n customAnnotations: custom,\n ...(ctx.formHighlight.value === undefined\n ? {}\n : { formHighlight: ctx.formHighlight.value }),\n // The field's own button again, while its menu is open, closes the menu.\n onSignField: (req) => {\n signRequest.value = signRequest.value?.widget === req.widget ? null : req\n },\n onFormAction: (a) => ctx.reportFormAction(a),\n onFormAlert: (message) => {\n alertMessage.value = message\n alertsShown.value += 1\n },\n onActivate: (id) => ctx.setActivated(id),\n onBuild: (vp, tc) => {\n ctx.viewport.value = vp\n ctx.tools.value = tc\n if (first) {\n // A new document starts from where the viewport opens (its zoom, on the first page:\n // `createViewport` always starts at page 0); only later changes are reported, the\n // restored page among them, since the engine has already scrolled to it.\n first = false\n lastReportedZoom = vp.zoom.get()\n lastReportedPage = 0\n }\n for (const stop of stops) stop()\n const reportZoom = (z: ZoomValue): void => {\n if (z === lastReportedZoom) return\n lastReportedZoom = z\n ctx.reportZoom(z)\n }\n const reportPage = (p: number): void => {\n if (p === lastReportedPage) return\n lastReportedPage = p\n ctx.reportPage(p)\n }\n // A tool that switches itself (a one-shot tool once used) is reported as a pick.\n const followTool = (id: ToolId): void => {\n if (id !== ctx.tool.value) ctx.setTool(id)\n }\n stops = [\n vp.zoom.subscribe(reportZoom),\n vp.currentPage.subscribe(reportPage),\n tc.active.subscribe(followTool),\n ]\n // Whatever the build restored is reported once; nothing is when it matched.\n reportZoom(vp.zoom.get())\n reportPage(vp.currentPage.get())\n },\n onPageMount: (page, mount, host) => {\n pages.value = [...pages.value, { build, page, host, mount, error: null, custom }]\n },\n onPageUnmount: (page) => {\n pages.value = pages.value.filter((p) => p.page !== page)\n },\n onPageError: (page, err) => {\n pages.value = pages.value.map((p) => (p.page === page ? { ...p, error: err } : p))\n },\n })\n}\n\nonMounted(() => {\n ctx.viewportEl.value = scroll.value\n watch([() => ctx.document.value, () => ctx.status.value.state], setup, {\n immediate: true,\n })\n})\n\n// A new decorateAnnotation re-applies to the mounted annotation elements at once.\nwatch(\n () => ctx.decorateAnnotation.value,\n () => engine.value?.redecorate()\n)\n// The tint follows `formHighlight` in place, without a new document.\nwatch(\n () => ctx.formHighlight.value,\n (on) => engine.value?.setFormHighlight(on ?? true)\n)\nwatch(\n () => ctx.zoomModel.value,\n (z: ZoomValue | undefined) => {\n const vp = engine.value?.viewport\n if (vp && z !== undefined && z !== vp.zoom.get()) vp.setZoom(z)\n }\n)\nwatch(\n () => ctx.pageModel.value,\n (p) => {\n const vp = engine.value?.viewport\n if (!vp || p === undefined || p === lastReportedPage) return\n if (p !== vp.currentPage.get()) vp.scrollToPage(p)\n }\n)\nwatch(\n () => ctx.tool.value,\n (t) => {\n const tc = engine.value?.tools\n if (tc && tc.active.get() !== t) tc.set(t)\n }\n)\nwatch(\n () => props.mode,\n (m) => engine.value?.viewport.setMode(m)\n)\nwatch(\n () => props.rotation,\n (r) => engine.value?.viewport.setRotation(r)\n)\n// Gap and buffer are construction options: rebuild for the same document, keeping zoom and page.\n// The slots are already updated when this runs, so it carries the annotation slot too: a slot\n// toggle in the same tick is then recorded here and `onBeforeUpdate` finds nothing left to do.\nwatch([() => props.gap, () => props.buffer], ([gap, buffer]) =>\n rebuild({ gap, buffer, customAnnotations: !!slots.annotation })\n)\n\n// Slots are not reactive, so a change to whether the annotation slot is given is caught here.\n// `onBeforeUpdate` runs after the new slots are in place and before this render: the rebuild\n// swaps `pages` for the new build's pages synchronously, so the render that follows never pairs\n// the annotation slot with an overlay built without it (or the reverse). The rebuild keeps the\n// scroll position.\nonBeforeUpdate(() => {\n const next = !!slots.annotation\n if (engine.value && next !== custom) rebuild({ customAnnotations: next })\n})\n\nonBeforeUnmount(() => {\n teardown()\n if (ctx.viewportEl.value === scroll.value) ctx.viewportEl.value = null\n})\n</script>\n\n<template>\n <div class=\"ink-viewport-root\" data-ink-viewport-root :data-state=\"state\">\n <div v-if=\"state === 'loading'\" class=\"ink-status\" role=\"status\" aria-live=\"polite\">\n <slot name=\"loading\">Loading document…</slot>\n </div>\n <div v-else-if=\"error\" class=\"ink-status ink-error\" role=\"alert\">\n <slot name=\"error\" :error=\"error\">\n <p class=\"ink-error-title\">This document could not be opened.</p>\n <!-- The message comes from the PDF engine and is not written for readers: it stays\n off the page, on the code's tooltip. -->\n <p>\n <code class=\"ink-error-code\" :title=\"error.message\">{{ error.code }}</code>\n </p>\n </slot>\n </div>\n <div v-else-if=\"state === 'idle'\" class=\"ink-status ink-empty\">\n <slot name=\"empty\" />\n </div>\n <div\n ref=\"scroll\"\n class=\"ink-scroll\"\n data-ink-scroll\n tabindex=\"0\"\n role=\"document\"\n aria-label=\"Document\"\n />\n <InkPage\n v-for=\"p in pages\"\n :key=\"`${p.build}:${p.page}`\"\n :page=\"p.page\"\n :host=\"p.host\"\n :mount=\"p.mount\"\n :error=\"p.error\"\n :custom-annotations=\"p.custom\"\n >\n <template v-if=\"slots.annotation\" #annotation=\"slotProps\">\n <slot name=\"annotation\" v-bind=\"slotProps\" />\n </template>\n <template v-if=\"slots['page-error']\" #page-error=\"slotProps\">\n <slot name=\"page-error\" v-bind=\"slotProps\" />\n </template>\n </InkPage>\n <!-- Keyed by document, so the menu and its dialog start afresh for each one. -->\n <InkSignFieldPopover\n :key=\"documentKey(ctx.document.value)\"\n :request=\"signRequest\"\n @close=\"signRequest = null\"\n />\n <InkFormAlert\n :message=\"alertMessage\"\n :shown=\"alertsShown\"\n :fallback=\"scroll\"\n @close=\"alertMessage = null\"\n />\n </div>\n</template>\n"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"SignatureForm.js","names":["_sfc_main","SignatureForm_vue_vue_type_script_setup_true_lang_default","SignatureForm_default"],"sources":["../../src/components/SignatureForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * The body of `InkSignatureDialog`, mounted while it is open: one draft per opening. Draw,\n * Type and Image tabs, then Cancel and Save.\n */\nimport {\n createSignatureDraft,\n fontFamilyValue,\n handleRadioKeys,\n handleTabKeys,\n SIGNATURE_LABELS,\n SIGNATURE_TABS,\n type SignatureDraftState,\n type SignatureTab,\n} from '@inkbar/core/dom'\nimport { DialogClose } from 'reka-ui'\nimport { computed, onBeforeUnmount, onMounted, useId, useTemplateRef } from 'vue'\n\nimport { useTool } from '../composables/useTool'\nimport { useToolOptions } from '../composables/useToolOptions'\nimport { useInkContext } from '../context'\nimport { useReadable } from '../use-readable'\n\nimport type { SignatureOptions, SignatureTemplate } from '@inkbar/core'\n\nconst props = defineProps<{ fonts?: readonly string[] | undefined }>()\nconst emit = defineEmits<{ saved: [template: SignatureTemplate]; close: [] }>()\n\nconst ctx = useInkContext('InkSignatureDialog')\nconst { setTool } = useTool()\nconst { setOptions } = useToolOptions<SignatureOptions>('signature')\nconst draft = createSignatureDraft({ fonts: props.fonts ?? ctx.signatureFonts.value })\nconst IDLE: SignatureDraftState = draft.state.get()\nconst s = useReadable(draft.state, IDLE)\nconst uid = useId()\nconst padHost = useTemplateRef<HTMLDivElement>('pad')\n\nlet detach: (() => void) | undefined\nonMounted(() => {\n if (padHost.value) detach = draft.attachPad(padHost.value)\n})\nonBeforeUnmount(() => {\n detach?.()\n draft.destroy()\n})\n\nfunction save(): void {\n draft.save(ctx.signatures.value).then(\n (t) => {\n if (!t) return\n setOptions({ templateId: t.id })\n setTool('signature')\n emit('saved', t)\n emit('close')\n },\n () => {\n // The host's store refused it: the dialog stays open to try again.\n }\n )\n}\n\nconst tabId = (tab: SignatureTab) => `${uid}-tab-${tab}`\nconst panelId = (tab: SignatureTab) => `${uid}-panel-${tab}`\nconst shown = computed(() => s.value.name.trim() || SIGNATURE_LABELS.namePlaceholder)\nconst onTabKeydown = (e: KeyboardEvent) =>\n handleTabKeys(e, e.currentTarget as HTMLElement)\nconst onRadioKeydown = (e: KeyboardEvent) =>\n handleRadioKeys(e, e.currentTarget as HTMLElement)\nconst onName = (e: Event) => draft.setName((e.target as HTMLInputElement).value)\nconst onFile = (e: Event) =>\n void draft.chooseFile((e.target as HTMLInputElement).files?.[0])\n</script>\n\n<template>\n <div\n role=\"tablist\"\n :aria-label=\"SIGNATURE_LABELS.tabs\"\n class=\"ink-tabs\"\n @keydown=\"onTabKeydown\"\n >\n <button\n v-for=\"tab in SIGNATURE_TABS\"\n :id=\"tabId(tab)\"\n :key=\"tab\"\n type=\"button\"\n role=\"tab\"\n :aria-controls=\"panelId(tab)\"\n :aria-selected=\"s.tab === tab\"\n :tabindex=\"s.tab === tab ? 0 : -1\"\n class=\"ink-tab\"\n :data-key=\"tab\"\n :data-state=\"s.tab === tab ? 'active' : 'inactive'\"\n @click=\"draft.setTab(tab)\"\n >\n {{ SIGNATURE_LABELS[tab] }}\n </button>\n </div>\n <div\n :id=\"panelId('draw')\"\n role=\"tabpanel\"\n :aria-labelledby=\"tabId('draw')\"\n class=\"ink-tab-panel\"\n data-key=\"draw\"\n :hidden=\"s.tab !== 'draw'\"\n >\n <div\n ref=\"pad\"\n class=\"ink-signature-pad-host\"\n data-ink-signature-pad-host\n role=\"img\"\n :aria-label=\"SIGNATURE_LABELS.pad\"\n />\n <div class=\"ink-dialog-row\">\n <button\n type=\"button\"\n class=\"ink-button\"\n data-ink-signature-clear\n :disabled=\"s.padEmpty\"\n @click=\"draft.clearPad()\"\n >\n {{ SIGNATURE_LABELS.clear }}\n </button>\n </div>\n </div>\n <div\n :id=\"panelId('type')\"\n role=\"tabpanel\"\n :aria-labelledby=\"tabId('type')\"\n class=\"ink-tab-panel\"\n data-key=\"type\"\n :hidden=\"s.tab !== 'type'\"\n >\n <label class=\"ink-field\">\n <span>{{ SIGNATURE_LABELS.name }}</span>\n <input\n type=\"text\"\n class=\"ink-input\"\n data-ink-signature-name\n autocomplete=\"name\"\n :placeholder=\"SIGNATURE_LABELS.namePlaceholder\"\n :value=\"s.name\"\n @input=\"onName\"\n />\n </label>\n <div\n role=\"radiogroup\"\n :aria-label=\"SIGNATURE_LABELS.fonts\"\n class=\"ink-signature-fonts\"\n @keydown=\"onRadioKeydown\"\n >\n <button\n v-for=\"font in draft.fonts\"\n :key=\"font\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"s.font === font\"\n :aria-label=\"font\"\n :tabindex=\"s.font === font ? 0 : -1\"\n class=\"ink-signature-font\"\n :data-key=\"font\"\n :data-state=\"s.font === font ? 'checked' : 'unchecked'\"\n @click=\"draft.setFont(font)\"\n >\n <!-- The font is the option itself, so it is set here, not left to the theme. -->\n <span\n class=\"ink-signature-font-sample\"\n :style=\"{ fontFamily: fontFamilyValue(font) }\"\n >\n {{ shown }}\n </span>\n </button>\n </div>\n </div>\n <div\n :id=\"panelId('image')\"\n role=\"tabpanel\"\n :aria-labelledby=\"tabId('image')\"\n class=\"ink-tab-panel\"\n data-key=\"image\"\n :hidden=\"s.tab !== 'image'\"\n >\n <label class=\"ink-field\">\n <span>{{ SIGNATURE_LABELS.file }}</span>\n <input\n type=\"file\"\n accept=\"image/png,image/jpeg\"\n class=\"ink-input\"\n data-ink-signature-file\n @change=\"onFile\"\n />\n </label>\n <img\n v-if=\"s.image?.kind === 'image'\"\n class=\"ink-signature-image\"\n :src=\"s.image.imagedata\"\n alt=\"\"\n />\n <p v-if=\"s.imageError\" class=\"ink-field-error\" role=\"alert\">{{ s.imageError }}</p>\n </div>\n <p\n v-if=\"s.saveError\"\n class=\"ink-field-error\"\n role=\"alert\"\n data-ink-signature-save-error\n >\n {{ s.saveError }}\n </p>\n <div class=\"ink-dialog-actions\">\n <DialogClose as-child>\n <button type=\"button\" class=\"ink-button\" data-ink-signature-cancel>\n {{ SIGNATURE_LABELS.cancel }}\n </button>\n </DialogClose>\n <button\n type=\"button\"\n class=\"ink-button\"\n data-variant=\"primary\"\n data-ink-signature-save\n :disabled=\"!s.canSave\"\n @click=\"save\"\n >\n {{ SIGNATURE_LABELS.save }}\n </button>\n </div>\n</template>\n"],"mappings":""}
1
+ {"version":3,"file":"SignatureForm.js","names":["_sfc_main","SignatureForm_vue_vue_type_script_setup_true_lang_default","SignatureForm_default"],"sources":["../../src/components/SignatureForm.vue"],"sourcesContent":["<script setup lang=\"ts\">\n/*\n * The body of `InkSignatureDialog`, mounted while it is open: one draft per opening. Draw,\n * Type and Image tabs, then Cancel and Save.\n */\nimport {\n createSignatureDraft,\n fontFamilyValue,\n handleRadioKeys,\n handleTabKeys,\n SIGNATURE_LABELS,\n SIGNATURE_TABS,\n type SignatureDraftState,\n type SignatureTab,\n} from '@inkbar/core/dom'\nimport { DialogClose } from 'reka-ui'\nimport { computed, onBeforeUnmount, onMounted, useId, useTemplateRef } from 'vue'\n\nimport { useTool } from '../composables/useTool'\nimport { useToolOptions } from '../composables/useToolOptions'\nimport { useInkContext } from '../context'\nimport { useReadable } from '../use-readable'\n\nimport type { SignatureOptions, SignatureTemplate } from '@inkbar/core'\n\nconst props = withDefaults(\n defineProps<{ fonts?: readonly string[] | undefined; select?: boolean }>(),\n { fonts: undefined, select: true }\n)\nconst emit = defineEmits<{ saved: [template: SignatureTemplate]; close: [] }>()\n\nconst ctx = useInkContext('InkSignatureDialog')\nconst { setTool } = useTool()\nconst { setOptions } = useToolOptions<SignatureOptions>('signature')\nconst draft = createSignatureDraft({ fonts: props.fonts ?? ctx.signatureFonts.value })\nconst IDLE: SignatureDraftState = draft.state.get()\nconst s = useReadable(draft.state, IDLE)\nconst uid = useId()\nconst padHost = useTemplateRef<HTMLDivElement>('pad')\n\nlet detach: (() => void) | undefined\nonMounted(() => {\n if (padHost.value) detach = draft.attachPad(padHost.value)\n})\nonBeforeUnmount(() => {\n detach?.()\n draft.destroy()\n})\n\nfunction save(): void {\n draft.save(ctx.signatures.value).then(\n (t) => {\n if (!t) return\n if (props.select) {\n setOptions({ templateId: t.id })\n setTool('signature')\n }\n emit('saved', t)\n emit('close')\n },\n () => {\n // The host's store refused it: the dialog stays open to try again.\n }\n )\n}\n\nconst tabId = (tab: SignatureTab) => `${uid}-tab-${tab}`\nconst panelId = (tab: SignatureTab) => `${uid}-panel-${tab}`\nconst shown = computed(() => s.value.name.trim() || SIGNATURE_LABELS.namePlaceholder)\nconst onTabKeydown = (e: KeyboardEvent) =>\n handleTabKeys(e, e.currentTarget as HTMLElement)\nconst onRadioKeydown = (e: KeyboardEvent) =>\n handleRadioKeys(e, e.currentTarget as HTMLElement)\nconst onName = (e: Event) => draft.setName((e.target as HTMLInputElement).value)\nconst onFile = (e: Event) =>\n void draft.chooseFile((e.target as HTMLInputElement).files?.[0])\n</script>\n\n<template>\n <div\n role=\"tablist\"\n :aria-label=\"SIGNATURE_LABELS.tabs\"\n class=\"ink-tabs\"\n @keydown=\"onTabKeydown\"\n >\n <button\n v-for=\"tab in SIGNATURE_TABS\"\n :id=\"tabId(tab)\"\n :key=\"tab\"\n type=\"button\"\n role=\"tab\"\n :aria-controls=\"panelId(tab)\"\n :aria-selected=\"s.tab === tab\"\n :tabindex=\"s.tab === tab ? 0 : -1\"\n class=\"ink-tab\"\n :data-key=\"tab\"\n :data-state=\"s.tab === tab ? 'active' : 'inactive'\"\n @click=\"draft.setTab(tab)\"\n >\n {{ SIGNATURE_LABELS[tab] }}\n </button>\n </div>\n <div\n :id=\"panelId('draw')\"\n role=\"tabpanel\"\n :aria-labelledby=\"tabId('draw')\"\n class=\"ink-tab-panel\"\n data-key=\"draw\"\n :hidden=\"s.tab !== 'draw'\"\n >\n <div\n ref=\"pad\"\n class=\"ink-signature-pad-host\"\n data-ink-signature-pad-host\n role=\"img\"\n :aria-label=\"SIGNATURE_LABELS.pad\"\n />\n <div class=\"ink-dialog-row\">\n <button\n type=\"button\"\n class=\"ink-button\"\n data-ink-signature-clear\n :disabled=\"s.padEmpty\"\n @click=\"draft.clearPad()\"\n >\n {{ SIGNATURE_LABELS.clear }}\n </button>\n </div>\n </div>\n <div\n :id=\"panelId('type')\"\n role=\"tabpanel\"\n :aria-labelledby=\"tabId('type')\"\n class=\"ink-tab-panel\"\n data-key=\"type\"\n :hidden=\"s.tab !== 'type'\"\n >\n <label class=\"ink-field\">\n <span>{{ SIGNATURE_LABELS.name }}</span>\n <input\n type=\"text\"\n class=\"ink-input\"\n data-ink-signature-name\n autocomplete=\"name\"\n :placeholder=\"SIGNATURE_LABELS.namePlaceholder\"\n :value=\"s.name\"\n @input=\"onName\"\n />\n </label>\n <div\n role=\"radiogroup\"\n :aria-label=\"SIGNATURE_LABELS.fonts\"\n class=\"ink-signature-fonts\"\n @keydown=\"onRadioKeydown\"\n >\n <button\n v-for=\"font in draft.fonts\"\n :key=\"font\"\n type=\"button\"\n role=\"radio\"\n :aria-checked=\"s.font === font\"\n :aria-label=\"font\"\n :tabindex=\"s.font === font ? 0 : -1\"\n class=\"ink-signature-font\"\n :data-key=\"font\"\n :data-state=\"s.font === font ? 'checked' : 'unchecked'\"\n @click=\"draft.setFont(font)\"\n >\n <!-- The font is the option itself, so it is set here, not left to the theme. -->\n <span\n class=\"ink-signature-font-sample\"\n :style=\"{ fontFamily: fontFamilyValue(font) }\"\n >\n {{ shown }}\n </span>\n </button>\n </div>\n </div>\n <div\n :id=\"panelId('image')\"\n role=\"tabpanel\"\n :aria-labelledby=\"tabId('image')\"\n class=\"ink-tab-panel\"\n data-key=\"image\"\n :hidden=\"s.tab !== 'image'\"\n >\n <label class=\"ink-field\">\n <span>{{ SIGNATURE_LABELS.file }}</span>\n <input\n type=\"file\"\n accept=\"image/png,image/jpeg\"\n class=\"ink-input\"\n data-ink-signature-file\n @change=\"onFile\"\n />\n </label>\n <img\n v-if=\"s.image?.kind === 'image'\"\n class=\"ink-signature-image\"\n :src=\"s.image.imagedata\"\n alt=\"\"\n />\n <p v-if=\"s.imageError\" class=\"ink-field-error\" role=\"alert\">{{ s.imageError }}</p>\n </div>\n <p\n v-if=\"s.saveError\"\n class=\"ink-field-error\"\n role=\"alert\"\n data-ink-signature-save-error\n >\n {{ s.saveError }}\n </p>\n <div class=\"ink-dialog-actions\">\n <DialogClose as-child>\n <button type=\"button\" class=\"ink-button\" data-ink-signature-cancel>\n {{ SIGNATURE_LABELS.cancel }}\n </button>\n </DialogClose>\n <button\n type=\"button\"\n class=\"ink-button\"\n data-variant=\"primary\"\n data-ink-signature-save\n :disabled=\"!s.canSave\"\n @click=\"save\"\n >\n {{ SIGNATURE_LABELS.save }}\n </button>\n </div>\n</template>\n"],"mappings":""}
@@ -1,14 +1,18 @@
1
1
  import { SignatureTemplate } from '@inkbar/core';
2
2
  type __VLS_Props = {
3
3
  fonts?: readonly string[] | undefined;
4
+ select?: boolean;
4
5
  };
5
6
  declare const __VLS_export: import('vue').DefineComponent<__VLS_Props, {}, {}, {}, {}, import('vue').ComponentOptionsMixin, import('vue').ComponentOptionsMixin, {
6
- saved: (template: SignatureTemplate) => any;
7
7
  close: () => any;
8
+ saved: (template: SignatureTemplate) => any;
8
9
  }, string, import('vue').PublicProps, Readonly<__VLS_Props> & Readonly<{
9
- onSaved?: (template: SignatureTemplate) => any;
10
10
  onClose?: () => any;
11
- }>, {}, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
11
+ onSaved?: (template: SignatureTemplate) => any;
12
+ }>, {
13
+ select: boolean;
14
+ fonts: readonly string[] | undefined;
15
+ }, {}, {}, {}, string, import('vue').ComponentProvideOptions, false, {}, any>;
12
16
  declare const _default: typeof __VLS_export;
13
17
  export default _default;
14
18
  //# sourceMappingURL=SignatureForm.vue.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"SignatureForm.vue.d.ts","sourceRoot":"","sources":["../../src/components/SignatureForm.vue"],"names":[],"mappings":"AAyPA,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAGvE,KAAK,WAAW,GAAG;IAAE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAA;CAAE,CAAC;AAsR7D,QAAA,MAAM,YAAY;;;;;;kFAGhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}
1
+ {"version":3,"file":"SignatureForm.vue.d.ts","sourceRoot":"","sources":["../../src/components/SignatureForm.vue"],"names":[],"mappings":"AA8PA,OAAO,KAAK,EAAoB,iBAAiB,EAAE,MAAM,cAAc,CAAA;AAGvE,KAAK,WAAW,GAAG;IAAE,KAAK,CAAC,EAAE,SAAS,MAAM,EAAE,GAAG,SAAS,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAE,CAAC;AA6R/E,QAAA,MAAM,YAAY;;;;;;;;;6EAIhB,CAAC;wBACkB,OAAO,YAAY;AAAxC,wBAAyC"}