@crewlethq/ui 0.31.0 → 0.32.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/README.md +25 -1
  2. package/dist/Canvas/Canvas.d.ts +48 -2
  3. package/dist/Canvas/ShortcutTip.d.ts +22 -0
  4. package/dist/Canvas/geometry.d.ts +13 -3
  5. package/dist/Canvas/index.d.ts +3 -1
  6. package/dist/Canvas/index.js +3 -1
  7. package/dist/{Canvas-YVXHMWLN.css → Canvas-LRVNW7XN.css} +39 -3
  8. package/dist/CanvasBoard/CanvasBoard.d.ts +75 -12
  9. package/dist/CanvasBoard/index.d.ts +2 -2
  10. package/dist/CanvasBoard/index.js +6 -2
  11. package/dist/{CanvasBoard-ZLPDINSM.css → CanvasBoard-MBZVBWDO.css} +135 -30
  12. package/dist/CanvasToolbar/CanvasToolbar.d.ts +48 -0
  13. package/dist/CanvasToolbar/index.d.ts +3 -0
  14. package/dist/CanvasToolbar/index.js +12 -0
  15. package/dist/CanvasToolbar-AE5MXH7T.css +32 -0
  16. package/dist/DataView/index.js +3 -3
  17. package/dist/EditingStatus/index.js +1 -1
  18. package/dist/{EditingStatus-NFADEPSX.css → EditingStatus-L6THUAN7.css} +21 -0
  19. package/dist/PresenterView/index.js +2 -2
  20. package/dist/RichEditor/index.js +1 -1
  21. package/dist/SheetGrid/index.js +3 -3
  22. package/dist/SlideCompare/index.js +2 -2
  23. package/dist/SlideFilmstrip/index.js +2 -2
  24. package/dist/SlideGrid/index.js +2 -2
  25. package/dist/SlideStage/SlideStage.d.ts +6 -1
  26. package/dist/SlideStage/index.js +1 -1
  27. package/dist/TreeCanvas/index.js +4 -2
  28. package/dist/{chunk-ALMSY7RL.js → chunk-CRKFL423.js} +1 -1
  29. package/dist/{chunk-O63MWA4G.js → chunk-E6CR5PEV.js} +2 -1
  30. package/dist/chunk-E6EWK5YY.js +33 -0
  31. package/dist/{chunk-3DPIU2Q7.js → chunk-FPS3JIPQ.js} +1 -1
  32. package/dist/{chunk-OGAAHVF6.js → chunk-MBNFJSZS.js} +1 -1
  33. package/dist/{chunk-OJ45PPCV.js → chunk-OTN35CI4.js} +113 -30
  34. package/dist/{chunk-JODJS7MR.js → chunk-PJD2KFHX.js} +243 -39
  35. package/dist/{chunk-LR5QTFOE.js → chunk-RV7SZAHR.js} +1 -1
  36. package/dist/{chunk-VOX3YRFO.js → chunk-V5IGUXMZ.js} +1 -1
  37. package/dist/{chunk-3C3BRIMY.js → chunk-VS3KOOOL.js} +1 -1
  38. package/dist/{chunk-S6VG5ZFZ.js → chunk-WTNUVQVI.js} +1 -1
  39. package/dist/generated/index.d.ts +1 -0
  40. package/dist/generated/index.js +49 -48
  41. package/dist/index.js +49 -48
  42. package/dist/styles.css +233 -36
  43. package/package.json +3 -3
  44. package/dist/{Editor-YZ7KG2RD.js → Editor-YWBUJAQU.js} +1 -1
  45. package/dist/{chunk-UVJ5FF5V.js → chunk-3ZAFJ4GK.js} +2 -2
  46. package/dist/{chunk-CT4EZF62.js → chunk-LWXRD6L6.js} +2 -2
  47. /package/dist/{chunk-OC46246T.js → chunk-TF2P3OPS.js} +0 -0
package/README.md CHANGED
@@ -54,7 +54,7 @@ import { Button } from '@crewlethq/ui/Button'; // 8 KB of CSS, not 182 KB
54
54
  | Arranging a page | `ArrangeBoard`, `ArrangeGrid`, `ArrangeFrame` (with `useGridResize`), `ArrangeGroup`, `ArrangeList`, and the reorder hooks under them (`useReorder`, `useReorderLists`, `moveKey`) |
55
55
  | Application shell | `AppShell` with `AppShell.Rail`, `AppShell.Topbar`, `AppShell.AccountRow` and `useAppShell`, `AuthLayout`, `SidebarNav` and `NavItem`, `BrandLockup`, `SearchTrigger`, `PageHeader`, `Toolbar`, `Stack`, `Inline`, `Spacer`, `AutoGrid`, `ErrorBoundary`, `useShortcut`, `useFullscreen` |
56
56
  | Preferences | `ThemeToggle`, `ThemeSwitcher`, `DensitySwitcher`, `ThemeGallery`, `ThemeSwatch`, `useThemePreference`, `useDensityPreference`, `useSystemTheme`, `resolveTheme`, `applyStoredPreferences`, and the framework-free `applyTheme`, `applyPalette`, `applyDensity` with `THEME_PREFERENCES`, `DENSITY_PREFERENCES` and their guards |
57
- | Artifacts | `SandboxedFrame`, `LinkInterstitial` and `openExternal`, `VersionTimeline`, `ShareDialog` with the role ladder (`ARTIFACT_ROLES`, `SHAREABLE_ROLES`, `roleAtLeast`), `PrincipalPicker`, `Principal` and `PrincipalAvatar`, `ArtifactCard` and `ArtifactKindGlyph`, `ArtifactBar` and `ArtifactVersionChip` (an open artifact's one slim bar), `ArtifactPanel` and `ArtifactWorkspace` (the panels it opens beside or over the artifact), `CanvasBoard` (a design's artboards and notes on a `Canvas`, with the visibility budget `liveBoards`), `CanvasSchematic` (a design's cover, drawn from its boards' frames), `AccessDeniedCard`, `ArtifactIcon` (with `artifactIconGlyph` and `ARTIFACT_ICON_WORDS`), the native gallery covers (`DocCover`, `SheetCover`, `DashboardCover`), `MergeConflicts`, the dashboard pieces (`WidgetFrame`, `DatasetSource` and `DatasetSourceList`), and `FrameProtocol` (the wire between a host page and an embedded artifact, with no React in it) |
57
+ | Artifacts | `SandboxedFrame`, `LinkInterstitial` and `openExternal`, `VersionTimeline`, `ShareDialog` with the role ladder (`ARTIFACT_ROLES`, `SHAREABLE_ROLES`, `roleAtLeast`), `PrincipalPicker`, `Principal` and `PrincipalAvatar`, `ArtifactCard` and `ArtifactKindGlyph`, `ArtifactBar` and `ArtifactVersionChip` (an open artifact's one slim bar), `ArtifactPanel` and `ArtifactWorkspace` (the panels it opens beside or over the artifact), `CanvasBoard` (a design's artboards and notes on a `Canvas`, with the visibility budget `liveBoards`), `CanvasToolbar` (a canvas's tool palette, with `toolForKey`), `CanvasSchematic` (a design's cover, drawn from its boards' frames), `AccessDeniedCard`, `ArtifactIcon` (with `artifactIconGlyph` and `ARTIFACT_ICON_WORDS`), the native gallery covers (`DocCover`, `SheetCover`, `DashboardCover`), `MergeConflicts`, the dashboard pieces (`WidgetFrame`, `DatasetSource` and `DatasetSourceList`), and `FrameProtocol` (the wire between a host page and an embedded artifact, with no React in it) |
58
58
  | Sheets | `SheetGrid`, the cells on their own (`CheckboxCell`, `TextCell`, `LongTextCell`, `NumberCell`, `SelectCell`, `MultiSelectCell`, `PersonCell`, `StatusCell`, `DateCell`, `UrlCell`, `RatingCell`, and `SheetCell` for any type), `ChangeMark`, and `SheetModel` (every rule a sheet is drawn from, with no React in it: values, parsing, the keyboard model, TSV, the pending edits of D23) |
59
59
  | Presence | `PresenceStack`, `PresenceRing` and `presenceHueClass`, `CursorFlag`, `LiveCursor`, `FollowBanner`, `PeopleRail` (with `peopleRailKey`), and `PresenceModel` (the roster order, one entry per person, the session hues from `PRESENCE_HUES`, with no React in it) |
60
60
  | Presentations | `SlideStage` (a slide laid out at 1920 by 1080 and scaled to its box, with `SLIDE_WIDTH` and `SLIDE_HEIGHT`), `useSlideKeys` (the keys a show and a clicker send), `SlideFilmstrip`, `SlideGrid`, `SlideCompare` (with `SLIDE_COMPARE_LABELS`), `PresenterView` (with `formatStopwatch`) |
@@ -204,6 +204,30 @@ places:
204
204
  - A surface that pans or zooms dispatches `LAYER_REPOSITION_EVENT` on its host
205
205
  when the content beneath it moves, and every panel anchored inside follows.
206
206
 
207
+ ## Changes in 0.32.0
208
+
209
+ **A canvas title fits its box.** `CanvasBoard` sets a title note in a size fitted to its box both ways (three fifths of its height, smaller when its text would run past its width; `titleSize` is that rule on its own), and the text clips at the box where an unusually wide face still overruns it. A long heading used to run past its box over the boards beside it, at every zoom.
210
+
211
+ **`SlideStage` names what it holds.** `roleDescription` replaces the screen reader's "slide" for a stage that holds something else, such as a design's board shown at its own layout ("artboard").
212
+
213
+ **`CanvasBoard` works like a design tool.** A floating palette (`CanvasToolbar`) holds Move (`V`) and Hand (`H`, or Space held), and on an editable canvas with `onNoteCreate` also Text (`T`) and Sticky (`S`), which place a note where the field is clicked, open it for typing and hand back Move. Under Move a click selects a board or a note and a click on the field clears it; a double-click hands a live board's page the pointer (a single click used to), Escape gives it back and a second Escape clears the selection. What a press will do next ("Double-click to use this board", "Press Escape to return to the canvas") is said in the canvas's status note beside the zoom bar, no longer under the board. A board's edge is a one pixel hairline at every zoom, drawn outside the page so it no longer narrows it by two pixels; hover outlines a board in the accent (hovering its name does too) and selection outlines it thicker. A selected item shows four resize corners: the bottom right takes the arrow keys as the old corner did, and the other three are a pointer's extra reach. A note is dragged by its paper, retyped on a double-click and deleted with Delete while selected. The fit goes down to `CANVAS_BOARD_MIN_ZOOM` (0.02), so a design laid out across many full size boards is shown whole rather than clipped at a quarter. `toolbar={false}` leaves the palette out; its keys still work.
214
+
215
+ What to change:
216
+
217
+ - A caller that hands a board the pointer on a single click should expect a double-click now.
218
+ - A caller with its own "add a note" controls passes `onNoteCreate(kind, at)` instead, adds the note at that canvas point and returns its id.
219
+ - A sticky's paper and a title's box no longer clip, so their tools and corners show; their text clips instead.
220
+
221
+ **`CanvasToolbar` is a canvas's tool palette.** Toggle buttons in a `Toolbar`, one pressed, each with a tip naming its key; `toolForKey` matches a press to a tool, for the surface that owns the keys.
222
+
223
+ **`Canvas` takes a pointer, a fit floor and actual size.** `cursor` (`hand`, `pointer`, `crosshair` or `text`) picks the pointer the field shows, from drawn cursors (`CURSORS` in `@crewlethq/icons/glyphs`) with keyword fallbacks, and a pan always shows the closed hand. `minZoom` lowers the floor the fit stops at (`fitView` and `zoomLimits` take it as `floor`). Shift 0 shows actual size and Shift 1 fits, on every canvas; `controlsActualSize` adds a 1:1 control to the zoom bar. Each zoom control's tip names its key (`ShortcutTip`, for any control that has one). The handle adds `zoomTo(scale)` and `worldAt(clientPoint)`. With `refitOnResize` (on in `CanvasBoard`), a resize refits a view nobody has moved since its last fit, so a window made narrower no longer cuts the content off; a view the reader has panned or zoomed is kept, as before.
224
+
225
+ **`@crewlethq/icons` draws pointer cursors.** `CURSORS` holds the pointer, hand, grabbing, crosshair and text cursors as complete CSS `cursor` values, generated from the vendored Lucide glyphs (`hand`, `hand-grab` and `text-cursor` are new, with `type`) in black over a white halo, as every platform draws its own pointer.
226
+
227
+ ## Changes in 0.31.1
228
+
229
+ **`EditingStatus` folds "Editing" on an artifact bar's narrow arrangement**, below 640px, and keeps its dot, so a phone's bar keeps the room for the title while Cancel and Save are there. The words stay in the status for a screen reader.
230
+
207
231
  ## Changes in 0.31.0
208
232
 
209
233
  **`ArtifactBar` carries an editing state**: a `status` slot said before the kind's tools (an `EditingStatus` while a doc is edited), never folded away, and a tool's `emphasis: 'primary'`, drawn as the primary button with its word kept at every width, for the one action the state turns on, such as Save.
@@ -56,7 +56,13 @@
56
56
  * - FIT HAPPENS ONCE, on the first layout that has both a measured viewport
57
57
  * and content bounds, and again only on request. A data push that changes
58
58
  * the content never moves the operator's view, and nothing is shown before
59
- * that first fit.
59
+ * that first fit. With `refitOnResize`, a RESIZE refits a view nobody has
60
+ * moved since its last fit: that view was the whole content, and a window
61
+ * made narrower that kept the old zoom would cut the content off for a
62
+ * reader who never asked to look at less of it. Once the reader has panned
63
+ * or zoomed, a resize keeps their view and only keeps the content
64
+ * reachable. It is opt in because a screen that chooses its own views
65
+ * after a fit (a chart held at a legible zoom) decides its own refits.
60
66
  * - MOTION ONLY ON REQUEST. A fit, a zoom button or a reveal eases; a drag, a
61
67
  * pinch, a wheel and a relayout anchor never do, and under reduced motion
62
68
  * nothing does. An easing lasts as long as its transition: a clamp that
@@ -85,6 +91,8 @@ export interface CanvasHandle {
85
91
  fit(): void;
86
92
  /** Zoom about the viewport centre. */
87
93
  zoomBy(factor: number): void;
94
+ /** Zoom about the viewport centre to one scale, 1 being actual size, within the limits. */
95
+ zoomTo(scale: number): void;
88
96
  /** Pan the least distance that brings a world rectangle into view. */
89
97
  reveal(target: CanvasRect): void;
90
98
  /** Keep a node still on screen across a relayout: see `anchorView`. */
@@ -125,12 +133,28 @@ export interface CanvasHandle {
125
133
  restoreView(): void;
126
134
  /** A world rectangle in viewport coordinates, for positioning an overlay. */
127
135
  screenRect(target: CanvasRect): CanvasRect;
136
+ /**
137
+ * The world point under a point on the page, such as a click's
138
+ * `clientX` and `clientY`: where a tool that places something puts it.
139
+ */
140
+ worldAt(client: CanvasPoint): CanvasPoint;
128
141
  }
142
+ /**
143
+ * The pointer the field shows while nothing is being dragged.
144
+ *
145
+ * `hand` says the field pans, which is what a chart moved by dragging it
146
+ * wants. A surface with tools names the one its tool means: `pointer` for
147
+ * selecting, `crosshair` for placing a thing, `text` for placing text. A pan
148
+ * in progress always shows the closed hand, whatever the tool.
149
+ */
150
+ export type CanvasCursor = 'hand' | 'pointer' | 'crosshair' | 'text';
129
151
  /** Every string the canvas renders on its own. */
130
152
  export interface CanvasLabels {
131
153
  zoomIn: string;
132
154
  zoomOut: string;
133
155
  fit: string;
156
+ /** The control that shows the content at actual size. */
157
+ actualSize: string;
134
158
  /**
135
159
  * The zoom readout, given the zoom as a whole percentage. It is also what
136
160
  * opens the box a reader types one into, so it says both.
@@ -184,6 +208,12 @@ export interface CanvasProps {
184
208
  * acts on the canvas from 800px away is a control nobody finds.
185
209
  */
186
210
  controlsExtra?: ReactNode | undefined;
211
+ /**
212
+ * A control in the zoom bar that shows the content at actual size, which
213
+ * Shift 0 does whether or not it is drawn. For a canvas of pages, where a
214
+ * page at its own size is a view a reader asks for by name.
215
+ */
216
+ controlsActualSize?: boolean | undefined;
187
217
  /**
188
218
  * Bars of the caller's own, stacked under the control bar in the same
189
219
  * corner: a switch between two drawings of the content, a note about a key.
@@ -231,6 +261,22 @@ export interface CanvasProps {
231
261
  * is the page's own ground and nothing else.
232
262
  */
233
263
  ground?: CanvasGround | undefined;
264
+ /**
265
+ * The smallest zoom a fit may take: see [fitView]. [CANVAS_MIN_ZOOM] unless
266
+ * it is named, which suits a chart of cards; a canvas of pages names a
267
+ * lower one, so that a fit shows all of a design however wide it is laid.
268
+ */
269
+ minZoom?: number | undefined;
270
+ /** The pointer the field shows: see [CanvasCursor]. */
271
+ cursor?: CanvasCursor | undefined;
272
+ /**
273
+ * Refit on a resize while the view is still the last fit, rather than keep
274
+ * the zoom the old size was fitted at. For a surface whose fit is the view
275
+ * a reader expects back whatever the window does, such as a design's
276
+ * boards. Off by default: a screen that adjusts the view after each fit
277
+ * (holding a zoom, revealing a card) runs its own refit.
278
+ */
279
+ refitOnResize?: boolean | undefined;
234
280
  /**
235
281
  * Whether one finger can pan the canvas at all.
236
282
  *
@@ -257,4 +303,4 @@ export interface CanvasProps {
257
303
  ref?: Ref<CanvasHandle> | undefined;
258
304
  className?: string | undefined;
259
305
  }
260
- export declare function Canvas({ label, content, children, overlay, controls, controlsPlacement, controlsExtra, controlsBelow, hint, largestItemWidth, dimmed, ground, touch, labels, onReady, onViewChange, ref, className, }: CanvasProps): import("react").JSX.Element;
306
+ export declare function Canvas({ label, content, children, overlay, controls, controlsPlacement, controlsExtra, controlsActualSize, controlsBelow, hint, largestItemWidth, dimmed, ground, minZoom, cursor, refitOnResize, touch, labels, onReady, onViewChange, ref, className, }: CanvasProps): import("react").JSX.Element;
@@ -0,0 +1,22 @@
1
+ /**
2
+ * A control's tooltip that names its keyboard shortcut: "Zoom in" and the key
3
+ * that does it, drawn as keycaps.
4
+ *
5
+ * Every control on a canvas has a key, and a reader who has to hover each one
6
+ * to learn its key is a reader who is told it the moment it is useful. The tip
7
+ * describes the control rather than naming it (see Tooltip), so a screen reader
8
+ * hears the control's own name and then its shortcut, which the keycaps read
9
+ * aloud through Kbd's spoken form.
10
+ */
11
+ import type { ReactElement } from 'react';
12
+ import { type TooltipPlacement } from '../Tooltip/index.js';
13
+ export interface ShortcutTipProps {
14
+ /** What the control does: the words the tooltip opens with. */
15
+ label: string;
16
+ /** The keys of the shortcut, in Kbd's names: `['Shift', '1']`, `['V']`. */
17
+ keys: readonly string[];
18
+ placement?: TooltipPlacement | undefined;
19
+ /** Exactly one element: the control. */
20
+ children: ReactElement;
21
+ }
22
+ export declare function ShortcutTip({ label, keys, placement, children }: ShortcutTipProps): import("react").JSX.Element;
@@ -160,8 +160,10 @@ export declare const CANVAS_ZOOM_LIMITS: CanvasZoomLimits;
160
160
  * width in world units of the widest one, which the chart above this knows
161
161
  * because it measured every card; with none given the fixed ceiling stands. It
162
162
  * never goes below that fixed ceiling and never above [CANVAS_ZOOM_CEILING].
163
+ *
164
+ * `floor` is the fit's own floor, passed through to [fitView].
163
165
  */
164
- export declare function zoomLimits(content: CanvasRect | null, viewport: CanvasSize, largestItem?: number, padding?: number): CanvasZoomLimits;
166
+ export declare function zoomLimits(content: CanvasRect | null, viewport: CanvasSize, largestItem?: number, padding?: number, floor?: number): CanvasZoomLimits;
165
167
  export declare function clampZoom(k: number, limits?: CanvasZoomLimits): number;
166
168
  /** Where a world point is drawn, in viewport coordinates. */
167
169
  export declare function toScreen(view: CanvasView, point: CanvasPoint): CanvasPoint;
@@ -177,8 +179,16 @@ export declare function zoomAt(view: CanvasView, factor: number, focus: CanvasPo
177
179
  * screen on each axis. A view that already satisfies it comes back unchanged.
178
180
  */
179
181
  export declare function clampPan(view: CanvasView, content: CanvasRect, viewport: CanvasSize, margin?: number): CanvasView;
180
- /** The view that shows all of `content`, centred, at no more than actual size. */
181
- export declare function fitView(content: CanvasRect, viewport: CanvasSize, padding?: number): CanvasView;
182
+ /**
183
+ * The view that shows all of `content`, centred, at no more than actual size.
184
+ *
185
+ * `floor` is the smallest zoom the fit may take, [CANVAS_MIN_ZOOM] unless it
186
+ * is named. A chart of cards keeps that floor, because below it a card is a
187
+ * grey smudge and panning the overflow is the better view. A canvas of pages
188
+ * names a lower one: a design laid out across many 1440px boards is several
189
+ * screens wide, and a fit that stops at a quarter clips it on every side.
190
+ */
191
+ export declare function fitView(content: CanvasRect, viewport: CanvasSize, padding?: number, floor?: number): CanvasView;
182
192
  /** The smallest pan that brings world rectangle `target` inside the padded viewport. */
183
193
  export declare function revealView(view: CanvasView, target: CanvasRect, viewport: CanvasSize, padding?: number): CanvasView;
184
194
  /**
@@ -1,5 +1,7 @@
1
1
  import './Canvas.css';
2
2
  export { Canvas } from './Canvas.js';
3
- export type { CanvasGround, CanvasHandle, CanvasLabels, CanvasProps } from './Canvas.js';
3
+ export type { CanvasCursor, CanvasGround, CanvasHandle, CanvasLabels, CanvasProps } from './Canvas.js';
4
+ export { ShortcutTip } from './ShortcutTip.js';
5
+ export type { ShortcutTipProps } from './ShortcutTip.js';
4
6
  export { CANVAS_COMPOSE_CONTEXT, CANVAS_FIT_MAX_ZOOM, CANVAS_FIT_PADDING, CANVAS_FOCUS_CONTEXT, CANVAS_MAX_ZOOM, CANVAS_MIN_ZOOM, CANVAS_PAN_MARGIN, CANVAS_PAN_STEP, CANVAS_REVEAL_PADDING, CANVAS_TAP_SLOP, CANVAS_WHEEL_LINE_PX, CANVAS_WHEEL_ZOOM_PER_PIXEL, CANVAS_ZOOM_CEILING, CANVAS_ZOOM_LIMITS, CANVAS_ZOOM_STEP, IDENTITY_VIEW, anchorView, beyondSlop, clampPan, clampZoom, fitView, panBy, pinchView, regionView, revealView, screenRect, toScreen, toWorld, wheelPixels, wheelZoomFactor, zoomAt, zoomLimits, } from './geometry.js';
5
7
  export type { CanvasPoint, CanvasRect, CanvasSize, CanvasView, CanvasZoomLimits, PinchStart, WheelLike, } from './geometry.js';
@@ -1,5 +1,7 @@
1
- export { CANVAS_COMPOSE_CONTEXT, CANVAS_FIT_MAX_ZOOM, CANVAS_FIT_PADDING, CANVAS_FOCUS_CONTEXT, CANVAS_MAX_ZOOM, CANVAS_MIN_ZOOM, CANVAS_PAN_MARGIN, CANVAS_PAN_STEP, CANVAS_REVEAL_PADDING, CANVAS_TAP_SLOP, CANVAS_WHEEL_LINE_PX, CANVAS_WHEEL_ZOOM_PER_PIXEL, CANVAS_ZOOM_CEILING, CANVAS_ZOOM_LIMITS, CANVAS_ZOOM_STEP, Canvas, IDENTITY_VIEW, anchorView, beyondSlop, clampPan, clampZoom, fitView, panBy, pinchView, regionView, revealView, screenRect, toScreen, toWorld, wheelPixels, wheelZoomFactor, zoomAt, zoomLimits } from '../chunk-OJ45PPCV.js';
1
+ export { CANVAS_COMPOSE_CONTEXT, CANVAS_FIT_MAX_ZOOM, CANVAS_FIT_PADDING, CANVAS_FOCUS_CONTEXT, CANVAS_MAX_ZOOM, CANVAS_MIN_ZOOM, CANVAS_PAN_MARGIN, CANVAS_PAN_STEP, CANVAS_REVEAL_PADDING, CANVAS_TAP_SLOP, CANVAS_WHEEL_LINE_PX, CANVAS_WHEEL_ZOOM_PER_PIXEL, CANVAS_ZOOM_CEILING, CANVAS_ZOOM_LIMITS, CANVAS_ZOOM_STEP, Canvas, IDENTITY_VIEW, ShortcutTip, anchorView, beyondSlop, clampPan, clampZoom, fitView, panBy, pinchView, regionView, revealView, screenRect, toScreen, toWorld, wheelPixels, wheelZoomFactor, zoomAt, zoomLimits } from '../chunk-OTN35CI4.js';
2
2
  import '../chunk-VYOKRN2I.js';
3
+ import '../chunk-SMBKXWML.js';
4
+ import '../chunk-QWOPX2MN.js';
3
5
  import '../chunk-HMPFGIWM.js';
4
6
  import '../chunk-R25RGPUB.js';
5
7
  import '../chunk-OG6CL3EB.js';
@@ -37,7 +37,7 @@
37
37
  position: absolute;
38
38
  inset: 0;
39
39
  overflow: hidden;
40
- cursor: grab;
40
+ cursor: var(--crewlet-canvas-cursor-hand, grab);
41
41
  /* Until it is tapped, one finger belongs to the page's vertical scroll. */
42
42
  touch-action: pan-y;
43
43
  }
@@ -55,8 +55,28 @@
55
55
  touch-action: none;
56
56
  }
57
57
 
58
- .crewlet-canvas[data-panning='true'] .crewlet-canvas__viewport {
59
- cursor: grabbing;
58
+ /*
59
+ * THE POINTER SAYS WHAT A PRESS WILL DO. The drawings are the component's
60
+ * (`CURSORS`, written on the root), each falling back to its keyword. A pan in
61
+ * progress shows the closed hand whatever the tool, and it is set on the whole
62
+ * canvas rather than the field, so a drag that crosses a board or a note keeps
63
+ * the hand it started with.
64
+ */
65
+ .crewlet-canvas[data-cursor='pointer'] .crewlet-canvas__viewport {
66
+ cursor: var(--crewlet-canvas-cursor-pointer, default);
67
+ }
68
+
69
+ .crewlet-canvas[data-cursor='crosshair'] .crewlet-canvas__viewport {
70
+ cursor: var(--crewlet-canvas-cursor-crosshair, crosshair);
71
+ }
72
+
73
+ .crewlet-canvas[data-cursor='text'] .crewlet-canvas__viewport {
74
+ cursor: var(--crewlet-canvas-cursor-text, text);
75
+ }
76
+
77
+ .crewlet-canvas[data-panning='true'] .crewlet-canvas__viewport,
78
+ .crewlet-canvas[data-panning='true'] .crewlet-canvas__viewport * {
79
+ cursor: var(--crewlet-canvas-cursor-grabbing, grabbing);
60
80
  user-select: none;
61
81
  }
62
82
 
@@ -261,6 +281,22 @@
261
281
  border-radius: var(--radius-chip);
262
282
  }
263
283
 
284
+ /* Actual size, as a ratio: the readout's own control, at the button step. */
285
+ .crewlet-canvas__actual {
286
+ width: var(--size-control-sm);
287
+ }
288
+
289
+ /*
290
+ * A control's tip with its shortcut (ShortcutTip.tsx): the words, then the
291
+ * keycaps, on one line.
292
+ */
293
+ .crewlet-shortcut-tip {
294
+ display: inline-flex;
295
+ align-items: center;
296
+ gap: var(--spacing-2);
297
+ white-space: nowrap;
298
+ }
299
+
264
300
  .crewlet-canvas__zoom:hover:not(:disabled) {
265
301
  color: var(--color-text-primary);
266
302
  background: var(--color-surface-hover);
@@ -11,23 +11,40 @@
11
11
  * [CANVAS_BOARD_MAX_LIVE] at once; every other board draws as a placeholder of
12
12
  * its size, so a canvas of sixty boards runs a dozen pages, not sixty.
13
13
  *
14
+ * TOOLS, AS A DESIGN TOOL HAS THEM (CanvasToolbar). Move (`V`) selects: a
15
+ * click on a board or a note selects it, a click on the field clears it, and
16
+ * the pointer is the arrow. Hand (`H`, or Space held) pans whatever is under
17
+ * the pointer, boards included, with the open hand. An editable canvas that
18
+ * can create notes adds Text (`T`) and Sticky (`S`): a click on the field
19
+ * places one there, opens it for typing, and hands back the Move tool. The
20
+ * pointer is always the tool's (the Canvas cursors), and a pan in progress is
21
+ * always the closed hand.
22
+ *
14
23
  * A PAGE INSIDE A BOARD NEVER TAKES THE CANVAS'S POINTER BY SURPRISE. A live
15
24
  * board is covered by a shield that pans the canvas like the field around it;
16
- * a click (not a drag) on it activates that board, and only then does the page
17
- * receive the pointer. Escape gives the pointer back to the canvas.
25
+ * a click on it selects the board, a double-click activates it, and only then
26
+ * does the page receive the pointer. Escape gives the pointer back to the
27
+ * canvas, and a second Escape clears the selection.
28
+ *
29
+ * EDITING (`editable`). A board's name becomes its move handle and a selected
30
+ * item's corners its resize handles, by pointer or, on the bottom right
31
+ * corner, by the arrow keys (Shift for a larger step); a note is moved by its
32
+ * own paper or its handle, retyped on a double-click, and deleted with Delete
33
+ * while selected. Each finished gesture is reported once, with whole canvas
34
+ * pixels, and the caller decides what a change is (for a design, a new
35
+ * version of its canvas).
18
36
  *
19
- * EDITING (`editable`). A board's title bar becomes its move handle and a
20
- * corner its resize handle, by pointer or by the arrow keys (Shift for a larger
21
- * step); a note gains the same handles, and its text is edited in place. Each
22
- * finished gesture is reported once, with whole canvas pixels, and the caller
23
- * decides what a change is (for a design, a new version of its canvas).
37
+ * HOVER AND SELECTION ARE OUTLINES IN THE ACCENT, at one screen pixel and two,
38
+ * over a board's own hairline edge; hovering a board's name outlines its board,
39
+ * because the name is how a reader picks a board out of a crowded canvas.
24
40
  *
25
- * THE CHROME STAYS ONE SIZE ON SCREEN. Titles and handles are counter-scaled
26
- * by the zoom, so a board's label is readable and its handle is a full pointer
27
- * target whatever the zoom, while the board itself scales with the canvas.
41
+ * THE CHROME STAYS ONE SIZE ON SCREEN. Titles, edges, outlines and handles
42
+ * are counter-scaled by the zoom, so a board's label is readable, its edge is
43
+ * a hairline and its handle is a full pointer target whatever the zoom, while
44
+ * the board itself scales with the canvas.
28
45
  */
29
46
  import { type ReactNode, type Ref } from 'react';
30
- import { type CanvasHandle, type CanvasLabels } from '../Canvas/index.js';
47
+ import { type CanvasHandle, type CanvasLabels, type CanvasPoint } from '../Canvas/index.js';
31
48
  /** An artboard: an id, its frame in canvas pixels and its title. */
32
49
  export interface CanvasBoardItem {
33
50
  id: string;
@@ -41,6 +58,17 @@ export interface CanvasBoardItem {
41
58
  export type CanvasNoteColor = 'yellow' | 'pink' | 'blue' | 'green' | 'purple';
42
59
  /** Every sticky colour, the first being a sticky's default. */
43
60
  export declare const CANVAS_NOTE_COLORS: readonly ["yellow", "pink", "blue", "green", "purple"];
61
+ /**
62
+ * The type size a title is set in, in canvas pixels: as large as its box's
63
+ * height allows (three fifths of it), and no larger than lets its text run
64
+ * the box's width on one line, measured at an average glyph of
65
+ * [TITLE_GLYPH_EM] of the size. A long title is set smaller rather than run
66
+ * past its box over the boards beside it; the box clips whatever an unusually
67
+ * wide face still overruns.
68
+ */
69
+ export declare function titleSize(note: Pick<CanvasBoardNote, 'w' | 'h' | 'text'>): number;
70
+ /** The average advance of a title's glyph, as a share of its size. */
71
+ export declare const TITLE_GLYPH_EM = 0.58;
44
72
  /** A note: a title over a group of boards, or a sticky beside them. */
45
73
  export interface CanvasBoardNote {
46
74
  id: string;
@@ -79,7 +107,32 @@ export interface CanvasBoardLabels {
79
107
  paused: string;
80
108
  /** Said by an active board, so a reader knows how to give the pointer back. */
81
109
  active: string;
110
+ /** Said by a selected live board, so a reader knows how to use its page. */
111
+ interact: string;
112
+ /** The tool palette's name. */
113
+ tools: string;
114
+ /** Each tool's name. */
115
+ moveTool: string;
116
+ handTool: string;
117
+ textTool: string;
118
+ stickyTool: string;
82
119
  }
120
+ /**
121
+ * The tools a board canvas offers. Move and Hand always; Text and Sticky only
122
+ * where a person may edit and the caller places notes (`onNoteCreate`).
123
+ *
124
+ * NO FRAME TOOL. A board is a page an agent or an upload made, with a file
125
+ * behind it, and an empty frame drawn here would be a board with nothing to
126
+ * render. NO COMMENT TOOL until a canvas has comments to anchor.
127
+ */
128
+ export type CanvasBoardTool = 'move' | 'hand' | 'text' | 'sticky';
129
+ /**
130
+ * The smallest zoom a board canvas fits at. A design laid out across many
131
+ * full-size pages is many screens wide, and every board has to be on screen
132
+ * at the fit, so the floor is far below a chart's; the visibility budget is
133
+ * what keeps a canvas this far out cheap.
134
+ */
135
+ export declare const CANVAS_BOARD_MIN_ZOOM = 0.02;
83
136
  export interface CanvasBoardProps {
84
137
  /** The accessible name of the canvas, such as "Launch landing page". */
85
138
  label: string;
@@ -102,16 +155,26 @@ export interface CanvasBoardProps {
102
155
  onNoteChange?: ((note: CanvasBoardNote) => void) | undefined;
103
156
  /** A note was deleted. */
104
157
  onNoteRemove?: ((id: string) => void) | undefined;
158
+ /**
159
+ * The Text or Sticky tool placed a note at a canvas point (its top left, in
160
+ * canvas pixels). The caller adds the note, with whatever size and starting
161
+ * text it gives new notes, and returns its id, which the canvas then opens
162
+ * for typing. Absent, or on a canvas that is not editable, neither tool is
163
+ * offered.
164
+ */
165
+ onNoteCreate?: ((kind: CanvasBoardNote['kind'], at: CanvasPoint) => string | undefined) | undefined;
105
166
  /** The control that opens one board full size; absent, there is none. */
106
167
  onOpen?: ((id: string) => void) | undefined;
107
168
  /** The board that holds the pointer changed; null is the canvas itself. */
108
169
  onActivate?: ((id: string | null) => void) | undefined;
109
170
  /** Anything else in the canvas's control bar. */
110
171
  controlsExtra?: ReactNode | undefined;
172
+ /** The floating tool palette. On by default; the keys work either way. */
173
+ toolbar?: boolean | undefined;
111
174
  labels?: Partial<CanvasBoardLabels> | undefined;
112
175
  canvasLabels?: Partial<CanvasLabels> | undefined;
113
176
  /** The canvas underneath, for a caller that reveals or fits a board. */
114
177
  canvasRef?: Ref<CanvasHandle> | undefined;
115
178
  className?: string | undefined;
116
179
  }
117
- export declare function CanvasBoard({ label, boards, notes, renderBoard, maxLive, minLiveWidth, editable, minSide, maxSide, onBoardChange, onNoteChange, onNoteRemove, onOpen, onActivate, controlsExtra, labels, canvasLabels, canvasRef, className, }: CanvasBoardProps): import("react").JSX.Element;
180
+ export declare function CanvasBoard({ label, boards, notes, renderBoard, maxLive, minLiveWidth, editable, minSide, maxSide, onBoardChange, onNoteChange, onNoteRemove, onNoteCreate, onOpen, onActivate, controlsExtra, toolbar, labels, canvasLabels, canvasRef, className, }: CanvasBoardProps): import("react").JSX.Element;
@@ -1,5 +1,5 @@
1
1
  import './CanvasBoard.css';
2
- export { CanvasBoard, CANVAS_NOTE_COLORS } from './CanvasBoard.js';
3
- export type { CanvasBoardItem, CanvasBoardLabels, CanvasBoardNote, CanvasBoardProps, CanvasBoardRect, CanvasNoteColor, } from './CanvasBoard.js';
2
+ export { CanvasBoard, CANVAS_BOARD_MIN_ZOOM, CANVAS_NOTE_COLORS, TITLE_GLYPH_EM, titleSize } from './CanvasBoard.js';
3
+ export type { CanvasBoardItem, CanvasBoardLabels, CanvasBoardNote, CanvasBoardProps, CanvasBoardRect, CanvasBoardTool, CanvasNoteColor, } from './CanvasBoard.js';
4
4
  export { CANVAS_BOARD_MAX_LIVE, CANVAS_BOARD_MIN_LIVE_WIDTH, liveBoards } from './budget.js';
5
5
  export type { BoardFrame } from './budget.js';
@@ -1,7 +1,11 @@
1
- export { CANVAS_BOARD_MAX_LIVE, CANVAS_BOARD_MIN_LIVE_WIDTH, CANVAS_NOTE_COLORS, CanvasBoard, liveBoards } from '../chunk-JODJS7MR.js';
1
+ export { CANVAS_BOARD_MAX_LIVE, CANVAS_BOARD_MIN_LIVE_WIDTH, CANVAS_BOARD_MIN_ZOOM, CANVAS_NOTE_COLORS, CanvasBoard, TITLE_GLYPH_EM, liveBoards, titleSize } from '../chunk-PJD2KFHX.js';
2
2
  import '../chunk-CJKMURZO.js';
3
- import '../chunk-OJ45PPCV.js';
3
+ import '../chunk-E6EWK5YY.js';
4
+ import '../chunk-PGRPNDWM.js';
5
+ import '../chunk-OTN35CI4.js';
4
6
  import '../chunk-VYOKRN2I.js';
7
+ import '../chunk-SMBKXWML.js';
8
+ import '../chunk-QWOPX2MN.js';
5
9
  import '../chunk-HMPFGIWM.js';
6
10
  import '../chunk-R25RGPUB.js';
7
11
  import '../chunk-OG6CL3EB.js';