@inkbar/react 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +72 -5
- package/dist/index.d.mts +325 -10
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +969 -52
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -51,10 +51,13 @@ import {
|
|
|
51
51
|
InkZoomSelect,
|
|
52
52
|
InkThreadPopover,
|
|
53
53
|
} from '@inkbar/react'
|
|
54
|
+
import { highlightTool, noteTool, redactAreaTool } from '@inkbar/core/dom'
|
|
54
55
|
import type { Annotation } from '@inkbar/core'
|
|
55
56
|
import { assets } from 'virtual:inkbar-assets'
|
|
56
57
|
import '@inkbar/theme/theme.css'
|
|
57
58
|
|
|
59
|
+
const tools = [highlightTool(), redactAreaTool(), noteTool()]
|
|
60
|
+
|
|
58
61
|
export function Review({
|
|
59
62
|
signedUrl,
|
|
60
63
|
documentId,
|
|
@@ -67,6 +70,7 @@ export function Review({
|
|
|
67
70
|
<InkRoot
|
|
68
71
|
src={{ url: signedUrl, key: documentId }}
|
|
69
72
|
assets={assets}
|
|
73
|
+
tools={tools}
|
|
70
74
|
annotations={annotations}
|
|
71
75
|
onAnnotationsChange={setAnnotations}
|
|
72
76
|
>
|
|
@@ -94,7 +98,9 @@ export function Review({
|
|
|
94
98
|
}
|
|
95
99
|
```
|
|
96
100
|
|
|
97
|
-
|
|
101
|
+
`select-text` and `select` are always there. Every other tool is opt-in: pass the ones you offer through `tools`, from the tool factories in `@inkbar/core/dom`, or `allTools()` for all of them. A button for a tool you did not pass renders disabled and hidden, a tool menu renders nothing, and each logs an `InkError('UnknownTool')` to the console once. If the active tool leaves `tools`, the viewer switches to `select-text` and reports it through `onToolChange`.
|
|
102
|
+
|
|
103
|
+
Each control is a separate component, so put them in any order or leave some out. Buttons show icons from `@inkbar/icons`. Pass your own as `children` to replace one, and use `InkIcon` to put the same icons on your own buttons. For a button of your own, such as `<InkToolbarButton onClick={download}>Download</InkToolbarButton>`, use `InkToolbarButton`: it joins the toolbar's arrow-key focus and looks like the other buttons.
|
|
98
104
|
|
|
99
105
|
`annotations` with `onAnnotationsChange` makes your list the source of truth. Leave them off and Inkbar keeps the state itself, with undo and redo.
|
|
100
106
|
|
|
@@ -155,7 +161,7 @@ The highlight tool leaves `contents` free for the comment. `doc.text(page)` retu
|
|
|
155
161
|
|
|
156
162
|
## Signatures
|
|
157
163
|
|
|
158
|
-
`InkSignatureMenu` lists saved signatures, picks one for the `signature` tool, removes them, and opens `InkSignatureDialog` to draw, type or upload a new one. These are marks on the page, not cryptographic signatures. Pass `signatures` to `InkRoot` to keep them somewhere: `createLocalSignatureStore(key)` from `@inkbar/core` uses `localStorage`, and without it they last until reload. `signatureFonts` lists the handwriting fonts the Type tab offers; load them with `@font-face`. A placed signature keeps its aspect ratio when resized.
|
|
164
|
+
`InkSignatureMenu` lists saved signatures, picks one for the `signature` tool, removes them, and opens `InkSignatureDialog` to draw, type or upload a new one. The menu needs `signatureTool()` in `tools`. The dialog and signature form fields work without it; the dialog then only saves the signature. These are marks on the page, not cryptographic signatures. Pass `signatures` to `InkRoot` to keep them somewhere: `createLocalSignatureStore(key)` from `@inkbar/core` uses `localStorage`, and without it they last until reload. `signatureFonts` lists the handwriting fonts the Type tab offers; load them with `@font-face`. A placed signature keeps its aspect ratio when resized.
|
|
159
165
|
|
|
160
166
|
## Plugins
|
|
161
167
|
|
|
@@ -181,11 +187,71 @@ Plugin stores undo with annotations. In controlled mode with `history`, each und
|
|
|
181
187
|
|
|
182
188
|
The bar handles Escape itself and stops it there, so an `onKeyDown` you pass to `InkFindBar` never sees Escape.
|
|
183
189
|
|
|
190
|
+
## Sidebars
|
|
191
|
+
|
|
192
|
+
`InkSidebar` holds `InkPanel`s and places nothing itself: put it beside `InkViewport` with your own flex or grid layout. Each panel has an `id`, a `label`, an optional `icon` (an `IconNode` from `@inkbar/icons`) and an optional `badge`, a number or short string hidden when empty or 0. Its children are the panel's content, your own component or one of Inkbar's.
|
|
193
|
+
|
|
194
|
+
```tsx
|
|
195
|
+
<InkRoot src={src} assets={assets}>
|
|
196
|
+
<div className="app">
|
|
197
|
+
<InkSidebar side="left" layout="rail" open={open} onOpenChange={setOpen}>
|
|
198
|
+
<InkPanel id="pages" label="Pages" icon={Square}>
|
|
199
|
+
<InkThumbnails />
|
|
200
|
+
</InkPanel>
|
|
201
|
+
<InkPanel id="notes" label="Notes" icon={MessageSquare} badge={noteCount}>
|
|
202
|
+
<MyNotes />
|
|
203
|
+
</InkPanel>
|
|
204
|
+
</InkSidebar>
|
|
205
|
+
<InkViewport />
|
|
206
|
+
</div>
|
|
207
|
+
</InkRoot>
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
`layout` is `rail` (default: icon buttons beside the document, and activating the open panel's button closes it), `tabs` (labelled tabs, one panel open) or `stack` (a header per panel, any number expanded). `side` (`left` by default) decides which edge the rail and border sit on. `open` is the open panel's id, or `null`, for rail and tabs, and the expanded ids for stack. Pass it with `onOpenChange` to control the sidebar, or pass `defaultOpen` to set only where it starts. With neither, the first panel starts open. When the open panel goes (rendered conditionally), rail and tabs open the first one left and stack drops it, and `onOpenChange` receives the new value. In the rail, the open panel shows its label as a title above its content. `visible` (default `true`) shows or hides the whole sidebar; it keeps the open panel, its content and its scroll for when it shows again. Your code decides when: `visible={hits > 0}` keeps a results sidebar out of the way until a search finds something.
|
|
211
|
+
|
|
212
|
+
Panels take their place in the order they appear in the DOM, including one rendered later. A closed panel stays mounted and hidden, so its content keeps its state and scroll position, across layout changes too. The keys and ARIA are core's: see the [`@inkbar/core` README](https://github.com/inkbar/inkbar/tree/main/packages/core#sidebars). The sidebar needs no document and works outside `InkRoot`. On the server it renders its frame with the open panel showing, and the stack headers; rail and tab buttons follow once it has mounted.
|
|
213
|
+
|
|
214
|
+
## Thumbnails
|
|
215
|
+
|
|
216
|
+
`InkThumbnail` draws one page (0-based `page`) as a small image, with its annotations and plugin marks. Give it a `width` or a `height` and the other follows the page; with neither it is 120px wide. `annotations={false}` and `marks={false}` leave those out, and `label` replaces its accessible name ("Page 4" for page 3). It is an image with no focus or click behaviour, so wrap it in the control you need, a checkbox label in a dialog for instance.
|
|
217
|
+
|
|
218
|
+
`InkThumbnails` lists the pages as thumbnails, each a button that scrolls the viewport to its page, with the current page marked `aria-current="page"`. It fills its parent's height and scrolls. `pages` lists the pages to show, in order. The thumbnails fill the list's width less a 16px gutter each side and follow it as it resizes; `width` fixes them at that many pixels instead. `annotations` and `marks` pass to each thumbnail. Only the items near the visible part are mounted. `renderItem` adds to each item after its page number:
|
|
219
|
+
|
|
220
|
+
```tsx
|
|
221
|
+
<InkThumbnails
|
|
222
|
+
renderItem={({ page }) => <span className="page-count">{counts[page]}</span>}
|
|
223
|
+
/>
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
An item's height is worked out from its page alone, so position what you add absolutely. What `renderItem` returns renders inside the item's `<button>`: keep it non-interactive, with no links, buttons or inputs. Both need `InkRoot` and render empty on the server.
|
|
227
|
+
|
|
228
|
+
## Record lists
|
|
229
|
+
|
|
230
|
+
`InkRecordList` lists a plugin's records as cards, the same cards their marks open, with the same actions. Name the records `<plugin>.<name>`:
|
|
231
|
+
|
|
232
|
+
```tsx
|
|
233
|
+
<InkPanel id="suggestions" label="Suggestions" icon={MessageSquare} badge={count}>
|
|
234
|
+
<InkRecordList
|
|
235
|
+
records="review.suggestions"
|
|
236
|
+
onCountChange={setCount}
|
|
237
|
+
empty="No suggestions left."
|
|
238
|
+
/>
|
|
239
|
+
</InkPanel>
|
|
240
|
+
```
|
|
241
|
+
|
|
242
|
+
The list and the marks share one selection: choosing a card selects its mark and scrolls the viewer there, and activating a mark selects its card. Each action runs in one undo step. `card`, `where` and `sort` replace the marks' card, filter and reading order. A new function rebuilds the list, so wrap them in `useCallback` or define them outside the component, never inline. `onCountChange` gets the number listed whenever it changes, ready for a panel badge. `empty` replaces "Nothing here yet.", and `empty={null}` shows nothing. To list something of your own, pass a `source` (a `RecordListSource`, as core's `recordsSource` and `searchSource` build) in place of `records`: it gets the same cards, keys and selection. With both, `source` wins.
|
|
243
|
+
|
|
244
|
+
`InkSearchResults` is the same list over the document search: one card per hit, titled with its page ("Page 2"), with its place on that page ("Match 3") and the text around it. Choosing a card makes its hit current, as the find bar's next and previous do. It has its own field, labelled "Find in document", above the cards and the find bar's match count in a footer below them; `field={false}` leaves the field out when a find bar is on the page. Field and find bar share the search state, so they never disagree.
|
|
245
|
+
|
|
246
|
+
To build your own, `useRecords<T>(name)` returns the live `records` and the `handle` to read and write them, each write one undo step, and `useRecordSelection(name)` returns `selected` and `select(id | null)`. Both need `InkRoot`. Plugins are set up once it has mounted, so until then `handle` is null, `records` is empty and nothing is selected. Both lists fill their parent's height and render empty on the server. The keys, focus and announcements are core's: see the [`@inkbar/core` README](https://github.com/inkbar/inkbar/tree/main/packages/core#record-lists).
|
|
247
|
+
|
|
184
248
|
## Custom rendering
|
|
185
249
|
|
|
186
|
-
Slots are render props. `InkViewport` takes `loading`, `empty`, `error`, `renderAnnotation` and `renderPageError`; `InkThreadPopover` takes a function as `children`; `InkToolButton` takes a node or a function of `{ active, label }`; `InkCommand` a node or a function of `{ label, busy }`. The props each receives are typed as `AnnotationSlotProps` and `ThreadPopoverSlotProps`, the same types the Vue package exports.
|
|
250
|
+
Slots are render props. `InkViewport` takes `loading`, `empty`, `error`, `renderAnnotation` and `renderPageError`; `InkThreadPopover` takes a function as `children`; `InkToolButton` takes a node or a function of `{ active, label }`; `InkCommand` a node or a function of `{ label, busy }`; `InkThumbnails` takes `renderItem`; `InkRecordList` takes `empty`. The props each receives are typed as `AnnotationSlotProps` and `ThreadPopoverSlotProps`, the same types the Vue package exports.
|
|
251
|
+
|
|
252
|
+
`InkDownloadButton` and `InkPrintButton` download and print the open PDF; `filename` names the download (the opened `File`'s name, else the URL's last segment, else `document.pdf`). Printing loads the file into a hidden frame and opens the browser's print dialog for it; on iOS the PDF opens in a new tab instead. Until saving into the PDF ships, both give the file as it was opened, without this session's annotations.
|
|
187
253
|
|
|
188
|
-
`InkRoot`, `InkToolbar`, `InkToolButton`, `InkCommand`, `InkUndo`, `InkRedo`, `InkPrevPage`, `InkNextPage`, `InkZoomIn` and `InkZoomOut` take `asChild` to render your own element in place of theirs, such as a button from your design system. Pass that element as the only child. The control keeps its behaviour and accessible name.
|
|
254
|
+
`InkRoot`, `InkToolbar`, `InkToolButton`, `InkCommand`, `InkUndo`, `InkRedo`, `InkDownloadButton`, `InkPrintButton`, `InkPrevPage`, `InkNextPage`, `InkZoomIn` and `InkZoomOut` take `asChild` to render your own element in place of theirs, such as a button from your design system. Pass that element as the only child. The control keeps its behaviour and accessible name.
|
|
189
255
|
|
|
190
256
|
## Keyboard
|
|
191
257
|
|
|
@@ -195,6 +261,7 @@ The scroll area takes focus. From there:
|
|
|
195
261
|
- With the `note` tool active, `Enter` places a note at the centre of the current page and opens it.
|
|
196
262
|
- With the `signature` tool active, `Enter` places the chosen signature at the centre of the current page.
|
|
197
263
|
- `Ctrl` or `Cmd` + `Z` undoes. `Ctrl` or `Cmd` + `Shift` + `Z`, or `Ctrl` + `Y`, redoes. `Ctrl` and `Cmd` both work on every platform. The keys do what `InkUndo` and `InkRedo` do, from anywhere in `InkRoot`, toolbars included, and act only while that button is enabled. In a text field or editable content the keys stay with the element, so a field keeps its own text undo. In a `<select>` they are left to the browser, which does nothing with them.
|
|
264
|
+
- `Ctrl` or `Cmd` + `P` prints the PDF and `Ctrl` or `Cmd` + `S` downloads it, as `InkPrintButton` and `InkDownloadButton` do, from anywhere in `InkRoot` once the document is ready. Outside `InkRoot` the browser keeps them.
|
|
198
265
|
|
|
199
266
|
On a focused annotation:
|
|
200
267
|
|
|
@@ -212,7 +279,7 @@ Components carry state on data attributes, for example `[data-annotation][data-s
|
|
|
212
279
|
|
|
213
280
|
## Hooks
|
|
214
281
|
|
|
215
|
-
`useDocument`, `useAnnotations`, `useTool`, `useSelection`, `useZoom`, `usePage`, `useHistory`, `useEmbedded` and `
|
|
282
|
+
`useDocument`, `useAnnotations`, `useTool`, `useSelection`, `useZoom`, `usePage`, `useHistory`, `useEmbedded`, `useCommand`, `useRecords` and `useRecordSelection` read the state of the nearest `InkRoot`. Build your own toolbar or side panel from them. Tool options, such as the shape kind, last as long as `InkRoot`. The tool style that `useToolStyle` reads and sets (colours, line style, text style) belongs to the document: opening another one starts again from the theme's defaults.
|
|
216
283
|
|
|
217
284
|
## Licence
|
|
218
285
|
|