@inkbar/react 0.9.0 → 0.11.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 +64 -3
- package/dist/index.d.mts +295 -4
- package/dist/index.d.mts.map +1 -1
- package/dist/index.mjs +835 -35
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -187,11 +187,71 @@ Plugin stores undo with annotations. In controlled mode with `history`, each und
|
|
|
187
187
|
|
|
188
188
|
The bar handles Escape itself and stops it there, so an `onKeyDown` you pass to `InkFindBar` never sees Escape.
|
|
189
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
|
+
|
|
190
248
|
## Custom rendering
|
|
191
249
|
|
|
192
|
-
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.
|
|
193
253
|
|
|
194
|
-
`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.
|
|
195
255
|
|
|
196
256
|
## Keyboard
|
|
197
257
|
|
|
@@ -201,6 +261,7 @@ The scroll area takes focus. From there:
|
|
|
201
261
|
- With the `note` tool active, `Enter` places a note at the centre of the current page and opens it.
|
|
202
262
|
- With the `signature` tool active, `Enter` places the chosen signature at the centre of the current page.
|
|
203
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.
|
|
204
265
|
|
|
205
266
|
On a focused annotation:
|
|
206
267
|
|
|
@@ -218,7 +279,7 @@ Components carry state on data attributes, for example `[data-annotation][data-s
|
|
|
218
279
|
|
|
219
280
|
## Hooks
|
|
220
281
|
|
|
221
|
-
`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.
|
|
222
283
|
|
|
223
284
|
## Licence
|
|
224
285
|
|