@scrawl-board/board 0.1.0-beta.8 → 0.1.0-beta.9
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/dist/browser.js +14 -6
- package/dist/index.d.ts +6 -17
- package/dist/index.js +155 -48
- package/dist/react.d.ts +6 -17
- package/dist/react.js +155 -48
- package/dist/styles.css +47 -47
- package/package.json +1 -1
package/dist/browser.js
CHANGED
|
@@ -5161,10 +5161,18 @@ function pointerToPlane(clientX, clientY, canvas, camera, z) {
|
|
|
5161
5161
|
};
|
|
5162
5162
|
}
|
|
5163
5163
|
//#endregion
|
|
5164
|
+
//#region src/renderer/canvas/displayColor.ts
|
|
5165
|
+
function displayColor(hex) {
|
|
5166
|
+
return new THREE.Color().setStyle(hex, THREE.NoColorSpace);
|
|
5167
|
+
}
|
|
5168
|
+
function setDisplayColor(color, hex) {
|
|
5169
|
+
color.setStyle(hex, THREE.NoColorSpace);
|
|
5170
|
+
}
|
|
5171
|
+
//#endregion
|
|
5164
5172
|
//#region src/renderer/canvas/fog.ts
|
|
5165
5173
|
function createFogUniforms(color) {
|
|
5166
5174
|
return {
|
|
5167
|
-
uFogColor: { value:
|
|
5175
|
+
uFogColor: { value: displayColor(color) },
|
|
5168
5176
|
uFogNear: { value: 100 },
|
|
5169
5177
|
uFogFar: { value: 900 }
|
|
5170
5178
|
};
|
|
@@ -5301,9 +5309,9 @@ function createBoardSurface(fog, theme) {
|
|
|
5301
5309
|
vertexShader: VERTEX$1,
|
|
5302
5310
|
fragmentShader: FRAGMENT,
|
|
5303
5311
|
uniforms: {
|
|
5304
|
-
uColor: { value:
|
|
5312
|
+
uColor: { value: displayColor(theme.color) },
|
|
5305
5313
|
uGridMode: { value: GRID_MODE_CODE[theme.gridMode] },
|
|
5306
|
-
uGridColor: { value:
|
|
5314
|
+
uGridColor: { value: displayColor(theme.gridColor) },
|
|
5307
5315
|
uGridSpacing: { value: Math.max(.001, theme.gridSpacing) },
|
|
5308
5316
|
uGridLineWidthPx: { value: Math.max(.1, theme.gridLineWidth) },
|
|
5309
5317
|
uWorldPerPixel: { value: .05 },
|
|
@@ -5319,9 +5327,9 @@ function createBoardSurface(fog, theme) {
|
|
|
5319
5327
|
/** Applies a theme change (initial or live) — everything except the per-frame fade/pixel-ratio. */
|
|
5320
5328
|
function setBoardSurfaceTheme(mesh, theme) {
|
|
5321
5329
|
const uniforms = mesh.material.uniforms;
|
|
5322
|
-
uniforms.uColor.value
|
|
5330
|
+
setDisplayColor(uniforms.uColor.value, theme.color);
|
|
5323
5331
|
uniforms.uGridMode.value = GRID_MODE_CODE[theme.gridMode];
|
|
5324
|
-
uniforms.uGridColor.value
|
|
5332
|
+
setDisplayColor(uniforms.uGridColor.value, theme.gridColor);
|
|
5325
5333
|
uniforms.uGridSpacing.value = Math.max(.001, theme.gridSpacing);
|
|
5326
5334
|
uniforms.uGridLineWidthPx.value = Math.max(.1, theme.gridLineWidth);
|
|
5327
5335
|
uniforms.uSurfaceTexture.value = TEXTURE_CODE[theme.surfaceTexture];
|
|
@@ -5674,7 +5682,7 @@ var InkMaterials = class {
|
|
|
5674
5682
|
vertexShader: VERTEX,
|
|
5675
5683
|
fragmentShader,
|
|
5676
5684
|
uniforms: {
|
|
5677
|
-
uColor: { value:
|
|
5685
|
+
uColor: { value: displayColor(color) },
|
|
5678
5686
|
...this.fog
|
|
5679
5687
|
},
|
|
5680
5688
|
transparent: true,
|
package/dist/index.d.ts
CHANGED
|
@@ -2910,16 +2910,8 @@ interface DefaultBoardChromeProps {
|
|
|
2910
2910
|
regions?: Partial<Record<DefaultUIRegion, boolean>>;
|
|
2911
2911
|
slots?: DefaultUISlots;
|
|
2912
2912
|
/**
|
|
2913
|
-
*
|
|
2914
|
-
*
|
|
2915
|
-
* `<svg>`, whatever). A tool with no entry here keeps its default text
|
|
2916
|
-
* label, so this is opt-in per tool, not all-or-nothing. Aria labeling
|
|
2917
|
-
* and the keyboard-shortcut tooltip stay text-based regardless, so the
|
|
2918
|
-
* board remains fully accessible even with icon-only buttons.
|
|
2919
|
-
*
|
|
2920
|
-
* Only affects the six primary toolbar buttons (select/marker/
|
|
2921
|
-
* highlighter/eraser/note/text) — the "more tools" overflow menu is a
|
|
2922
|
-
* native `<select>`, which can only render plain text `<option>`s.
|
|
2913
|
+
* Override any built-in tool icon. Tools without an override keep the
|
|
2914
|
+
* SDK's outline icon. Accessible labels and shortcut tooltips are preserved.
|
|
2923
2915
|
*/
|
|
2924
2916
|
icons?: Partial<Record<BuiltInTool, ReactNode>>;
|
|
2925
2917
|
}
|
|
@@ -3022,11 +3014,8 @@ interface StyleShelfProps {
|
|
|
3022
3014
|
controller: BoardController;
|
|
3023
3015
|
snapshot: BoardSnapshot;
|
|
3024
3016
|
/**
|
|
3025
|
-
* Root-relative
|
|
3026
|
-
*
|
|
3027
|
-
* `.scrawl-board__tools` toolbar. `DefaultBoardChrome` supplies this
|
|
3028
|
-
* automatically; omit it when rendering `StyleShelf` standalone (its own
|
|
3029
|
-
* layout has no toolbar button to anchor to) to keep the centred default.
|
|
3017
|
+
* Root-relative point beside the active tool. DefaultBoardChrome supplies
|
|
3018
|
+
* this automatically and keeps the shelf within the board bounds.
|
|
3030
3019
|
*/
|
|
3031
3020
|
anchor?: {
|
|
3032
3021
|
x: number;
|
|
@@ -3072,7 +3061,7 @@ interface ScrawlProps extends Omit<CreateBoardControllerOptions, "canvas"> {
|
|
|
3072
3061
|
/** Provide null for a headless Board, or an existing canvas to control its identity. */
|
|
3073
3062
|
canvas?: HTMLCanvasElement | null;
|
|
3074
3063
|
/**
|
|
3075
|
-
* Per-tool icon override for the default toolbar
|
|
3064
|
+
* Per-tool icon override for the default toolbar and More tools buttons —
|
|
3076
3065
|
* only applies when you don't supply `children` (i.e. you're using the
|
|
3077
3066
|
* SDK's default UI). See DefaultBoardChromeProps.icons.
|
|
3078
3067
|
*/
|
|
@@ -3104,7 +3093,7 @@ interface ScrawlDefaultUIProps {
|
|
|
3104
3093
|
regions?: Partial<Record<DefaultUIRegion, boolean>>;
|
|
3105
3094
|
/** Replace (component) or hide (null) a coarse region; omit for the SDK default. */
|
|
3106
3095
|
slots?: DefaultUISlots;
|
|
3107
|
-
/** Per-tool icon override for the
|
|
3096
|
+
/** Per-tool icon override for the toolbar and More tools buttons — see DefaultBoardChromeProps.icons. */
|
|
3108
3097
|
icons?: Partial<Record<BuiltInTool, ReactNode>>;
|
|
3109
3098
|
}
|
|
3110
3099
|
declare function ScrawlDefaultUI({ className, style, regions, slots, icons }: ScrawlDefaultUIProps): react.JSX.Element;
|
package/dist/index.js
CHANGED
|
@@ -5319,10 +5319,18 @@ function pointerToPlane(clientX, clientY, canvas, camera, z) {
|
|
|
5319
5319
|
};
|
|
5320
5320
|
}
|
|
5321
5321
|
//#endregion
|
|
5322
|
+
//#region src/renderer/canvas/displayColor.ts
|
|
5323
|
+
function displayColor(hex) {
|
|
5324
|
+
return new THREE.Color().setStyle(hex, THREE.NoColorSpace);
|
|
5325
|
+
}
|
|
5326
|
+
function setDisplayColor(color, hex) {
|
|
5327
|
+
color.setStyle(hex, THREE.NoColorSpace);
|
|
5328
|
+
}
|
|
5329
|
+
//#endregion
|
|
5322
5330
|
//#region src/renderer/canvas/fog.ts
|
|
5323
5331
|
function createFogUniforms(color) {
|
|
5324
5332
|
return {
|
|
5325
|
-
uFogColor: { value:
|
|
5333
|
+
uFogColor: { value: displayColor(color) },
|
|
5326
5334
|
uFogNear: { value: 100 },
|
|
5327
5335
|
uFogFar: { value: 900 }
|
|
5328
5336
|
};
|
|
@@ -5459,9 +5467,9 @@ function createBoardSurface(fog, theme) {
|
|
|
5459
5467
|
vertexShader: VERTEX$1,
|
|
5460
5468
|
fragmentShader: FRAGMENT,
|
|
5461
5469
|
uniforms: {
|
|
5462
|
-
uColor: { value:
|
|
5470
|
+
uColor: { value: displayColor(theme.color) },
|
|
5463
5471
|
uGridMode: { value: GRID_MODE_CODE[theme.gridMode] },
|
|
5464
|
-
uGridColor: { value:
|
|
5472
|
+
uGridColor: { value: displayColor(theme.gridColor) },
|
|
5465
5473
|
uGridSpacing: { value: Math.max(.001, theme.gridSpacing) },
|
|
5466
5474
|
uGridLineWidthPx: { value: Math.max(.1, theme.gridLineWidth) },
|
|
5467
5475
|
uWorldPerPixel: { value: .05 },
|
|
@@ -5477,9 +5485,9 @@ function createBoardSurface(fog, theme) {
|
|
|
5477
5485
|
/** Applies a theme change (initial or live) — everything except the per-frame fade/pixel-ratio. */
|
|
5478
5486
|
function setBoardSurfaceTheme(mesh, theme) {
|
|
5479
5487
|
const uniforms = mesh.material.uniforms;
|
|
5480
|
-
uniforms.uColor.value
|
|
5488
|
+
setDisplayColor(uniforms.uColor.value, theme.color);
|
|
5481
5489
|
uniforms.uGridMode.value = GRID_MODE_CODE[theme.gridMode];
|
|
5482
|
-
uniforms.uGridColor.value
|
|
5490
|
+
setDisplayColor(uniforms.uGridColor.value, theme.gridColor);
|
|
5483
5491
|
uniforms.uGridSpacing.value = Math.max(.001, theme.gridSpacing);
|
|
5484
5492
|
uniforms.uGridLineWidthPx.value = Math.max(.1, theme.gridLineWidth);
|
|
5485
5493
|
uniforms.uSurfaceTexture.value = TEXTURE_CODE[theme.surfaceTexture];
|
|
@@ -5832,7 +5840,7 @@ var InkMaterials = class {
|
|
|
5832
5840
|
vertexShader: VERTEX,
|
|
5833
5841
|
fragmentShader,
|
|
5834
5842
|
uniforms: {
|
|
5835
|
-
uColor: { value:
|
|
5843
|
+
uColor: { value: displayColor(color) },
|
|
5836
5844
|
...this.fog
|
|
5837
5845
|
},
|
|
5838
5846
|
transparent: true,
|
|
@@ -23048,6 +23056,50 @@ function LockDuplicateDelete({ controller, mutable, locked, ids }) {
|
|
|
23048
23056
|
});
|
|
23049
23057
|
}
|
|
23050
23058
|
//#endregion
|
|
23059
|
+
//#region src/ui/toolbar/tool-icon.tsx
|
|
23060
|
+
var PATHS = {
|
|
23061
|
+
select: "m5 3 14 9-7 1-3 7Z",
|
|
23062
|
+
marker: "m4 16 11-11 4 4L8 20H4Zm9-9 4 4",
|
|
23063
|
+
highlighter: "m5 14 8-10 6 5-8 10Zm0 0 6 5M4 20h8",
|
|
23064
|
+
eraser: "m3 14 9-10 9 8-7 8H9Zm4-4 9 8M14 20h7",
|
|
23065
|
+
note: "M5 3h14v12l-6 6H5ZM13 21v-6h6",
|
|
23066
|
+
text: "M4 5h16M12 5v15M8 20h8M4 5v3M20 5v3",
|
|
23067
|
+
pan: "M8 12V5a2 2 0 0 1 4 0v6-7a2 2 0 0 1 4 0v7-5a2 2 0 0 1 4 0v9c0 8-11 9-14 3l-3-5a2 2 0 0 1 3-2l2 2",
|
|
23068
|
+
table: "M3 4h18v16H3ZM3 10h18M9 4v16",
|
|
23069
|
+
image: "M3 4h18v16H3Zm0 12 5-5 5 5 3-3 5 5M15 8h.01",
|
|
23070
|
+
comment: "M4 4h16v12H9l-5 4Z",
|
|
23071
|
+
stamp: "M8 14v-3a5 5 0 1 1 8 0v3l4 3v3H4v-3ZM4 17h16",
|
|
23072
|
+
timer: "M9 2h6M12 6a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm0 3v5l3 2",
|
|
23073
|
+
"shape:line": "M4 20 20 4",
|
|
23074
|
+
"shape:arrow": "M4 20 20 4M9 4h11v11",
|
|
23075
|
+
"shape:rect": "M4 5h16v14H4Z",
|
|
23076
|
+
"shape:ellipse": "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Z",
|
|
23077
|
+
"shape:triangle": "m12 3 10 18H2Z",
|
|
23078
|
+
"shape:diamond": "m12 2 10 10-10 10L2 12Z",
|
|
23079
|
+
"shape:pentagon": "m12 2 10 8-4 12H6L2 10Z",
|
|
23080
|
+
"shape:hexagon": "m7 3 10 0 5 9-5 9H7l-5-9Z",
|
|
23081
|
+
"shape:octagon": "M8 2h8l6 6v8l-6 6H8l-6-6V8Z",
|
|
23082
|
+
"shape:star": "m12 2 3 7 7 1-5 5 1 7-6-4-6 4 1-7-5-5 7-1Z",
|
|
23083
|
+
"shape:heart": "M12 21 3 12C-3 3 8-1 12 6c4-7 15-3 9 6Z",
|
|
23084
|
+
more: "M12 4v16M4 12h16",
|
|
23085
|
+
undo: "M9 5 4 10l5 5M4 10h10a6 6 0 0 1 6 6v3",
|
|
23086
|
+
redo: "m15 5 5 5-5 5M20 10H10a6 6 0 0 0-6 6v3"
|
|
23087
|
+
};
|
|
23088
|
+
function ToolIcon({ tool }) {
|
|
23089
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
23090
|
+
"aria-hidden": "true",
|
|
23091
|
+
width: "22",
|
|
23092
|
+
height: "22",
|
|
23093
|
+
viewBox: "0 0 24 24",
|
|
23094
|
+
fill: "none",
|
|
23095
|
+
stroke: "currentColor",
|
|
23096
|
+
strokeWidth: "1.7",
|
|
23097
|
+
strokeLinecap: "round",
|
|
23098
|
+
strokeLinejoin: "round",
|
|
23099
|
+
children: /* @__PURE__ */ jsx("path", { d: PATHS[tool] ?? PATHS.more })
|
|
23100
|
+
});
|
|
23101
|
+
}
|
|
23102
|
+
//#endregion
|
|
23051
23103
|
//#region src/ui/toolbar/style-shelf.tsx
|
|
23052
23104
|
var ERASER_RADII = [
|
|
23053
23105
|
["Fine", 1],
|
|
@@ -23096,6 +23148,7 @@ function StyleShelf({ controller, snapshot, anchor }) {
|
|
|
23096
23148
|
className: "scrawl-board__style-shelf",
|
|
23097
23149
|
"data-anchored": !!anchor,
|
|
23098
23150
|
role: "toolbar",
|
|
23151
|
+
"aria-orientation": "vertical",
|
|
23099
23152
|
style: anchor ? {
|
|
23100
23153
|
left: anchor.x,
|
|
23101
23154
|
top: anchor.y
|
|
@@ -23132,17 +23185,29 @@ function StyleShelf({ controller, snapshot, anchor }) {
|
|
|
23132
23185
|
snapshot.tool === "eraser" && /* @__PURE__ */ jsxs("div", {
|
|
23133
23186
|
className: "scrawl-board__style-group",
|
|
23134
23187
|
children: [/* @__PURE__ */ jsx("span", { children: "Eraser size" }), ERASER_RADII.map(([label, radius]) => /* @__PURE__ */ jsx("button", {
|
|
23188
|
+
"aria-label": label,
|
|
23189
|
+
title: label,
|
|
23135
23190
|
"aria-pressed": style.eraserRadius === radius,
|
|
23136
23191
|
disabled: !mutable,
|
|
23137
23192
|
onClick: () => controller.style.set({ eraserRadius: radius }),
|
|
23138
23193
|
type: "button",
|
|
23139
|
-
children:
|
|
23194
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
23195
|
+
"aria-hidden": "true",
|
|
23196
|
+
style: {
|
|
23197
|
+
backgroundColor: "currentColor",
|
|
23198
|
+
width: 4 + radius * 3,
|
|
23199
|
+
height: 4 + radius * 3
|
|
23200
|
+
}
|
|
23201
|
+
})
|
|
23140
23202
|
}, label))]
|
|
23141
23203
|
}),
|
|
23142
23204
|
snapshot.tool === "table" && /* @__PURE__ */ jsxs("div", {
|
|
23143
23205
|
className: "scrawl-board__style-group",
|
|
23144
23206
|
children: [
|
|
23145
|
-
/* @__PURE__ */
|
|
23207
|
+
/* @__PURE__ */ jsxs("span", {
|
|
23208
|
+
"aria-live": "polite",
|
|
23209
|
+
children: ["Rows: ", style.tableRows]
|
|
23210
|
+
}),
|
|
23146
23211
|
/* @__PURE__ */ jsx("button", {
|
|
23147
23212
|
"aria-label": "Fewer rows",
|
|
23148
23213
|
disabled: !mutable,
|
|
@@ -23150,10 +23215,6 @@ function StyleShelf({ controller, snapshot, anchor }) {
|
|
|
23150
23215
|
type: "button",
|
|
23151
23216
|
children: "−"
|
|
23152
23217
|
}),
|
|
23153
|
-
/* @__PURE__ */ jsx("span", {
|
|
23154
|
-
"aria-live": "polite",
|
|
23155
|
-
children: style.tableRows
|
|
23156
|
-
}),
|
|
23157
23218
|
/* @__PURE__ */ jsx("button", {
|
|
23158
23219
|
"aria-label": "More rows",
|
|
23159
23220
|
disabled: !mutable,
|
|
@@ -23161,7 +23222,10 @@ function StyleShelf({ controller, snapshot, anchor }) {
|
|
|
23161
23222
|
type: "button",
|
|
23162
23223
|
children: "+"
|
|
23163
23224
|
}),
|
|
23164
|
-
/* @__PURE__ */
|
|
23225
|
+
/* @__PURE__ */ jsxs("span", {
|
|
23226
|
+
"aria-live": "polite",
|
|
23227
|
+
children: ["Cols: ", style.tableCols]
|
|
23228
|
+
}),
|
|
23165
23229
|
/* @__PURE__ */ jsx("button", {
|
|
23166
23230
|
"aria-label": "Fewer columns",
|
|
23167
23231
|
disabled: !mutable,
|
|
@@ -23169,10 +23233,6 @@ function StyleShelf({ controller, snapshot, anchor }) {
|
|
|
23169
23233
|
type: "button",
|
|
23170
23234
|
children: "−"
|
|
23171
23235
|
}),
|
|
23172
|
-
/* @__PURE__ */ jsx("span", {
|
|
23173
|
-
"aria-live": "polite",
|
|
23174
|
-
children: style.tableCols
|
|
23175
|
-
}),
|
|
23176
23236
|
/* @__PURE__ */ jsx("button", {
|
|
23177
23237
|
"aria-label": "More columns",
|
|
23178
23238
|
disabled: !mutable,
|
|
@@ -23327,6 +23387,7 @@ var INKS = [
|
|
|
23327
23387
|
];
|
|
23328
23388
|
function DefaultBoardChrome({ controller, snapshot, renderPortal, className, style, regions, slots, icons }) {
|
|
23329
23389
|
const [panel, setPanel] = useState(null);
|
|
23390
|
+
const [moreOpen, setMoreOpen] = useState(false);
|
|
23330
23391
|
const [query, setQuery] = useState("");
|
|
23331
23392
|
const [announcement, setAnnouncement] = useState("Board controls ready");
|
|
23332
23393
|
const [styleShelfAnchor, setStyleShelfAnchor] = useState(void 0);
|
|
@@ -23341,25 +23402,46 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23341
23402
|
if (panel === "search") searchRef.current?.focus();
|
|
23342
23403
|
}, [panel]);
|
|
23343
23404
|
useEffect(() => {
|
|
23405
|
+
const root = chromeRef.current;
|
|
23406
|
+
if (!root) return;
|
|
23344
23407
|
const recompute = () => {
|
|
23345
|
-
const
|
|
23346
|
-
|
|
23347
|
-
if (!root || !button) {
|
|
23408
|
+
const button = root.querySelector(".scrawl-board__tools button[aria-pressed=\"true\"]") ?? root.querySelector("[aria-label=\"More Board tools\"]");
|
|
23409
|
+
if (!button) {
|
|
23348
23410
|
setStyleShelfAnchor(void 0);
|
|
23349
23411
|
return;
|
|
23350
23412
|
}
|
|
23351
23413
|
const buttonRect = button.getBoundingClientRect();
|
|
23352
23414
|
const rootRect = root.getBoundingClientRect();
|
|
23415
|
+
const shelf = root.querySelector(".scrawl-board__style-shelf");
|
|
23353
23416
|
setStyleShelfAnchor({
|
|
23354
|
-
x: buttonRect.
|
|
23355
|
-
y: buttonRect.top - rootRect.top
|
|
23417
|
+
x: buttonRect.right - rootRect.left + 12,
|
|
23418
|
+
y: Math.max(12, Math.min(buttonRect.top - rootRect.top, rootRect.height - (shelf?.offsetHeight ?? 0) - 12))
|
|
23356
23419
|
});
|
|
23357
23420
|
};
|
|
23358
23421
|
recompute();
|
|
23422
|
+
const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(recompute);
|
|
23423
|
+
observer?.observe(root);
|
|
23424
|
+
const shelf = root.querySelector(".scrawl-board__style-shelf");
|
|
23425
|
+
if (shelf) observer?.observe(shelf);
|
|
23426
|
+
root.addEventListener("scroll", recompute, true);
|
|
23359
23427
|
window.addEventListener("resize", recompute);
|
|
23360
|
-
return () =>
|
|
23361
|
-
|
|
23428
|
+
return () => {
|
|
23429
|
+
observer?.disconnect();
|
|
23430
|
+
root.removeEventListener("scroll", recompute, true);
|
|
23431
|
+
window.removeEventListener("resize", recompute);
|
|
23432
|
+
};
|
|
23433
|
+
}, [snapshot.tool, moreOpen]);
|
|
23434
|
+
useEffect(() => {
|
|
23435
|
+
if (!moreOpen) return;
|
|
23436
|
+
chromeRef.current?.querySelector(".scrawl-board__more-tools button")?.focus();
|
|
23437
|
+
const dismiss = (event) => {
|
|
23438
|
+
if (!event.target.closest(".scrawl-board__more-tools, [aria-label=\"More Board tools\"]")) setMoreOpen(false);
|
|
23439
|
+
};
|
|
23440
|
+
document.addEventListener("pointerdown", dismiss);
|
|
23441
|
+
return () => document.removeEventListener("pointerdown", dismiss);
|
|
23442
|
+
}, [moreOpen]);
|
|
23362
23443
|
const open = (next, opener) => {
|
|
23444
|
+
setMoreOpen(false);
|
|
23363
23445
|
openerRef.current = opener;
|
|
23364
23446
|
setPanel(next);
|
|
23365
23447
|
};
|
|
@@ -23368,6 +23450,8 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23368
23450
|
queueMicrotask(() => openerRef.current?.focus());
|
|
23369
23451
|
}, []);
|
|
23370
23452
|
const chooseTool = (tool) => {
|
|
23453
|
+
if (moreOpen) chromeRef.current?.querySelector("[aria-label=\"More Board tools\"]")?.focus();
|
|
23454
|
+
setMoreOpen(false);
|
|
23371
23455
|
controller.tools.select(tool);
|
|
23372
23456
|
const label = [...PRIMARY_TOOLS, ...MORE_TOOLS].find((item) => item.tool === tool)?.label ?? tool;
|
|
23373
23457
|
setAnnouncement(`${label} tool selected`);
|
|
@@ -23375,6 +23459,13 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23375
23459
|
const onKeyDown = (event) => {
|
|
23376
23460
|
const editing = event.target.matches("input, textarea, select, [contenteditable=true]");
|
|
23377
23461
|
const modifier = event.metaKey || event.ctrlKey;
|
|
23462
|
+
if (event.key === "Escape" && moreOpen) {
|
|
23463
|
+
event.preventDefault();
|
|
23464
|
+
event.stopPropagation();
|
|
23465
|
+
setMoreOpen(false);
|
|
23466
|
+
chromeRef.current?.querySelector("[aria-label=\"More Board tools\"]")?.focus();
|
|
23467
|
+
return;
|
|
23468
|
+
}
|
|
23378
23469
|
if (event.key === "Escape" && panel) {
|
|
23379
23470
|
event.preventDefault();
|
|
23380
23471
|
event.stopPropagation();
|
|
@@ -23491,6 +23582,7 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23491
23582
|
"aria-label": "Board tools",
|
|
23492
23583
|
className: "scrawl-board__tools",
|
|
23493
23584
|
role: "toolbar",
|
|
23585
|
+
"aria-orientation": "vertical",
|
|
23494
23586
|
children: [
|
|
23495
23587
|
enabled("tools") && /* @__PURE__ */ jsxs(Fragment, { children: [PRIMARY_TOOLS.map(({ tool, label, key }) => /* @__PURE__ */ jsx("button", {
|
|
23496
23588
|
"aria-label": `${label} tool`,
|
|
@@ -23499,37 +23591,31 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23499
23591
|
onClick: () => chooseTool(tool),
|
|
23500
23592
|
title: `${label} (${key})`,
|
|
23501
23593
|
type: "button",
|
|
23502
|
-
children:
|
|
23594
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
23503
23595
|
"aria-hidden": "true",
|
|
23504
|
-
children: icons[tool]
|
|
23505
|
-
})
|
|
23506
|
-
}, tool)), /* @__PURE__ */
|
|
23507
|
-
|
|
23508
|
-
|
|
23509
|
-
|
|
23510
|
-
|
|
23511
|
-
|
|
23512
|
-
|
|
23513
|
-
|
|
23514
|
-
value: "",
|
|
23515
|
-
children: "Choose"
|
|
23516
|
-
}), MORE_TOOLS.map(({ tool, label }) => /* @__PURE__ */ jsx("option", {
|
|
23517
|
-
value: tool,
|
|
23518
|
-
children: label
|
|
23519
|
-
}, tool))]
|
|
23520
|
-
})]
|
|
23596
|
+
children: icons?.[tool] ?? /* @__PURE__ */ jsx(ToolIcon, { tool })
|
|
23597
|
+
})
|
|
23598
|
+
}, tool)), /* @__PURE__ */ jsx("button", {
|
|
23599
|
+
"aria-label": "More Board tools",
|
|
23600
|
+
"aria-expanded": moreOpen,
|
|
23601
|
+
disabled: !mutable,
|
|
23602
|
+
onClick: () => setMoreOpen((value) => !value),
|
|
23603
|
+
title: "More tools",
|
|
23604
|
+
type: "button",
|
|
23605
|
+
children: /* @__PURE__ */ jsx(ToolIcon, { tool: "more" })
|
|
23521
23606
|
})] }),
|
|
23522
|
-
enabled("style") && /* @__PURE__ */
|
|
23607
|
+
enabled("style") && /* @__PURE__ */ jsx("button", {
|
|
23523
23608
|
"aria-label": "Ink style",
|
|
23524
23609
|
"aria-expanded": panel === "style",
|
|
23525
23610
|
disabled: !mutable,
|
|
23611
|
+
title: "Ink style",
|
|
23526
23612
|
onClick: (event) => open("style", event.currentTarget),
|
|
23527
23613
|
type: "button",
|
|
23528
|
-
children:
|
|
23614
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
23529
23615
|
"aria-hidden": "true",
|
|
23530
23616
|
className: "scrawl-board__ink-sample",
|
|
23531
23617
|
style: { backgroundColor: snapshot.style.inkColor }
|
|
23532
|
-
})
|
|
23618
|
+
})
|
|
23533
23619
|
}),
|
|
23534
23620
|
enabled("history") && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
23535
23621
|
"aria-label": "Undo",
|
|
@@ -23538,8 +23624,9 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23538
23624
|
controller.history.undo();
|
|
23539
23625
|
setAnnouncement("Undo");
|
|
23540
23626
|
},
|
|
23627
|
+
title: "Undo",
|
|
23541
23628
|
type: "button",
|
|
23542
|
-
children: "
|
|
23629
|
+
children: /* @__PURE__ */ jsx(ToolIcon, { tool: "undo" })
|
|
23543
23630
|
}), /* @__PURE__ */ jsx("button", {
|
|
23544
23631
|
"aria-label": "Redo",
|
|
23545
23632
|
disabled: !snapshot.canRedo || !mutable,
|
|
@@ -23547,11 +23634,31 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23547
23634
|
controller.history.redo();
|
|
23548
23635
|
setAnnouncement("Redo");
|
|
23549
23636
|
},
|
|
23637
|
+
title: "Redo",
|
|
23550
23638
|
type: "button",
|
|
23551
|
-
children: "
|
|
23639
|
+
children: /* @__PURE__ */ jsx(ToolIcon, { tool: "redo" })
|
|
23552
23640
|
})] })
|
|
23553
23641
|
]
|
|
23554
23642
|
})),
|
|
23643
|
+
moreOpen && enabled("tools") && slots?.toolbar !== null && /* @__PURE__ */ jsx("div", {
|
|
23644
|
+
"aria-label": "More tools",
|
|
23645
|
+
className: "scrawl-board__more-tools",
|
|
23646
|
+
role: "toolbar",
|
|
23647
|
+
"aria-orientation": "vertical",
|
|
23648
|
+
children: MORE_TOOLS.map(({ tool, label }) => /* @__PURE__ */ jsx("button", {
|
|
23649
|
+
"aria-label": `${label} tool`,
|
|
23650
|
+
"aria-pressed": snapshot.tool === tool,
|
|
23651
|
+
disabled: !mutable,
|
|
23652
|
+
"data-tool": tool,
|
|
23653
|
+
onClick: () => chooseTool(tool),
|
|
23654
|
+
title: label,
|
|
23655
|
+
type: "button",
|
|
23656
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
23657
|
+
"aria-hidden": "true",
|
|
23658
|
+
children: icons?.[tool] ?? /* @__PURE__ */ jsx(ToolIcon, { tool })
|
|
23659
|
+
})
|
|
23660
|
+
}, tool))
|
|
23661
|
+
}),
|
|
23555
23662
|
enabled("view") && /* @__PURE__ */ jsxs("div", {
|
|
23556
23663
|
"aria-label": "Board view",
|
|
23557
23664
|
className: "scrawl-board__view",
|
|
@@ -23586,7 +23693,7 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23586
23693
|
})
|
|
23587
23694
|
]
|
|
23588
23695
|
}),
|
|
23589
|
-
renderSlot(slots?.stylePanel, enabled("styleShelf") && /* @__PURE__ */ jsx(StyleShelf, {
|
|
23696
|
+
renderSlot(slots?.stylePanel, enabled("styleShelf") && !moreOpen && /* @__PURE__ */ jsx(StyleShelf, {
|
|
23590
23697
|
anchor: styleShelfAnchor,
|
|
23591
23698
|
controller,
|
|
23592
23699
|
snapshot
|
package/dist/react.d.ts
CHANGED
|
@@ -1735,16 +1735,8 @@ interface DefaultBoardChromeProps {
|
|
|
1735
1735
|
regions?: Partial<Record<DefaultUIRegion, boolean>>;
|
|
1736
1736
|
slots?: DefaultUISlots;
|
|
1737
1737
|
/**
|
|
1738
|
-
*
|
|
1739
|
-
*
|
|
1740
|
-
* `<svg>`, whatever). A tool with no entry here keeps its default text
|
|
1741
|
-
* label, so this is opt-in per tool, not all-or-nothing. Aria labeling
|
|
1742
|
-
* and the keyboard-shortcut tooltip stay text-based regardless, so the
|
|
1743
|
-
* board remains fully accessible even with icon-only buttons.
|
|
1744
|
-
*
|
|
1745
|
-
* Only affects the six primary toolbar buttons (select/marker/
|
|
1746
|
-
* highlighter/eraser/note/text) — the "more tools" overflow menu is a
|
|
1747
|
-
* native `<select>`, which can only render plain text `<option>`s.
|
|
1738
|
+
* Override any built-in tool icon. Tools without an override keep the
|
|
1739
|
+
* SDK's outline icon. Accessible labels and shortcut tooltips are preserved.
|
|
1748
1740
|
*/
|
|
1749
1741
|
icons?: Partial<Record<BuiltInTool, ReactNode>>;
|
|
1750
1742
|
}
|
|
@@ -1847,11 +1839,8 @@ interface StyleShelfProps {
|
|
|
1847
1839
|
controller: BoardController;
|
|
1848
1840
|
snapshot: BoardSnapshot;
|
|
1849
1841
|
/**
|
|
1850
|
-
* Root-relative
|
|
1851
|
-
*
|
|
1852
|
-
* `.scrawl-board__tools` toolbar. `DefaultBoardChrome` supplies this
|
|
1853
|
-
* automatically; omit it when rendering `StyleShelf` standalone (its own
|
|
1854
|
-
* layout has no toolbar button to anchor to) to keep the centred default.
|
|
1842
|
+
* Root-relative point beside the active tool. DefaultBoardChrome supplies
|
|
1843
|
+
* this automatically and keeps the shelf within the board bounds.
|
|
1855
1844
|
*/
|
|
1856
1845
|
anchor?: {
|
|
1857
1846
|
x: number;
|
|
@@ -1897,7 +1886,7 @@ interface ScrawlProps extends Omit<CreateBoardControllerOptions, "canvas"> {
|
|
|
1897
1886
|
/** Provide null for a headless Board, or an existing canvas to control its identity. */
|
|
1898
1887
|
canvas?: HTMLCanvasElement | null;
|
|
1899
1888
|
/**
|
|
1900
|
-
* Per-tool icon override for the default toolbar
|
|
1889
|
+
* Per-tool icon override for the default toolbar and More tools buttons —
|
|
1901
1890
|
* only applies when you don't supply `children` (i.e. you're using the
|
|
1902
1891
|
* SDK's default UI). See DefaultBoardChromeProps.icons.
|
|
1903
1892
|
*/
|
|
@@ -1929,7 +1918,7 @@ interface ScrawlDefaultUIProps {
|
|
|
1929
1918
|
regions?: Partial<Record<DefaultUIRegion, boolean>>;
|
|
1930
1919
|
/** Replace (component) or hide (null) a coarse region; omit for the SDK default. */
|
|
1931
1920
|
slots?: DefaultUISlots;
|
|
1932
|
-
/** Per-tool icon override for the
|
|
1921
|
+
/** Per-tool icon override for the toolbar and More tools buttons — see DefaultBoardChromeProps.icons. */
|
|
1933
1922
|
icons?: Partial<Record<BuiltInTool, ReactNode>>;
|
|
1934
1923
|
}
|
|
1935
1924
|
declare function ScrawlDefaultUI({ className, style, regions, slots, icons }: ScrawlDefaultUIProps): react.JSX.Element;
|
package/dist/react.js
CHANGED
|
@@ -5203,10 +5203,18 @@ function pointerToPlane(clientX, clientY, canvas, camera, z) {
|
|
|
5203
5203
|
};
|
|
5204
5204
|
}
|
|
5205
5205
|
//#endregion
|
|
5206
|
+
//#region src/renderer/canvas/displayColor.ts
|
|
5207
|
+
function displayColor(hex) {
|
|
5208
|
+
return new THREE.Color().setStyle(hex, THREE.NoColorSpace);
|
|
5209
|
+
}
|
|
5210
|
+
function setDisplayColor(color, hex) {
|
|
5211
|
+
color.setStyle(hex, THREE.NoColorSpace);
|
|
5212
|
+
}
|
|
5213
|
+
//#endregion
|
|
5206
5214
|
//#region src/renderer/canvas/fog.ts
|
|
5207
5215
|
function createFogUniforms(color) {
|
|
5208
5216
|
return {
|
|
5209
|
-
uFogColor: { value:
|
|
5217
|
+
uFogColor: { value: displayColor(color) },
|
|
5210
5218
|
uFogNear: { value: 100 },
|
|
5211
5219
|
uFogFar: { value: 900 }
|
|
5212
5220
|
};
|
|
@@ -5343,9 +5351,9 @@ function createBoardSurface(fog, theme) {
|
|
|
5343
5351
|
vertexShader: VERTEX$1,
|
|
5344
5352
|
fragmentShader: FRAGMENT,
|
|
5345
5353
|
uniforms: {
|
|
5346
|
-
uColor: { value:
|
|
5354
|
+
uColor: { value: displayColor(theme.color) },
|
|
5347
5355
|
uGridMode: { value: GRID_MODE_CODE[theme.gridMode] },
|
|
5348
|
-
uGridColor: { value:
|
|
5356
|
+
uGridColor: { value: displayColor(theme.gridColor) },
|
|
5349
5357
|
uGridSpacing: { value: Math.max(.001, theme.gridSpacing) },
|
|
5350
5358
|
uGridLineWidthPx: { value: Math.max(.1, theme.gridLineWidth) },
|
|
5351
5359
|
uWorldPerPixel: { value: .05 },
|
|
@@ -5361,9 +5369,9 @@ function createBoardSurface(fog, theme) {
|
|
|
5361
5369
|
/** Applies a theme change (initial or live) — everything except the per-frame fade/pixel-ratio. */
|
|
5362
5370
|
function setBoardSurfaceTheme(mesh, theme) {
|
|
5363
5371
|
const uniforms = mesh.material.uniforms;
|
|
5364
|
-
uniforms.uColor.value
|
|
5372
|
+
setDisplayColor(uniforms.uColor.value, theme.color);
|
|
5365
5373
|
uniforms.uGridMode.value = GRID_MODE_CODE[theme.gridMode];
|
|
5366
|
-
uniforms.uGridColor.value
|
|
5374
|
+
setDisplayColor(uniforms.uGridColor.value, theme.gridColor);
|
|
5367
5375
|
uniforms.uGridSpacing.value = Math.max(.001, theme.gridSpacing);
|
|
5368
5376
|
uniforms.uGridLineWidthPx.value = Math.max(.1, theme.gridLineWidth);
|
|
5369
5377
|
uniforms.uSurfaceTexture.value = TEXTURE_CODE[theme.surfaceTexture];
|
|
@@ -5716,7 +5724,7 @@ var InkMaterials = class {
|
|
|
5716
5724
|
vertexShader: VERTEX,
|
|
5717
5725
|
fragmentShader,
|
|
5718
5726
|
uniforms: {
|
|
5719
|
-
uColor: { value:
|
|
5727
|
+
uColor: { value: displayColor(color) },
|
|
5720
5728
|
...this.fog
|
|
5721
5729
|
},
|
|
5722
5730
|
transparent: true,
|
|
@@ -22765,6 +22773,50 @@ function LockDuplicateDelete({ controller, mutable, locked, ids }) {
|
|
|
22765
22773
|
});
|
|
22766
22774
|
}
|
|
22767
22775
|
//#endregion
|
|
22776
|
+
//#region src/ui/toolbar/tool-icon.tsx
|
|
22777
|
+
var PATHS = {
|
|
22778
|
+
select: "m5 3 14 9-7 1-3 7Z",
|
|
22779
|
+
marker: "m4 16 11-11 4 4L8 20H4Zm9-9 4 4",
|
|
22780
|
+
highlighter: "m5 14 8-10 6 5-8 10Zm0 0 6 5M4 20h8",
|
|
22781
|
+
eraser: "m3 14 9-10 9 8-7 8H9Zm4-4 9 8M14 20h7",
|
|
22782
|
+
note: "M5 3h14v12l-6 6H5ZM13 21v-6h6",
|
|
22783
|
+
text: "M4 5h16M12 5v15M8 20h8M4 5v3M20 5v3",
|
|
22784
|
+
pan: "M8 12V5a2 2 0 0 1 4 0v6-7a2 2 0 0 1 4 0v7-5a2 2 0 0 1 4 0v9c0 8-11 9-14 3l-3-5a2 2 0 0 1 3-2l2 2",
|
|
22785
|
+
table: "M3 4h18v16H3ZM3 10h18M9 4v16",
|
|
22786
|
+
image: "M3 4h18v16H3Zm0 12 5-5 5 5 3-3 5 5M15 8h.01",
|
|
22787
|
+
comment: "M4 4h16v12H9l-5 4Z",
|
|
22788
|
+
stamp: "M8 14v-3a5 5 0 1 1 8 0v3l4 3v3H4v-3ZM4 17h16",
|
|
22789
|
+
timer: "M9 2h6M12 6a8 8 0 1 0 0 16 8 8 0 0 0 0-16Zm0 3v5l3 2",
|
|
22790
|
+
"shape:line": "M4 20 20 4",
|
|
22791
|
+
"shape:arrow": "M4 20 20 4M9 4h11v11",
|
|
22792
|
+
"shape:rect": "M4 5h16v14H4Z",
|
|
22793
|
+
"shape:ellipse": "M12 3a9 9 0 1 0 0 18 9 9 0 0 0 0-18Z",
|
|
22794
|
+
"shape:triangle": "m12 3 10 18H2Z",
|
|
22795
|
+
"shape:diamond": "m12 2 10 10-10 10L2 12Z",
|
|
22796
|
+
"shape:pentagon": "m12 2 10 8-4 12H6L2 10Z",
|
|
22797
|
+
"shape:hexagon": "m7 3 10 0 5 9-5 9H7l-5-9Z",
|
|
22798
|
+
"shape:octagon": "M8 2h8l6 6v8l-6 6H8l-6-6V8Z",
|
|
22799
|
+
"shape:star": "m12 2 3 7 7 1-5 5 1 7-6-4-6 4 1-7-5-5 7-1Z",
|
|
22800
|
+
"shape:heart": "M12 21 3 12C-3 3 8-1 12 6c4-7 15-3 9 6Z",
|
|
22801
|
+
more: "M12 4v16M4 12h16",
|
|
22802
|
+
undo: "M9 5 4 10l5 5M4 10h10a6 6 0 0 1 6 6v3",
|
|
22803
|
+
redo: "m15 5 5 5-5 5M20 10H10a6 6 0 0 0-6 6v3"
|
|
22804
|
+
};
|
|
22805
|
+
function ToolIcon({ tool }) {
|
|
22806
|
+
return /* @__PURE__ */ jsx("svg", {
|
|
22807
|
+
"aria-hidden": "true",
|
|
22808
|
+
width: "22",
|
|
22809
|
+
height: "22",
|
|
22810
|
+
viewBox: "0 0 24 24",
|
|
22811
|
+
fill: "none",
|
|
22812
|
+
stroke: "currentColor",
|
|
22813
|
+
strokeWidth: "1.7",
|
|
22814
|
+
strokeLinecap: "round",
|
|
22815
|
+
strokeLinejoin: "round",
|
|
22816
|
+
children: /* @__PURE__ */ jsx("path", { d: PATHS[tool] ?? PATHS.more })
|
|
22817
|
+
});
|
|
22818
|
+
}
|
|
22819
|
+
//#endregion
|
|
22768
22820
|
//#region src/ui/toolbar/style-shelf.tsx
|
|
22769
22821
|
var ERASER_RADII = [
|
|
22770
22822
|
["Fine", 1],
|
|
@@ -22813,6 +22865,7 @@ function StyleShelf({ controller, snapshot, anchor }) {
|
|
|
22813
22865
|
className: "scrawl-board__style-shelf",
|
|
22814
22866
|
"data-anchored": !!anchor,
|
|
22815
22867
|
role: "toolbar",
|
|
22868
|
+
"aria-orientation": "vertical",
|
|
22816
22869
|
style: anchor ? {
|
|
22817
22870
|
left: anchor.x,
|
|
22818
22871
|
top: anchor.y
|
|
@@ -22849,17 +22902,29 @@ function StyleShelf({ controller, snapshot, anchor }) {
|
|
|
22849
22902
|
snapshot.tool === "eraser" && /* @__PURE__ */ jsxs("div", {
|
|
22850
22903
|
className: "scrawl-board__style-group",
|
|
22851
22904
|
children: [/* @__PURE__ */ jsx("span", { children: "Eraser size" }), ERASER_RADII.map(([label, radius]) => /* @__PURE__ */ jsx("button", {
|
|
22905
|
+
"aria-label": label,
|
|
22906
|
+
title: label,
|
|
22852
22907
|
"aria-pressed": style.eraserRadius === radius,
|
|
22853
22908
|
disabled: !mutable,
|
|
22854
22909
|
onClick: () => controller.style.set({ eraserRadius: radius }),
|
|
22855
22910
|
type: "button",
|
|
22856
|
-
children:
|
|
22911
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
22912
|
+
"aria-hidden": "true",
|
|
22913
|
+
style: {
|
|
22914
|
+
backgroundColor: "currentColor",
|
|
22915
|
+
width: 4 + radius * 3,
|
|
22916
|
+
height: 4 + radius * 3
|
|
22917
|
+
}
|
|
22918
|
+
})
|
|
22857
22919
|
}, label))]
|
|
22858
22920
|
}),
|
|
22859
22921
|
snapshot.tool === "table" && /* @__PURE__ */ jsxs("div", {
|
|
22860
22922
|
className: "scrawl-board__style-group",
|
|
22861
22923
|
children: [
|
|
22862
|
-
/* @__PURE__ */
|
|
22924
|
+
/* @__PURE__ */ jsxs("span", {
|
|
22925
|
+
"aria-live": "polite",
|
|
22926
|
+
children: ["Rows: ", style.tableRows]
|
|
22927
|
+
}),
|
|
22863
22928
|
/* @__PURE__ */ jsx("button", {
|
|
22864
22929
|
"aria-label": "Fewer rows",
|
|
22865
22930
|
disabled: !mutable,
|
|
@@ -22867,10 +22932,6 @@ function StyleShelf({ controller, snapshot, anchor }) {
|
|
|
22867
22932
|
type: "button",
|
|
22868
22933
|
children: "−"
|
|
22869
22934
|
}),
|
|
22870
|
-
/* @__PURE__ */ jsx("span", {
|
|
22871
|
-
"aria-live": "polite",
|
|
22872
|
-
children: style.tableRows
|
|
22873
|
-
}),
|
|
22874
22935
|
/* @__PURE__ */ jsx("button", {
|
|
22875
22936
|
"aria-label": "More rows",
|
|
22876
22937
|
disabled: !mutable,
|
|
@@ -22878,7 +22939,10 @@ function StyleShelf({ controller, snapshot, anchor }) {
|
|
|
22878
22939
|
type: "button",
|
|
22879
22940
|
children: "+"
|
|
22880
22941
|
}),
|
|
22881
|
-
/* @__PURE__ */
|
|
22942
|
+
/* @__PURE__ */ jsxs("span", {
|
|
22943
|
+
"aria-live": "polite",
|
|
22944
|
+
children: ["Cols: ", style.tableCols]
|
|
22945
|
+
}),
|
|
22882
22946
|
/* @__PURE__ */ jsx("button", {
|
|
22883
22947
|
"aria-label": "Fewer columns",
|
|
22884
22948
|
disabled: !mutable,
|
|
@@ -22886,10 +22950,6 @@ function StyleShelf({ controller, snapshot, anchor }) {
|
|
|
22886
22950
|
type: "button",
|
|
22887
22951
|
children: "−"
|
|
22888
22952
|
}),
|
|
22889
|
-
/* @__PURE__ */ jsx("span", {
|
|
22890
|
-
"aria-live": "polite",
|
|
22891
|
-
children: style.tableCols
|
|
22892
|
-
}),
|
|
22893
22953
|
/* @__PURE__ */ jsx("button", {
|
|
22894
22954
|
"aria-label": "More columns",
|
|
22895
22955
|
disabled: !mutable,
|
|
@@ -23044,6 +23104,7 @@ var INKS = [
|
|
|
23044
23104
|
];
|
|
23045
23105
|
function DefaultBoardChrome({ controller, snapshot, renderPortal, className, style, regions, slots, icons }) {
|
|
23046
23106
|
const [panel, setPanel] = useState(null);
|
|
23107
|
+
const [moreOpen, setMoreOpen] = useState(false);
|
|
23047
23108
|
const [query, setQuery] = useState("");
|
|
23048
23109
|
const [announcement, setAnnouncement] = useState("Board controls ready");
|
|
23049
23110
|
const [styleShelfAnchor, setStyleShelfAnchor] = useState(void 0);
|
|
@@ -23058,25 +23119,46 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23058
23119
|
if (panel === "search") searchRef.current?.focus();
|
|
23059
23120
|
}, [panel]);
|
|
23060
23121
|
useEffect(() => {
|
|
23122
|
+
const root = chromeRef.current;
|
|
23123
|
+
if (!root) return;
|
|
23061
23124
|
const recompute = () => {
|
|
23062
|
-
const
|
|
23063
|
-
|
|
23064
|
-
if (!root || !button) {
|
|
23125
|
+
const button = root.querySelector(".scrawl-board__tools button[aria-pressed=\"true\"]") ?? root.querySelector("[aria-label=\"More Board tools\"]");
|
|
23126
|
+
if (!button) {
|
|
23065
23127
|
setStyleShelfAnchor(void 0);
|
|
23066
23128
|
return;
|
|
23067
23129
|
}
|
|
23068
23130
|
const buttonRect = button.getBoundingClientRect();
|
|
23069
23131
|
const rootRect = root.getBoundingClientRect();
|
|
23132
|
+
const shelf = root.querySelector(".scrawl-board__style-shelf");
|
|
23070
23133
|
setStyleShelfAnchor({
|
|
23071
|
-
x: buttonRect.
|
|
23072
|
-
y: buttonRect.top - rootRect.top
|
|
23134
|
+
x: buttonRect.right - rootRect.left + 12,
|
|
23135
|
+
y: Math.max(12, Math.min(buttonRect.top - rootRect.top, rootRect.height - (shelf?.offsetHeight ?? 0) - 12))
|
|
23073
23136
|
});
|
|
23074
23137
|
};
|
|
23075
23138
|
recompute();
|
|
23139
|
+
const observer = typeof ResizeObserver === "undefined" ? void 0 : new ResizeObserver(recompute);
|
|
23140
|
+
observer?.observe(root);
|
|
23141
|
+
const shelf = root.querySelector(".scrawl-board__style-shelf");
|
|
23142
|
+
if (shelf) observer?.observe(shelf);
|
|
23143
|
+
root.addEventListener("scroll", recompute, true);
|
|
23076
23144
|
window.addEventListener("resize", recompute);
|
|
23077
|
-
return () =>
|
|
23078
|
-
|
|
23145
|
+
return () => {
|
|
23146
|
+
observer?.disconnect();
|
|
23147
|
+
root.removeEventListener("scroll", recompute, true);
|
|
23148
|
+
window.removeEventListener("resize", recompute);
|
|
23149
|
+
};
|
|
23150
|
+
}, [snapshot.tool, moreOpen]);
|
|
23151
|
+
useEffect(() => {
|
|
23152
|
+
if (!moreOpen) return;
|
|
23153
|
+
chromeRef.current?.querySelector(".scrawl-board__more-tools button")?.focus();
|
|
23154
|
+
const dismiss = (event) => {
|
|
23155
|
+
if (!event.target.closest(".scrawl-board__more-tools, [aria-label=\"More Board tools\"]")) setMoreOpen(false);
|
|
23156
|
+
};
|
|
23157
|
+
document.addEventListener("pointerdown", dismiss);
|
|
23158
|
+
return () => document.removeEventListener("pointerdown", dismiss);
|
|
23159
|
+
}, [moreOpen]);
|
|
23079
23160
|
const open = (next, opener) => {
|
|
23161
|
+
setMoreOpen(false);
|
|
23080
23162
|
openerRef.current = opener;
|
|
23081
23163
|
setPanel(next);
|
|
23082
23164
|
};
|
|
@@ -23085,6 +23167,8 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23085
23167
|
queueMicrotask(() => openerRef.current?.focus());
|
|
23086
23168
|
}, []);
|
|
23087
23169
|
const chooseTool = (tool) => {
|
|
23170
|
+
if (moreOpen) chromeRef.current?.querySelector("[aria-label=\"More Board tools\"]")?.focus();
|
|
23171
|
+
setMoreOpen(false);
|
|
23088
23172
|
controller.tools.select(tool);
|
|
23089
23173
|
const label = [...PRIMARY_TOOLS, ...MORE_TOOLS].find((item) => item.tool === tool)?.label ?? tool;
|
|
23090
23174
|
setAnnouncement(`${label} tool selected`);
|
|
@@ -23092,6 +23176,13 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23092
23176
|
const onKeyDown = (event) => {
|
|
23093
23177
|
const editing = event.target.matches("input, textarea, select, [contenteditable=true]");
|
|
23094
23178
|
const modifier = event.metaKey || event.ctrlKey;
|
|
23179
|
+
if (event.key === "Escape" && moreOpen) {
|
|
23180
|
+
event.preventDefault();
|
|
23181
|
+
event.stopPropagation();
|
|
23182
|
+
setMoreOpen(false);
|
|
23183
|
+
chromeRef.current?.querySelector("[aria-label=\"More Board tools\"]")?.focus();
|
|
23184
|
+
return;
|
|
23185
|
+
}
|
|
23095
23186
|
if (event.key === "Escape" && panel) {
|
|
23096
23187
|
event.preventDefault();
|
|
23097
23188
|
event.stopPropagation();
|
|
@@ -23208,6 +23299,7 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23208
23299
|
"aria-label": "Board tools",
|
|
23209
23300
|
className: "scrawl-board__tools",
|
|
23210
23301
|
role: "toolbar",
|
|
23302
|
+
"aria-orientation": "vertical",
|
|
23211
23303
|
children: [
|
|
23212
23304
|
enabled("tools") && /* @__PURE__ */ jsxs(Fragment, { children: [PRIMARY_TOOLS.map(({ tool, label, key }) => /* @__PURE__ */ jsx("button", {
|
|
23213
23305
|
"aria-label": `${label} tool`,
|
|
@@ -23216,37 +23308,31 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23216
23308
|
onClick: () => chooseTool(tool),
|
|
23217
23309
|
title: `${label} (${key})`,
|
|
23218
23310
|
type: "button",
|
|
23219
|
-
children:
|
|
23311
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
23220
23312
|
"aria-hidden": "true",
|
|
23221
|
-
children: icons[tool]
|
|
23222
|
-
})
|
|
23223
|
-
}, tool)), /* @__PURE__ */
|
|
23224
|
-
|
|
23225
|
-
|
|
23226
|
-
|
|
23227
|
-
|
|
23228
|
-
|
|
23229
|
-
|
|
23230
|
-
|
|
23231
|
-
value: "",
|
|
23232
|
-
children: "Choose"
|
|
23233
|
-
}), MORE_TOOLS.map(({ tool, label }) => /* @__PURE__ */ jsx("option", {
|
|
23234
|
-
value: tool,
|
|
23235
|
-
children: label
|
|
23236
|
-
}, tool))]
|
|
23237
|
-
})]
|
|
23313
|
+
children: icons?.[tool] ?? /* @__PURE__ */ jsx(ToolIcon, { tool })
|
|
23314
|
+
})
|
|
23315
|
+
}, tool)), /* @__PURE__ */ jsx("button", {
|
|
23316
|
+
"aria-label": "More Board tools",
|
|
23317
|
+
"aria-expanded": moreOpen,
|
|
23318
|
+
disabled: !mutable,
|
|
23319
|
+
onClick: () => setMoreOpen((value) => !value),
|
|
23320
|
+
title: "More tools",
|
|
23321
|
+
type: "button",
|
|
23322
|
+
children: /* @__PURE__ */ jsx(ToolIcon, { tool: "more" })
|
|
23238
23323
|
})] }),
|
|
23239
|
-
enabled("style") && /* @__PURE__ */
|
|
23324
|
+
enabled("style") && /* @__PURE__ */ jsx("button", {
|
|
23240
23325
|
"aria-label": "Ink style",
|
|
23241
23326
|
"aria-expanded": panel === "style",
|
|
23242
23327
|
disabled: !mutable,
|
|
23328
|
+
title: "Ink style",
|
|
23243
23329
|
onClick: (event) => open("style", event.currentTarget),
|
|
23244
23330
|
type: "button",
|
|
23245
|
-
children:
|
|
23331
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
23246
23332
|
"aria-hidden": "true",
|
|
23247
23333
|
className: "scrawl-board__ink-sample",
|
|
23248
23334
|
style: { backgroundColor: snapshot.style.inkColor }
|
|
23249
|
-
})
|
|
23335
|
+
})
|
|
23250
23336
|
}),
|
|
23251
23337
|
enabled("history") && /* @__PURE__ */ jsxs(Fragment, { children: [/* @__PURE__ */ jsx("button", {
|
|
23252
23338
|
"aria-label": "Undo",
|
|
@@ -23255,8 +23341,9 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23255
23341
|
controller.history.undo();
|
|
23256
23342
|
setAnnouncement("Undo");
|
|
23257
23343
|
},
|
|
23344
|
+
title: "Undo",
|
|
23258
23345
|
type: "button",
|
|
23259
|
-
children: "
|
|
23346
|
+
children: /* @__PURE__ */ jsx(ToolIcon, { tool: "undo" })
|
|
23260
23347
|
}), /* @__PURE__ */ jsx("button", {
|
|
23261
23348
|
"aria-label": "Redo",
|
|
23262
23349
|
disabled: !snapshot.canRedo || !mutable,
|
|
@@ -23264,11 +23351,31 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23264
23351
|
controller.history.redo();
|
|
23265
23352
|
setAnnouncement("Redo");
|
|
23266
23353
|
},
|
|
23354
|
+
title: "Redo",
|
|
23267
23355
|
type: "button",
|
|
23268
|
-
children: "
|
|
23356
|
+
children: /* @__PURE__ */ jsx(ToolIcon, { tool: "redo" })
|
|
23269
23357
|
})] })
|
|
23270
23358
|
]
|
|
23271
23359
|
})),
|
|
23360
|
+
moreOpen && enabled("tools") && slots?.toolbar !== null && /* @__PURE__ */ jsx("div", {
|
|
23361
|
+
"aria-label": "More tools",
|
|
23362
|
+
className: "scrawl-board__more-tools",
|
|
23363
|
+
role: "toolbar",
|
|
23364
|
+
"aria-orientation": "vertical",
|
|
23365
|
+
children: MORE_TOOLS.map(({ tool, label }) => /* @__PURE__ */ jsx("button", {
|
|
23366
|
+
"aria-label": `${label} tool`,
|
|
23367
|
+
"aria-pressed": snapshot.tool === tool,
|
|
23368
|
+
disabled: !mutable,
|
|
23369
|
+
"data-tool": tool,
|
|
23370
|
+
onClick: () => chooseTool(tool),
|
|
23371
|
+
title: label,
|
|
23372
|
+
type: "button",
|
|
23373
|
+
children: /* @__PURE__ */ jsx("span", {
|
|
23374
|
+
"aria-hidden": "true",
|
|
23375
|
+
children: icons?.[tool] ?? /* @__PURE__ */ jsx(ToolIcon, { tool })
|
|
23376
|
+
})
|
|
23377
|
+
}, tool))
|
|
23378
|
+
}),
|
|
23272
23379
|
enabled("view") && /* @__PURE__ */ jsxs("div", {
|
|
23273
23380
|
"aria-label": "Board view",
|
|
23274
23381
|
className: "scrawl-board__view",
|
|
@@ -23303,7 +23410,7 @@ function DefaultBoardChrome({ controller, snapshot, renderPortal, className, sty
|
|
|
23303
23410
|
})
|
|
23304
23411
|
]
|
|
23305
23412
|
}),
|
|
23306
|
-
renderSlot(slots?.stylePanel, enabled("styleShelf") && /* @__PURE__ */ jsx(StyleShelf, {
|
|
23413
|
+
renderSlot(slots?.stylePanel, enabled("styleShelf") && !moreOpen && /* @__PURE__ */ jsx(StyleShelf, {
|
|
23307
23414
|
anchor: styleShelfAnchor,
|
|
23308
23415
|
controller,
|
|
23309
23416
|
snapshot
|
package/dist/styles.css
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
--scrawl-color-text-muted: #62625c;
|
|
8
8
|
--scrawl-color-edge: #d4d4cc;
|
|
9
9
|
--scrawl-color-focus: #1856a3;
|
|
10
|
+
--scrawl-color-primary: #1856a3;
|
|
10
11
|
--scrawl-color-selection: #d9e8f7;
|
|
11
12
|
--scrawl-color-danger: #a3302b;
|
|
12
13
|
--scrawl-color-warning: #805400;
|
|
@@ -102,9 +103,10 @@
|
|
|
102
103
|
[data-scrawl-root] .scrawl-board__actions,
|
|
103
104
|
[data-scrawl-root] .scrawl-board__tools,
|
|
104
105
|
[data-scrawl-root] .scrawl-board__view,
|
|
106
|
+
[data-scrawl-root] .scrawl-board__more-tools,
|
|
105
107
|
[data-scrawl-root] .scrawl-board__style-shelf {
|
|
106
108
|
align-items: center;
|
|
107
|
-
background: var(--scrawl-color-surface
|
|
109
|
+
background: var(--scrawl-color-surface);
|
|
108
110
|
border: 1px solid color-mix(in srgb, var(--scrawl-color-surface-raised), var(--scrawl-color-text) 12%);
|
|
109
111
|
border-radius: var(--scrawl-radius-panel);
|
|
110
112
|
display: flex;
|
|
@@ -115,25 +117,45 @@
|
|
|
115
117
|
}
|
|
116
118
|
|
|
117
119
|
[data-scrawl-root] .scrawl-board__actions { inset-block-start: 12px; inset-inline-end: 12px; }
|
|
118
|
-
[data-scrawl-root] .scrawl-board__tools {
|
|
120
|
+
[data-scrawl-root] .scrawl-board__tools {
|
|
121
|
+
inset-block-start: 50%;
|
|
122
|
+
inset-inline-start: 12px;
|
|
123
|
+
inline-size: 54px;
|
|
124
|
+
max-block-size: calc(100% - 144px);
|
|
125
|
+
overflow-y: auto;
|
|
126
|
+
flex-direction: column;
|
|
127
|
+
transform: translateY(-50%);
|
|
128
|
+
box-shadow: var(--scrawl-elevation-low);
|
|
129
|
+
}
|
|
130
|
+
[data-scrawl-root] .scrawl-board__tools > button { flex: 0 0 44px; inline-size: 44px; }
|
|
131
|
+
[data-scrawl-root] .scrawl-board__tools button,
|
|
132
|
+
[data-scrawl-root] .scrawl-board__more-tools button { display: flex; align-items: center; justify-content: center; }
|
|
133
|
+
[data-scrawl-root] .scrawl-board__tools svg,
|
|
134
|
+
[data-scrawl-root] .scrawl-board__more-tools svg { display: block; inline-size: 22px; block-size: 22px; }
|
|
135
|
+
[data-scrawl-root] .scrawl-board__tools .scrawl-board__ink-sample { margin: 0; }
|
|
136
|
+
[data-scrawl-root] .scrawl-board__tools :focus-visible,
|
|
137
|
+
[data-scrawl-root] .scrawl-board__more-tools :focus-visible,
|
|
138
|
+
[data-scrawl-root] .scrawl-board__style-shelf :focus-visible { outline-offset: -3px; }
|
|
119
139
|
[data-scrawl-root] .scrawl-board__view { inset-block-end: 12px; inset-inline-end: 12px; }
|
|
140
|
+
[data-scrawl-root] .scrawl-board__more-tools,
|
|
120
141
|
[data-scrawl-root] .scrawl-board__style-shelf {
|
|
121
|
-
|
|
122
|
-
inset-block-
|
|
123
|
-
|
|
124
|
-
max-
|
|
125
|
-
overflow-
|
|
126
|
-
|
|
142
|
+
inset-inline-start: 78px;
|
|
143
|
+
inset-block-start: 72px;
|
|
144
|
+
inline-size: 106px;
|
|
145
|
+
max-block-size: calc(100% - 84px);
|
|
146
|
+
overflow-y: auto;
|
|
147
|
+
box-shadow: var(--scrawl-elevation-low);
|
|
127
148
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
transform: translate(-50%, calc(-100% - 12px));
|
|
149
|
+
[data-scrawl-root] .scrawl-board__more-tools { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 4px; }
|
|
150
|
+
[data-scrawl-root] .scrawl-board__style-shelf .scrawl-board__style-group {
|
|
151
|
+
display: grid;
|
|
152
|
+
grid-template-columns: repeat(2, minmax(0, 1fr));
|
|
153
|
+
gap: 4px;
|
|
154
|
+
inline-size: 100%;
|
|
155
|
+
white-space: normal;
|
|
136
156
|
}
|
|
157
|
+
[data-scrawl-root] .scrawl-board__style-shelf .scrawl-board__style-group > span { grid-column: 1 / -1; text-align: center; }
|
|
158
|
+
[data-scrawl-root] .scrawl-board__style-shelf .scrawl-board__style-group button { padding: 4px; font-size: 12px; }
|
|
137
159
|
[data-scrawl-root] .scrawl-board__style-group { align-items: center; display: flex; gap: 6px; white-space: nowrap; }
|
|
138
160
|
[data-scrawl-root] .scrawl-board__style-group > span:first-child { color: var(--scrawl-color-text-muted); font-size: 0.75rem; font-weight: var(--scrawl-font-weight-strong); text-transform: uppercase; }
|
|
139
161
|
[data-scrawl-root] .scrawl-board__style-group button { align-items: center; display: flex; justify-content: center; padding: 6px 10px; }
|
|
@@ -221,30 +243,6 @@
|
|
|
221
243
|
[data-scrawl-root] [data-scrawl-default-ui] button:disabled,
|
|
222
244
|
[data-scrawl-root] [data-scrawl-default-ui] select:disabled { color: var(--scrawl-color-text-muted); cursor: not-allowed; }
|
|
223
245
|
|
|
224
|
-
[data-scrawl-root] .scrawl-board__tool-select {
|
|
225
|
-
align-items: center;
|
|
226
|
-
display: flex;
|
|
227
|
-
min-block-size: 44px;
|
|
228
|
-
}
|
|
229
|
-
|
|
230
|
-
[data-scrawl-root] .scrawl-board__tool-select > span { block-size: 1px; clip: rect(0, 0, 0, 0); inline-size: 1px; overflow: hidden; position: absolute; }
|
|
231
|
-
[data-scrawl-root] .scrawl-board__tool-select select {
|
|
232
|
-
-webkit-appearance: none;
|
|
233
|
-
appearance: none;
|
|
234
|
-
/* Chevron drawn from two gradient triangles rather than an embedded asset
|
|
235
|
-
reference (disallowed by this stylesheet's policy, see styles.test.ts),
|
|
236
|
-
and currentColor keeps it Theme-aware where a bitmap/SVG asset would
|
|
237
|
-
need its own colour. */
|
|
238
|
-
background-image:
|
|
239
|
-
linear-gradient(45deg, transparent 50%, currentColor 50%),
|
|
240
|
-
linear-gradient(135deg, currentColor 50%, transparent 50%);
|
|
241
|
-
background-position: right 14px center, right 9px center;
|
|
242
|
-
background-repeat: no-repeat;
|
|
243
|
-
background-size: 5px 5px, 5px 5px;
|
|
244
|
-
min-block-size: 44px;
|
|
245
|
-
padding-inline: 8px 24px;
|
|
246
|
-
}
|
|
247
|
-
|
|
248
246
|
[data-scrawl-root] .scrawl-board__ink-sample,
|
|
249
247
|
[data-scrawl-portal] .scrawl-board__ink-options span {
|
|
250
248
|
border: 1px solid color-mix(in srgb, currentColor, transparent 72%);
|
|
@@ -334,14 +332,16 @@
|
|
|
334
332
|
}
|
|
335
333
|
[data-scrawl-portal] .scrawl-board__inline-editor-actions button { min-block-size: auto; min-inline-size: auto; padding: 2px 6px; }
|
|
336
334
|
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
335
|
+
[data-scrawl-portal] .scrawl-board__panel[aria-label="Ink style"] {
|
|
336
|
+
inset-inline-start: 78px;
|
|
337
|
+
inset-block-start: 72px;
|
|
338
|
+
inset-block-end: auto;
|
|
339
|
+
inline-size: 156px;
|
|
340
|
+
max-block-size: calc(100% - 84px);
|
|
341
|
+
transform: none;
|
|
344
342
|
}
|
|
343
|
+
[data-scrawl-portal] .scrawl-board__panel[aria-label="Ink style"] .scrawl-board__panel-heading { flex-direction: column; align-items: stretch; }
|
|
344
|
+
[data-scrawl-portal] .scrawl-board__ink-options { grid-template-columns: 1fr; }
|
|
345
345
|
|
|
346
346
|
[data-scrawl-root] .scrawl-board__status, [data-scrawl-portal] .scrawl-board__status {
|
|
347
347
|
block-size: 1px;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@scrawl-board/board",
|
|
3
|
-
"version": "0.1.0-beta.
|
|
3
|
+
"version": "0.1.0-beta.9",
|
|
4
4
|
"description": "The Scrawl Board SDK: an embeddable, collaborative infinite-canvas whiteboard for React and vanilla JS/TS apps.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|