orio-ui 1.20.0 → 1.23.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (139) hide show
  1. package/README.md +12 -5
  2. package/dist/module.json +1 -1
  3. package/dist/module.mjs +5 -2
  4. package/dist/runtime/canvas.d.ts +21 -0
  5. package/dist/runtime/canvas.js +49 -0
  6. package/dist/runtime/components/Canvas/REQUIREMENTS.md +174 -0
  7. package/dist/runtime/components/Canvas/components/Stage.d.vue.ts +3 -0
  8. package/dist/runtime/components/Canvas/components/Stage.vue +150 -0
  9. package/dist/runtime/components/Canvas/components/Stage.vue.d.ts +3 -0
  10. package/dist/runtime/components/Canvas/components/ToolButton.d.vue.ts +24 -0
  11. package/dist/runtime/components/Canvas/components/ToolButton.vue +62 -0
  12. package/dist/runtime/components/Canvas/components/ToolButton.vue.d.ts +24 -0
  13. package/dist/runtime/components/Canvas/components/Toolbar.d.vue.ts +24 -0
  14. package/dist/runtime/components/Canvas/components/Toolbar.vue +48 -0
  15. package/dist/runtime/components/Canvas/components/Toolbar.vue.d.ts +24 -0
  16. package/dist/runtime/components/Canvas/composables/useCanvasHistory.d.ts +17 -0
  17. package/dist/runtime/components/Canvas/composables/useCanvasHistory.js +76 -0
  18. package/dist/runtime/components/Canvas/composables/useCanvasNodes.d.ts +13 -0
  19. package/dist/runtime/components/Canvas/composables/useCanvasNodes.js +60 -0
  20. package/dist/runtime/components/Canvas/composables/useCanvasSetup.d.ts +5 -0
  21. package/dist/runtime/components/Canvas/composables/useCanvasSetup.js +19 -0
  22. package/dist/runtime/components/Canvas/context.d.ts +38 -0
  23. package/dist/runtime/components/Canvas/context.js +11 -0
  24. package/dist/runtime/components/Canvas/index.d.vue.ts +77 -0
  25. package/dist/runtime/components/Canvas/index.vue +208 -0
  26. package/dist/runtime/components/Canvas/index.vue.d.ts +77 -0
  27. package/dist/runtime/components/Canvas/registry.d.ts +1 -0
  28. package/dist/runtime/components/Canvas/registry.js +2 -0
  29. package/dist/runtime/components/Canvas/tools/ColorPickerWidget.d.vue.ts +7 -0
  30. package/dist/runtime/components/Canvas/tools/ColorPickerWidget.vue +32 -0
  31. package/dist/runtime/components/Canvas/tools/ColorPickerWidget.vue.d.ts +7 -0
  32. package/dist/runtime/components/Canvas/tools/clearTool.d.ts +1 -0
  33. package/dist/runtime/components/Canvas/tools/clearTool.js +16 -0
  34. package/dist/runtime/components/Canvas/tools/colorPickerTool.d.ts +6 -0
  35. package/dist/runtime/components/Canvas/tools/colorPickerTool.js +15 -0
  36. package/dist/runtime/components/Canvas/tools/drawTool.d.ts +16 -0
  37. package/dist/runtime/components/Canvas/tools/drawTool.js +92 -0
  38. package/dist/runtime/components/Canvas/tools/eraseTool.d.ts +5 -0
  39. package/dist/runtime/components/Canvas/tools/eraseTool.js +62 -0
  40. package/dist/runtime/components/Canvas/tools/exportTool.d.ts +18 -0
  41. package/dist/runtime/components/Canvas/tools/exportTool.js +89 -0
  42. package/dist/runtime/components/Canvas/tools/highlightTool.d.ts +11 -0
  43. package/dist/runtime/components/Canvas/tools/highlightTool.js +51 -0
  44. package/dist/runtime/components/Canvas/tools/hitTest.d.ts +20 -0
  45. package/dist/runtime/components/Canvas/tools/hitTest.js +111 -0
  46. package/dist/runtime/components/Canvas/tools/imageTool.d.ts +18 -0
  47. package/dist/runtime/components/Canvas/tools/imageTool.js +163 -0
  48. package/dist/runtime/components/Canvas/tools/moveTool.d.ts +5 -0
  49. package/dist/runtime/components/Canvas/tools/moveTool.js +94 -0
  50. package/dist/runtime/components/Canvas/tools/redoTool.d.ts +1 -0
  51. package/dist/runtime/components/Canvas/tools/redoTool.js +17 -0
  52. package/dist/runtime/components/Canvas/tools/resizeTool.d.ts +7 -0
  53. package/dist/runtime/components/Canvas/tools/resizeTool.js +132 -0
  54. package/dist/runtime/components/Canvas/tools/rotateTool.d.ts +5 -0
  55. package/dist/runtime/components/Canvas/tools/rotateTool.js +109 -0
  56. package/dist/runtime/components/Canvas/tools/textTool.d.ts +14 -0
  57. package/dist/runtime/components/Canvas/tools/textTool.js +99 -0
  58. package/dist/runtime/components/Canvas/tools/tooltips/Clear.d.vue.ts +3 -0
  59. package/dist/runtime/components/Canvas/tools/tooltips/Clear.vue +12 -0
  60. package/dist/runtime/components/Canvas/tools/tooltips/Clear.vue.d.ts +3 -0
  61. package/dist/runtime/components/Canvas/tools/tooltips/Draw.d.vue.ts +3 -0
  62. package/dist/runtime/components/Canvas/tools/tooltips/Draw.vue +12 -0
  63. package/dist/runtime/components/Canvas/tools/tooltips/Draw.vue.d.ts +3 -0
  64. package/dist/runtime/components/Canvas/tools/tooltips/Erase.d.vue.ts +3 -0
  65. package/dist/runtime/components/Canvas/tools/tooltips/Erase.vue +12 -0
  66. package/dist/runtime/components/Canvas/tools/tooltips/Erase.vue.d.ts +3 -0
  67. package/dist/runtime/components/Canvas/tools/tooltips/Export.d.vue.ts +3 -0
  68. package/dist/runtime/components/Canvas/tools/tooltips/Export.vue +13 -0
  69. package/dist/runtime/components/Canvas/tools/tooltips/Export.vue.d.ts +3 -0
  70. package/dist/runtime/components/Canvas/tools/tooltips/Highlight.d.vue.ts +3 -0
  71. package/dist/runtime/components/Canvas/tools/tooltips/Highlight.vue +12 -0
  72. package/dist/runtime/components/Canvas/tools/tooltips/Highlight.vue.d.ts +3 -0
  73. package/dist/runtime/components/Canvas/tools/tooltips/Image.d.vue.ts +3 -0
  74. package/dist/runtime/components/Canvas/tools/tooltips/Image.vue +13 -0
  75. package/dist/runtime/components/Canvas/tools/tooltips/Image.vue.d.ts +3 -0
  76. package/dist/runtime/components/Canvas/tools/tooltips/Move.d.vue.ts +3 -0
  77. package/dist/runtime/components/Canvas/tools/tooltips/Move.vue +13 -0
  78. package/dist/runtime/components/Canvas/tools/tooltips/Move.vue.d.ts +3 -0
  79. package/dist/runtime/components/Canvas/tools/tooltips/Redo.d.vue.ts +3 -0
  80. package/dist/runtime/components/Canvas/tools/tooltips/Redo.vue +13 -0
  81. package/dist/runtime/components/Canvas/tools/tooltips/Redo.vue.d.ts +3 -0
  82. package/dist/runtime/components/Canvas/tools/tooltips/Resize.d.vue.ts +3 -0
  83. package/dist/runtime/components/Canvas/tools/tooltips/Resize.vue +13 -0
  84. package/dist/runtime/components/Canvas/tools/tooltips/Resize.vue.d.ts +3 -0
  85. package/dist/runtime/components/Canvas/tools/tooltips/Rotate.d.vue.ts +3 -0
  86. package/dist/runtime/components/Canvas/tools/tooltips/Rotate.vue +13 -0
  87. package/dist/runtime/components/Canvas/tools/tooltips/Rotate.vue.d.ts +3 -0
  88. package/dist/runtime/components/Canvas/tools/tooltips/Text.d.vue.ts +3 -0
  89. package/dist/runtime/components/Canvas/tools/tooltips/Text.vue +13 -0
  90. package/dist/runtime/components/Canvas/tools/tooltips/Text.vue.d.ts +3 -0
  91. package/dist/runtime/components/Canvas/tools/tooltips/Transform.d.vue.ts +3 -0
  92. package/dist/runtime/components/Canvas/tools/tooltips/Transform.vue +14 -0
  93. package/dist/runtime/components/Canvas/tools/tooltips/Transform.vue.d.ts +3 -0
  94. package/dist/runtime/components/Canvas/tools/tooltips/Undo.d.vue.ts +3 -0
  95. package/dist/runtime/components/Canvas/tools/tooltips/Undo.vue +13 -0
  96. package/dist/runtime/components/Canvas/tools/tooltips/Undo.vue.d.ts +3 -0
  97. package/dist/runtime/components/Canvas/tools/transformHandles.d.ts +74 -0
  98. package/dist/runtime/components/Canvas/tools/transformHandles.js +191 -0
  99. package/dist/runtime/components/Canvas/tools/transformTool.d.ts +7 -0
  100. package/dist/runtime/components/Canvas/tools/transformTool.js +210 -0
  101. package/dist/runtime/components/Canvas/tools/undoTool.d.ts +1 -0
  102. package/dist/runtime/components/Canvas/tools/undoTool.js +17 -0
  103. package/dist/runtime/components/Canvas/types.d.ts +125 -0
  104. package/dist/runtime/components/Canvas/types.js +3 -0
  105. package/dist/runtime/components/ControlElement.vue +5 -1
  106. package/dist/runtime/components/DateRangePicker.vue +16 -4
  107. package/dist/runtime/components/Icon.vue +2 -2
  108. package/dist/runtime/components/LocaleSwitcher.d.vue.ts +13 -0
  109. package/dist/runtime/components/LocaleSwitcher.vue +43 -0
  110. package/dist/runtime/components/LocaleSwitcher.vue.d.ts +13 -0
  111. package/dist/runtime/components/Selector.vue +14 -5
  112. package/dist/runtime/components/Tooltip.vue +17 -7
  113. package/dist/runtime/components/ZoomableContainer.d.vue.ts +48 -0
  114. package/dist/runtime/components/ZoomableContainer.vue +238 -0
  115. package/dist/runtime/components/ZoomableContainer.vue.d.ts +48 -0
  116. package/dist/runtime/components/gallery/Carousel.vue +1 -1
  117. package/dist/runtime/components/gallery/CarouselPreview.d.vue.ts +31 -0
  118. package/dist/runtime/components/gallery/CarouselPreview.vue +64 -0
  119. package/dist/runtime/components/gallery/CarouselPreview.vue.d.ts +31 -0
  120. package/dist/runtime/components/view/Dates.vue +5 -3
  121. package/dist/runtime/components/view/KeyBinds.d.vue.ts +7 -0
  122. package/dist/runtime/components/view/KeyBinds.vue +36 -0
  123. package/dist/runtime/components/view/KeyBinds.vue.d.ts +7 -0
  124. package/dist/runtime/components/view/Text.vue +4 -4
  125. package/dist/runtime/composables/useInertia.d.ts +10 -0
  126. package/dist/runtime/composables/useInertia.js +49 -0
  127. package/dist/runtime/composables/usePinchZoom.d.ts +13 -0
  128. package/dist/runtime/composables/usePinchZoom.js +66 -0
  129. package/dist/runtime/composables/useValidation.js +11 -1
  130. package/dist/runtime/i18n/en.json +20 -0
  131. package/dist/runtime/i18n/index.d.ts +11 -0
  132. package/dist/runtime/i18n/index.js +19 -0
  133. package/dist/runtime/i18n/uk.json +20 -0
  134. package/dist/runtime/index.d.ts +5 -0
  135. package/dist/runtime/index.js +16 -0
  136. package/dist/runtime/plugins/i18n.d.ts +2 -0
  137. package/dist/runtime/plugins/i18n.js +18 -0
  138. package/dist/runtime/utils/icon-registry.js +13 -1
  139. package/package.json +9 -4
@@ -0,0 +1,48 @@
1
+ <script setup>
2
+ import { computed, provide, watchEffect } from "vue";
3
+ import { useI18n } from "vue-i18n";
4
+ import { CANVAS_CONTEXT } from "../context";
5
+ import { canvasRegistry } from "../registry";
6
+ import ToolButton from "./ToolButton.vue";
7
+ const props = defineProps({
8
+ canvas: { type: String, required: true }
9
+ });
10
+ const { t } = useI18n();
11
+ const ctx = computed(() => canvasRegistry.get(props.canvas));
12
+ watchEffect(() => {
13
+ if (import.meta.env.DEV && !ctx.value) {
14
+ console.warn(
15
+ `[orio-canvas-toolbar] no canvas registered with name "${props.canvas}". Make sure <orio-canvas name="${props.canvas}" /> is mounted.`
16
+ );
17
+ }
18
+ });
19
+ const proxy = new Proxy({}, {
20
+ get(_, key) {
21
+ const real = ctx.value;
22
+ if (!real) return void 0;
23
+ return real[key];
24
+ }
25
+ });
26
+ provide(CANVAS_CONTEXT, proxy);
27
+ </script>
28
+
29
+ <template>
30
+ <div
31
+ v-if="ctx"
32
+ class="canvas-toolbar"
33
+ role="toolbar"
34
+ :aria-label="t('canvas.toolbarLabel')"
35
+ >
36
+ <slot
37
+ :tools="ctx.tools.value"
38
+ :active-tool-id="ctx.activeToolId.value"
39
+ :set-active-tool="ctx.setActiveTool"
40
+ >
41
+ <ToolButton v-for="tool in ctx.tools.value" :key="tool.id" :tool />
42
+ </slot>
43
+ </div>
44
+ </template>
45
+
46
+ <style scoped>
47
+ .canvas-toolbar{display:flex;gap:.25rem;padding:.25rem}
48
+ </style>
@@ -0,0 +1,24 @@
1
+ interface Props {
2
+ /**
3
+ * Name of the `<orio-canvas>` instance to bind to. Lets the toolbar live
4
+ * anywhere in the app — header, sidebar, modal — not just inside the canvas.
5
+ */
6
+ canvas: string;
7
+ }
8
+ declare var __VLS_1: {
9
+ tools: any;
10
+ activeToolId: any;
11
+ setActiveTool: any;
12
+ };
13
+ type __VLS_Slots = {} & {
14
+ default?: (props: typeof __VLS_1) => any;
15
+ };
16
+ declare const __VLS_base: import("vue").DefineComponent<Props, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {}, string, import("vue").PublicProps, Readonly<Props> & Readonly<{}>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
17
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
18
+ declare const _default: typeof __VLS_export;
19
+ export default _default;
20
+ type __VLS_WithSlots<T, S> = T & {
21
+ new (): {
22
+ $slots: S;
23
+ };
24
+ };
@@ -0,0 +1,17 @@
1
+ import { type Ref } from "vue";
2
+ import type { CanvasNode } from "../types.js";
3
+ export declare function useCanvasHistory(nodes: Ref<CanvasNode[]>, opts: {
4
+ maxHistory: () => number;
5
+ requestRender: () => void;
6
+ }): {
7
+ canUndo: import("vue").ComputedRef<boolean>;
8
+ canRedo: import("vue").ComputedRef<boolean>;
9
+ undo: () => void;
10
+ redo: () => void;
11
+ commit: () => void;
12
+ autoCommit: () => void;
13
+ beginAction: () => void;
14
+ endAction: () => void;
15
+ resetBaseline: () => void;
16
+ onKeyDown: (e: KeyboardEvent) => void;
17
+ };
@@ -0,0 +1,76 @@
1
+ import { computed, shallowRef } from "vue";
2
+ function deepClone(arr) {
3
+ return JSON.parse(JSON.stringify(arr));
4
+ }
5
+ export function useCanvasHistory(nodes, opts) {
6
+ const undoStack = shallowRef([]);
7
+ const redoStack = shallowRef([]);
8
+ const canUndo = computed(() => undoStack.value.length > 0);
9
+ const canRedo = computed(() => redoStack.value.length > 0);
10
+ let committed = deepClone(nodes.value);
11
+ let pointerDown = false;
12
+ function commit() {
13
+ const current = deepClone(nodes.value);
14
+ if (JSON.stringify(committed) === JSON.stringify(current)) return;
15
+ const next = [...undoStack.value, committed];
16
+ if (next.length > opts.maxHistory()) next.shift();
17
+ undoStack.value = next;
18
+ if (redoStack.value.length > 0) redoStack.value = [];
19
+ committed = current;
20
+ }
21
+ function beginAction() {
22
+ pointerDown = true;
23
+ }
24
+ function endAction() {
25
+ pointerDown = false;
26
+ commit();
27
+ }
28
+ function undo() {
29
+ if (!canUndo.value) return;
30
+ const stack = [...undoStack.value];
31
+ const prev = stack.pop();
32
+ undoStack.value = stack;
33
+ redoStack.value = [...redoStack.value, committed];
34
+ committed = prev;
35
+ nodes.value = deepClone(prev);
36
+ opts.requestRender();
37
+ }
38
+ function redo() {
39
+ if (!canRedo.value) return;
40
+ const stack = [...redoStack.value];
41
+ const next = stack.pop();
42
+ redoStack.value = stack;
43
+ undoStack.value = [...undoStack.value, committed];
44
+ committed = next;
45
+ nodes.value = deepClone(next);
46
+ opts.requestRender();
47
+ }
48
+ function autoCommit() {
49
+ if (!pointerDown) commit();
50
+ }
51
+ function resetBaseline() {
52
+ committed = deepClone(nodes.value);
53
+ }
54
+ function onKeyDown(e) {
55
+ const mod = e.metaKey || e.ctrlKey;
56
+ if (!mod || e.key.toLowerCase() !== "z") return;
57
+ e.preventDefault();
58
+ if (e.shiftKey) {
59
+ redo();
60
+ } else {
61
+ undo();
62
+ }
63
+ }
64
+ return {
65
+ canUndo,
66
+ canRedo,
67
+ undo,
68
+ redo,
69
+ commit,
70
+ autoCommit,
71
+ beginAction,
72
+ endAction,
73
+ resetBaseline,
74
+ onKeyDown
75
+ };
76
+ }
@@ -0,0 +1,13 @@
1
+ import type { Ref } from "vue";
2
+ import type { CanvasNode } from "../types.js";
3
+ export declare function useCanvasNodes(nodes: Ref<CanvasNode[]>, opts: {
4
+ onMutate: () => void;
5
+ }): {
6
+ addNode: (node: Omit<CanvasNode, "id"> & {
7
+ id?: string;
8
+ }) => CanvasNode;
9
+ updateNode: (id: string, patch: Partial<Omit<CanvasNode, "id">>) => void;
10
+ getNode: (id: string) => any;
11
+ removeNode: (id: string) => void;
12
+ clear: () => void;
13
+ };
@@ -0,0 +1,60 @@
1
+ export function useCanvasNodes(nodes, opts) {
2
+ let pending = null;
3
+ function read() {
4
+ return pending ?? nodes.value;
5
+ }
6
+ function write(value) {
7
+ pending = value;
8
+ nodes.value = value;
9
+ queueMicrotask(() => {
10
+ pending = null;
11
+ });
12
+ }
13
+ function _addNode(node) {
14
+ const current = read();
15
+ const maxZ = current.reduce((m, n) => Math.max(m, n.zIndex ?? 0), -1);
16
+ const created = {
17
+ zIndex: maxZ + 1,
18
+ ...node,
19
+ id: node.id ?? crypto.randomUUID()
20
+ };
21
+ write([...current, created]);
22
+ return created;
23
+ }
24
+ function _updateNode(id, patch) {
25
+ const current = read();
26
+ const idx = current.findIndex((n) => n.id === id);
27
+ if (idx === -1) return;
28
+ const { id: _ignored, ...safePatch } = patch;
29
+ const next = current.slice();
30
+ next[idx] = { ...next[idx], ...safePatch };
31
+ write(next);
32
+ }
33
+ function getNode(id) {
34
+ return read().find((n) => n.id === id);
35
+ }
36
+ function _removeNode(id) {
37
+ write(read().filter((n) => n.id !== id));
38
+ }
39
+ function _clear() {
40
+ write(read().filter((n) => n.frozen));
41
+ }
42
+ function addNode(node) {
43
+ const created = _addNode(node);
44
+ opts.onMutate();
45
+ return created;
46
+ }
47
+ function updateNode(id, patch) {
48
+ _updateNode(id, patch);
49
+ opts.onMutate();
50
+ }
51
+ function removeNode(id) {
52
+ _removeNode(id);
53
+ opts.onMutate();
54
+ }
55
+ function clear() {
56
+ _clear();
57
+ opts.onMutate();
58
+ }
59
+ return { addNode, updateNode, getNode, removeNode, clear };
60
+ }
@@ -0,0 +1,5 @@
1
+ import type { CanvasToolApi } from "../types.js";
2
+ export declare function useCanvasSetup(setupFn: ((api: CanvasToolApi) => void | Promise<void>) | undefined, opts: {
3
+ getToolApi: () => CanvasToolApi;
4
+ resetBaseline: () => void;
5
+ }): void;
@@ -0,0 +1,19 @@
1
+ import { onMounted } from "vue";
2
+ export function useCanvasSetup(setupFn, opts) {
3
+ onMounted(() => {
4
+ if (!setupFn) return;
5
+ const api = opts.getToolApi();
6
+ const done = () => {
7
+ opts.resetBaseline();
8
+ requestAnimationFrame(() => api.requestRender());
9
+ };
10
+ const result = setupFn(api);
11
+ if (result && typeof result.then === "function") {
12
+ result.then(done, (err) => {
13
+ console.error("[Canvas] setup() rejected:", err);
14
+ });
15
+ } else {
16
+ done();
17
+ }
18
+ });
19
+ }
@@ -0,0 +1,38 @@
1
+ import { type ComputedRef, type InjectionKey, type Ref } from "vue";
2
+ import type { CanvasNode, CanvasTool, CanvasToolApi } from "./types.js";
3
+ export interface CanvasContext {
4
+ tools: Ref<CanvasTool[]>;
5
+ activeToolId: Ref<string | null>;
6
+ setActiveTool: (id: string | null) => void;
7
+ nodes: Ref<CanvasNode[]>;
8
+ addNode: CanvasToolApi["addNode"];
9
+ updateNode: CanvasToolApi["updateNode"];
10
+ removeNode: CanvasToolApi["removeNode"];
11
+ getNode: CanvasToolApi["getNode"];
12
+ clear: CanvasToolApi["clear"];
13
+ requestRender: () => void;
14
+ /** Stage installs its render function through this. */
15
+ installRenderer: (fn: () => void) => void;
16
+ getToolOptions: <T extends Record<string, unknown>>(id: string) => T;
17
+ getToolApi: <T extends Record<string, unknown>>(id: string) => CanvasToolApi<T>;
18
+ stageEl: Ref<HTMLElement | null>;
19
+ size: Ref<{
20
+ width: number;
21
+ height: number;
22
+ }>;
23
+ /** Reactive cursor override. `null` falls back to the active tool's cursor. */
24
+ cursorOverride: Ref<string | null>;
25
+ setCursor: (cursor: string | null) => void;
26
+ undo: () => void;
27
+ redo: () => void;
28
+ canUndo: ComputedRef<boolean>;
29
+ canRedo: ComputedRef<boolean>;
30
+ /** Mark the start of a user interaction (pointer down). */
31
+ beginAction: () => void;
32
+ /** Mark the end of a user interaction (pointer up) — records one undo step. */
33
+ endAction: () => void;
34
+ /** Keyboard handler for undo/redo shortcuts. Attach to the stage element. */
35
+ onKeyDown: (e: KeyboardEvent) => void;
36
+ }
37
+ export declare const CANVAS_CONTEXT: InjectionKey<CanvasContext>;
38
+ export declare function useCanvasContext(): CanvasContext;
@@ -0,0 +1,11 @@
1
+ import { inject } from "vue";
2
+ export const CANVAS_CONTEXT = Symbol("OrioCanvas");
3
+ export function useCanvasContext() {
4
+ const ctx = inject(CANVAS_CONTEXT);
5
+ if (!ctx) {
6
+ throw new Error(
7
+ "useCanvasContext() must be used inside <orio-canvas>. Did you forget to wrap the component?"
8
+ );
9
+ }
10
+ return ctx;
11
+ }
@@ -0,0 +1,77 @@
1
+ import type { CanvasNode, CanvasTool, CanvasToolApi } from "./types.js";
2
+ export interface CanvasProps {
3
+ /**
4
+ * Unique name for this canvas instance. Required so detached toolbars and
5
+ * other UI can bind to this canvas via the `canvas` prop, e.g.
6
+ * `<orio-canvas-toolbar canvas="editor" />` rendered anywhere in the app.
7
+ */
8
+ name: string;
9
+ /** Tools available to the canvas. Empty by default — specify all tools explicitly. */
10
+ tools?: CanvasTool[];
11
+ /** Drawing surface width in CSS pixels. */
12
+ width?: number;
13
+ /** Drawing surface height in CSS pixels. */
14
+ height?: number;
15
+ /** Initial active tool id. Defaults to the first tool. */
16
+ defaultTool?: string;
17
+ /** CSS background applied to the stage wrapper. */
18
+ background?: string;
19
+ /** Maximum undo history depth. */
20
+ maxHistory?: number;
21
+ /**
22
+ * Called once on mount with the full tool API. Use it to seed the canvas
23
+ * with initial nodes (images, text, shapes). Nodes added here can be
24
+ * `frozen: true` to lock them from user interaction.
25
+ *
26
+ * The toolbar controls what the *user* can do — setup can add any node
27
+ * type regardless of which tools are registered.
28
+ */
29
+ setup?: (api: CanvasToolApi) => void | Promise<void>;
30
+ }
31
+ type __VLS_Props = CanvasProps;
32
+ declare function getToolOptions<T extends Record<string, unknown>>(id: string): T;
33
+ declare function setActiveTool(id: string | null): void;
34
+ type __VLS_ModelProps = {
35
+ "nodes"?: CanvasNode[];
36
+ };
37
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
38
+ declare var __VLS_1: {};
39
+ type __VLS_Slots = {} & {
40
+ default?: (props: typeof __VLS_1) => any;
41
+ };
42
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
43
+ activeTool: import("vue").ComputedRef<any>;
44
+ setActiveTool: typeof setActiveTool;
45
+ addNode: (node: Omit<CanvasNode, "id"> & {
46
+ id?: string;
47
+ }) => CanvasNode;
48
+ updateNode: (id: string, patch: Partial<Omit<CanvasNode, "id">>) => void;
49
+ removeNode: (id: string) => void;
50
+ clear: () => void;
51
+ nodes: import("vue").ModelRef<CanvasNode<unknown>[], string, CanvasNode<unknown>[], CanvasNode<unknown>[]>;
52
+ getToolOptions: typeof getToolOptions;
53
+ undo: () => void;
54
+ redo: () => void;
55
+ canUndo: import("vue").ComputedRef<boolean>;
56
+ canRedo: import("vue").ComputedRef<boolean>;
57
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
58
+ "update:nodes": (value: CanvasNode<unknown>[]) => any;
59
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
60
+ "onUpdate:nodes"?: ((value: CanvasNode<unknown>[]) => any) | undefined;
61
+ }>, {
62
+ setup: (api: CanvasToolApi) => void | Promise<void>;
63
+ width: number;
64
+ height: number;
65
+ tools: CanvasTool[];
66
+ defaultTool: string;
67
+ background: string;
68
+ maxHistory: number;
69
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
70
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
71
+ declare const _default: typeof __VLS_export;
72
+ export default _default;
73
+ type __VLS_WithSlots<T, S> = T & {
74
+ new (): {
75
+ $slots: S;
76
+ };
77
+ };
@@ -0,0 +1,208 @@
1
+ <script setup>
2
+ import {
3
+ computed,
4
+ onBeforeUnmount,
5
+ provide,
6
+ reactive,
7
+ ref,
8
+ shallowRef,
9
+ watch
10
+ } from "vue";
11
+ import { CANVAS_CONTEXT } from "./context";
12
+ import { canvasRegistry } from "./registry";
13
+ import { useCanvasHistory } from "./composables/useCanvasHistory";
14
+ import { useCanvasNodes } from "./composables/useCanvasNodes";
15
+ import { useCanvasSetup } from "./composables/useCanvasSetup";
16
+ import Stage from "./components/Stage.vue";
17
+ import Toolbar from "./components/Toolbar.vue";
18
+ const props = defineProps({
19
+ name: { type: String, required: true },
20
+ tools: { type: Array, required: false, default: () => [] },
21
+ width: { type: Number, required: false, default: 800 },
22
+ height: { type: Number, required: false, default: 500 },
23
+ defaultTool: { type: String, required: false, default: void 0 },
24
+ background: { type: String, required: false, default: "transparent" },
25
+ maxHistory: { type: Number, required: false, default: 50 },
26
+ setup: { type: Function, required: false, default: void 0 }
27
+ });
28
+ const nodes = defineModel("nodes", { type: Array, ...{ default: () => [] } });
29
+ const toolsRef = computed(() => props.tools);
30
+ const size = computed(() => ({ width: props.width, height: props.height }));
31
+ const stageEl = ref(null);
32
+ const cursorOverride = ref(null);
33
+ function setCursor(cursor) {
34
+ cursorOverride.value = cursor;
35
+ }
36
+ const activeToolId = ref(
37
+ props.defaultTool ?? props.tools.find((t) => !t.kind || t.kind === "interaction")?.id ?? null
38
+ );
39
+ const toolOptionsMap = reactive({});
40
+ watch(
41
+ toolsRef,
42
+ (list) => {
43
+ for (const t of list) {
44
+ if (!toolOptionsMap[t.id]) {
45
+ toolOptionsMap[t.id] = reactive({ ...t.defaultOptions ?? {} });
46
+ }
47
+ }
48
+ },
49
+ { immediate: true }
50
+ );
51
+ function getToolOptions(id) {
52
+ if (!toolOptionsMap[id]) toolOptionsMap[id] = reactive({});
53
+ return toolOptionsMap[id];
54
+ }
55
+ const rendererFn = shallowRef(() => {
56
+ });
57
+ function requestRender() {
58
+ rendererFn.value();
59
+ }
60
+ function installRenderer(fn) {
61
+ rendererFn.value = fn;
62
+ }
63
+ const {
64
+ undo,
65
+ redo,
66
+ canUndo,
67
+ canRedo,
68
+ autoCommit,
69
+ beginAction,
70
+ endAction,
71
+ resetBaseline,
72
+ onKeyDown
73
+ } = useCanvasHistory(nodes, {
74
+ maxHistory: () => props.maxHistory,
75
+ requestRender
76
+ });
77
+ const { addNode, updateNode, getNode, removeNode, clear } = useCanvasNodes(
78
+ nodes,
79
+ { onMutate: autoCommit }
80
+ );
81
+ const toolApiCache = /* @__PURE__ */ new Map();
82
+ function getToolApi(id) {
83
+ let api = toolApiCache.get(id);
84
+ if (!api) {
85
+ api = {
86
+ options: getToolOptions(id),
87
+ nodes,
88
+ addNode,
89
+ updateNode,
90
+ removeNode,
91
+ getNode,
92
+ clear,
93
+ requestRender,
94
+ stageEl: () => stageEl.value,
95
+ size: () => ({ width: props.width, height: props.height }),
96
+ undo,
97
+ redo,
98
+ canUndo,
99
+ canRedo,
100
+ getToolOptions,
101
+ getTools: () => props.tools,
102
+ setCursor
103
+ };
104
+ toolApiCache.set(id, api);
105
+ }
106
+ return api;
107
+ }
108
+ function setActiveTool(id) {
109
+ if (id === activeToolId.value) return;
110
+ const prev = props.tools.find((t) => t.id === activeToolId.value);
111
+ prev?.onDeactivate?.(getToolApi(prev.id));
112
+ cursorOverride.value = null;
113
+ activeToolId.value = id;
114
+ const next = id ? props.tools.find((t) => t.id === id) : null;
115
+ next?.onActivate?.(getToolApi(next.id));
116
+ }
117
+ function handleKeyDown(e) {
118
+ const tool = props.tools.find((t) => t.id === activeToolId.value);
119
+ if (tool?.onKeyDown) {
120
+ tool.onKeyDown(e, getToolApi(tool.id));
121
+ if (e.defaultPrevented) return;
122
+ }
123
+ onKeyDown(e);
124
+ }
125
+ const context = {
126
+ tools: toolsRef,
127
+ activeToolId,
128
+ setActiveTool,
129
+ nodes,
130
+ addNode,
131
+ updateNode,
132
+ removeNode,
133
+ getNode,
134
+ clear,
135
+ requestRender,
136
+ installRenderer,
137
+ getToolOptions,
138
+ getToolApi,
139
+ stageEl,
140
+ size,
141
+ undo,
142
+ redo,
143
+ canUndo,
144
+ canRedo,
145
+ beginAction,
146
+ endAction,
147
+ onKeyDown: handleKeyDown,
148
+ cursorOverride,
149
+ setCursor
150
+ };
151
+ provide(CANVAS_CONTEXT, context);
152
+ let registeredName = null;
153
+ watch(
154
+ () => props.name,
155
+ (newName, oldName) => {
156
+ if (oldName && canvasRegistry.get(oldName) === context) {
157
+ canvasRegistry.delete(oldName);
158
+ }
159
+ if (import.meta.env.DEV && canvasRegistry.has(newName)) {
160
+ console.warn(
161
+ `[orio-canvas] duplicate canvas name "${newName}" \u2014 detached toolbars bound to this name will resolve to the most recently mounted canvas.`
162
+ );
163
+ }
164
+ canvasRegistry.set(newName, context);
165
+ registeredName = newName;
166
+ },
167
+ { immediate: true }
168
+ );
169
+ onBeforeUnmount(() => {
170
+ if (registeredName && canvasRegistry.get(registeredName) === context) {
171
+ canvasRegistry.delete(registeredName);
172
+ }
173
+ });
174
+ useCanvasSetup(props.setup, {
175
+ getToolApi: () => getToolApi("__setup__"),
176
+ resetBaseline
177
+ });
178
+ const activeTool = computed(
179
+ () => props.tools.find((t) => t.id === activeToolId.value) ?? null
180
+ );
181
+ defineExpose({
182
+ activeTool,
183
+ setActiveTool,
184
+ addNode,
185
+ updateNode,
186
+ removeNode,
187
+ clear,
188
+ nodes,
189
+ getToolOptions,
190
+ undo,
191
+ redo,
192
+ canUndo,
193
+ canRedo
194
+ });
195
+ </script>
196
+
197
+ <template>
198
+ <div class="canvas-root" :style="{ background }">
199
+ <slot>
200
+ <Toolbar :canvas="name" />
201
+ <Stage />
202
+ </slot>
203
+ </div>
204
+ </template>
205
+
206
+ <style scoped>
207
+ .canvas-root{display:flex;flex-direction:column;gap:.5rem;width:-moz-fit-content;width:fit-content}
208
+ </style>
@@ -0,0 +1,77 @@
1
+ import type { CanvasNode, CanvasTool, CanvasToolApi } from "./types.js";
2
+ export interface CanvasProps {
3
+ /**
4
+ * Unique name for this canvas instance. Required so detached toolbars and
5
+ * other UI can bind to this canvas via the `canvas` prop, e.g.
6
+ * `<orio-canvas-toolbar canvas="editor" />` rendered anywhere in the app.
7
+ */
8
+ name: string;
9
+ /** Tools available to the canvas. Empty by default — specify all tools explicitly. */
10
+ tools?: CanvasTool[];
11
+ /** Drawing surface width in CSS pixels. */
12
+ width?: number;
13
+ /** Drawing surface height in CSS pixels. */
14
+ height?: number;
15
+ /** Initial active tool id. Defaults to the first tool. */
16
+ defaultTool?: string;
17
+ /** CSS background applied to the stage wrapper. */
18
+ background?: string;
19
+ /** Maximum undo history depth. */
20
+ maxHistory?: number;
21
+ /**
22
+ * Called once on mount with the full tool API. Use it to seed the canvas
23
+ * with initial nodes (images, text, shapes). Nodes added here can be
24
+ * `frozen: true` to lock them from user interaction.
25
+ *
26
+ * The toolbar controls what the *user* can do — setup can add any node
27
+ * type regardless of which tools are registered.
28
+ */
29
+ setup?: (api: CanvasToolApi) => void | Promise<void>;
30
+ }
31
+ type __VLS_Props = CanvasProps;
32
+ declare function getToolOptions<T extends Record<string, unknown>>(id: string): T;
33
+ declare function setActiveTool(id: string | null): void;
34
+ type __VLS_ModelProps = {
35
+ "nodes"?: CanvasNode[];
36
+ };
37
+ type __VLS_PublicProps = __VLS_Props & __VLS_ModelProps;
38
+ declare var __VLS_1: {};
39
+ type __VLS_Slots = {} & {
40
+ default?: (props: typeof __VLS_1) => any;
41
+ };
42
+ declare const __VLS_base: import("vue").DefineComponent<__VLS_PublicProps, {
43
+ activeTool: import("vue").ComputedRef<any>;
44
+ setActiveTool: typeof setActiveTool;
45
+ addNode: (node: Omit<CanvasNode, "id"> & {
46
+ id?: string;
47
+ }) => CanvasNode;
48
+ updateNode: (id: string, patch: Partial<Omit<CanvasNode, "id">>) => void;
49
+ removeNode: (id: string) => void;
50
+ clear: () => void;
51
+ nodes: import("vue").ModelRef<CanvasNode<unknown>[], string, CanvasNode<unknown>[], CanvasNode<unknown>[]>;
52
+ getToolOptions: typeof getToolOptions;
53
+ undo: () => void;
54
+ redo: () => void;
55
+ canUndo: import("vue").ComputedRef<boolean>;
56
+ canRedo: import("vue").ComputedRef<boolean>;
57
+ }, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
58
+ "update:nodes": (value: CanvasNode<unknown>[]) => any;
59
+ }, string, import("vue").PublicProps, Readonly<__VLS_PublicProps> & Readonly<{
60
+ "onUpdate:nodes"?: ((value: CanvasNode<unknown>[]) => any) | undefined;
61
+ }>, {
62
+ setup: (api: CanvasToolApi) => void | Promise<void>;
63
+ width: number;
64
+ height: number;
65
+ tools: CanvasTool[];
66
+ defaultTool: string;
67
+ background: string;
68
+ maxHistory: number;
69
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, false, {}, any>;
70
+ declare const __VLS_export: __VLS_WithSlots<typeof __VLS_base, __VLS_Slots>;
71
+ declare const _default: typeof __VLS_export;
72
+ export default _default;
73
+ type __VLS_WithSlots<T, S> = T & {
74
+ new (): {
75
+ $slots: S;
76
+ };
77
+ };
@@ -0,0 +1 @@
1
+ export declare const canvasRegistry: any;
@@ -0,0 +1,2 @@
1
+ import { shallowReactive } from "vue";
2
+ export const canvasRegistry = shallowReactive(/* @__PURE__ */ new Map());