@osdk/react-components 0.2.0-beta.25 → 0.2.0-beta.26

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 (107) hide show
  1. package/AGENTS.md +1 -0
  2. package/CHANGELOG.md +21 -0
  3. package/build/browser/action-form/ActionForm.js +15 -11
  4. package/build/browser/action-form/ActionForm.js.map +1 -1
  5. package/build/browser/action-form/FormFieldApi.js.map +1 -1
  6. package/build/browser/action-form/fields/CustomField.js +30 -0
  7. package/build/browser/action-form/fields/CustomField.js.map +1 -0
  8. package/build/browser/action-form/fields/FormFieldRenderer.js +13 -21
  9. package/build/browser/action-form/fields/FormFieldRenderer.js.map +1 -1
  10. package/build/browser/action-form/fields/RadioButtonsField.js +1 -1
  11. package/build/browser/action-form/fields/RadioButtonsField.js.map +1 -1
  12. package/build/browser/action-form/utils/getDefaultFieldDefinitions.js +97 -10
  13. package/build/browser/action-form/utils/getDefaultFieldDefinitions.js.map +1 -1
  14. package/build/browser/base-components/dialog/Dialog.js +4 -2
  15. package/build/browser/base-components/dialog/Dialog.js.map +1 -1
  16. package/build/browser/object-table/ObjectTableApi.js.map +1 -1
  17. package/build/browser/object-table/hooks/useFunctionColumnsData.js +57 -192
  18. package/build/browser/object-table/hooks/useFunctionColumnsData.js.map +1 -1
  19. package/build/browser/pdf-viewer/PdfViewer.js +10 -5
  20. package/build/browser/pdf-viewer/PdfViewer.js.map +1 -1
  21. package/build/browser/pdf-viewer/PdfViewerContext.js +108 -0
  22. package/build/browser/pdf-viewer/PdfViewerContext.js.map +1 -0
  23. package/build/browser/pdf-viewer/hooks/usePdfFormFields.js +26 -11
  24. package/build/browser/pdf-viewer/hooks/usePdfFormFields.js.map +1 -1
  25. package/build/browser/pdf-viewer/hooks/usePdfHighlightMode.js +29 -1
  26. package/build/browser/pdf-viewer/hooks/usePdfHighlightMode.js.map +1 -1
  27. package/build/browser/pdf-viewer/hooks/usePdfViewerCore.js +3 -3
  28. package/build/browser/pdf-viewer/hooks/usePdfViewerCore.js.map +1 -1
  29. package/build/browser/pdf-viewer/hooks/usePdfViewerSearch.js +3 -3
  30. package/build/browser/pdf-viewer/hooks/usePdfViewerSearch.js.map +1 -1
  31. package/build/browser/pdf-viewer/hooks/usePdfViewerState.js +3 -3
  32. package/build/browser/pdf-viewer/hooks/usePdfViewerState.js.map +1 -1
  33. package/build/browser/pdf-viewer/types.js.map +1 -1
  34. package/build/browser/public/experimental.js +2 -0
  35. package/build/browser/public/experimental.js.map +1 -1
  36. package/build/cjs/{chunk-GT6IV6LV.cjs → chunk-MRRRZN44.cjs} +6 -4
  37. package/build/cjs/chunk-MRRRZN44.cjs.map +1 -0
  38. package/build/cjs/public/experimental.cjs +1384 -1343
  39. package/build/cjs/public/experimental.cjs.map +1 -1
  40. package/build/cjs/public/experimental.d.cts +115 -8
  41. package/build/cjs/public/primitives.cjs +5 -5
  42. package/build/cjs/public/primitives.d.cts +3 -2
  43. package/build/esm/action-form/ActionForm.js +15 -11
  44. package/build/esm/action-form/ActionForm.js.map +1 -1
  45. package/build/esm/action-form/FormFieldApi.js.map +1 -1
  46. package/build/esm/action-form/fields/CustomField.js +30 -0
  47. package/build/esm/action-form/fields/CustomField.js.map +1 -0
  48. package/build/esm/action-form/fields/FormFieldRenderer.js +13 -21
  49. package/build/esm/action-form/fields/FormFieldRenderer.js.map +1 -1
  50. package/build/esm/action-form/fields/RadioButtonsField.js +1 -1
  51. package/build/esm/action-form/fields/RadioButtonsField.js.map +1 -1
  52. package/build/esm/action-form/utils/getDefaultFieldDefinitions.js +97 -10
  53. package/build/esm/action-form/utils/getDefaultFieldDefinitions.js.map +1 -1
  54. package/build/esm/base-components/dialog/Dialog.js +4 -2
  55. package/build/esm/base-components/dialog/Dialog.js.map +1 -1
  56. package/build/esm/object-table/ObjectTableApi.js.map +1 -1
  57. package/build/esm/object-table/hooks/useFunctionColumnsData.js +57 -192
  58. package/build/esm/object-table/hooks/useFunctionColumnsData.js.map +1 -1
  59. package/build/esm/pdf-viewer/PdfViewer.js +10 -5
  60. package/build/esm/pdf-viewer/PdfViewer.js.map +1 -1
  61. package/build/esm/pdf-viewer/PdfViewerContext.js +108 -0
  62. package/build/esm/pdf-viewer/PdfViewerContext.js.map +1 -0
  63. package/build/esm/pdf-viewer/hooks/usePdfFormFields.js +26 -11
  64. package/build/esm/pdf-viewer/hooks/usePdfFormFields.js.map +1 -1
  65. package/build/esm/pdf-viewer/hooks/usePdfHighlightMode.js +29 -1
  66. package/build/esm/pdf-viewer/hooks/usePdfHighlightMode.js.map +1 -1
  67. package/build/esm/pdf-viewer/hooks/usePdfViewerCore.js +3 -3
  68. package/build/esm/pdf-viewer/hooks/usePdfViewerCore.js.map +1 -1
  69. package/build/esm/pdf-viewer/hooks/usePdfViewerSearch.js +3 -3
  70. package/build/esm/pdf-viewer/hooks/usePdfViewerSearch.js.map +1 -1
  71. package/build/esm/pdf-viewer/hooks/usePdfViewerState.js +3 -3
  72. package/build/esm/pdf-viewer/hooks/usePdfViewerState.js.map +1 -1
  73. package/build/esm/pdf-viewer/types.js.map +1 -1
  74. package/build/esm/public/experimental.js +2 -0
  75. package/build/esm/public/experimental.js.map +1 -1
  76. package/build/types/action-form/FormFieldApi.d.ts +3 -3
  77. package/build/types/action-form/FormFieldApi.d.ts.map +1 -1
  78. package/build/types/action-form/fields/CustomField.d.ts +9 -0
  79. package/build/types/action-form/fields/CustomField.d.ts.map +1 -0
  80. package/build/types/action-form/fields/FormFieldRenderer.d.ts.map +1 -1
  81. package/build/types/action-form/fields/RadioButtonsField.d.ts +2 -2
  82. package/build/types/action-form/fields/RadioButtonsField.d.ts.map +1 -1
  83. package/build/types/action-form/utils/getDefaultFieldDefinitions.d.ts +7 -4
  84. package/build/types/action-form/utils/getDefaultFieldDefinitions.d.ts.map +1 -1
  85. package/build/types/base-components/dialog/Dialog.d.ts +3 -2
  86. package/build/types/base-components/dialog/Dialog.d.ts.map +1 -1
  87. package/build/types/object-table/ObjectTableApi.d.ts +4 -3
  88. package/build/types/object-table/ObjectTableApi.d.ts.map +1 -1
  89. package/build/types/object-table/hooks/useFunctionColumnsData.d.ts.map +1 -1
  90. package/build/types/pdf-viewer/PdfViewer.d.ts +2 -2
  91. package/build/types/pdf-viewer/PdfViewer.d.ts.map +1 -1
  92. package/build/types/pdf-viewer/PdfViewerContext.d.ts +64 -0
  93. package/build/types/pdf-viewer/PdfViewerContext.d.ts.map +1 -0
  94. package/build/types/pdf-viewer/hooks/usePdfHighlightMode.d.ts +1 -0
  95. package/build/types/pdf-viewer/hooks/usePdfHighlightMode.d.ts.map +1 -1
  96. package/build/types/pdf-viewer/types.d.ts +47 -0
  97. package/build/types/pdf-viewer/types.d.ts.map +1 -1
  98. package/build/types/public/experimental.d.ts +2 -0
  99. package/build/types/public/experimental.d.ts.map +1 -1
  100. package/package.json +6 -6
  101. package/build/browser/action-form/utils/getDefaultFieldComponent.js +0 -56
  102. package/build/browser/action-form/utils/getDefaultFieldComponent.js.map +0 -1
  103. package/build/cjs/chunk-GT6IV6LV.cjs.map +0 -1
  104. package/build/esm/action-form/utils/getDefaultFieldComponent.js +0 -56
  105. package/build/esm/action-form/utils/getDefaultFieldComponent.js.map +0 -1
  106. package/build/types/action-form/utils/getDefaultFieldComponent.d.ts +0 -6
  107. package/build/types/action-form/utils/getDefaultFieldComponent.d.ts.map +0 -1
@@ -0,0 +1,108 @@
1
+ /*
2
+ * Copyright 2025 Palantir Technologies, Inc. All rights reserved.
3
+ *
4
+ * Licensed under the Apache License, Version 2.0 (the "License");
5
+ * you may not use this file except in compliance with the License.
6
+ * You may obtain a copy of the License at
7
+ *
8
+ * http://www.apache.org/licenses/LICENSE-2.0
9
+ *
10
+ * Unless required by applicable law or agreed to in writing, software
11
+ * distributed under the License is distributed on an "AS IS" BASIS,
12
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
+ * See the License for the specific language governing permissions and
14
+ * limitations under the License.
15
+ */
16
+
17
+ import React, { createContext, useContext, useMemo } from "react";
18
+ import { EMPTY_ANNOTATION_ARRAY } from "./constants.js";
19
+ import { usePdfAnnotationsByPage } from "./hooks/usePdfAnnotationsByPage.js";
20
+ import { usePdfFormFields } from "./hooks/usePdfFormFields.js";
21
+ import { usePdfHighlightMode } from "./hooks/usePdfHighlightMode.js";
22
+ import { usePdfViewerState } from "./hooks/usePdfViewerState.js";
23
+
24
+ /** The shape of the value provided by {@link PdfViewerProvider}. */
25
+
26
+ const PdfViewerContext = /*#__PURE__*/createContext(null);
27
+ /** Provides {@link PdfViewerContextValue} to descendant components. */
28
+ export function PdfViewerProvider({
29
+ value,
30
+ children
31
+ }) {
32
+ return /*#__PURE__*/React.createElement(PdfViewerContext.Provider, {
33
+ value: value
34
+ }, children);
35
+ }
36
+
37
+ /**
38
+ * Returns the nearest {@link PdfViewerContextValue} from a {@link PdfViewerProvider}.
39
+ * Throws if called outside a provider.
40
+ */
41
+ export function usePdfViewerContext() {
42
+ const ctx = useContext(PdfViewerContext);
43
+ if (ctx == null) {
44
+ throw new Error("usePdfViewerContext must be used within a PdfViewerProvider");
45
+ }
46
+ return ctx;
47
+ }
48
+
49
+ /**
50
+ * Consumer-facing setup hook analogous to react-hook-form's `useForm()`.
51
+ * Accepts viewer options and returns a {@link PdfViewerContextValue} suitable
52
+ * for passing to {@link PdfViewerProvider}.
53
+ */
54
+ export function usePdfViewerInstance(options) {
55
+ const viewer = usePdfViewerState({
56
+ src: options.src,
57
+ initialPage: options.initialPage,
58
+ initialScale: options.initialScale,
59
+ initialSidebarOpen: options.initialSidebarOpen,
60
+ sidebarMode: options.sidebarMode,
61
+ onDownload: options.onDownload
62
+ });
63
+ const {
64
+ highlightModeActive,
65
+ toggleHighlightMode
66
+ } = usePdfHighlightMode({
67
+ pdfViewerRef: viewer.pdfViewerRef,
68
+ document: viewer.document,
69
+ enabled: options.highlightEnabled ?? false,
70
+ onTextHighlight: options.onTextHighlight,
71
+ onHighlightDelete: options.onHighlightDelete
72
+ });
73
+ const {
74
+ hasFormFields,
75
+ submitFormData
76
+ } = usePdfFormFields({
77
+ pdfViewerRef: viewer.pdfViewerRef,
78
+ eventBusRef: viewer.eventBusRef,
79
+ document: viewer.document,
80
+ formData: options.formData,
81
+ onFormSubmit: options.onFormSubmit,
82
+ onFormChange: options.onFormChange
83
+ });
84
+ const annotations = options.annotations ?? EMPTY_ANNOTATION_ARRAY;
85
+ const annotationsByPage = usePdfAnnotationsByPage(annotations);
86
+ const highlightEnabled = options.highlightEnabled ?? false;
87
+ const enableDownload = options.enableDownload ?? false;
88
+ const enableFormSave = options.onFormSubmit != null && hasFormFields;
89
+ const {
90
+ onAnnotationClick,
91
+ outlineIcons
92
+ } = options;
93
+ return useMemo(() => ({
94
+ ...viewer,
95
+ highlightEnabled,
96
+ highlightModeActive,
97
+ toggleHighlightMode,
98
+ hasFormFields,
99
+ submitFormData,
100
+ annotations,
101
+ annotationsByPage,
102
+ onAnnotationClick,
103
+ enableDownload,
104
+ enableFormSave,
105
+ outlineIcons
106
+ }), [viewer, highlightEnabled, highlightModeActive, toggleHighlightMode, hasFormFields, submitFormData, annotations, annotationsByPage, onAnnotationClick, enableDownload, enableFormSave, outlineIcons]);
107
+ }
108
+ //# sourceMappingURL=PdfViewerContext.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"PdfViewerContext.js","names":["React","createContext","useContext","useMemo","EMPTY_ANNOTATION_ARRAY","usePdfAnnotationsByPage","usePdfFormFields","usePdfHighlightMode","usePdfViewerState","PdfViewerContext","PdfViewerProvider","value","children","createElement","Provider","usePdfViewerContext","ctx","Error","usePdfViewerInstance","options","viewer","src","initialPage","initialScale","initialSidebarOpen","sidebarMode","onDownload","highlightModeActive","toggleHighlightMode","pdfViewerRef","document","enabled","highlightEnabled","onTextHighlight","onHighlightDelete","hasFormFields","submitFormData","eventBusRef","formData","onFormSubmit","onFormChange","annotations","annotationsByPage","enableDownload","enableFormSave","onAnnotationClick","outlineIcons"],"sources":["PdfViewerContext.tsx"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PDFDocumentProxy } from \"pdfjs-dist\";\nimport type {\n EventBus,\n PDFFindController,\n PDFViewer,\n} from \"pdfjs-dist/web/pdf_viewer.mjs\";\nimport React, { createContext, useContext, useMemo } from \"react\";\nimport type { RefObject } from \"react\";\nimport { EMPTY_ANNOTATION_ARRAY } from \"./constants.js\";\nimport type {\n AnnotationPortalTarget,\n} from \"./hooks/usePdfAnnotationPortals.js\";\nimport { usePdfAnnotationsByPage } from \"./hooks/usePdfAnnotationsByPage.js\";\nimport { usePdfFormFields } from \"./hooks/usePdfFormFields.js\";\nimport { usePdfHighlightMode } from \"./hooks/usePdfHighlightMode.js\";\nimport type { UsePdfViewerSearchResult } from \"./hooks/usePdfViewerSearch.js\";\nimport { usePdfViewerState } from \"./hooks/usePdfViewerState.js\";\nimport type {\n OutlineItem,\n PdfAnnotation,\n PdfViewerInstanceOptions,\n SidebarMode,\n} from \"./types.js\";\n\n/** The shape of the value provided by {@link PdfViewerProvider}. */\nexport interface PdfViewerContextValue {\n // From UsePdfViewerStateResult (core + state)\n document: PDFDocumentProxy | undefined;\n numPages: number;\n loading: boolean;\n error: Error | undefined;\n containerRef: RefObject<HTMLDivElement>;\n viewerRef: RefObject<HTMLDivElement>;\n currentPage: number;\n scrollToPage: (page: number) => void;\n scale: number;\n setScale: (scale: number) => void;\n zoomIn: () => void;\n zoomOut: () => void;\n rotation: number;\n rotateLeft: () => void;\n rotateRight: () => void;\n sidebarOpen: boolean;\n sidebarMode: SidebarMode;\n setSidebarMode: (mode: SidebarMode) => void;\n toggleSidebar: () => void;\n search: UsePdfViewerSearchResult;\n outlineItems: OutlineItem[];\n download: (filename?: string) => void;\n portalTargets: AnnotationPortalTarget[];\n\n // From UsePdfHighlightModeResult\n highlightEnabled: boolean;\n highlightModeActive: boolean;\n toggleHighlightMode: () => void;\n\n // From UsePdfFormFieldsResult\n hasFormFields: boolean;\n submitFormData: () => void;\n\n // Annotations\n annotations: PdfAnnotation[];\n annotationsByPage: Record<number, PdfAnnotation[]>;\n onAnnotationClick: ((annotation: PdfAnnotation) => void) | undefined;\n\n // Config props passed through\n enableDownload: boolean;\n enableFormSave: boolean;\n outlineIcons: Partial<Record<number, React.ComponentType>> | undefined;\n\n // Internal refs (for advanced consumers)\n pdfViewerRef: RefObject<PDFViewer | null>;\n eventBusRef: RefObject<EventBus | null>;\n findControllerRef: RefObject<PDFFindController | null>;\n}\n\nconst PdfViewerContext = createContext<PdfViewerContextValue | null>(null);\n\nexport interface PdfViewerProviderProps {\n value: PdfViewerContextValue;\n children: React.ReactNode;\n}\n\n/** Provides {@link PdfViewerContextValue} to descendant components. */\nexport function PdfViewerProvider(\n { value, children }: PdfViewerProviderProps,\n): React.ReactElement {\n return (\n <PdfViewerContext.Provider value={value}>\n {children}\n </PdfViewerContext.Provider>\n );\n}\n\n/**\n * Returns the nearest {@link PdfViewerContextValue} from a {@link PdfViewerProvider}.\n * Throws if called outside a provider.\n */\nexport function usePdfViewerContext(): PdfViewerContextValue {\n const ctx = useContext(PdfViewerContext);\n if (ctx == null) {\n throw new Error(\n \"usePdfViewerContext must be used within a PdfViewerProvider\",\n );\n }\n return ctx;\n}\n\n/**\n * Consumer-facing setup hook analogous to react-hook-form's `useForm()`.\n * Accepts viewer options and returns a {@link PdfViewerContextValue} suitable\n * for passing to {@link PdfViewerProvider}.\n */\nexport function usePdfViewerInstance(\n options: PdfViewerInstanceOptions,\n): PdfViewerContextValue {\n const viewer = usePdfViewerState({\n src: options.src,\n initialPage: options.initialPage,\n initialScale: options.initialScale,\n initialSidebarOpen: options.initialSidebarOpen,\n sidebarMode: options.sidebarMode,\n onDownload: options.onDownload,\n });\n\n const { highlightModeActive, toggleHighlightMode } = usePdfHighlightMode({\n pdfViewerRef: viewer.pdfViewerRef,\n document: viewer.document,\n enabled: options.highlightEnabled ?? false,\n onTextHighlight: options.onTextHighlight,\n onHighlightDelete: options.onHighlightDelete,\n });\n\n const { hasFormFields, submitFormData } = usePdfFormFields({\n pdfViewerRef: viewer.pdfViewerRef,\n eventBusRef: viewer.eventBusRef,\n document: viewer.document,\n formData: options.formData,\n onFormSubmit: options.onFormSubmit,\n onFormChange: options.onFormChange,\n });\n\n const annotations = options.annotations ?? EMPTY_ANNOTATION_ARRAY;\n const annotationsByPage = usePdfAnnotationsByPage(annotations);\n\n const highlightEnabled = options.highlightEnabled ?? false;\n const enableDownload = options.enableDownload ?? false;\n const enableFormSave = options.onFormSubmit != null && hasFormFields;\n const { onAnnotationClick, outlineIcons } = options;\n\n return useMemo(\n () => ({\n ...viewer,\n highlightEnabled,\n highlightModeActive,\n toggleHighlightMode,\n hasFormFields,\n submitFormData,\n annotations,\n annotationsByPage,\n onAnnotationClick,\n enableDownload,\n enableFormSave,\n outlineIcons,\n }),\n [\n viewer,\n highlightEnabled,\n highlightModeActive,\n toggleHighlightMode,\n hasFormFields,\n submitFormData,\n annotations,\n annotationsByPage,\n onAnnotationClick,\n enableDownload,\n enableFormSave,\n outlineIcons,\n ],\n );\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAQA,OAAOA,KAAK,IAAIC,aAAa,EAAEC,UAAU,EAAEC,OAAO,QAAQ,OAAO;AAEjE,SAASC,sBAAsB,QAAQ,gBAAgB;AAIvD,SAASC,uBAAuB,QAAQ,oCAAoC;AAC5E,SAASC,gBAAgB,QAAQ,6BAA6B;AAC9D,SAASC,mBAAmB,QAAQ,gCAAgC;AAEpE,SAASC,iBAAiB,QAAQ,8BAA8B;;AAQhE;;AAoDA,MAAMC,gBAAgB,gBAAGR,aAAa,CAA+B,IAAI,CAAC;AAO1E;AACA,OAAO,SAASS,iBAAiBA,CAC/B;EAAEC,KAAK;EAAEC;AAAiC,CAAC,EACvB;EACpB,oBACEZ,KAAA,CAAAa,aAAA,CAACJ,gBAAgB,CAACK,QAAQ;IAACH,KAAK,EAAEA;EAAM,GACrCC,QACwB,CAAC;AAEhC;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASG,mBAAmBA,CAAA,EAA0B;EAC3D,MAAMC,GAAG,GAAGd,UAAU,CAACO,gBAAgB,CAAC;EACxC,IAAIO,GAAG,IAAI,IAAI,EAAE;IACf,MAAM,IAAIC,KAAK,CACb,6DACF,CAAC;EACH;EACA,OAAOD,GAAG;AACZ;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASE,oBAAoBA,CAClCC,OAAiC,EACV;EACvB,MAAMC,MAAM,GAAGZ,iBAAiB,CAAC;IAC/Ba,GAAG,EAAEF,OAAO,CAACE,GAAG;IAChBC,WAAW,EAAEH,OAAO,CAACG,WAAW;IAChCC,YAAY,EAAEJ,OAAO,CAACI,YAAY;IAClCC,kBAAkB,EAAEL,OAAO,CAACK,kBAAkB;IAC9CC,WAAW,EAAEN,OAAO,CAACM,WAAW;IAChCC,UAAU,EAAEP,OAAO,CAACO;EACtB,CAAC,CAAC;EAEF,MAAM;IAAEC,mBAAmB;IAAEC;EAAoB,CAAC,GAAGrB,mBAAmB,CAAC;IACvEsB,YAAY,EAAET,MAAM,CAACS,YAAY;IACjCC,QAAQ,EAAEV,MAAM,CAACU,QAAQ;IACzBC,OAAO,EAAEZ,OAAO,CAACa,gBAAgB,IAAI,KAAK;IAC1CC,eAAe,EAAEd,OAAO,CAACc,eAAe;IACxCC,iBAAiB,EAAEf,OAAO,CAACe;EAC7B,CAAC,CAAC;EAEF,MAAM;IAAEC,aAAa;IAAEC;EAAe,CAAC,GAAG9B,gBAAgB,CAAC;IACzDuB,YAAY,EAAET,MAAM,CAACS,YAAY;IACjCQ,WAAW,EAAEjB,MAAM,CAACiB,WAAW;IAC/BP,QAAQ,EAAEV,MAAM,CAACU,QAAQ;IACzBQ,QAAQ,EAAEnB,OAAO,CAACmB,QAAQ;IAC1BC,YAAY,EAAEpB,OAAO,CAACoB,YAAY;IAClCC,YAAY,EAAErB,OAAO,CAACqB;EACxB,CAAC,CAAC;EAEF,MAAMC,WAAW,GAAGtB,OAAO,CAACsB,WAAW,IAAIrC,sBAAsB;EACjE,MAAMsC,iBAAiB,GAAGrC,uBAAuB,CAACoC,WAAW,CAAC;EAE9D,MAAMT,gBAAgB,GAAGb,OAAO,CAACa,gBAAgB,IAAI,KAAK;EAC1D,MAAMW,cAAc,GAAGxB,OAAO,CAACwB,cAAc,IAAI,KAAK;EACtD,MAAMC,cAAc,GAAGzB,OAAO,CAACoB,YAAY,IAAI,IAAI,IAAIJ,aAAa;EACpE,MAAM;IAAEU,iBAAiB;IAAEC;EAAa,CAAC,GAAG3B,OAAO;EAEnD,OAAOhB,OAAO,CACZ,OAAO;IACL,GAAGiB,MAAM;IACTY,gBAAgB;IAChBL,mBAAmB;IACnBC,mBAAmB;IACnBO,aAAa;IACbC,cAAc;IACdK,WAAW;IACXC,iBAAiB;IACjBG,iBAAiB;IACjBF,cAAc;IACdC,cAAc;IACdE;EACF,CAAC,CAAC,EACF,CACE1B,MAAM,EACNY,gBAAgB,EAChBL,mBAAmB,EACnBC,mBAAmB,EACnBO,aAAa,EACbC,cAAc,EACdK,WAAW,EACXC,iBAAiB,EACjBG,iBAAiB,EACjBF,cAAc,EACdC,cAAc,EACdE,YAAY,CAEhB,CAAC;AACH","ignoreList":[]}
@@ -167,20 +167,35 @@ export function usePdfFormFields({
167
167
  for (const id of ids) {
168
168
  const entry = fieldMapRef.current.get(id);
169
169
  if (entry == null) continue;
170
- const storageVal = toStorageValue(value, entry.fieldType);
171
- storage.setValue(id, {
172
- value: storageVal
173
- });
174
170
 
175
171
  // Also update the DOM element directly (scoped to viewer container)
176
172
  const el = container?.querySelector(`[data-element-id="${CSS.escape(id)}"]`);
177
- if (el == null) continue;
178
- if (entry.fieldType === "checkbox" && el instanceof HTMLInputElement) {
179
- el.checked = typeof storageVal === "boolean" ? storageVal : storageVal !== "Off";
180
- } else if (el instanceof HTMLSelectElement) {
181
- el.value = String(value);
182
- } else if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
183
- el.value = String(value);
173
+ if (entry.fieldType === "radiobutton") {
174
+ // Radio buttons: each ID is a separate option. Check the one
175
+ // whose export value matches, uncheck the others.
176
+ // Use exportValues if defined, otherwise fall back to the DOM
177
+ // element's value attribute (mirrors handleFieldChange logic).
178
+ const optionValue = entry.exportValues ?? (el instanceof HTMLInputElement ? el.value : undefined);
179
+ const isSelected = optionValue === String(value);
180
+ storage.setValue(id, {
181
+ value: isSelected ? entry.exportValues ?? String(value) : "Off"
182
+ });
183
+ if (el instanceof HTMLInputElement) {
184
+ el.checked = isSelected;
185
+ }
186
+ } else {
187
+ const storageVal = toStorageValue(value, entry.fieldType);
188
+ storage.setValue(id, {
189
+ value: storageVal
190
+ });
191
+ if (el == null) continue;
192
+ if (entry.fieldType === "checkbox" && el instanceof HTMLInputElement) {
193
+ el.checked = typeof storageVal === "boolean" ? storageVal : storageVal !== "Off";
194
+ } else if (el instanceof HTMLSelectElement) {
195
+ el.value = String(value);
196
+ } else if (el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement) {
197
+ el.value = String(value);
198
+ }
184
199
  }
185
200
  }
186
201
  }
@@ -1 +1 @@
1
- {"version":3,"file":"usePdfFormFields.js","names":["useCallback","useEffect","useRef","useState","ANNOTATION_LAYER_RENDERED_EVENT","getViewerContainer","viewer","container","normalizeFieldType","type","toFormFieldValue","storageValue","fieldType","Array","isArray","map","String","toStorageValue","formValue","usePdfFormFields","pdfViewerRef","eventBusRef","document","formData","onFormSubmit","onFormChange","hasFormFields","setHasFormFields","onFormSubmitRef","onFormChangeRef","formDataRef","current","fieldMapRef","Map","nameToIdsRef","doc","clear","cancelled","getFieldObjects","then","fieldObjects","fieldMap","nameToIds","fieldName","objects","Object","entries","ids","obj","typedObj","id","set","exportValues","page","push","length","size","populateFields","data","pdfViewer","storage","annotationStorage","value","get","entry","storageVal","setValue","el","querySelector","CSS","escape","HTMLInputElement","checked","HTMLSelectElement","HTMLTextAreaElement","eventBus","onAnnotationLayerRendered","on","off","listeners","handleFieldChange","getAttribute","attachListeners","root","elements","querySelectorAll","has","handler","addEventListener","observer","MutationObserver","mutations","mutation","node","addedNodes","Element","observe","childList","subtree","disconnect","removeEventListener","submitFormData","allValues","getAll","result","resolvedRadios","Set","add","groupIds","selectedValue","radioId","stored","val","radioEntry","rawValue"],"sources":["usePdfFormFields.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PDFDocumentProxy } from \"pdfjs-dist\";\nimport type { EventBus, PDFViewer } from \"pdfjs-dist/web/pdf_viewer.mjs\";\nimport type { RefObject } from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { ANNOTATION_LAYER_RENDERED_EVENT } from \"../constants.js\";\nimport type { PdfFormFieldValue } from \"../types.js\";\n\n/** Get the viewer's container element (typed loosely by pdf.js). */\nfunction getViewerContainer(viewer: PDFViewer): HTMLElement | undefined {\n return (viewer as { container?: HTMLElement }).container;\n}\n\n/** Internal metadata for a single form field annotation. */\ninterface FieldEntry {\n fieldName: string;\n fieldType: \"text\" | \"checkbox\" | \"radiobutton\" | \"combobox\" | \"listbox\";\n exportValues: string | undefined;\n page: number;\n}\n\nexport interface UsePdfFormFieldsOptions {\n pdfViewerRef: RefObject<PDFViewer | null>;\n eventBusRef: RefObject<EventBus | null>;\n document: PDFDocumentProxy | undefined;\n formData?: Record<string, PdfFormFieldValue>;\n onFormSubmit?: (data: Record<string, PdfFormFieldValue>) => void;\n onFormChange?: (fieldName: string, value: PdfFormFieldValue) => void;\n}\n\nexport interface UsePdfFormFieldsResult {\n /** Whether the loaded PDF contains interactive form fields. */\n hasFormFields: boolean;\n /** Collect all current form field values and invoke onFormSubmit. */\n submitFormData: () => void;\n}\n\n/**\n * Normalizes a pdf.js field object type string to a simplified form type.\n * @internal Exported for testing.\n */\nexport function normalizeFieldType(\n type: string,\n): \"text\" | \"checkbox\" | \"radiobutton\" | \"combobox\" | \"listbox\" {\n switch (type) {\n case \"checkbox\":\n case \"radiobutton\":\n case \"combobox\":\n case \"listbox\":\n return type;\n default:\n return \"text\";\n }\n}\n\n/**\n * Converts a pdf.js annotationStorage value to a consumer-friendly PdfFormFieldValue.\n * @internal Exported for testing.\n */\nexport function toFormFieldValue(\n storageValue: unknown,\n fieldType: \"text\" | \"checkbox\" | \"radiobutton\" | \"combobox\" | \"listbox\",\n): PdfFormFieldValue {\n if (fieldType === \"checkbox\") {\n if (typeof storageValue === \"boolean\") {\n return storageValue;\n }\n // pdf.js stores checkbox as the export value when checked, \"Off\" when unchecked\n return storageValue !== \"Off\" && storageValue !== false;\n }\n if (fieldType === \"radiobutton\") {\n return typeof storageValue === \"string\" ? storageValue : \"\";\n }\n if (typeof storageValue === \"string\") {\n return storageValue;\n }\n if (Array.isArray(storageValue)) {\n return storageValue.map(String);\n }\n return \"\";\n}\n\n/**\n * Converts a consumer PdfFormFieldValue to a pdf.js annotationStorage value.\n * @internal Exported for testing.\n */\nexport function toStorageValue(\n formValue: PdfFormFieldValue,\n fieldType: \"text\" | \"checkbox\" | \"radiobutton\" | \"combobox\" | \"listbox\",\n): unknown {\n if (fieldType === \"checkbox\") {\n if (typeof formValue === \"boolean\") {\n return formValue;\n }\n return formValue !== \"Off\" && formValue !== \"\";\n }\n return formValue;\n}\n\n/**\n * Hook that manages interactive PDF form fields.\n * Builds a field metadata map, pre-populates initial values,\n * listens for field changes, and collects values on submit.\n */\nexport function usePdfFormFields({\n pdfViewerRef,\n eventBusRef,\n document,\n formData,\n onFormSubmit,\n onFormChange,\n}: UsePdfFormFieldsOptions): UsePdfFormFieldsResult {\n const [hasFormFields, setHasFormFields] = useState(false);\n\n // Stable refs for callbacks\n const onFormSubmitRef = useRef(onFormSubmit);\n const onFormChangeRef = useRef(onFormChange);\n const formDataRef = useRef(formData);\n\n useEffect(function syncCallbackRefs() {\n onFormSubmitRef.current = onFormSubmit;\n onFormChangeRef.current = onFormChange;\n formDataRef.current = formData;\n }, [onFormSubmit, onFormChange, formData]);\n\n // Map from annotation ID -> field metadata\n const fieldMapRef = useRef<Map<string, FieldEntry>>(new Map());\n // Reverse map from field name -> annotation IDs\n const nameToIdsRef = useRef<Map<string, string[]>>(new Map());\n\n // Effect 1: Build field metadata map when document loads\n useEffect(function buildFieldMap() {\n const doc = document;\n if (doc == null) {\n fieldMapRef.current.clear();\n nameToIdsRef.current.clear();\n setHasFormFields(false);\n return;\n }\n\n let cancelled = false;\n\n void doc.getFieldObjects().then((fieldObjects) => {\n if (cancelled || fieldObjects == null) {\n setHasFormFields(false);\n return;\n }\n\n const fieldMap = new Map<string, FieldEntry>();\n const nameToIds = new Map<string, string[]>();\n\n for (const [fieldName, objects] of Object.entries(fieldObjects)) {\n const ids: string[] = [];\n for (const obj of objects) {\n const typedObj = obj as {\n id?: string;\n type?: string;\n exportValues?: string;\n page?: number;\n };\n if (typedObj.id == null) continue;\n\n const fieldType = normalizeFieldType(typedObj.type ?? \"text\");\n fieldMap.set(typedObj.id, {\n fieldName,\n fieldType,\n exportValues: typedObj.exportValues,\n page: typedObj.page ?? 0,\n });\n ids.push(typedObj.id);\n }\n if (ids.length > 0) {\n nameToIds.set(fieldName, ids);\n }\n }\n\n fieldMapRef.current = fieldMap;\n nameToIdsRef.current = nameToIds;\n setHasFormFields(fieldMap.size > 0);\n });\n\n return () => {\n cancelled = true;\n };\n }, [document]);\n\n // Populates form fields from formData into both annotation storage and DOM elements.\n // Scoped to the viewer container to avoid conflicts with multiple viewer instances.\n const populateFields = useCallback(() => {\n const data = formDataRef.current;\n const doc = document;\n const pdfViewer = pdfViewerRef.current;\n if (data == null || doc == null || pdfViewer == null) return;\n if (fieldMapRef.current.size === 0) return;\n\n const storage = doc.annotationStorage;\n const container = getViewerContainer(pdfViewer);\n\n for (const [fieldName, value] of Object.entries(data)) {\n const ids = nameToIdsRef.current.get(fieldName);\n if (ids == null) continue;\n\n for (const id of ids) {\n const entry = fieldMapRef.current.get(id);\n if (entry == null) continue;\n\n const storageVal = toStorageValue(\n value,\n entry.fieldType,\n );\n storage.setValue(id, { value: storageVal });\n\n // Also update the DOM element directly (scoped to viewer container)\n const el = container?.querySelector(\n `[data-element-id=\"${CSS.escape(id)}\"]`,\n );\n if (el == null) continue;\n\n if (\n entry.fieldType === \"checkbox\" && el instanceof HTMLInputElement\n ) {\n el.checked = typeof storageVal === \"boolean\"\n ? storageVal\n : storageVal !== \"Off\";\n } else if (el instanceof HTMLSelectElement) {\n el.value = String(value);\n } else if (\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement\n ) {\n el.value = String(value);\n }\n }\n }\n }, [document, pdfViewerRef]);\n\n // Effect 2: Populate initial form data when annotation layers render\n useEffect(function populateFormData() {\n const eventBus = eventBusRef.current;\n if (eventBus == null || document == null || formDataRef.current == null) {\n return;\n }\n\n // Attempt population immediately in case the field map was built\n // after the annotation layer already rendered (race condition fix).\n populateFields();\n\n const onAnnotationLayerRendered = () => {\n populateFields();\n };\n\n eventBus.on(ANNOTATION_LAYER_RENDERED_EVENT, onAnnotationLayerRendered);\n\n return () => {\n eventBus.off(ANNOTATION_LAYER_RENDERED_EVENT, onAnnotationLayerRendered);\n };\n }, [eventBusRef, document, hasFormFields, populateFields]);\n\n // Effect 3: Listen for field changes via MutationObserver + event listeners\n useEffect(function listenForFieldChanges() {\n const pdfViewer = pdfViewerRef.current;\n if (pdfViewer == null || document == null) {\n return;\n }\n\n // Get the viewer's container element\n const container = getViewerContainer(pdfViewer);\n if (container == null) return;\n\n const listeners = new Map<Element, () => void>();\n\n function handleFieldChange(\n el: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement,\n ) {\n const id = el.getAttribute(\"data-element-id\");\n if (id == null) return;\n\n const entry = fieldMapRef.current.get(id);\n if (entry == null) return;\n\n let value: PdfFormFieldValue;\n if (entry.fieldType === \"checkbox\" && el instanceof HTMLInputElement) {\n value = el.checked;\n } else if (\n entry.fieldType === \"radiobutton\" && el instanceof HTMLInputElement\n ) {\n // For radio buttons, the value is the button's export value when selected\n value = el.checked ? (entry.exportValues ?? el.value) : \"\";\n } else {\n value = el.value;\n }\n\n onFormChangeRef.current?.(entry.fieldName, value);\n }\n\n function attachListeners(root: Node) {\n const elements = (root as Element).querySelectorAll?.(\n \".annotationLayer input, .annotationLayer select, .annotationLayer textarea\",\n );\n if (elements == null) return;\n\n for (const el of elements) {\n if (listeners.has(el)) continue;\n\n const handler = () => {\n handleFieldChange(\n el as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement,\n );\n };\n el.addEventListener(\"input\", handler);\n el.addEventListener(\"change\", handler);\n listeners.set(el, handler);\n }\n }\n\n // Attach to any elements already in the DOM\n attachListeners(container);\n\n // Watch for new form elements being added (pages render lazily)\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (node instanceof Element) {\n attachListeners(node);\n }\n }\n }\n });\n\n observer.observe(container, { childList: true, subtree: true });\n\n return () => {\n observer.disconnect();\n for (const [el, handler] of listeners) {\n el.removeEventListener(\"input\", handler);\n el.removeEventListener(\"change\", handler);\n }\n listeners.clear();\n };\n }, [pdfViewerRef, document]);\n\n // Callback: collect all form data and invoke onFormSubmit\n const submitFormData = useCallback(() => {\n const doc = document;\n if (doc == null) return;\n\n const storage = doc.annotationStorage;\n const allValues = storage.getAll() as Record<string, unknown> | null;\n const result: Record<string, PdfFormFieldValue> = {};\n\n // Track which radio group has been resolved\n const resolvedRadios = new Set<string>();\n\n for (const [id, entry] of fieldMapRef.current) {\n const { fieldName, fieldType } = entry;\n\n // For radio buttons, only process each group once\n if (fieldType === \"radiobutton\") {\n if (resolvedRadios.has(fieldName)) continue;\n resolvedRadios.add(fieldName);\n\n // Find the selected radio in the group\n const groupIds = nameToIdsRef.current.get(fieldName) ?? [];\n let selectedValue = \"\";\n for (const radioId of groupIds) {\n const stored = allValues?.[radioId];\n const val =\n stored != null && typeof stored === \"object\" && \"value\" in stored\n ? (stored as { value: unknown }).value\n : stored;\n if (val === true || (typeof val === \"string\" && val !== \"Off\")) {\n const radioEntry = fieldMapRef.current.get(radioId);\n selectedValue = radioEntry?.exportValues ?? String(val);\n break;\n }\n }\n result[fieldName] = selectedValue;\n continue;\n }\n\n const stored = allValues?.[id];\n const rawValue =\n stored != null && typeof stored === \"object\" && \"value\" in stored\n ? (stored as { value: unknown }).value\n : stored;\n\n // Skip if we already have a value for this field name (e.g. duplicate IDs)\n if (fieldName in result) continue;\n\n result[fieldName] = toFormFieldValue(rawValue, fieldType);\n }\n\n onFormSubmitRef.current?.(result);\n }, [document]);\n\n return {\n hasFormFields,\n submitFormData,\n };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"usePdfFormFields.js","names":["useCallback","useEffect","useRef","useState","ANNOTATION_LAYER_RENDERED_EVENT","getViewerContainer","viewer","container","normalizeFieldType","type","toFormFieldValue","storageValue","fieldType","Array","isArray","map","String","toStorageValue","formValue","usePdfFormFields","pdfViewerRef","eventBusRef","document","formData","onFormSubmit","onFormChange","hasFormFields","setHasFormFields","onFormSubmitRef","onFormChangeRef","formDataRef","current","fieldMapRef","Map","nameToIdsRef","doc","clear","cancelled","getFieldObjects","then","fieldObjects","fieldMap","nameToIds","fieldName","objects","Object","entries","ids","obj","typedObj","id","set","exportValues","page","push","length","size","populateFields","data","pdfViewer","storage","annotationStorage","value","get","entry","el","querySelector","CSS","escape","optionValue","HTMLInputElement","undefined","isSelected","setValue","checked","storageVal","HTMLSelectElement","HTMLTextAreaElement","eventBus","onAnnotationLayerRendered","on","off","listeners","handleFieldChange","getAttribute","attachListeners","root","elements","querySelectorAll","has","handler","addEventListener","observer","MutationObserver","mutations","mutation","node","addedNodes","Element","observe","childList","subtree","disconnect","removeEventListener","submitFormData","allValues","getAll","result","resolvedRadios","Set","add","groupIds","selectedValue","radioId","stored","val","radioEntry","rawValue"],"sources":["usePdfFormFields.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PDFDocumentProxy } from \"pdfjs-dist\";\nimport type { EventBus, PDFViewer } from \"pdfjs-dist/web/pdf_viewer.mjs\";\nimport type { RefObject } from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport { ANNOTATION_LAYER_RENDERED_EVENT } from \"../constants.js\";\nimport type { PdfFormFieldValue } from \"../types.js\";\n\n/** Get the viewer's container element (typed loosely by pdf.js). */\nfunction getViewerContainer(viewer: PDFViewer): HTMLElement | undefined {\n return (viewer as { container?: HTMLElement }).container;\n}\n\n/** Internal metadata for a single form field annotation. */\ninterface FieldEntry {\n fieldName: string;\n fieldType: \"text\" | \"checkbox\" | \"radiobutton\" | \"combobox\" | \"listbox\";\n exportValues: string | undefined;\n page: number;\n}\n\nexport interface UsePdfFormFieldsOptions {\n pdfViewerRef: RefObject<PDFViewer | null>;\n eventBusRef: RefObject<EventBus | null>;\n document: PDFDocumentProxy | undefined;\n formData?: Record<string, PdfFormFieldValue>;\n onFormSubmit?: (data: Record<string, PdfFormFieldValue>) => void;\n onFormChange?: (fieldName: string, value: PdfFormFieldValue) => void;\n}\n\nexport interface UsePdfFormFieldsResult {\n /** Whether the loaded PDF contains interactive form fields. */\n hasFormFields: boolean;\n /** Collect all current form field values and invoke onFormSubmit. */\n submitFormData: () => void;\n}\n\n/**\n * Normalizes a pdf.js field object type string to a simplified form type.\n * @internal Exported for testing.\n */\nexport function normalizeFieldType(\n type: string,\n): \"text\" | \"checkbox\" | \"radiobutton\" | \"combobox\" | \"listbox\" {\n switch (type) {\n case \"checkbox\":\n case \"radiobutton\":\n case \"combobox\":\n case \"listbox\":\n return type;\n default:\n return \"text\";\n }\n}\n\n/**\n * Converts a pdf.js annotationStorage value to a consumer-friendly PdfFormFieldValue.\n * @internal Exported for testing.\n */\nexport function toFormFieldValue(\n storageValue: unknown,\n fieldType: \"text\" | \"checkbox\" | \"radiobutton\" | \"combobox\" | \"listbox\",\n): PdfFormFieldValue {\n if (fieldType === \"checkbox\") {\n if (typeof storageValue === \"boolean\") {\n return storageValue;\n }\n // pdf.js stores checkbox as the export value when checked, \"Off\" when unchecked\n return storageValue !== \"Off\" && storageValue !== false;\n }\n if (fieldType === \"radiobutton\") {\n return typeof storageValue === \"string\" ? storageValue : \"\";\n }\n if (typeof storageValue === \"string\") {\n return storageValue;\n }\n if (Array.isArray(storageValue)) {\n return storageValue.map(String);\n }\n return \"\";\n}\n\n/**\n * Converts a consumer PdfFormFieldValue to a pdf.js annotationStorage value.\n * @internal Exported for testing.\n */\nexport function toStorageValue(\n formValue: PdfFormFieldValue,\n fieldType: \"text\" | \"checkbox\" | \"radiobutton\" | \"combobox\" | \"listbox\",\n): unknown {\n if (fieldType === \"checkbox\") {\n if (typeof formValue === \"boolean\") {\n return formValue;\n }\n return formValue !== \"Off\" && formValue !== \"\";\n }\n return formValue;\n}\n\n/**\n * Hook that manages interactive PDF form fields.\n * Builds a field metadata map, pre-populates initial values,\n * listens for field changes, and collects values on submit.\n */\nexport function usePdfFormFields({\n pdfViewerRef,\n eventBusRef,\n document,\n formData,\n onFormSubmit,\n onFormChange,\n}: UsePdfFormFieldsOptions): UsePdfFormFieldsResult {\n const [hasFormFields, setHasFormFields] = useState(false);\n\n // Stable refs for callbacks\n const onFormSubmitRef = useRef(onFormSubmit);\n const onFormChangeRef = useRef(onFormChange);\n const formDataRef = useRef(formData);\n\n useEffect(function syncCallbackRefs() {\n onFormSubmitRef.current = onFormSubmit;\n onFormChangeRef.current = onFormChange;\n formDataRef.current = formData;\n }, [onFormSubmit, onFormChange, formData]);\n\n // Map from annotation ID -> field metadata\n const fieldMapRef = useRef<Map<string, FieldEntry>>(new Map());\n // Reverse map from field name -> annotation IDs\n const nameToIdsRef = useRef<Map<string, string[]>>(new Map());\n\n // Effect 1: Build field metadata map when document loads\n useEffect(function buildFieldMap() {\n const doc = document;\n if (doc == null) {\n fieldMapRef.current.clear();\n nameToIdsRef.current.clear();\n setHasFormFields(false);\n return;\n }\n\n let cancelled = false;\n\n void doc.getFieldObjects().then((fieldObjects) => {\n if (cancelled || fieldObjects == null) {\n setHasFormFields(false);\n return;\n }\n\n const fieldMap = new Map<string, FieldEntry>();\n const nameToIds = new Map<string, string[]>();\n\n for (const [fieldName, objects] of Object.entries(fieldObjects)) {\n const ids: string[] = [];\n for (const obj of objects) {\n const typedObj = obj as {\n id?: string;\n type?: string;\n exportValues?: string;\n page?: number;\n };\n if (typedObj.id == null) continue;\n\n const fieldType = normalizeFieldType(typedObj.type ?? \"text\");\n fieldMap.set(typedObj.id, {\n fieldName,\n fieldType,\n exportValues: typedObj.exportValues,\n page: typedObj.page ?? 0,\n });\n ids.push(typedObj.id);\n }\n if (ids.length > 0) {\n nameToIds.set(fieldName, ids);\n }\n }\n\n fieldMapRef.current = fieldMap;\n nameToIdsRef.current = nameToIds;\n setHasFormFields(fieldMap.size > 0);\n });\n\n return () => {\n cancelled = true;\n };\n }, [document]);\n\n // Populates form fields from formData into both annotation storage and DOM elements.\n // Scoped to the viewer container to avoid conflicts with multiple viewer instances.\n const populateFields = useCallback(() => {\n const data = formDataRef.current;\n const doc = document;\n const pdfViewer = pdfViewerRef.current;\n if (data == null || doc == null || pdfViewer == null) return;\n if (fieldMapRef.current.size === 0) return;\n\n const storage = doc.annotationStorage;\n const container = getViewerContainer(pdfViewer);\n\n for (const [fieldName, value] of Object.entries(data)) {\n const ids = nameToIdsRef.current.get(fieldName);\n if (ids == null) continue;\n\n for (const id of ids) {\n const entry = fieldMapRef.current.get(id);\n if (entry == null) continue;\n\n // Also update the DOM element directly (scoped to viewer container)\n const el = container?.querySelector(\n `[data-element-id=\"${CSS.escape(id)}\"]`,\n );\n\n if (entry.fieldType === \"radiobutton\") {\n // Radio buttons: each ID is a separate option. Check the one\n // whose export value matches, uncheck the others.\n // Use exportValues if defined, otherwise fall back to the DOM\n // element's value attribute (mirrors handleFieldChange logic).\n const optionValue = entry.exportValues\n ?? (el instanceof HTMLInputElement ? el.value : undefined);\n const isSelected = optionValue === String(value);\n storage.setValue(id, {\n value: isSelected ? (entry.exportValues ?? String(value)) : \"Off\",\n });\n if (el instanceof HTMLInputElement) {\n el.checked = isSelected;\n }\n } else {\n const storageVal = toStorageValue(value, entry.fieldType);\n storage.setValue(id, { value: storageVal });\n\n if (el == null) continue;\n\n if (\n entry.fieldType === \"checkbox\" && el instanceof HTMLInputElement\n ) {\n el.checked = typeof storageVal === \"boolean\"\n ? storageVal\n : storageVal !== \"Off\";\n } else if (el instanceof HTMLSelectElement) {\n el.value = String(value);\n } else if (\n el instanceof HTMLInputElement || el instanceof HTMLTextAreaElement\n ) {\n el.value = String(value);\n }\n }\n }\n }\n }, [document, pdfViewerRef]);\n\n // Effect 2: Populate initial form data when annotation layers render\n useEffect(function populateFormData() {\n const eventBus = eventBusRef.current;\n if (eventBus == null || document == null || formDataRef.current == null) {\n return;\n }\n\n // Attempt population immediately in case the field map was built\n // after the annotation layer already rendered (race condition fix).\n populateFields();\n\n const onAnnotationLayerRendered = () => {\n populateFields();\n };\n\n eventBus.on(ANNOTATION_LAYER_RENDERED_EVENT, onAnnotationLayerRendered);\n\n return () => {\n eventBus.off(ANNOTATION_LAYER_RENDERED_EVENT, onAnnotationLayerRendered);\n };\n }, [eventBusRef, document, hasFormFields, populateFields]);\n\n // Effect 3: Listen for field changes via MutationObserver + event listeners\n useEffect(function listenForFieldChanges() {\n const pdfViewer = pdfViewerRef.current;\n if (pdfViewer == null || document == null) {\n return;\n }\n\n // Get the viewer's container element\n const container = getViewerContainer(pdfViewer);\n if (container == null) return;\n\n const listeners = new Map<Element, () => void>();\n\n function handleFieldChange(\n el: HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement,\n ) {\n const id = el.getAttribute(\"data-element-id\");\n if (id == null) return;\n\n const entry = fieldMapRef.current.get(id);\n if (entry == null) return;\n\n let value: PdfFormFieldValue;\n if (entry.fieldType === \"checkbox\" && el instanceof HTMLInputElement) {\n value = el.checked;\n } else if (\n entry.fieldType === \"radiobutton\" && el instanceof HTMLInputElement\n ) {\n // For radio buttons, the value is the button's export value when selected\n value = el.checked ? (entry.exportValues ?? el.value) : \"\";\n } else {\n value = el.value;\n }\n\n onFormChangeRef.current?.(entry.fieldName, value);\n }\n\n function attachListeners(root: Node) {\n const elements = (root as Element).querySelectorAll?.(\n \".annotationLayer input, .annotationLayer select, .annotationLayer textarea\",\n );\n if (elements == null) return;\n\n for (const el of elements) {\n if (listeners.has(el)) continue;\n\n const handler = () => {\n handleFieldChange(\n el as HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement,\n );\n };\n el.addEventListener(\"input\", handler);\n el.addEventListener(\"change\", handler);\n listeners.set(el, handler);\n }\n }\n\n // Attach to any elements already in the DOM\n attachListeners(container);\n\n // Watch for new form elements being added (pages render lazily)\n const observer = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n for (const node of mutation.addedNodes) {\n if (node instanceof Element) {\n attachListeners(node);\n }\n }\n }\n });\n\n observer.observe(container, { childList: true, subtree: true });\n\n return () => {\n observer.disconnect();\n for (const [el, handler] of listeners) {\n el.removeEventListener(\"input\", handler);\n el.removeEventListener(\"change\", handler);\n }\n listeners.clear();\n };\n }, [pdfViewerRef, document]);\n\n // Callback: collect all form data and invoke onFormSubmit\n const submitFormData = useCallback(() => {\n const doc = document;\n if (doc == null) return;\n\n const storage = doc.annotationStorage;\n const allValues = storage.getAll() as Record<string, unknown> | null;\n const result: Record<string, PdfFormFieldValue> = {};\n\n // Track which radio group has been resolved\n const resolvedRadios = new Set<string>();\n\n for (const [id, entry] of fieldMapRef.current) {\n const { fieldName, fieldType } = entry;\n\n // For radio buttons, only process each group once\n if (fieldType === \"radiobutton\") {\n if (resolvedRadios.has(fieldName)) continue;\n resolvedRadios.add(fieldName);\n\n // Find the selected radio in the group\n const groupIds = nameToIdsRef.current.get(fieldName) ?? [];\n let selectedValue = \"\";\n for (const radioId of groupIds) {\n const stored = allValues?.[radioId];\n const val =\n stored != null && typeof stored === \"object\" && \"value\" in stored\n ? (stored as { value: unknown }).value\n : stored;\n if (val === true || (typeof val === \"string\" && val !== \"Off\")) {\n const radioEntry = fieldMapRef.current.get(radioId);\n selectedValue = radioEntry?.exportValues ?? String(val);\n break;\n }\n }\n result[fieldName] = selectedValue;\n continue;\n }\n\n const stored = allValues?.[id];\n const rawValue =\n stored != null && typeof stored === \"object\" && \"value\" in stored\n ? (stored as { value: unknown }).value\n : stored;\n\n // Skip if we already have a value for this field name (e.g. duplicate IDs)\n if (fieldName in result) continue;\n\n result[fieldName] = toFormFieldValue(rawValue, fieldType);\n }\n\n onFormSubmitRef.current?.(result);\n }, [document]);\n\n return {\n hasFormFields,\n submitFormData,\n };\n}\n"],"mappings":"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","ignoreList":[]}
@@ -171,6 +171,7 @@ export function usePdfHighlightMode({
171
171
  // Extract text from the editor's aria-label (stores selected text)
172
172
  const selectedText = typeof entry.div?.getAttribute === "function" ? entry.div.getAttribute("aria-label") ?? "" : "";
173
173
  const event = {
174
+ editorId: id,
174
175
  page: serialized.pageIndex + 1,
175
176
  rects,
176
177
  selectedText,
@@ -191,9 +192,36 @@ export function usePdfHighlightMode({
191
192
  const toggleHighlightMode = useCallback(() => {
192
193
  setHighlightModeActive(prev => !prev);
193
194
  }, []);
195
+ const deleteHighlight = useCallback(editorId => {
196
+ if (document == null) return;
197
+ const storage = document.annotationStorage;
198
+
199
+ // Fire the delete callback directly. The monkey-patch on storage.remove
200
+ // only exists while highlight mode is active, so we handle it here to
201
+ // ensure the callback fires regardless of highlight mode state.
202
+ // Clean up refs first so the monkey-patch (if active) won't double-fire.
203
+ const savedEvent = editorEventsRef.current.get(editorId);
204
+ if (savedEvent != null) {
205
+ editorEventsRef.current.delete(editorId);
206
+ knownEditorIdsRef.current.delete(editorId);
207
+ onHighlightDeleteRef.current?.(savedEvent);
208
+ }
209
+
210
+ // Remove the editor's DOM element if it exists
211
+ const allEntries = storage.getAll();
212
+ const entry = allEntries?.[editorId];
213
+ if (hasDiv(entry) && entry.div instanceof HTMLElement) {
214
+ entry.div.remove();
215
+ }
216
+ storage.remove(editorId);
217
+ }, [document]);
194
218
  return {
195
219
  highlightModeActive,
196
- toggleHighlightMode
220
+ toggleHighlightMode,
221
+ deleteHighlight
197
222
  };
198
223
  }
224
+ function hasDiv(obj) {
225
+ return obj != null && typeof obj === "object" && "div" in obj;
226
+ }
199
227
  //# sourceMappingURL=usePdfHighlightMode.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"usePdfHighlightMode.js","names":["AnnotationEditorType","useCallback","useEffect","useRef","useState","rgbArrayToHex","color","r","Math","round","g","b","toString","padStart","quadPointsToRects","quadPoints","rects","i","length","x1","y1","x2","y3","x","min","y","width","abs","height","push","usePdfHighlightMode","pdfViewerRef","document","enabled","onTextHighlight","onHighlightDelete","highlightModeActive","setHighlightModeActive","onTextHighlightRef","onHighlightDeleteRef","knownEditorIdsRef","Set","editorEventsRef","Map","current","pdfViewer","annotationEditorMode","mode","HIGHLIGHT","NONE","prevDocumentRef","undefined","clear","storage","annotationStorage","previousOnAnnotationEditor","onAnnotationEditor","originalRemove","remove","bind","key","savedEvent","get","delete","type","allEntries","getAll","id","entry","Object","entries","has","serialize","add","serialized","annotationType","Array","isArray","selectedText","div","getAttribute","event","page","pageIndex","set","toggleHighlightMode","prev"],"sources":["usePdfHighlightMode.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AnnotationEditorType } from \"pdfjs-dist\";\nimport type { PDFDocumentProxy } from \"pdfjs-dist\";\nimport type { PDFViewer } from \"pdfjs-dist/web/pdf_viewer.mjs\";\nimport type { RefObject } from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { PdfRect, PdfTextHighlightEvent } from \"../types.js\";\n\nexport interface UsePdfHighlightModeOptions {\n pdfViewerRef: RefObject<PDFViewer | null>;\n document: PDFDocumentProxy | undefined;\n enabled: boolean;\n onTextHighlight?: (event: PdfTextHighlightEvent) => void;\n onHighlightDelete?: (event: PdfTextHighlightEvent) => void;\n}\n\nexport interface UsePdfHighlightModeResult {\n highlightModeActive: boolean;\n toggleHighlightMode: () => void;\n}\n\n/**\n * Converts an RGB array (0.0–1.0 per channel) to a CSS hex color string.\n */\nexport function rgbArrayToHex(color: number[]): string {\n const r = Math.round(color[0] * 255);\n const g = Math.round(color[1] * 255);\n const b = Math.round(color[2] * 255);\n return `#${r.toString(16).padStart(2, \"0\")}${\n g.toString(16).padStart(2, \"0\")\n }${b.toString(16).padStart(2, \"0\")}`;\n}\n\n/**\n * Converts quadPoints (PDF spec format) back to PdfRect[].\n * QuadPoints are 8 values per quad: [x1,y1, x2,y2, x3,y3, x4,y4]\n * representing the four corners of each highlight rect.\n *\n * PDF.js highlight editor produces axis-aligned quads with ordering:\n * (x1,y1)=top-left, (x2,y2)=top-right, (x3,y3)=bottom-left, (x4,y4)=bottom-right\n * We derive the bounding rect from x1, y1 (top), x2 (right edge), y3 (bottom).\n */\nexport function quadPointsToRects(\n quadPoints: Float32Array,\n): PdfRect[] {\n const rects: PdfRect[] = [];\n for (let i = 0; i < quadPoints.length; i += 8) {\n const x1 = quadPoints[i];\n const y1 = quadPoints[i + 1];\n const x2 = quadPoints[i + 2];\n const y3 = quadPoints[i + 5];\n\n const x = Math.min(x1, x2);\n const y = Math.min(y1, y3);\n const width = Math.abs(x2 - x1);\n const height = Math.abs(y1 - y3);\n\n rects.push({ x, y, width, height });\n }\n return rects;\n}\n\n/**\n * Hook that manages the PDF.js built-in highlight annotation editor mode.\n * When active, users can select text to create highlights.\n * A callback fires with the annotation details for persistence.\n */\nexport function usePdfHighlightMode({\n pdfViewerRef,\n document,\n enabled,\n onTextHighlight,\n onHighlightDelete,\n}: UsePdfHighlightModeOptions): UsePdfHighlightModeResult {\n const [highlightModeActive, setHighlightModeActive] = useState(false);\n const onTextHighlightRef = useRef(onTextHighlight);\n const onHighlightDeleteRef = useRef(onHighlightDelete);\n const knownEditorIdsRef = useRef<Set<string>>(new Set());\n const editorEventsRef = useRef<Map<string, PdfTextHighlightEvent>>(new Map());\n\n // Keep callback refs in sync\n useEffect(function syncCallbackRef() {\n onTextHighlightRef.current = onTextHighlight;\n onHighlightDeleteRef.current = onHighlightDelete;\n }, [onTextHighlight, onHighlightDelete]);\n\n // Toggle the PDF.js annotation editor mode\n useEffect(function syncHighlightMode() {\n const pdfViewer = pdfViewerRef.current;\n if (pdfViewer == null || document == null) {\n return;\n }\n\n try {\n if (highlightModeActive && enabled) {\n pdfViewer.annotationEditorMode = {\n mode: AnnotationEditorType.HIGHLIGHT,\n };\n } else {\n pdfViewer.annotationEditorMode = {\n mode: AnnotationEditorType.NONE,\n };\n }\n } catch {\n // AnnotationEditorUIManager may not be initialized yet\n }\n }, [pdfViewerRef, document, highlightModeActive, enabled]);\n\n // Deactivate highlight mode when the feature is disabled\n useEffect(function deactivateOnDisable() {\n if (!enabled) {\n setHighlightModeActive(false);\n }\n }, [enabled]);\n\n // Clear tracked editors when the document changes (but not on highlight toggle)\n const prevDocumentRef = useRef<PDFDocumentProxy | undefined>(undefined);\n useEffect(function clearOnDocumentChange() {\n if (document !== prevDocumentRef.current) {\n knownEditorIdsRef.current.clear();\n editorEventsRef.current.clear();\n prevDocumentRef.current = document;\n }\n }, [document]);\n\n // Listen for new highlights being added to annotation storage\n useEffect(function listenForHighlights() {\n if (document == null || !highlightModeActive || !enabled) {\n return;\n }\n\n const storage = document.annotationStorage;\n const previousOnAnnotationEditor = storage.onAnnotationEditor;\n\n // Monkey-patch storage.remove to detect highlight deletions.\n // This is fragile but necessary because PDF.js does not emit events\n // when editors are removed. Restore the original on cleanup.\n const originalRemove = storage.remove.bind(storage);\n storage.remove = (key: string) => {\n const savedEvent = editorEventsRef.current.get(key);\n if (savedEvent != null) {\n editorEventsRef.current.delete(key);\n knownEditorIdsRef.current.delete(key);\n onHighlightDeleteRef.current?.(savedEvent);\n }\n originalRemove(key);\n };\n\n storage.onAnnotationEditor = (type: string | number | null) => {\n // onAnnotationEditor fires with the editor's static _type (string \"highlight\")\n if (type !== \"highlight\") {\n return;\n }\n\n // Find new editors in storage\n const allEntries = storage.getAll();\n if (allEntries == null) {\n return;\n }\n\n for (const [id, entry] of Object.entries(allEntries)) {\n if (knownEditorIdsRef.current.has(id)) {\n continue;\n }\n\n // Check if this is a highlight editor with serializable data\n if (\n typeof entry === \"object\" && entry != null\n && \"serialize\" in entry\n && typeof entry.serialize === \"function\"\n ) {\n knownEditorIdsRef.current.add(id);\n const serialized = entry.serialize(false);\n if (\n serialized == null\n || serialized.annotationType !== AnnotationEditorType.HIGHLIGHT\n ) {\n continue;\n }\n\n const rects = serialized.quadPoints != null\n ? quadPointsToRects(serialized.quadPoints)\n : [];\n\n if (rects.length === 0) {\n continue;\n }\n\n const color = Array.isArray(serialized.color)\n ? rgbArrayToHex(serialized.color)\n : \"#fff066\";\n\n // Extract text from the editor's aria-label (stores selected text)\n const selectedText = typeof entry.div?.getAttribute === \"function\"\n ? entry.div.getAttribute(\"aria-label\") ?? \"\"\n : \"\";\n\n const event: PdfTextHighlightEvent = {\n page: serialized.pageIndex + 1,\n rects,\n selectedText,\n color,\n };\n\n // Store the event so we can provide it back on delete\n editorEventsRef.current.set(id, event);\n\n onTextHighlightRef.current?.(event);\n }\n }\n };\n\n return () => {\n storage.onAnnotationEditor = previousOnAnnotationEditor;\n storage.remove = originalRemove;\n };\n }, [document, highlightModeActive, enabled]);\n\n const toggleHighlightMode = useCallback(() => {\n setHighlightModeActive((prev) => !prev);\n }, []);\n\n return {\n highlightModeActive,\n toggleHighlightMode,\n };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"usePdfHighlightMode.js","names":["AnnotationEditorType","useCallback","useEffect","useRef","useState","rgbArrayToHex","color","r","Math","round","g","b","toString","padStart","quadPointsToRects","quadPoints","rects","i","length","x1","y1","x2","y3","x","min","y","width","abs","height","push","usePdfHighlightMode","pdfViewerRef","document","enabled","onTextHighlight","onHighlightDelete","highlightModeActive","setHighlightModeActive","onTextHighlightRef","onHighlightDeleteRef","knownEditorIdsRef","Set","editorEventsRef","Map","current","pdfViewer","annotationEditorMode","mode","HIGHLIGHT","NONE","prevDocumentRef","undefined","clear","storage","annotationStorage","previousOnAnnotationEditor","onAnnotationEditor","originalRemove","remove","bind","key","savedEvent","get","delete","type","allEntries","getAll","id","entry","Object","entries","has","serialize","add","serialized","annotationType","Array","isArray","selectedText","div","getAttribute","event","editorId","page","pageIndex","set","toggleHighlightMode","prev","deleteHighlight","hasDiv","HTMLElement","obj"],"sources":["usePdfHighlightMode.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport { AnnotationEditorType } from \"pdfjs-dist\";\nimport type { PDFDocumentProxy } from \"pdfjs-dist\";\nimport type { PDFViewer } from \"pdfjs-dist/web/pdf_viewer.mjs\";\nimport type { RefObject } from \"react\";\nimport { useCallback, useEffect, useRef, useState } from \"react\";\nimport type { PdfRect, PdfTextHighlightEvent } from \"../types.js\";\n\nexport interface UsePdfHighlightModeOptions {\n pdfViewerRef: RefObject<PDFViewer | null>;\n document: PDFDocumentProxy | undefined;\n enabled: boolean;\n onTextHighlight?: (event: PdfTextHighlightEvent) => void;\n onHighlightDelete?: (event: PdfTextHighlightEvent) => void;\n}\n\nexport interface UsePdfHighlightModeResult {\n highlightModeActive: boolean;\n toggleHighlightMode: () => void;\n deleteHighlight: (editorId: string) => void;\n}\n\n/**\n * Converts an RGB array (0.0–1.0 per channel) to a CSS hex color string.\n */\nexport function rgbArrayToHex(color: number[]): string {\n const r = Math.round(color[0] * 255);\n const g = Math.round(color[1] * 255);\n const b = Math.round(color[2] * 255);\n return `#${r.toString(16).padStart(2, \"0\")}${\n g.toString(16).padStart(2, \"0\")\n }${b.toString(16).padStart(2, \"0\")}`;\n}\n\n/**\n * Converts quadPoints (PDF spec format) back to PdfRect[].\n * QuadPoints are 8 values per quad: [x1,y1, x2,y2, x3,y3, x4,y4]\n * representing the four corners of each highlight rect.\n *\n * PDF.js highlight editor produces axis-aligned quads with ordering:\n * (x1,y1)=top-left, (x2,y2)=top-right, (x3,y3)=bottom-left, (x4,y4)=bottom-right\n * We derive the bounding rect from x1, y1 (top), x2 (right edge), y3 (bottom).\n */\nexport function quadPointsToRects(\n quadPoints: Float32Array,\n): PdfRect[] {\n const rects: PdfRect[] = [];\n for (let i = 0; i < quadPoints.length; i += 8) {\n const x1 = quadPoints[i];\n const y1 = quadPoints[i + 1];\n const x2 = quadPoints[i + 2];\n const y3 = quadPoints[i + 5];\n\n const x = Math.min(x1, x2);\n const y = Math.min(y1, y3);\n const width = Math.abs(x2 - x1);\n const height = Math.abs(y1 - y3);\n\n rects.push({ x, y, width, height });\n }\n return rects;\n}\n\n/**\n * Hook that manages the PDF.js built-in highlight annotation editor mode.\n * When active, users can select text to create highlights.\n * A callback fires with the annotation details for persistence.\n */\nexport function usePdfHighlightMode({\n pdfViewerRef,\n document,\n enabled,\n onTextHighlight,\n onHighlightDelete,\n}: UsePdfHighlightModeOptions): UsePdfHighlightModeResult {\n const [highlightModeActive, setHighlightModeActive] = useState(false);\n const onTextHighlightRef = useRef(onTextHighlight);\n const onHighlightDeleteRef = useRef(onHighlightDelete);\n const knownEditorIdsRef = useRef<Set<string>>(new Set());\n const editorEventsRef = useRef<Map<string, PdfTextHighlightEvent>>(new Map());\n\n // Keep callback refs in sync\n useEffect(function syncCallbackRef() {\n onTextHighlightRef.current = onTextHighlight;\n onHighlightDeleteRef.current = onHighlightDelete;\n }, [onTextHighlight, onHighlightDelete]);\n\n // Toggle the PDF.js annotation editor mode\n useEffect(function syncHighlightMode() {\n const pdfViewer = pdfViewerRef.current;\n if (pdfViewer == null || document == null) {\n return;\n }\n\n try {\n if (highlightModeActive && enabled) {\n pdfViewer.annotationEditorMode = {\n mode: AnnotationEditorType.HIGHLIGHT,\n };\n } else {\n pdfViewer.annotationEditorMode = {\n mode: AnnotationEditorType.NONE,\n };\n }\n } catch {\n // AnnotationEditorUIManager may not be initialized yet\n }\n }, [pdfViewerRef, document, highlightModeActive, enabled]);\n\n // Deactivate highlight mode when the feature is disabled\n useEffect(function deactivateOnDisable() {\n if (!enabled) {\n setHighlightModeActive(false);\n }\n }, [enabled]);\n\n // Clear tracked editors when the document changes (but not on highlight toggle)\n const prevDocumentRef = useRef<PDFDocumentProxy | undefined>(undefined);\n useEffect(function clearOnDocumentChange() {\n if (document !== prevDocumentRef.current) {\n knownEditorIdsRef.current.clear();\n editorEventsRef.current.clear();\n prevDocumentRef.current = document;\n }\n }, [document]);\n\n // Listen for new highlights being added to annotation storage\n useEffect(function listenForHighlights() {\n if (document == null || !highlightModeActive || !enabled) {\n return;\n }\n\n const storage = document.annotationStorage;\n const previousOnAnnotationEditor = storage.onAnnotationEditor;\n\n // Monkey-patch storage.remove to detect highlight deletions.\n // This is fragile but necessary because PDF.js does not emit events\n // when editors are removed. Restore the original on cleanup.\n const originalRemove = storage.remove.bind(storage);\n storage.remove = (key: string) => {\n const savedEvent = editorEventsRef.current.get(key);\n if (savedEvent != null) {\n editorEventsRef.current.delete(key);\n knownEditorIdsRef.current.delete(key);\n onHighlightDeleteRef.current?.(savedEvent);\n }\n originalRemove(key);\n };\n\n storage.onAnnotationEditor = (type: string | number | null) => {\n // onAnnotationEditor fires with the editor's static _type (string \"highlight\")\n if (type !== \"highlight\") {\n return;\n }\n\n // Find new editors in storage\n const allEntries = storage.getAll();\n if (allEntries == null) {\n return;\n }\n\n for (const [id, entry] of Object.entries(allEntries)) {\n if (knownEditorIdsRef.current.has(id)) {\n continue;\n }\n\n // Check if this is a highlight editor with serializable data\n if (\n typeof entry === \"object\" && entry != null\n && \"serialize\" in entry\n && typeof entry.serialize === \"function\"\n ) {\n knownEditorIdsRef.current.add(id);\n const serialized = entry.serialize(false);\n if (\n serialized == null\n || serialized.annotationType !== AnnotationEditorType.HIGHLIGHT\n ) {\n continue;\n }\n\n const rects = serialized.quadPoints != null\n ? quadPointsToRects(serialized.quadPoints)\n : [];\n\n if (rects.length === 0) {\n continue;\n }\n\n const color = Array.isArray(serialized.color)\n ? rgbArrayToHex(serialized.color)\n : \"#fff066\";\n\n // Extract text from the editor's aria-label (stores selected text)\n const selectedText = typeof entry.div?.getAttribute === \"function\"\n ? entry.div.getAttribute(\"aria-label\") ?? \"\"\n : \"\";\n\n const event: PdfTextHighlightEvent = {\n editorId: id,\n page: serialized.pageIndex + 1,\n rects,\n selectedText,\n color,\n };\n\n // Store the event so we can provide it back on delete\n editorEventsRef.current.set(id, event);\n\n onTextHighlightRef.current?.(event);\n }\n }\n };\n\n return () => {\n storage.onAnnotationEditor = previousOnAnnotationEditor;\n storage.remove = originalRemove;\n };\n }, [document, highlightModeActive, enabled]);\n\n const toggleHighlightMode = useCallback(() => {\n setHighlightModeActive((prev) => !prev);\n }, []);\n\n const deleteHighlight = useCallback((editorId: string) => {\n if (document == null) return;\n const storage = document.annotationStorage;\n\n // Fire the delete callback directly. The monkey-patch on storage.remove\n // only exists while highlight mode is active, so we handle it here to\n // ensure the callback fires regardless of highlight mode state.\n // Clean up refs first so the monkey-patch (if active) won't double-fire.\n const savedEvent = editorEventsRef.current.get(editorId);\n if (savedEvent != null) {\n editorEventsRef.current.delete(editorId);\n knownEditorIdsRef.current.delete(editorId);\n onHighlightDeleteRef.current?.(savedEvent);\n }\n\n // Remove the editor's DOM element if it exists\n const allEntries = storage.getAll() as Record<string, unknown> | null;\n const entry = allEntries?.[editorId];\n if (hasDiv(entry) && entry.div instanceof HTMLElement) {\n entry.div.remove();\n }\n\n storage.remove(editorId);\n }, [document]);\n\n return {\n highlightModeActive,\n toggleHighlightMode,\n deleteHighlight,\n };\n}\n\nfunction hasDiv(obj: unknown): obj is { div: unknown } {\n return obj != null && typeof obj === \"object\" && \"div\" in obj;\n}\n"],"mappings":"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","ignoreList":[]}
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { useCallback, useRef, useState } from "react";
17
+ import { useCallback, useMemo, useRef, useState } from "react";
18
18
  import { usePdfAnnotationPortals } from "./usePdfAnnotationPortals.js";
19
19
  import { usePdfDocument } from "./usePdfDocument.js";
20
20
  import { usePdfViewer } from "./usePdfViewer.js";
@@ -60,7 +60,7 @@ export function usePdfViewerCore({
60
60
  syncScrollToPage(page);
61
61
  }, [syncScrollToPage]);
62
62
  const portalTargets = usePdfAnnotationPortals(pdfViewerRef, eventBusRef, document);
63
- return {
63
+ return useMemo(() => ({
64
64
  document,
65
65
  numPages,
66
66
  loading,
@@ -75,6 +75,6 @@ export function usePdfViewerCore({
75
75
  pdfViewerRef,
76
76
  eventBusRef,
77
77
  findControllerRef
78
- };
78
+ }), [document, numPages, loading, error, currentPage, scrollToPage, scale, setScale, portalTargets]);
79
79
  }
80
80
  //# sourceMappingURL=usePdfViewerCore.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"usePdfViewerCore.js","names":["useCallback","useRef","useState","usePdfAnnotationPortals","usePdfDocument","usePdfViewer","usePdfViewerSync","usePdfViewerCore","src","initialPage","initialScale","document","numPages","loading","error","scale","setScale","currentPage","setCurrentPage","containerRef","viewerRef","pdfViewerRef","eventBusRef","findControllerRef","handleScaleChange","newScale","handlePageChange","page","scrollToPage","syncScrollToPage","onScaleChange","onPageChange","portalTargets"],"sources":["usePdfViewerCore.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PDFDocumentProxy } from \"pdfjs-dist\";\nimport type {\n EventBus,\n PDFFindController,\n PDFViewer,\n} from \"pdfjs-dist/web/pdf_viewer.mjs\";\nimport type { RefObject } from \"react\";\nimport { useCallback, useRef, useState } from \"react\";\nimport type { AnnotationPortalTarget } from \"./usePdfAnnotationPortals.js\";\nimport { usePdfAnnotationPortals } from \"./usePdfAnnotationPortals.js\";\nimport { usePdfDocument } from \"./usePdfDocument.js\";\nimport { usePdfViewer } from \"./usePdfViewer.js\";\nimport { usePdfViewerSync } from \"./usePdfViewerSync.js\";\n\nexport interface UsePdfViewerCoreOptions {\n /** PDF source — URL string or ArrayBuffer */\n src: string | ArrayBuffer;\n /** Initial page number (1-indexed, default 1) */\n initialPage?: number;\n /** Initial zoom scale (default 1.0) */\n initialScale?: number;\n}\n\nexport interface UsePdfViewerCoreResult {\n /** The loaded PDF document, or undefined while loading */\n document: PDFDocumentProxy | undefined;\n /** Total number of pages in the document */\n numPages: number;\n /** Whether the document is currently loading */\n loading: boolean;\n /** Error encountered while loading the document */\n error: Error | undefined;\n\n /** Ref to attach to the scroll-container div */\n containerRef: RefObject<HTMLDivElement>;\n /** Ref to attach to the inner viewer div (className=\"pdfViewer\") */\n viewerRef: RefObject<HTMLDivElement>;\n\n /** Current page number (1-indexed) */\n currentPage: number;\n /** Navigate to a specific page (updates state and scrolls the viewer) */\n scrollToPage: (page: number) => void;\n\n /** Current zoom scale */\n scale: number;\n /** Set the zoom scale directly */\n setScale: (scale: number) => void;\n\n /** Portal targets for rendering annotation overlays on each page */\n portalTargets: AnnotationPortalTarget[];\n\n /** Ref to the internal pdfjs PDFViewer instance */\n pdfViewerRef: RefObject<PDFViewer | null>;\n /** Ref to the internal pdfjs EventBus instance */\n eventBusRef: RefObject<EventBus | null>;\n /** Ref to the internal pdfjs PDFFindController instance */\n findControllerRef: RefObject<PDFFindController | null>;\n}\n\nexport function usePdfViewerCore({\n src,\n initialPage = 1,\n initialScale = 1.0,\n}: UsePdfViewerCoreOptions): UsePdfViewerCoreResult {\n const { document, numPages, loading, error } = usePdfDocument(src);\n const [scale, setScale] = useState(initialScale);\n const [currentPage, setCurrentPage] = useState(initialPage);\n const containerRef = useRef<HTMLDivElement>(null);\n const viewerRef = useRef<HTMLDivElement>(null);\n\n const { pdfViewerRef, eventBusRef, findControllerRef } = usePdfViewer(\n containerRef,\n viewerRef,\n document,\n initialScale,\n initialPage,\n );\n\n const handleScaleChange = useCallback((newScale: number) => {\n setScale(newScale);\n }, []);\n\n const handlePageChange = useCallback((page: number) => {\n setCurrentPage(page);\n }, []);\n\n const { scrollToPage: syncScrollToPage } = usePdfViewerSync({\n pdfViewerRef,\n eventBusRef,\n document,\n scale,\n onScaleChange: handleScaleChange,\n onPageChange: handlePageChange,\n });\n\n const scrollToPage = useCallback(\n (page: number) => {\n setCurrentPage(page);\n syncScrollToPage(page);\n },\n [syncScrollToPage],\n );\n\n const portalTargets = usePdfAnnotationPortals(\n pdfViewerRef,\n eventBusRef,\n document,\n );\n\n return {\n document,\n numPages,\n loading,\n error,\n containerRef,\n viewerRef,\n currentPage,\n scrollToPage,\n scale,\n setScale,\n portalTargets,\n pdfViewerRef,\n eventBusRef,\n findControllerRef,\n };\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AASA,SAASA,WAAW,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,OAAO;AAErD,SAASC,uBAAuB,QAAQ,8BAA8B;AACtE,SAASC,cAAc,QAAQ,qBAAqB;AACpD,SAASC,YAAY,QAAQ,mBAAmB;AAChD,SAASC,gBAAgB,QAAQ,uBAAuB;AA+CxD,OAAO,SAASC,gBAAgBA,CAAC;EAC/BC,GAAG;EACHC,WAAW,GAAG,CAAC;EACfC,YAAY,GAAG;AACQ,CAAC,EAA0B;EAClD,MAAM;IAAEC,QAAQ;IAAEC,QAAQ;IAAEC,OAAO;IAAEC;EAAM,CAAC,GAAGV,cAAc,CAACI,GAAG,CAAC;EAClE,MAAM,CAACO,KAAK,EAAEC,QAAQ,CAAC,GAAGd,QAAQ,CAACQ,YAAY,CAAC;EAChD,MAAM,CAACO,WAAW,EAAEC,cAAc,CAAC,GAAGhB,QAAQ,CAACO,WAAW,CAAC;EAC3D,MAAMU,YAAY,GAAGlB,MAAM,CAAiB,IAAI,CAAC;EACjD,MAAMmB,SAAS,GAAGnB,MAAM,CAAiB,IAAI,CAAC;EAE9C,MAAM;IAAEoB,YAAY;IAAEC,WAAW;IAAEC;EAAkB,CAAC,GAAGlB,YAAY,CACnEc,YAAY,EACZC,SAAS,EACTT,QAAQ,EACRD,YAAY,EACZD,WACF,CAAC;EAED,MAAMe,iBAAiB,GAAGxB,WAAW,CAAEyB,QAAgB,IAAK;IAC1DT,QAAQ,CAACS,QAAQ,CAAC;EACpB,CAAC,EAAE,EAAE,CAAC;EAEN,MAAMC,gBAAgB,GAAG1B,WAAW,CAAE2B,IAAY,IAAK;IACrDT,cAAc,CAACS,IAAI,CAAC;EACtB,CAAC,EAAE,EAAE,CAAC;EAEN,MAAM;IAAEC,YAAY,EAAEC;EAAiB,CAAC,GAAGvB,gBAAgB,CAAC;IAC1De,YAAY;IACZC,WAAW;IACXX,QAAQ;IACRI,KAAK;IACLe,aAAa,EAAEN,iBAAiB;IAChCO,YAAY,EAAEL;EAChB,CAAC,CAAC;EAEF,MAAME,YAAY,GAAG5B,WAAW,CAC7B2B,IAAY,IAAK;IAChBT,cAAc,CAACS,IAAI,CAAC;IACpBE,gBAAgB,CAACF,IAAI,CAAC;EACxB,CAAC,EACD,CAACE,gBAAgB,CACnB,CAAC;EAED,MAAMG,aAAa,GAAG7B,uBAAuB,CAC3CkB,YAAY,EACZC,WAAW,EACXX,QACF,CAAC;EAED,OAAO;IACLA,QAAQ;IACRC,QAAQ;IACRC,OAAO;IACPC,KAAK;IACLK,YAAY;IACZC,SAAS;IACTH,WAAW;IACXW,YAAY;IACZb,KAAK;IACLC,QAAQ;IACRgB,aAAa;IACbX,YAAY;IACZC,WAAW;IACXC;EACF,CAAC;AACH","ignoreList":[]}
1
+ {"version":3,"file":"usePdfViewerCore.js","names":["useCallback","useMemo","useRef","useState","usePdfAnnotationPortals","usePdfDocument","usePdfViewer","usePdfViewerSync","usePdfViewerCore","src","initialPage","initialScale","document","numPages","loading","error","scale","setScale","currentPage","setCurrentPage","containerRef","viewerRef","pdfViewerRef","eventBusRef","findControllerRef","handleScaleChange","newScale","handlePageChange","page","scrollToPage","syncScrollToPage","onScaleChange","onPageChange","portalTargets"],"sources":["usePdfViewerCore.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PDFDocumentProxy } from \"pdfjs-dist\";\nimport type {\n EventBus,\n PDFFindController,\n PDFViewer,\n} from \"pdfjs-dist/web/pdf_viewer.mjs\";\nimport type { RefObject } from \"react\";\nimport { useCallback, useMemo, useRef, useState } from \"react\";\nimport type { AnnotationPortalTarget } from \"./usePdfAnnotationPortals.js\";\nimport { usePdfAnnotationPortals } from \"./usePdfAnnotationPortals.js\";\nimport { usePdfDocument } from \"./usePdfDocument.js\";\nimport { usePdfViewer } from \"./usePdfViewer.js\";\nimport { usePdfViewerSync } from \"./usePdfViewerSync.js\";\n\nexport interface UsePdfViewerCoreOptions {\n /** PDF source — URL string or ArrayBuffer */\n src: string | ArrayBuffer;\n /** Initial page number (1-indexed, default 1) */\n initialPage?: number;\n /** Initial zoom scale (default 1.0) */\n initialScale?: number;\n}\n\nexport interface UsePdfViewerCoreResult {\n /** The loaded PDF document, or undefined while loading */\n document: PDFDocumentProxy | undefined;\n /** Total number of pages in the document */\n numPages: number;\n /** Whether the document is currently loading */\n loading: boolean;\n /** Error encountered while loading the document */\n error: Error | undefined;\n\n /** Ref to attach to the scroll-container div */\n containerRef: RefObject<HTMLDivElement>;\n /** Ref to attach to the inner viewer div (className=\"pdfViewer\") */\n viewerRef: RefObject<HTMLDivElement>;\n\n /** Current page number (1-indexed) */\n currentPage: number;\n /** Navigate to a specific page (updates state and scrolls the viewer) */\n scrollToPage: (page: number) => void;\n\n /** Current zoom scale */\n scale: number;\n /** Set the zoom scale directly */\n setScale: (scale: number) => void;\n\n /** Portal targets for rendering annotation overlays on each page */\n portalTargets: AnnotationPortalTarget[];\n\n /** Ref to the internal pdfjs PDFViewer instance */\n pdfViewerRef: RefObject<PDFViewer | null>;\n /** Ref to the internal pdfjs EventBus instance */\n eventBusRef: RefObject<EventBus | null>;\n /** Ref to the internal pdfjs PDFFindController instance */\n findControllerRef: RefObject<PDFFindController | null>;\n}\n\nexport function usePdfViewerCore({\n src,\n initialPage = 1,\n initialScale = 1.0,\n}: UsePdfViewerCoreOptions): UsePdfViewerCoreResult {\n const { document, numPages, loading, error } = usePdfDocument(src);\n const [scale, setScale] = useState(initialScale);\n const [currentPage, setCurrentPage] = useState(initialPage);\n const containerRef = useRef<HTMLDivElement>(null);\n const viewerRef = useRef<HTMLDivElement>(null);\n\n const { pdfViewerRef, eventBusRef, findControllerRef } = usePdfViewer(\n containerRef,\n viewerRef,\n document,\n initialScale,\n initialPage,\n );\n\n const handleScaleChange = useCallback((newScale: number) => {\n setScale(newScale);\n }, []);\n\n const handlePageChange = useCallback((page: number) => {\n setCurrentPage(page);\n }, []);\n\n const { scrollToPage: syncScrollToPage } = usePdfViewerSync({\n pdfViewerRef,\n eventBusRef,\n document,\n scale,\n onScaleChange: handleScaleChange,\n onPageChange: handlePageChange,\n });\n\n const scrollToPage = useCallback(\n (page: number) => {\n setCurrentPage(page);\n syncScrollToPage(page);\n },\n [syncScrollToPage],\n );\n\n const portalTargets = usePdfAnnotationPortals(\n pdfViewerRef,\n eventBusRef,\n document,\n );\n\n return useMemo((): UsePdfViewerCoreResult => ({\n document,\n numPages,\n loading,\n error,\n containerRef,\n viewerRef,\n currentPage,\n scrollToPage,\n scale,\n setScale,\n portalTargets,\n pdfViewerRef,\n eventBusRef,\n findControllerRef,\n }), [\n document,\n numPages,\n loading,\n error,\n currentPage,\n scrollToPage,\n scale,\n setScale,\n portalTargets,\n ]);\n}\n"],"mappings":"AAAA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AASA,SAASA,WAAW,EAAEC,OAAO,EAAEC,MAAM,EAAEC,QAAQ,QAAQ,OAAO;AAE9D,SAASC,uBAAuB,QAAQ,8BAA8B;AACtE,SAASC,cAAc,QAAQ,qBAAqB;AACpD,SAASC,YAAY,QAAQ,mBAAmB;AAChD,SAASC,gBAAgB,QAAQ,uBAAuB;AA+CxD,OAAO,SAASC,gBAAgBA,CAAC;EAC/BC,GAAG;EACHC,WAAW,GAAG,CAAC;EACfC,YAAY,GAAG;AACQ,CAAC,EAA0B;EAClD,MAAM;IAAEC,QAAQ;IAAEC,QAAQ;IAAEC,OAAO;IAAEC;EAAM,CAAC,GAAGV,cAAc,CAACI,GAAG,CAAC;EAClE,MAAM,CAACO,KAAK,EAAEC,QAAQ,CAAC,GAAGd,QAAQ,CAACQ,YAAY,CAAC;EAChD,MAAM,CAACO,WAAW,EAAEC,cAAc,CAAC,GAAGhB,QAAQ,CAACO,WAAW,CAAC;EAC3D,MAAMU,YAAY,GAAGlB,MAAM,CAAiB,IAAI,CAAC;EACjD,MAAMmB,SAAS,GAAGnB,MAAM,CAAiB,IAAI,CAAC;EAE9C,MAAM;IAAEoB,YAAY;IAAEC,WAAW;IAAEC;EAAkB,CAAC,GAAGlB,YAAY,CACnEc,YAAY,EACZC,SAAS,EACTT,QAAQ,EACRD,YAAY,EACZD,WACF,CAAC;EAED,MAAMe,iBAAiB,GAAGzB,WAAW,CAAE0B,QAAgB,IAAK;IAC1DT,QAAQ,CAACS,QAAQ,CAAC;EACpB,CAAC,EAAE,EAAE,CAAC;EAEN,MAAMC,gBAAgB,GAAG3B,WAAW,CAAE4B,IAAY,IAAK;IACrDT,cAAc,CAACS,IAAI,CAAC;EACtB,CAAC,EAAE,EAAE,CAAC;EAEN,MAAM;IAAEC,YAAY,EAAEC;EAAiB,CAAC,GAAGvB,gBAAgB,CAAC;IAC1De,YAAY;IACZC,WAAW;IACXX,QAAQ;IACRI,KAAK;IACLe,aAAa,EAAEN,iBAAiB;IAChCO,YAAY,EAAEL;EAChB,CAAC,CAAC;EAEF,MAAME,YAAY,GAAG7B,WAAW,CAC7B4B,IAAY,IAAK;IAChBT,cAAc,CAACS,IAAI,CAAC;IACpBE,gBAAgB,CAACF,IAAI,CAAC;EACxB,CAAC,EACD,CAACE,gBAAgB,CACnB,CAAC;EAED,MAAMG,aAAa,GAAG7B,uBAAuB,CAC3CkB,YAAY,EACZC,WAAW,EACXX,QACF,CAAC;EAED,OAAOX,OAAO,CAAC,OAA+B;IAC5CW,QAAQ;IACRC,QAAQ;IACRC,OAAO;IACPC,KAAK;IACLK,YAAY;IACZC,SAAS;IACTH,WAAW;IACXW,YAAY;IACZb,KAAK;IACLC,QAAQ;IACRgB,aAAa;IACbX,YAAY;IACZC,WAAW;IACXC;EACF,CAAC,CAAC,EAAE,CACFZ,QAAQ,EACRC,QAAQ,EACRC,OAAO,EACPC,KAAK,EACLG,WAAW,EACXW,YAAY,EACZb,KAAK,EACLC,QAAQ,EACRgB,aAAa,CACd,CAAC;AACJ","ignoreList":[]}
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { useCallback, useEffect, useState } from "react";
17
+ import { useCallback, useEffect, useMemo, useState } from "react";
18
18
  import { EMPTY_STRING, FIND_EVENT, UPDATE_FIND_CONTROL_STATE_EVENT, UPDATE_FIND_MATCHES_COUNT_EVENT } from "../constants.js";
19
19
  export function usePdfViewerSearch(eventBusRef, findControllerRef, document) {
20
20
  const [query, setQueryState] = useState(EMPTY_STRING);
@@ -91,7 +91,7 @@ export function usePdfViewerSearch(eventBusRef, findControllerRef, document) {
91
91
  eventBus.off(UPDATE_FIND_CONTROL_STATE_EVENT, handleControlState);
92
92
  };
93
93
  }, [eventBusRef, findControllerRef, document]);
94
- return {
94
+ return useMemo(() => ({
95
95
  query,
96
96
  totalMatches,
97
97
  currentMatchIndex,
@@ -101,6 +101,6 @@ export function usePdfViewerSearch(eventBusRef, findControllerRef, document) {
101
101
  prevMatch,
102
102
  openSearch,
103
103
  closeSearch
104
- };
104
+ }), [query, totalMatches, currentMatchIndex, isSearchOpen, setQuery, nextMatch, prevMatch, openSearch, closeSearch]);
105
105
  }
106
106
  //# sourceMappingURL=usePdfViewerSearch.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"usePdfViewerSearch.js","names":["useCallback","useEffect","useState","EMPTY_STRING","FIND_EVENT","UPDATE_FIND_CONTROL_STATE_EVENT","UPDATE_FIND_MATCHES_COUNT_EVENT","usePdfViewerSearch","eventBusRef","findControllerRef","document","query","setQueryState","totalMatches","setTotalMatches","currentMatchIndex","setCurrentMatchIndex","isSearchOpen","setIsSearchOpen","dispatchFind","queryString","findPrevious","current","dispatch","source","undefined","type","caseSensitive","highlightAll","phraseSearch","dispatchFindAgain","setQuery","input","nextMatch","prevMatch","openSearch","closeSearch","eventBus","handleMatchesCount","evt","matchesCount","total","handleControlState","on","off"],"sources":["usePdfViewerSearch.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PDFDocumentProxy } from \"pdfjs-dist\";\nimport type {\n EventBus,\n PDFFindController,\n} from \"pdfjs-dist/web/pdf_viewer.mjs\";\nimport type { RefObject } from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\nimport {\n EMPTY_STRING,\n FIND_EVENT,\n UPDATE_FIND_CONTROL_STATE_EVENT,\n UPDATE_FIND_MATCHES_COUNT_EVENT,\n} from \"../constants.js\";\n\nexport interface UsePdfViewerSearchResult {\n query: string;\n totalMatches: number;\n currentMatchIndex: number;\n isSearchOpen: boolean;\n setQuery: (query: string) => void;\n nextMatch: () => void;\n prevMatch: () => void;\n openSearch: () => void;\n closeSearch: () => void;\n}\n\nexport function usePdfViewerSearch(\n eventBusRef: RefObject<EventBus | null>,\n findControllerRef: RefObject<PDFFindController | null>,\n document: PDFDocumentProxy | undefined,\n): UsePdfViewerSearchResult {\n const [query, setQueryState] = useState(EMPTY_STRING);\n const [totalMatches, setTotalMatches] = useState(0);\n const [currentMatchIndex, setCurrentMatchIndex] = useState(0);\n const [isSearchOpen, setIsSearchOpen] = useState(false);\n\n const dispatchFind = useCallback(\n (queryString: string, findPrevious: boolean) => {\n eventBusRef.current?.dispatch(FIND_EVENT, {\n source: undefined,\n type: \"\",\n query: queryString,\n caseSensitive: false,\n highlightAll: true,\n phraseSearch: true,\n findPrevious,\n });\n },\n [eventBusRef],\n );\n\n const dispatchFindAgain = useCallback(\n (findPrevious: boolean) => {\n eventBusRef.current?.dispatch(FIND_EVENT, {\n source: undefined,\n type: \"again\",\n query,\n caseSensitive: false,\n highlightAll: true,\n phraseSearch: true,\n findPrevious,\n });\n },\n [eventBusRef, query],\n );\n\n const setQuery = useCallback(\n (input: string) => {\n setQueryState(input);\n\n if (input === EMPTY_STRING) {\n setTotalMatches(0);\n setCurrentMatchIndex(0);\n // Dispatch empty query to clear highlights\n dispatchFind(EMPTY_STRING, false);\n return;\n }\n\n dispatchFind(input, false);\n },\n [dispatchFind],\n );\n\n const nextMatch = useCallback(() => {\n dispatchFindAgain(false);\n }, [dispatchFindAgain]);\n\n const prevMatch = useCallback(() => {\n dispatchFindAgain(true);\n }, [dispatchFindAgain]);\n\n const openSearch = useCallback(() => {\n setIsSearchOpen(true);\n }, []);\n\n const closeSearch = useCallback(() => {\n setIsSearchOpen(false);\n setQueryState(EMPTY_STRING);\n setTotalMatches(0);\n setCurrentMatchIndex(0);\n // Clear highlights\n dispatchFind(EMPTY_STRING, false);\n }, [dispatchFind]);\n\n useEffect(function subscribeSearchEvents() {\n const eventBus = eventBusRef.current;\n if (eventBus == null) {\n return;\n }\n\n const handleMatchesCount = (evt: {\n matchesCount: { current: number; total: number };\n }) => {\n setTotalMatches(evt.matchesCount.total);\n setCurrentMatchIndex(\n evt.matchesCount.current > 0 ? evt.matchesCount.current - 1 : 0,\n );\n };\n\n const handleControlState = (evt: {\n state: number;\n matchesCount: { current: number; total: number };\n }) => {\n setTotalMatches(evt.matchesCount.total);\n setCurrentMatchIndex(\n evt.matchesCount.current > 0 ? evt.matchesCount.current - 1 : 0,\n );\n };\n\n eventBus.on(UPDATE_FIND_MATCHES_COUNT_EVENT, handleMatchesCount);\n eventBus.on(UPDATE_FIND_CONTROL_STATE_EVENT, handleControlState);\n\n return () => {\n eventBus.off(UPDATE_FIND_MATCHES_COUNT_EVENT, handleMatchesCount);\n eventBus.off(UPDATE_FIND_CONTROL_STATE_EVENT, handleControlState);\n };\n }, [eventBusRef, findControllerRef, document]);\n\n return {\n query,\n totalMatches,\n currentMatchIndex,\n isSearchOpen,\n setQuery,\n nextMatch,\n prevMatch,\n openSearch,\n closeSearch,\n };\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"usePdfViewerSearch.js","names":["useCallback","useEffect","useMemo","useState","EMPTY_STRING","FIND_EVENT","UPDATE_FIND_CONTROL_STATE_EVENT","UPDATE_FIND_MATCHES_COUNT_EVENT","usePdfViewerSearch","eventBusRef","findControllerRef","document","query","setQueryState","totalMatches","setTotalMatches","currentMatchIndex","setCurrentMatchIndex","isSearchOpen","setIsSearchOpen","dispatchFind","queryString","findPrevious","current","dispatch","source","undefined","type","caseSensitive","highlightAll","phraseSearch","dispatchFindAgain","setQuery","input","nextMatch","prevMatch","openSearch","closeSearch","eventBus","handleMatchesCount","evt","matchesCount","total","handleControlState","on","off"],"sources":["usePdfViewerSearch.ts"],"sourcesContent":["/*\n * Copyright 2025 Palantir Technologies, Inc. All rights reserved.\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { PDFDocumentProxy } from \"pdfjs-dist\";\nimport type {\n EventBus,\n PDFFindController,\n} from \"pdfjs-dist/web/pdf_viewer.mjs\";\nimport type { RefObject } from \"react\";\nimport { useCallback, useEffect, useMemo, useState } from \"react\";\nimport {\n EMPTY_STRING,\n FIND_EVENT,\n UPDATE_FIND_CONTROL_STATE_EVENT,\n UPDATE_FIND_MATCHES_COUNT_EVENT,\n} from \"../constants.js\";\n\nexport interface UsePdfViewerSearchResult {\n query: string;\n totalMatches: number;\n currentMatchIndex: number;\n isSearchOpen: boolean;\n setQuery: (query: string) => void;\n nextMatch: () => void;\n prevMatch: () => void;\n openSearch: () => void;\n closeSearch: () => void;\n}\n\nexport function usePdfViewerSearch(\n eventBusRef: RefObject<EventBus | null>,\n findControllerRef: RefObject<PDFFindController | null>,\n document: PDFDocumentProxy | undefined,\n): UsePdfViewerSearchResult {\n const [query, setQueryState] = useState(EMPTY_STRING);\n const [totalMatches, setTotalMatches] = useState(0);\n const [currentMatchIndex, setCurrentMatchIndex] = useState(0);\n const [isSearchOpen, setIsSearchOpen] = useState(false);\n\n const dispatchFind = useCallback(\n (queryString: string, findPrevious: boolean) => {\n eventBusRef.current?.dispatch(FIND_EVENT, {\n source: undefined,\n type: \"\",\n query: queryString,\n caseSensitive: false,\n highlightAll: true,\n phraseSearch: true,\n findPrevious,\n });\n },\n [eventBusRef],\n );\n\n const dispatchFindAgain = useCallback(\n (findPrevious: boolean) => {\n eventBusRef.current?.dispatch(FIND_EVENT, {\n source: undefined,\n type: \"again\",\n query,\n caseSensitive: false,\n highlightAll: true,\n phraseSearch: true,\n findPrevious,\n });\n },\n [eventBusRef, query],\n );\n\n const setQuery = useCallback(\n (input: string) => {\n setQueryState(input);\n\n if (input === EMPTY_STRING) {\n setTotalMatches(0);\n setCurrentMatchIndex(0);\n // Dispatch empty query to clear highlights\n dispatchFind(EMPTY_STRING, false);\n return;\n }\n\n dispatchFind(input, false);\n },\n [dispatchFind],\n );\n\n const nextMatch = useCallback(() => {\n dispatchFindAgain(false);\n }, [dispatchFindAgain]);\n\n const prevMatch = useCallback(() => {\n dispatchFindAgain(true);\n }, [dispatchFindAgain]);\n\n const openSearch = useCallback(() => {\n setIsSearchOpen(true);\n }, []);\n\n const closeSearch = useCallback(() => {\n setIsSearchOpen(false);\n setQueryState(EMPTY_STRING);\n setTotalMatches(0);\n setCurrentMatchIndex(0);\n // Clear highlights\n dispatchFind(EMPTY_STRING, false);\n }, [dispatchFind]);\n\n useEffect(function subscribeSearchEvents() {\n const eventBus = eventBusRef.current;\n if (eventBus == null) {\n return;\n }\n\n const handleMatchesCount = (evt: {\n matchesCount: { current: number; total: number };\n }) => {\n setTotalMatches(evt.matchesCount.total);\n setCurrentMatchIndex(\n evt.matchesCount.current > 0 ? evt.matchesCount.current - 1 : 0,\n );\n };\n\n const handleControlState = (evt: {\n state: number;\n matchesCount: { current: number; total: number };\n }) => {\n setTotalMatches(evt.matchesCount.total);\n setCurrentMatchIndex(\n evt.matchesCount.current > 0 ? evt.matchesCount.current - 1 : 0,\n );\n };\n\n eventBus.on(UPDATE_FIND_MATCHES_COUNT_EVENT, handleMatchesCount);\n eventBus.on(UPDATE_FIND_CONTROL_STATE_EVENT, handleControlState);\n\n return () => {\n eventBus.off(UPDATE_FIND_MATCHES_COUNT_EVENT, handleMatchesCount);\n eventBus.off(UPDATE_FIND_CONTROL_STATE_EVENT, handleControlState);\n };\n }, [eventBusRef, findControllerRef, document]);\n\n return useMemo((): UsePdfViewerSearchResult => ({\n query,\n totalMatches,\n currentMatchIndex,\n isSearchOpen,\n setQuery,\n nextMatch,\n prevMatch,\n openSearch,\n closeSearch,\n }), [\n query,\n totalMatches,\n currentMatchIndex,\n isSearchOpen,\n setQuery,\n nextMatch,\n prevMatch,\n openSearch,\n closeSearch,\n ]);\n}\n"],"mappings":"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","ignoreList":[]}
@@ -14,7 +14,7 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { useCallback, useEffect, useState } from "react";
17
+ import { useCallback, useEffect, useMemo, useState } from "react";
18
18
  import { MAX_SCALE, MIN_SCALE, SCALE_STEP } from "../constants.js";
19
19
  import { usePdfOutline } from "./usePdfOutline.js";
20
20
  import { usePdfViewerCore } from "./usePdfViewerCore.js";
@@ -105,7 +105,7 @@ export function usePdfViewerState({
105
105
  });
106
106
  });
107
107
  }, [core.document, src, onDownload]);
108
- return {
108
+ return useMemo(() => ({
109
109
  ...core,
110
110
  zoomIn,
111
111
  zoomOut,
@@ -119,7 +119,7 @@ export function usePdfViewerState({
119
119
  search,
120
120
  outlineItems,
121
121
  download
122
- };
122
+ }), [core, zoomIn, zoomOut, rotation, rotateLeft, rotateRight, sidebarOpen, sidebarMode, setSidebarMode, toggleSidebar, search, outlineItems, download]);
123
123
  }
124
124
 
125
125
  /** Derive a download filename from an explicit name, the src URL, or a fallback. */