@aspicio/react
Advanced tools
+6
-0
@@ -35,2 +35,8 @@ import { CSSProperties, ReactElement } from "react"; | ||
| showDownload?: boolean; | ||
| /** | ||
| * Enable keyboard shortcuts on the (focused) viewer: `F` fit, `+`/`-` zoom, | ||
| * `R` reset rotation, `A` show all layers. Default: false. The embed must be | ||
| * focused (click it) to receive keys, so multiple embeds don't collide. | ||
| */ | ||
| shortcuts?: boolean; | ||
| } | ||
@@ -37,0 +43,0 @@ /** |
+17
-2
| import { forwardRef, useEffect, useImperativeHandle, useRef, useState } from "react"; | ||
| import { jsx, jsxs } from "react/jsx-runtime"; | ||
| import { DxfViewer } from "@aspicio/core"; | ||
| import { DxfViewer, attachShortcuts } from "@aspicio/core"; | ||
| //#region src/theme.ts | ||
@@ -463,3 +463,3 @@ /** Design tokens lifted from the Aspicio demo app. */ | ||
| */ | ||
| const DxfPreview = forwardRef(function DxfPreview({ src, srcUrl, options, className, style, onLoaded, onError, onViewer, onHoverLayer, showDownload = true }, ref) { | ||
| const DxfPreview = forwardRef(function DxfPreview({ src, srcUrl, options, className, style, onLoaded, onError, onViewer, onHoverLayer, showDownload = true, shortcuts = false }, ref) { | ||
| const containerRef = useRef(null); | ||
@@ -542,2 +542,17 @@ const [viewer, setViewer] = useState(null); | ||
| }, [viewer, hoverEnabled]); | ||
| useEffect(() => { | ||
| const container = containerRef.current; | ||
| if (!viewer || !container || !shortcuts) return; | ||
| if (container.tabIndex < 0) container.tabIndex = 0; | ||
| container.style.outline = "none"; | ||
| const focus = () => container.focus(); | ||
| container.addEventListener("pointerdown", focus); | ||
| const detach = attachShortcuts(container, viewer, { onShowAll: () => { | ||
| for (const layer of viewer.getLayers()) viewer.setLayerVisible(layer.name, true); | ||
| } }); | ||
| return () => { | ||
| container.removeEventListener("pointerdown", focus); | ||
| detach(); | ||
| }; | ||
| }, [viewer, shortcuts]); | ||
| useImperativeHandle(ref, () => viewer, [viewer]); | ||
@@ -544,0 +559,0 @@ return /* @__PURE__ */ jsx("div", { |
+3
-3
| { | ||
| "name": "@aspicio/react", | ||
| "version": "0.3.0", | ||
| "version": "0.4.0", | ||
| "description": "React bindings for the Aspicio DXF viewer.", | ||
| "homepage": "https://github.com/frontsail-ai/aspicio/tree/main/packages/react#readme", | ||
| "homepage": "https://github.com/frontsail-ai/aspicio/tree/master/packages/react#readme", | ||
| "license": "MIT", | ||
@@ -33,3 +33,3 @@ "author": "Dmitri Kochelorov <kochelorov.dmitri@gmail.com>", | ||
| "dependencies": { | ||
| "@aspicio/core": "^0.3.0" | ||
| "@aspicio/core": "^0.4.0" | ||
| }, | ||
@@ -36,0 +36,0 @@ "devDependencies": { |
+9
-0
@@ -81,5 +81,14 @@ # @aspicio/react | ||
| callable via the ref regardless. | ||
| - `shortcuts` (default off) enables keyboard control on the **focused** embed: | ||
| `F` fit, `+`/`-` zoom, `R` reset rotation, `A` show all layers. Click the | ||
| embed to focus it — keys are scoped to the focused container so multiple | ||
| embeds on a page don't collide. See `apps/react-example` for a full setup. | ||
| - Camera state is deliberately not React state — subscribe to the `render` | ||
| event on the viewer if you need to display it. | ||
| - **Shareable/deep-linked views:** the embed doesn't touch the URL itself. | ||
| Read `viewer.view` (+ `getLayers()`) on `render` to serialize a pose into | ||
| your router, and `viewer.setView(saved)` after `onLoaded` to restore it. | ||
| Since a fixed `srcUrl` already identifies the drawing, this gives embedders | ||
| "open at this view" links. The demo's `viewurl.ts` is a full reference. | ||
| - StrictMode and SSR safe: the viewer is created in an effect and disposed on | ||
| unmount. |
32004
5.17%748
2.05%94
10.59%+ Added
- Removed
Updated