@unityevolv/ofiskit-ui-builder 0.1.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/LICENSE +661 -0
- package/README.md +30 -0
- package/dist/BuilderSteps.d.ts +35 -0
- package/dist/BuilderSteps.d.ts.map +1 -0
- package/dist/BuilderSteps.js +104 -0
- package/dist/BuilderSteps.js.map +1 -0
- package/dist/OfficeBuilder.d.ts +30 -0
- package/dist/OfficeBuilder.d.ts.map +1 -0
- package/dist/OfficeBuilder.js +55 -0
- package/dist/OfficeBuilder.js.map +1 -0
- package/dist/background/BackgroundStep.d.ts +35 -0
- package/dist/background/BackgroundStep.d.ts.map +1 -0
- package/dist/background/BackgroundStep.js +104 -0
- package/dist/background/BackgroundStep.js.map +1 -0
- package/dist/background/darkVersion.d.ts +25 -0
- package/dist/background/darkVersion.d.ts.map +1 -0
- package/dist/background/darkVersion.js +124 -0
- package/dist/background/darkVersion.js.map +1 -0
- package/dist/canvas/BuilderCanvas.d.ts +32 -0
- package/dist/canvas/BuilderCanvas.d.ts.map +1 -0
- package/dist/canvas/BuilderCanvas.js +40 -0
- package/dist/canvas/BuilderCanvas.js.map +1 -0
- package/dist/canvas/RoomShape.d.ts +27 -0
- package/dist/canvas/RoomShape.d.ts.map +1 -0
- package/dist/canvas/RoomShape.js +45 -0
- package/dist/canvas/RoomShape.js.map +1 -0
- package/dist/canvas/geometry.d.ts +115 -0
- package/dist/canvas/geometry.d.ts.map +1 -0
- package/dist/canvas/geometry.js +222 -0
- package/dist/canvas/geometry.js.map +1 -0
- package/dist/canvas/types.d.ts +47 -0
- package/dist/canvas/types.d.ts.map +1 -0
- package/dist/canvas/types.js +2 -0
- package/dist/canvas/types.js.map +1 -0
- package/dist/canvas/useCanvasDrag.d.ts +35 -0
- package/dist/canvas/useCanvasDrag.d.ts.map +1 -0
- package/dist/canvas/useCanvasDrag.js +138 -0
- package/dist/canvas/useCanvasDrag.js.map +1 -0
- package/dist/canvas/useCanvasKeys.d.ts +11 -0
- package/dist/canvas/useCanvasKeys.d.ts.map +1 -0
- package/dist/canvas/useCanvasKeys.js +43 -0
- package/dist/canvas/useCanvasKeys.js.map +1 -0
- package/dist/history.d.ts +31 -0
- package/dist/history.d.ts.map +1 -0
- package/dist/history.js +49 -0
- package/dist/history.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/panel/AreaInspector.d.ts +10 -0
- package/dist/panel/AreaInspector.d.ts.map +1 -0
- package/dist/panel/AreaInspector.js +30 -0
- package/dist/panel/AreaInspector.js.map +1 -0
- package/dist/panel/RoomInspector.d.ts +16 -0
- package/dist/panel/RoomInspector.d.ts.map +1 -0
- package/dist/panel/RoomInspector.js +26 -0
- package/dist/panel/RoomInspector.js.map +1 -0
- package/dist/panel/SaveBar.d.ts +7 -0
- package/dist/panel/SaveBar.d.ts.map +1 -0
- package/dist/panel/SaveBar.js +16 -0
- package/dist/panel/SaveBar.js.map +1 -0
- package/dist/panel/TemplateDetails.d.ts +16 -0
- package/dist/panel/TemplateDetails.d.ts.map +1 -0
- package/dist/panel/TemplateDetails.js +16 -0
- package/dist/panel/TemplateDetails.js.map +1 -0
- package/dist/panel/ToolBar.d.ts +34 -0
- package/dist/panel/ToolBar.d.ts.map +1 -0
- package/dist/panel/ToolBar.js +8 -0
- package/dist/panel/ToolBar.js.map +1 -0
- package/dist/prompt/PromptStep.d.ts +23 -0
- package/dist/prompt/PromptStep.d.ts.map +1 -0
- package/dist/prompt/PromptStep.js +24 -0
- package/dist/prompt/PromptStep.js.map +1 -0
- package/dist/prompt/prompt.d.ts +59 -0
- package/dist/prompt/prompt.d.ts.map +1 -0
- package/dist/prompt/prompt.js +40 -0
- package/dist/prompt/prompt.js.map +1 -0
- package/package.json +23 -0
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type Template } from '@unityevolv/ofiskit-template';
|
|
2
|
+
import type { OnChange, Selection, Tool } from './types.js';
|
|
3
|
+
/**
|
|
4
|
+
* The drawing surface.
|
|
5
|
+
*
|
|
6
|
+
* Two rules do most of the work, and both live in `geometry.ts`. Rooms snap to a
|
|
7
|
+
* grid and to each other's edges, because freehand rectangles never line up and an
|
|
8
|
+
* office of almost aligned rooms looks broken. User areas are *sized* in whole
|
|
9
|
+
* avatar units, so a fractional area is not something the author has to be told
|
|
10
|
+
* about — it is something they cannot draw.
|
|
11
|
+
*
|
|
12
|
+
* Sized, and not positioned: an area holding two people holds exactly two wherever
|
|
13
|
+
* it sits, so its corner moves on the same fine grid as everything else.
|
|
14
|
+
*
|
|
15
|
+
* What is left here is the surface itself: the element, the background, the shapes
|
|
16
|
+
* and the two handlers. The gestures are in `useCanvasDrag`, the keys are in
|
|
17
|
+
* `useCanvasKeys`, and what any of it means to a template is in `geometry.ts`.
|
|
18
|
+
*/
|
|
19
|
+
export interface BuilderCanvasProps {
|
|
20
|
+
template: Template;
|
|
21
|
+
/** An object URL while the author is working; a file name once saved. */
|
|
22
|
+
imageUrl: string | null;
|
|
23
|
+
tool: Tool;
|
|
24
|
+
selection: Selection;
|
|
25
|
+
onSelect(selection: Selection): void;
|
|
26
|
+
onChange: OnChange;
|
|
27
|
+
onToolDone(): void;
|
|
28
|
+
/** Draws a ghost avatar in every cell, so a full room can be judged empty. */
|
|
29
|
+
showGhosts: boolean;
|
|
30
|
+
}
|
|
31
|
+
export declare function BuilderCanvas(props: BuilderCanvasProps): import("react").JSX.Element;
|
|
32
|
+
//# sourceMappingURL=BuilderCanvas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BuilderCanvas.d.ts","sourceRoot":"","sources":["../../src/canvas/BuilderCanvas.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AAIxE,OAAO,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAI3D;;;;;;;;;;;;;;;GAeG;AAEH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,EAAE,QAAQ,CAAA;IAClB,yEAAyE;IACzE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,IAAI,EAAE,IAAI,CAAA;IACV,SAAS,EAAE,SAAS,CAAA;IACpB,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;IACpC,QAAQ,EAAE,QAAQ,CAAA;IAClB,UAAU,IAAI,IAAI,CAAA;IAClB,8EAA8E;IAC9E,UAAU,EAAE,OAAO,CAAA;CACpB;AAQD,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,+BA6FtD"}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { avatarUnit } from '@unityevolv/ofiskit-template';
|
|
3
|
+
import { useRef } from 'react';
|
|
4
|
+
import { RoomShape, percent } from './RoomShape.js';
|
|
5
|
+
import { useCanvasDrag } from './useCanvasDrag.js';
|
|
6
|
+
import { useCanvasKeys } from './useCanvasKeys.js';
|
|
7
|
+
const RATIOS = {
|
|
8
|
+
landscape: '16 / 9',
|
|
9
|
+
square: '1 / 1',
|
|
10
|
+
portrait: '3 / 4',
|
|
11
|
+
};
|
|
12
|
+
export function BuilderCanvas(props) {
|
|
13
|
+
const { template, tool, selection } = props;
|
|
14
|
+
const surface = useRef(null);
|
|
15
|
+
const unit = avatarUnit(template.canvas, template.avatarSize);
|
|
16
|
+
const drag = useCanvasDrag({
|
|
17
|
+
template,
|
|
18
|
+
tool,
|
|
19
|
+
unit,
|
|
20
|
+
surface,
|
|
21
|
+
onSelect: props.onSelect,
|
|
22
|
+
onChange: props.onChange,
|
|
23
|
+
onToolDone: props.onToolDone,
|
|
24
|
+
});
|
|
25
|
+
const onKeyDown = useCanvasKeys({ template, selection, unit, onChange: props.onChange });
|
|
26
|
+
return (_jsxs("div", { ref: surface, className: "relative aspect-[var(--canvas-ratio)] w-full touch-none select-none overflow-hidden rounded-lg bg-base-300 ring-1 ring-base-300", style: {
|
|
27
|
+
'--canvas-ratio': RATIOS[template.canvas],
|
|
28
|
+
cursor: tool === 'select' ? 'default' : 'crosshair',
|
|
29
|
+
}, onPointerDown: drag.onPointerDown, onPointerMove: drag.onPointerMove, onPointerUp: drag.onPointerUp, onPointerCancel: drag.onPointerUp, onKeyDown: onKeyDown,
|
|
30
|
+
/*
|
|
31
|
+
* One focus stop, and the arrow keys move what is selected.
|
|
32
|
+
*
|
|
33
|
+
* Drawing a rectangle is a pointer gesture and there is no honest keyboard
|
|
34
|
+
* equivalent of dragging one out. Adjusting one is a different matter: an
|
|
35
|
+
* author who can select a room from the panel but cannot then nudge it has
|
|
36
|
+
* a tool they cannot use, so the keys are not a courtesy.
|
|
37
|
+
*/
|
|
38
|
+
role: "application", tabIndex: 0, "aria-label": "Office layout. Select a room in the panel, then use the arrow keys to move it and shift with the arrow keys to resize it.", "data-testid": "builder-canvas", children: [props.imageUrl && (_jsx("img", { src: props.imageUrl, alt: "", "aria-hidden": "true", className: "absolute inset-0 h-full w-full", draggable: false })), template.rooms.map((room) => (_jsx(RoomShape, { room: room, canvas: template.canvas, unit: unit, selection: selection, showGhosts: props.showGhosts }, room.id))), drag.preview && (_jsx("div", { className: "absolute border-2 border-dashed border-primary bg-primary/10", style: percent(drag.preview), "aria-hidden": "true" })), drag.guides.x.map((at) => (_jsx("div", { className: "absolute inset-y-0 w-px bg-primary", style: { left: `${at * 100}%` }, "aria-hidden": "true" }, `x${at}`))), drag.guides.y.map((at) => (_jsx("div", { className: "absolute inset-x-0 h-px bg-primary", style: { top: `${at * 100}%` }, "aria-hidden": "true" }, `y${at}`)))] }));
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=BuilderCanvas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"BuilderCanvas.js","sourceRoot":"","sources":["../../src/canvas/BuilderCanvas.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import { type CanvasShape, type Rect, type Room } from '@unityevolv/ofiskit-template';
|
|
2
|
+
import type { Unit } from './geometry.js';
|
|
3
|
+
import type { Selection } from './types.js';
|
|
4
|
+
/**
|
|
5
|
+
* One room on the canvas, with its user areas over it.
|
|
6
|
+
*
|
|
7
|
+
* Drawing only: everything it knows arrives as props, and it changes nothing. The
|
|
8
|
+
* bar is drawn where it will actually be rather than as a label, because the whole
|
|
9
|
+
* reason an author moves it to the other edge is that it lands on something in the
|
|
10
|
+
* picture, and they cannot judge that from a legend.
|
|
11
|
+
*/
|
|
12
|
+
declare const percent: (rect: Rect) => {
|
|
13
|
+
left: string;
|
|
14
|
+
top: string;
|
|
15
|
+
width: string;
|
|
16
|
+
height: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function RoomShape(props: {
|
|
19
|
+
room: Room;
|
|
20
|
+
canvas: CanvasShape;
|
|
21
|
+
unit: Unit;
|
|
22
|
+
selection: Selection;
|
|
23
|
+
/** Draws a ghost avatar in every cell, so a full room can be judged empty. */
|
|
24
|
+
showGhosts: boolean;
|
|
25
|
+
}): import("react").JSX.Element;
|
|
26
|
+
export { percent };
|
|
27
|
+
//# sourceMappingURL=RoomShape.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RoomShape.d.ts","sourceRoot":"","sources":["../../src/canvas/RoomShape.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAW,KAAK,WAAW,EAAE,KAAK,IAAI,EAAE,KAAK,IAAI,EAAE,MAAM,8BAA8B,CAAA;AAE9F,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,eAAe,CAAA;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,YAAY,CAAA;AAE3C;;;;;;;GAOG;AAEH,QAAA,MAAM,OAAO,GAAI,MAAM,IAAI;;;;;CAKzB,CAAA;AAEF,wBAAgB,SAAS,CAAC,KAAK,EAAE;IAC/B,IAAI,EAAE,IAAI,CAAA;IACV,MAAM,EAAE,WAAW,CAAA;IACnB,IAAI,EAAE,IAAI,CAAA;IACV,SAAS,EAAE,SAAS,CAAA;IACpB,8EAA8E;IAC9E,UAAU,EAAE,OAAO,CAAA;CACpB,+BAgFA;AAED,OAAO,EAAE,OAAO,EAAE,CAAA"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { barRect } from '@unityevolv/ofiskit-template';
|
|
3
|
+
/**
|
|
4
|
+
* One room on the canvas, with its user areas over it.
|
|
5
|
+
*
|
|
6
|
+
* Drawing only: everything it knows arrives as props, and it changes nothing. The
|
|
7
|
+
* bar is drawn where it will actually be rather than as a label, because the whole
|
|
8
|
+
* reason an author moves it to the other edge is that it lands on something in the
|
|
9
|
+
* picture, and they cannot judge that from a legend.
|
|
10
|
+
*/
|
|
11
|
+
const percent = (rect) => ({
|
|
12
|
+
left: `${rect.x * 100}%`,
|
|
13
|
+
top: `${rect.y * 100}%`,
|
|
14
|
+
width: `${rect.width * 100}%`,
|
|
15
|
+
height: `${rect.height * 100}%`,
|
|
16
|
+
});
|
|
17
|
+
export function RoomShape(props) {
|
|
18
|
+
const { room, canvas, unit, selection, showGhosts } = props;
|
|
19
|
+
const chosen = selection.kind !== 'none' && 'roomId' in selection && selection.roomId === room.id;
|
|
20
|
+
const bar = barRect(room.rect, room.bar, canvas);
|
|
21
|
+
return (_jsxs("div", { children: [_jsxs("div", { className: [
|
|
22
|
+
'absolute rounded-sm border-2',
|
|
23
|
+
chosen ? 'border-primary bg-primary/10' : 'border-base-content/50 bg-base-content/5',
|
|
24
|
+
].join(' '), style: percent(room.rect), children: [_jsx("div", { className: "absolute inset-x-0 truncate bg-base-100/90 px-1 text-[10px] leading-tight text-base-content", style: {
|
|
25
|
+
top: room.bar === 'top' ? 0 : undefined,
|
|
26
|
+
bottom: room.bar === 'bottom' ? 0 : undefined,
|
|
27
|
+
height: `${(bar.height / room.rect.height) * 100}%`,
|
|
28
|
+
}, children: room.name }), chosen && (_jsx("span", { className: "absolute -bottom-1 -right-1 h-3 w-3 cursor-se-resize rounded-sm bg-primary", "aria-hidden": "true" }))] }), room.areas.map((area) => {
|
|
29
|
+
const areaChosen = selection.kind === 'area' && selection.areaId === area.id;
|
|
30
|
+
return (_jsxs("div", { className: [
|
|
31
|
+
'absolute border border-dashed',
|
|
32
|
+
areaChosen ? 'border-primary bg-primary/20' : 'border-primary/60 bg-primary/5',
|
|
33
|
+
].join(' '), style: percent({
|
|
34
|
+
x: area.x,
|
|
35
|
+
y: area.y,
|
|
36
|
+
width: area.columns * unit.width,
|
|
37
|
+
height: area.rows * unit.height,
|
|
38
|
+
}), children: [showGhosts && (_jsx("div", { className: "grid h-full w-full", style: {
|
|
39
|
+
gridTemplateColumns: `repeat(${area.columns}, 1fr)`,
|
|
40
|
+
gridTemplateRows: `repeat(${area.rows}, 1fr)`,
|
|
41
|
+
}, children: Array.from({ length: area.columns * area.rows }, (_, index) => (_jsxs("div", { className: "flex flex-col items-center justify-center gap-[2px] p-[2px]", children: [_jsx("span", { className: "aspect-square w-[58%] rounded-full bg-base-content/25" }), _jsx("span", { className: "h-[3px] w-[70%] rounded-full bg-base-content/20" })] }, index))) })), _jsxs("span", { className: "absolute -top-4 left-0 rounded bg-primary px-1 text-[9px] text-primary-content", children: [area.columns, "x", area.rows] })] }, area.id));
|
|
42
|
+
})] }));
|
|
43
|
+
}
|
|
44
|
+
export { percent };
|
|
45
|
+
//# sourceMappingURL=RoomShape.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"RoomShape.js","sourceRoot":"","sources":["../../src/canvas/RoomShape.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAA0C,MAAM,8BAA8B,CAAA;AAK9F;;;;;;;GAOG;AAEH,MAAM,OAAO,GAAG,CAAC,IAAU,EAAE,EAAE,CAAC,CAAC;IAC/B,IAAI,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,GAAG,GAAG;IACxB,GAAG,EAAE,GAAG,IAAI,CAAC,CAAC,GAAG,GAAG,GAAG;IACvB,KAAK,EAAE,GAAG,IAAI,CAAC,KAAK,GAAG,GAAG,GAAG;IAC7B,MAAM,EAAE,GAAG,IAAI,CAAC,MAAM,GAAG,GAAG,GAAG;CAChC,CAAC,CAAA;AAEF,MAAM,UAAU,SAAS,CAAC,KAOzB;IACC,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,SAAS,EAAE,UAAU,EAAE,GAAG,KAAK,CAAA;IAC3D,MAAM,MAAM,GAAG,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,QAAQ,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE,CAAA;IACjG,MAAM,GAAG,GAAG,OAAO,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,GAAG,EAAE,MAAM,CAAC,CAAA;IAEhD,OAAO,CACL,0BACE,eACE,SAAS,EAAE;oBACT,8BAA8B;oBAC9B,MAAM,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,0CAA0C;iBACrF,CAAC,IAAI,CAAC,GAAG,CAAC,EACX,KAAK,EAAE,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,aAEzB,cACE,SAAS,EAAC,6FAA6F,EACvG,KAAK,EAAE;4BACL,GAAG,EAAE,IAAI,CAAC,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;4BACvC,MAAM,EAAE,IAAI,CAAC,GAAG,KAAK,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,SAAS;4BAC7C,MAAM,EAAE,GAAG,CAAC,GAAG,CAAC,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,GAAG,GAAG;yBACpD,YAEA,IAAI,CAAC,IAAI,GACN,EAEL,MAAM,IAAI,CACT,eACE,SAAS,EAAC,4EAA4E,iBAC1E,MAAM,GAClB,CACH,IACG,EAEL,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;gBACvB,MAAM,UAAU,GAAG,SAAS,CAAC,IAAI,KAAK,MAAM,IAAI,SAAS,CAAC,MAAM,KAAK,IAAI,CAAC,EAAE,CAAA;gBAE5E,OAAO,CACL,eAEE,SAAS,EAAE;wBACT,+BAA+B;wBAC/B,UAAU,CAAC,CAAC,CAAC,8BAA8B,CAAC,CAAC,CAAC,gCAAgC;qBAC/E,CAAC,IAAI,CAAC,GAAG,CAAC,EACX,KAAK,EAAE,OAAO,CAAC;wBACb,CAAC,EAAE,IAAI,CAAC,CAAC;wBACT,CAAC,EAAE,IAAI,CAAC,CAAC;wBACT,KAAK,EAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,KAAK;wBAChC,MAAM,EAAE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,MAAM;qBAChC,CAAC,aAID,UAAU,IAAI,CACb,cACE,SAAS,EAAC,oBAAoB,EAC9B,KAAK,EAAE;gCACL,mBAAmB,EAAE,UAAU,IAAI,CAAC,OAAO,QAAQ;gCACnD,gBAAgB,EAAE,UAAU,IAAI,CAAC,IAAI,QAAQ;6BAC9C,YAEA,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC,CAC9D,eAEE,SAAS,EAAC,6DAA6D,aAEvE,eAAM,SAAS,EAAC,uDAAuD,GAAG,EAC1E,eAAM,SAAS,EAAC,iDAAiD,GAAG,KAJ/D,KAAK,CAKN,CACP,CAAC,GACE,CACP,EAED,gBAAM,SAAS,EAAC,gFAAgF,aAC7F,IAAI,CAAC,OAAO,OAAG,IAAI,CAAC,IAAI,IACpB,KApCF,IAAI,CAAC,EAAE,CAqCR,CACP,CAAA;YACH,CAAC,CAAC,IACE,CACP,CAAA;AACH,CAAC;AAED,OAAO,EAAE,OAAO,EAAE,CAAA"}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { type Rect, type Room, type Template, type UserArea } from '@unityevolv/ofiskit-template';
|
|
2
|
+
/**
|
|
3
|
+
* The rules a drawing tool is made of, with no React in them.
|
|
4
|
+
*
|
|
5
|
+
* Everything here takes a template and returns a template, or answers a question
|
|
6
|
+
* about one. That is the whole point of the file: what a drag *means* — where a
|
|
7
|
+
* room may sit, how close is close enough to snap, what happens to a room's user
|
|
8
|
+
* areas when the room moves — is the part worth being sure about, and none of it
|
|
9
|
+
* needs a pointer, an element or a rendered component to be true.
|
|
10
|
+
*
|
|
11
|
+
* The component above turns events into calls to these; it decides nothing.
|
|
12
|
+
*/
|
|
13
|
+
/** Rooms snap to this fraction of the canvas, and to other rooms' edges. */
|
|
14
|
+
export declare const GRID = 0.01;
|
|
15
|
+
export declare const SNAP_TO_EDGE = 0.012;
|
|
16
|
+
/** A room has to be at least this big to be a room somebody meant to draw. */
|
|
17
|
+
export declare const MIN_DRAWN = 0.03;
|
|
18
|
+
export declare const clamp: (value: number, low: number, high: number) => number;
|
|
19
|
+
export type Unit = {
|
|
20
|
+
width: number;
|
|
21
|
+
height: number;
|
|
22
|
+
};
|
|
23
|
+
export type Direction = {
|
|
24
|
+
x: number;
|
|
25
|
+
y: number;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Snap a room edge to the grid, or to another room's edge if one is close.
|
|
29
|
+
*
|
|
30
|
+
* Edges win over the grid: lining up with the room next door is what the author
|
|
31
|
+
* is actually trying to do, and the grid is only there so that a room with
|
|
32
|
+
* nothing near it still lands somewhere tidy. The second value is the edge it
|
|
33
|
+
* snapped to, or null — which is what the alignment guide draws.
|
|
34
|
+
*/
|
|
35
|
+
export declare function snapRoom(template: Template, value: number, axis: 'x' | 'y', ignoreId?: string): [number, number | null];
|
|
36
|
+
/**
|
|
37
|
+
* Where a user area lands.
|
|
38
|
+
*
|
|
39
|
+
* Its **size** is whole avatar units and always will be: half an avatar is not a
|
|
40
|
+
* place anybody can stand. Its **position** is not, and the two were once
|
|
41
|
+
* conflated. An area holding two people holds exactly two wherever it sits, so
|
|
42
|
+
* snapping its corner to a lattice of whole avatars buys nothing — and it costs a
|
|
43
|
+
* great deal, because one avatar unit is about a sixth of the height of a
|
|
44
|
+
* landscape office, which leaves a typical room with two or three places an area
|
|
45
|
+
* can be at all. It reads as a broken drag rather than as a rule.
|
|
46
|
+
*
|
|
47
|
+
* So the corner snaps to the same fine grid as everything else, and the area is
|
|
48
|
+
* kept inside the room it belongs to — an area outside its room is not somewhere
|
|
49
|
+
* a person can be put, and it used to be reported afterwards as a validation
|
|
50
|
+
* warning instead of being prevented.
|
|
51
|
+
*/
|
|
52
|
+
export declare function placeArea(wanted: {
|
|
53
|
+
x: number;
|
|
54
|
+
y: number;
|
|
55
|
+
}, area: {
|
|
56
|
+
columns: number;
|
|
57
|
+
rows: number;
|
|
58
|
+
}, usable: Rect, unit: Unit): {
|
|
59
|
+
x: number;
|
|
60
|
+
y: number;
|
|
61
|
+
};
|
|
62
|
+
export declare function roomAt(template: Template, at: {
|
|
63
|
+
x: number;
|
|
64
|
+
y: number;
|
|
65
|
+
}): Room | null;
|
|
66
|
+
export declare function areaAt(template: Template, at: {
|
|
67
|
+
x: number;
|
|
68
|
+
y: number;
|
|
69
|
+
}): {
|
|
70
|
+
room: Room;
|
|
71
|
+
area: UserArea;
|
|
72
|
+
} | null;
|
|
73
|
+
/** Whether a point is on a room's resize corner rather than on the room. */
|
|
74
|
+
export declare function onResizeCorner(room: Room, at: {
|
|
75
|
+
x: number;
|
|
76
|
+
y: number;
|
|
77
|
+
}): boolean;
|
|
78
|
+
export declare function mapRoom(template: Template, roomId: string, change: (room: Room) => Room): Template;
|
|
79
|
+
/**
|
|
80
|
+
* Move a room, and take its user areas with it.
|
|
81
|
+
*
|
|
82
|
+
* The areas are the reason this is a function rather than two lines at the call
|
|
83
|
+
* site: leaving them behind puts every one of them outside its room, which is a
|
|
84
|
+
* validation error the author did not ask for and would have to undo by hand.
|
|
85
|
+
*/
|
|
86
|
+
export declare function moveRoom(template: Template, roomId: string, to: {
|
|
87
|
+
x: number;
|
|
88
|
+
y: number;
|
|
89
|
+
}): Template;
|
|
90
|
+
/** Resize a room by dragging its bottom-right corner to a point. */
|
|
91
|
+
export declare function resizeRoom(template: Template, roomId: string, corner: {
|
|
92
|
+
x: number;
|
|
93
|
+
y: number;
|
|
94
|
+
}, unit: Unit): Template;
|
|
95
|
+
/** Move one user area within its room. */
|
|
96
|
+
export declare function moveArea(template: Template, roomId: string, areaId: string, wanted: {
|
|
97
|
+
x: number;
|
|
98
|
+
y: number;
|
|
99
|
+
}, unit: Unit): Template;
|
|
100
|
+
/** Add a user area to a room, sized in whole cells. */
|
|
101
|
+
export declare function addArea(template: Template, roomId: string, rect: Rect, unit: Unit, id?: string): Template;
|
|
102
|
+
/** A new room opens with the bar at the top and one 1x1 area, ready to adjust. */
|
|
103
|
+
export declare function addRoom(template: Template, rect: Rect, unit: Unit, id?: string): Template;
|
|
104
|
+
/**
|
|
105
|
+
* Nudge a room, or resize it.
|
|
106
|
+
*
|
|
107
|
+
* The keyboard half of the tool, and not a courtesy: an author who can select a
|
|
108
|
+
* room from the panel but cannot then move it has a tool they cannot use.
|
|
109
|
+
*/
|
|
110
|
+
export declare function nudgeRoom(template: Template, roomId: string, direction: Direction, step: number, options?: {
|
|
111
|
+
resize?: boolean;
|
|
112
|
+
}): Template;
|
|
113
|
+
/** Nudge one user area, on the grid and never out of its room. */
|
|
114
|
+
export declare function nudgeArea(template: Template, roomId: string, areaId: string, direction: Direction, step: number, unit: Unit): Template;
|
|
115
|
+
//# sourceMappingURL=geometry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.d.ts","sourceRoot":"","sources":["../../src/canvas/geometry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { avatarUnit, usableRect, } from '@unityevolv/ofiskit-template';
|
|
2
|
+
/**
|
|
3
|
+
* The rules a drawing tool is made of, with no React in them.
|
|
4
|
+
*
|
|
5
|
+
* Everything here takes a template and returns a template, or answers a question
|
|
6
|
+
* about one. That is the whole point of the file: what a drag *means* — where a
|
|
7
|
+
* room may sit, how close is close enough to snap, what happens to a room's user
|
|
8
|
+
* areas when the room moves — is the part worth being sure about, and none of it
|
|
9
|
+
* needs a pointer, an element or a rendered component to be true.
|
|
10
|
+
*
|
|
11
|
+
* The component above turns events into calls to these; it decides nothing.
|
|
12
|
+
*/
|
|
13
|
+
/** Rooms snap to this fraction of the canvas, and to other rooms' edges. */
|
|
14
|
+
export const GRID = 0.01;
|
|
15
|
+
export const SNAP_TO_EDGE = 0.012;
|
|
16
|
+
/** A room has to be at least this big to be a room somebody meant to draw. */
|
|
17
|
+
export const MIN_DRAWN = 0.03;
|
|
18
|
+
/** Half a pixel at any sane canvas size: enough to forgive a rounded edge. */
|
|
19
|
+
const EPSILON = 0.0005;
|
|
20
|
+
export const clamp = (value, low, high) => Math.min(Math.max(value, low), high);
|
|
21
|
+
/**
|
|
22
|
+
* Snap a room edge to the grid, or to another room's edge if one is close.
|
|
23
|
+
*
|
|
24
|
+
* Edges win over the grid: lining up with the room next door is what the author
|
|
25
|
+
* is actually trying to do, and the grid is only there so that a room with
|
|
26
|
+
* nothing near it still lands somewhere tidy. The second value is the edge it
|
|
27
|
+
* snapped to, or null — which is what the alignment guide draws.
|
|
28
|
+
*/
|
|
29
|
+
export function snapRoom(template, value, axis, ignoreId) {
|
|
30
|
+
for (const room of template.rooms) {
|
|
31
|
+
if (room.id === ignoreId)
|
|
32
|
+
continue;
|
|
33
|
+
const edges = axis === 'x'
|
|
34
|
+
? [room.rect.x, room.rect.x + room.rect.width]
|
|
35
|
+
: [room.rect.y, room.rect.y + room.rect.height];
|
|
36
|
+
for (const edge of edges) {
|
|
37
|
+
if (Math.abs(edge - value) < SNAP_TO_EDGE)
|
|
38
|
+
return [edge, edge];
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return [Math.round(value / GRID) * GRID, null];
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Where a user area lands.
|
|
45
|
+
*
|
|
46
|
+
* Its **size** is whole avatar units and always will be: half an avatar is not a
|
|
47
|
+
* place anybody can stand. Its **position** is not, and the two were once
|
|
48
|
+
* conflated. An area holding two people holds exactly two wherever it sits, so
|
|
49
|
+
* snapping its corner to a lattice of whole avatars buys nothing — and it costs a
|
|
50
|
+
* great deal, because one avatar unit is about a sixth of the height of a
|
|
51
|
+
* landscape office, which leaves a typical room with two or three places an area
|
|
52
|
+
* can be at all. It reads as a broken drag rather than as a rule.
|
|
53
|
+
*
|
|
54
|
+
* So the corner snaps to the same fine grid as everything else, and the area is
|
|
55
|
+
* kept inside the room it belongs to — an area outside its room is not somewhere
|
|
56
|
+
* a person can be put, and it used to be reported afterwards as a validation
|
|
57
|
+
* warning instead of being prevented.
|
|
58
|
+
*/
|
|
59
|
+
export function placeArea(wanted, area, usable, unit) {
|
|
60
|
+
const width = area.columns * unit.width;
|
|
61
|
+
const height = area.rows * unit.height;
|
|
62
|
+
return {
|
|
63
|
+
// `Math.max` on the far edge because a room can be resized smaller than an
|
|
64
|
+
// area already in it, and a clamp with its ends crossed returns nonsense.
|
|
65
|
+
x: clamp(Math.round(wanted.x / GRID) * GRID, usable.x, Math.max(usable.x, usable.x + usable.width - width)),
|
|
66
|
+
y: clamp(Math.round(wanted.y / GRID) * GRID, usable.y, Math.max(usable.y, usable.y + usable.height - height)),
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
// ------------------------------------------------------------------ hit tests
|
|
70
|
+
export function roomAt(template, at) {
|
|
71
|
+
// Reverse order so the most recently added room wins where they overlap,
|
|
72
|
+
// which is what the author just drew and therefore what they mean.
|
|
73
|
+
for (let index = template.rooms.length - 1; index >= 0; index -= 1) {
|
|
74
|
+
const room = template.rooms[index];
|
|
75
|
+
if (!room)
|
|
76
|
+
continue;
|
|
77
|
+
const { x, y, width, height } = room.rect;
|
|
78
|
+
if (at.x >= x - EPSILON && at.x <= x + width && at.y >= y - EPSILON && at.y <= y + height) {
|
|
79
|
+
return room;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
return null;
|
|
83
|
+
}
|
|
84
|
+
export function areaAt(template, at) {
|
|
85
|
+
const unit = avatarUnit(template.canvas, template.avatarSize);
|
|
86
|
+
for (const room of template.rooms) {
|
|
87
|
+
for (const area of room.areas) {
|
|
88
|
+
const width = area.columns * unit.width;
|
|
89
|
+
const height = area.rows * unit.height;
|
|
90
|
+
if (at.x >= area.x && at.x <= area.x + width && at.y >= area.y && at.y <= area.y + height) {
|
|
91
|
+
return { room, area };
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
97
|
+
/** Whether a point is on a room's resize corner rather than on the room. */
|
|
98
|
+
export function onResizeCorner(room, at) {
|
|
99
|
+
return (Math.abs(at.x - (room.rect.x + room.rect.width)) < 0.015 &&
|
|
100
|
+
Math.abs(at.y - (room.rect.y + room.rect.height)) < 0.015);
|
|
101
|
+
}
|
|
102
|
+
// ----------------------------------------------------------------- transforms
|
|
103
|
+
export function mapRoom(template, roomId, change) {
|
|
104
|
+
return {
|
|
105
|
+
...template,
|
|
106
|
+
rooms: template.rooms.map((room) => (room.id === roomId ? change(room) : room)),
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Move a room, and take its user areas with it.
|
|
111
|
+
*
|
|
112
|
+
* The areas are the reason this is a function rather than two lines at the call
|
|
113
|
+
* site: leaving them behind puts every one of them outside its room, which is a
|
|
114
|
+
* validation error the author did not ask for and would have to undo by hand.
|
|
115
|
+
*/
|
|
116
|
+
export function moveRoom(template, roomId, to) {
|
|
117
|
+
return mapRoom(template, roomId, (room) => {
|
|
118
|
+
const x = clamp(to.x, 0, 1 - room.rect.width);
|
|
119
|
+
const y = clamp(to.y, 0, 1 - room.rect.height);
|
|
120
|
+
return {
|
|
121
|
+
...room,
|
|
122
|
+
rect: { ...room.rect, x, y },
|
|
123
|
+
areas: room.areas.map((area) => ({
|
|
124
|
+
...area,
|
|
125
|
+
x: area.x + x - room.rect.x,
|
|
126
|
+
y: area.y + y - room.rect.y,
|
|
127
|
+
})),
|
|
128
|
+
};
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
/** Resize a room by dragging its bottom-right corner to a point. */
|
|
132
|
+
export function resizeRoom(template, roomId, corner, unit) {
|
|
133
|
+
return mapRoom(template, roomId, (room) => ({
|
|
134
|
+
...room,
|
|
135
|
+
rect: {
|
|
136
|
+
...room.rect,
|
|
137
|
+
width: clamp(corner.x - room.rect.x, unit.width, 1 - room.rect.x),
|
|
138
|
+
height: clamp(corner.y - room.rect.y, unit.height, 1 - room.rect.y),
|
|
139
|
+
},
|
|
140
|
+
}));
|
|
141
|
+
}
|
|
142
|
+
/** Move one user area within its room. */
|
|
143
|
+
export function moveArea(template, roomId, areaId, wanted, unit) {
|
|
144
|
+
const room = template.rooms.find((candidate) => candidate.id === roomId);
|
|
145
|
+
if (!room)
|
|
146
|
+
return template;
|
|
147
|
+
const usable = usableRect(room, template.canvas);
|
|
148
|
+
return mapRoom(template, roomId, (current) => ({
|
|
149
|
+
...current,
|
|
150
|
+
areas: current.areas.map((area) => area.id === areaId ? { ...area, ...placeArea(wanted, area, usable, unit) } : area),
|
|
151
|
+
}));
|
|
152
|
+
}
|
|
153
|
+
/** Add a user area to a room, sized in whole cells. */
|
|
154
|
+
export function addArea(template, roomId, rect, unit, id = `area-${Date.now().toString(36)}`) {
|
|
155
|
+
const columns = Math.max(1, Math.round(rect.width / unit.width));
|
|
156
|
+
const rows = Math.max(1, Math.round(rect.height / unit.height));
|
|
157
|
+
return mapRoom(template, roomId, (room) => ({
|
|
158
|
+
...room,
|
|
159
|
+
areas: [...room.areas, { id, x: rect.x, y: rect.y, columns, rows }],
|
|
160
|
+
}));
|
|
161
|
+
}
|
|
162
|
+
/** A new room opens with the bar at the top and one 1x1 area, ready to adjust. */
|
|
163
|
+
export function addRoom(template, rect, unit, id = `room-${Date.now().toString(36)}`) {
|
|
164
|
+
const used = template.rooms.filter((room) => room.type === 'workspace' || room.type === 'meeting');
|
|
165
|
+
return {
|
|
166
|
+
...template,
|
|
167
|
+
rooms: [
|
|
168
|
+
...template.rooms,
|
|
169
|
+
{
|
|
170
|
+
id,
|
|
171
|
+
name: `Room ${used.length + 1}`,
|
|
172
|
+
type: 'workspace',
|
|
173
|
+
rect,
|
|
174
|
+
bar: 'top',
|
|
175
|
+
areas: [
|
|
176
|
+
{
|
|
177
|
+
id: `area-${id}`,
|
|
178
|
+
x: rect.x + (rect.width - unit.width) / 2,
|
|
179
|
+
y: rect.y + (rect.height - unit.height) / 2,
|
|
180
|
+
columns: 1,
|
|
181
|
+
rows: 1,
|
|
182
|
+
},
|
|
183
|
+
],
|
|
184
|
+
},
|
|
185
|
+
],
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
// ------------------------------------------------------------------- keyboard
|
|
189
|
+
/**
|
|
190
|
+
* Nudge a room, or resize it.
|
|
191
|
+
*
|
|
192
|
+
* The keyboard half of the tool, and not a courtesy: an author who can select a
|
|
193
|
+
* room from the panel but cannot then move it has a tool they cannot use.
|
|
194
|
+
*/
|
|
195
|
+
export function nudgeRoom(template, roomId, direction, step, options = {}) {
|
|
196
|
+
const dx = direction.x * step;
|
|
197
|
+
const dy = direction.y * step;
|
|
198
|
+
if (options.resize) {
|
|
199
|
+
return mapRoom(template, roomId, (room) => ({
|
|
200
|
+
...room,
|
|
201
|
+
rect: {
|
|
202
|
+
...room.rect,
|
|
203
|
+
width: clamp(room.rect.width + dx, GRID * 4, 1 - room.rect.x),
|
|
204
|
+
height: clamp(room.rect.height + dy, GRID * 4, 1 - room.rect.y),
|
|
205
|
+
},
|
|
206
|
+
}));
|
|
207
|
+
}
|
|
208
|
+
const room = template.rooms.find((candidate) => candidate.id === roomId);
|
|
209
|
+
if (!room)
|
|
210
|
+
return template;
|
|
211
|
+
return moveRoom(template, roomId, { x: room.rect.x + dx, y: room.rect.y + dy });
|
|
212
|
+
}
|
|
213
|
+
/** Nudge one user area, on the grid and never out of its room. */
|
|
214
|
+
export function nudgeArea(template, roomId, areaId, direction, step, unit) {
|
|
215
|
+
const area = template.rooms
|
|
216
|
+
.find((candidate) => candidate.id === roomId)
|
|
217
|
+
?.areas.find((candidate) => candidate.id === areaId);
|
|
218
|
+
if (!area)
|
|
219
|
+
return template;
|
|
220
|
+
return moveArea(template, roomId, areaId, { x: area.x + direction.x * step, y: area.y + direction.y * step }, unit);
|
|
221
|
+
}
|
|
222
|
+
//# sourceMappingURL=geometry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"geometry.js","sourceRoot":"","sources":["../../src/canvas/geometry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { Template } from '@unityevolv/ofiskit-template';
|
|
2
|
+
/**
|
|
3
|
+
* What the canvas is for and what is picked, shared by the surface and the panel.
|
|
4
|
+
*
|
|
5
|
+
* In their own file because the hooks, the surface and the panel all need them,
|
|
6
|
+
* and a type imported from the component that renders it is how two files end up
|
|
7
|
+
* importing each other.
|
|
8
|
+
*/
|
|
9
|
+
export type Tool = 'select' | 'room' | 'area';
|
|
10
|
+
export type Selection = {
|
|
11
|
+
kind: 'none';
|
|
12
|
+
} | {
|
|
13
|
+
kind: 'room';
|
|
14
|
+
roomId: string;
|
|
15
|
+
} | {
|
|
16
|
+
kind: 'area';
|
|
17
|
+
roomId: string;
|
|
18
|
+
areaId: string;
|
|
19
|
+
};
|
|
20
|
+
/** How a change reaches the history: replacing a step, or opening a new one. */
|
|
21
|
+
export interface ChangeOptions {
|
|
22
|
+
/** Part of a drag in progress. Replaces the current step rather than adding one. */
|
|
23
|
+
transient?: boolean;
|
|
24
|
+
/** The template as it was before this gesture, which is what undo goes back to. */
|
|
25
|
+
before?: Template;
|
|
26
|
+
}
|
|
27
|
+
export type OnChange = (template: Template, options?: ChangeOptions) => void;
|
|
28
|
+
/** One gesture in progress. */
|
|
29
|
+
export interface Drag {
|
|
30
|
+
kind: 'draw-room' | 'draw-area' | 'move-room' | 'resize-room' | 'move-area';
|
|
31
|
+
roomId?: string;
|
|
32
|
+
areaId?: string;
|
|
33
|
+
/** Where the pointer went down, in canvas coordinates. */
|
|
34
|
+
from: {
|
|
35
|
+
x: number;
|
|
36
|
+
y: number;
|
|
37
|
+
};
|
|
38
|
+
/** The template before the drag, so the whole drag is one undo step. */
|
|
39
|
+
before: Template;
|
|
40
|
+
/** Offset from the shape's corner, so it does not jump under the cursor. */
|
|
41
|
+
grab?: {
|
|
42
|
+
x: number;
|
|
43
|
+
y: number;
|
|
44
|
+
};
|
|
45
|
+
handle?: 'se' | 'nw';
|
|
46
|
+
}
|
|
47
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/canvas/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AAE5D;;;;;;GAMG;AAEH,MAAM,MAAM,IAAI,GAAG,QAAQ,GAAG,MAAM,GAAG,MAAM,CAAA;AAE7C,MAAM,MAAM,SAAS,GACjB;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,GAChC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAEpD,gFAAgF;AAChF,MAAM,WAAW,aAAa;IAC5B,oFAAoF;IACpF,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,mFAAmF;IACnF,MAAM,CAAC,EAAE,QAAQ,CAAA;CAClB;AAED,MAAM,MAAM,QAAQ,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,OAAO,CAAC,EAAE,aAAa,KAAK,IAAI,CAAA;AAE5E,+BAA+B;AAC/B,MAAM,WAAW,IAAI;IACnB,IAAI,EAAE,WAAW,GAAG,WAAW,GAAG,WAAW,GAAG,aAAa,GAAG,WAAW,CAAA;IAC3E,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,0DAA0D;IAC1D,IAAI,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC9B,wEAAwE;IACxE,MAAM,EAAE,QAAQ,CAAA;IAChB,4EAA4E;IAC5E,IAAI,CAAC,EAAE;QAAE,CAAC,EAAE,MAAM,CAAC;QAAC,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;IAC/B,MAAM,CAAC,EAAE,IAAI,GAAG,IAAI,CAAA;CACrB"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/canvas/types.ts"],"names":[],"mappings":""}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { type Rect, type Template } from '@unityevolv/ofiskit-template';
|
|
2
|
+
import { type PointerEvent as ReactPointerEvent, type RefObject } from 'react';
|
|
3
|
+
import { type Unit } from './geometry.js';
|
|
4
|
+
import type { OnChange, Selection, Tool } from './types.js';
|
|
5
|
+
/**
|
|
6
|
+
* One gesture at a time, from pointer down to pointer up.
|
|
7
|
+
*
|
|
8
|
+
* Held apart from the surface because it is a small state machine and the surface
|
|
9
|
+
* is a large piece of markup, and mixing the two made both hard to follow. What is
|
|
10
|
+
* here is only the sequencing — which gesture is running, what it looks like while
|
|
11
|
+
* it runs, and when it becomes an undo step. What each gesture *means* is in
|
|
12
|
+
* `geometry.ts`, where it can be tested without a pointer.
|
|
13
|
+
*/
|
|
14
|
+
export interface CanvasDrag {
|
|
15
|
+
/** The rectangle being drawn, while one is. Nothing to do with the template yet. */
|
|
16
|
+
preview: Rect | null;
|
|
17
|
+
/** Alignment guides: an edge this drag has snapped to, so the author can see it. */
|
|
18
|
+
guides: {
|
|
19
|
+
x: number[];
|
|
20
|
+
y: number[];
|
|
21
|
+
};
|
|
22
|
+
onPointerDown(event: ReactPointerEvent): void;
|
|
23
|
+
onPointerMove(event: ReactPointerEvent): void;
|
|
24
|
+
onPointerUp(): void;
|
|
25
|
+
}
|
|
26
|
+
export declare function useCanvasDrag(options: {
|
|
27
|
+
template: Template;
|
|
28
|
+
tool: Tool;
|
|
29
|
+
unit: Unit;
|
|
30
|
+
surface: RefObject<HTMLDivElement | null>;
|
|
31
|
+
onSelect(selection: Selection): void;
|
|
32
|
+
onChange: OnChange;
|
|
33
|
+
onToolDone(): void;
|
|
34
|
+
}): CanvasDrag;
|
|
35
|
+
//# sourceMappingURL=useCanvasDrag.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useCanvasDrag.d.ts","sourceRoot":"","sources":["../../src/canvas/useCanvasDrag.ts"],"names":[],"mappings":"AAAA,OAAO,EAAc,KAAK,IAAI,EAAE,KAAK,QAAQ,EAAE,MAAM,8BAA8B,CAAA;AACnF,OAAO,EAAY,KAAK,YAAY,IAAI,iBAAiB,EAAE,KAAK,SAAS,EAAE,MAAM,OAAO,CAAA;AAExF,OAAO,EAaL,KAAK,IAAI,EACV,MAAM,eAAe,CAAA;AACtB,OAAO,KAAK,EAAQ,QAAQ,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,YAAY,CAAA;AAEjE;;;;;;;;GAQG;AAEH,MAAM,WAAW,UAAU;IACzB,oFAAoF;IACpF,OAAO,EAAE,IAAI,GAAG,IAAI,CAAA;IACpB,oFAAoF;IACpF,MAAM,EAAE;QAAE,CAAC,EAAE,MAAM,EAAE,CAAC;QAAC,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAA;IACpC,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC7C,aAAa,CAAC,KAAK,EAAE,iBAAiB,GAAG,IAAI,CAAA;IAC7C,WAAW,IAAI,IAAI,CAAA;CACpB;AAED,wBAAgB,aAAa,CAAC,OAAO,EAAE;IACrC,QAAQ,EAAE,QAAQ,CAAA;IAClB,IAAI,EAAE,IAAI,CAAA;IACV,IAAI,EAAE,IAAI,CAAA;IACV,OAAO,EAAE,SAAS,CAAC,cAAc,GAAG,IAAI,CAAC,CAAA;IACzC,QAAQ,CAAC,SAAS,EAAE,SAAS,GAAG,IAAI,CAAA;IACpC,QAAQ,EAAE,QAAQ,CAAA;IAClB,UAAU,IAAI,IAAI,CAAA;CACnB,GAAG,UAAU,CA2Jb"}
|