@draftably/vue 0.2.1 → 0.5.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/README.md CHANGED
@@ -5,8 +5,14 @@ Draftably diagrams in Vue. `DraftDiagram` wraps the `<draft-diagram>` element fr
5
5
  cleanup when it unmounts. Any number of diagrams can share a page.
6
6
 
7
7
  ```sh
8
- pnpm add @draftably/vue
8
+ pnpm add @draftably/vue @draftably/runtime
9
9
  ```
10
10
 
11
- See [Frameworks](https://draftably.dev/docs/frameworks) for the component, server
12
- rendering with the `svg` prop, and a complete example.
11
+ Import `@draftably/runtime/host.css` once in your client entry: it reserves each element's height
12
+ and hides slot content until the element loads, and it lets marked content take the pointer. With
13
+ Vite 5–7, add `optimizeDeps: { exclude: ['@draftably/runtime', '@draftably/editor'] }` to
14
+ `vite.config`.
15
+
16
+ [Vue and Nuxt](https://draftably.dev/docs/vue) has a complete, type-checked app: both components,
17
+ an event, your content on a node, a dark page and server rendering with the `svg` prop. CI
18
+ compiles it from the packed packages. The same page has the Nuxt server and strict-CSP code.
package/dist/editor.d.ts CHANGED
@@ -1,22 +1,82 @@
1
1
  /** `<DraftEditor>` for Vue 3: drag boxes and lines, and get the `.draft` source back. */
2
- import type { DraftEditorElement } from '@draftably/editor/element';
3
- export type { DraftEditorElement };
2
+ import type { DraftEditorElement, EditorOptions } from '@draftably/editor/element';
3
+ import { type PropType } from 'vue';
4
+ export type { DraftEditorElement, EditorOptions };
4
5
  export declare const DraftEditor: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
5
- /** `.draft` source. Passing back the text from `change` does not redraw. */
6
+ /** `.draft` source. Passing back the source from `change` does not redraw. */
6
7
  source: {
7
8
  type: StringConstructor;
8
9
  required: true;
9
10
  };
11
+ /** Every `mountEditor` option but `source` and `onChange`. Callbacks apply live. */
12
+ options: {
13
+ type: PropType<EditorOptions>;
14
+ default: undefined;
15
+ };
10
16
  }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
11
17
  [key: string]: any;
12
- }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
13
- change: (_source: string) => true;
14
- }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
15
- /** `.draft` source. Passing back the text from `change` does not redraw. */
18
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, import("./events.js").HostEmits, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
19
+ /** `.draft` source. Passing back the source from `change` does not redraw. */
16
20
  source: {
17
21
  type: StringConstructor;
18
22
  required: true;
19
23
  };
24
+ /** Every `mountEditor` option but `source` and `onChange`. Callbacks apply live. */
25
+ options: {
26
+ type: PropType<EditorOptions>;
27
+ default: undefined;
28
+ };
20
29
  }>> & Readonly<{
21
- onChange?: ((_source: string) => any) | undefined;
22
- }>, {}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
30
+ onAnchors?: ((detail: {
31
+ anchors: readonly import("@draftably/runtime/viewer").Anchor[];
32
+ }) => any) | undefined;
33
+ onUpdate?: ((detail: import("@draftably/runtime/viewer").UpdateResult | import("@draftably/runtime/host").EditorUpdate) => any) | undefined;
34
+ onRender?: ((detail: {
35
+ revision: number;
36
+ value: Extract<import("@draftably/runtime").RenderResult, {
37
+ ok: true;
38
+ }>["value"];
39
+ geometry: import("@draftably/runtime").Geometry | null;
40
+ }) => any) | undefined;
41
+ onDiagnostics?: ((detail: {
42
+ revision: number;
43
+ diagnostics: readonly import("@draftably/runtime").Diagnostic[];
44
+ stale: boolean;
45
+ }) => any) | undefined;
46
+ onCamera?: ((detail: {
47
+ camera: import("@draftably/runtime/viewer").Camera;
48
+ reason: import("@draftably/runtime/viewer").CameraReason;
49
+ }) => any) | undefined;
50
+ onItemactivate?: ((detail: {
51
+ id: string;
52
+ kind: "node" | "group" | "edge";
53
+ input: "pointer" | "keyboard";
54
+ revision: number;
55
+ }) => any) | undefined;
56
+ onLink?: ((detail: Omit<{
57
+ itemId: string;
58
+ href: string;
59
+ target: "_self" | "_blank";
60
+ preventDefault(): void;
61
+ }, "preventDefault"> & {
62
+ preventDefault(): void;
63
+ }) => any) | undefined;
64
+ onContextmenu?: ((detail: Omit<{
65
+ id: string | null;
66
+ kind?: import("@draftably/runtime/viewer").ItemKind;
67
+ clientX: number;
68
+ clientY: number;
69
+ preventDefault(): void;
70
+ }, "preventDefault"> & {
71
+ preventDefault(): void;
72
+ }) => any) | undefined;
73
+ onSelect?: ((detail: {
74
+ ids: readonly string[];
75
+ reason: "user" | "api";
76
+ }) => any) | undefined;
77
+ onChange?: ((detail: {
78
+ source: string;
79
+ }) => any) | undefined;
80
+ }>, {
81
+ options: EditorOptions;
82
+ }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
package/dist/editor.js CHANGED
@@ -1,21 +1,31 @@
1
- import { defineComponent, h, onBeforeUnmount, onMounted, ref } from 'vue';
1
+ import { defineComponent, h, onMounted, ref } from 'vue';
2
+ import { hostEmits, listenHostEvents } from './events.js';
2
3
  export const DraftEditor = defineComponent({
3
4
  name: 'DraftEditor',
4
5
  props: {
5
- /** `.draft` source. Passing back the text from `change` does not redraw. */
6
+ /** `.draft` source. Passing back the source from `change` does not redraw. */
6
7
  source: { type: String, required: true },
8
+ /** Every `mountEditor` option but `source` and `onChange`. Callbacks apply live. */
9
+ options: { type: Object, default: undefined },
7
10
  },
8
- emits: { change: (_source) => true },
9
- setup(props, { emit, expose }) {
11
+ emits: hostEmits,
12
+ setup(props, { emit, expose, slots }) {
10
13
  const el = ref();
11
- const listen = (event) => emit('change', event.detail);
12
14
  onMounted(() => {
15
+ listenHostEvents(el.value, emit);
13
16
  // The editor loads in the browser only, so server rendering and viewer-only pages skip it.
14
- void import('@draftably/editor/element');
15
- el.value?.addEventListener('draft-change', listen);
17
+ import('@draftably/editor/element').catch((error) => {
18
+ el.value?.setAttribute('status', 'error');
19
+ el.value?.setAttribute('data-error', String(error));
20
+ });
16
21
  });
17
- onBeforeUnmount(() => el.value?.removeEventListener('draft-change', listen));
18
22
  expose({ element: el });
19
- return () => h('draft-editor', { ref: el, source: props.source });
23
+ // The default slot holds slot content: children marked `data-draft-slot`.
24
+ return () => h('draft-editor', {
25
+ ref: el,
26
+ source: props.source,
27
+ // Only when set, so options a host set through the ref survive.
28
+ ...(props.options === undefined ? {} : { '.options': props.options }),
29
+ }, slots.default?.());
20
30
  },
21
31
  });
@@ -0,0 +1,13 @@
1
+ /** Emits from the runtime's one list of events: a new event adds no lines here. */
2
+ import { type HostEventDetail, type HostEventName, type HostEvents } from '@draftably/runtime/host';
3
+ export type { HostEventDetail, HostEventName, HostEvents };
4
+ /**
5
+ * `@camera`, `@select`, … : each receives the event's `detail`. `@link` and `@contextmenu` also
6
+ * get `preventDefault()`.
7
+ */
8
+ export type HostEmits = {
9
+ [K in HostEventName]: (detail: HostEventDetail<K>) => true;
10
+ };
11
+ export declare const hostEmits: HostEmits;
12
+ /** Re-emit every host event from `node`. */
13
+ export declare function listenHostEvents(node: HTMLElement, emit: (name: HostEventName, detail: unknown) => void): void;
package/dist/events.js ADDED
@@ -0,0 +1,8 @@
1
+ /** Emits from the runtime's one list of events: a new event adds no lines here. */
2
+ import { HOST_EVENTS, hostDetail, } from '@draftably/runtime/host';
3
+ export const hostEmits = Object.fromEntries(HOST_EVENTS.map(name => [name, null]));
4
+ /** Re-emit every host event from `node`. */
5
+ export function listenHostEvents(node, emit) {
6
+ for (const name of HOST_EVENTS)
7
+ node.addEventListener(`draft-${name}`, event => emit(name, hostDetail(event)));
8
+ }
package/dist/index.d.ts CHANGED
@@ -1,8 +1,11 @@
1
- /** `<DraftDiagram>` for Vue 3: the `<draft-diagram>` element with Vue props and an `update` event. */
1
+ /** `<DraftDiagram>` for Vue 3: the `<draft-diagram>` element with Vue props and events. */
2
2
  import '@draftably/runtime/element';
3
3
  import type { DraftDiagramElement } from '@draftably/runtime/element';
4
4
  import type { UpdateResult, ViewerOptions } from '@draftably/runtime/viewer';
5
5
  import { type PropType } from 'vue';
6
+ export { renderSvg } from '@draftably/runtime';
7
+ export { HOST_EVENTS } from '@draftably/runtime/host';
8
+ export type { HostEmits, HostEventDetail, HostEventName, HostEvents } from './events.js';
6
9
  export type { DraftDiagramElement, UpdateResult, ViewerOptions };
7
10
  export declare const DraftDiagram: import("vue").DefineComponent<import("vue").ExtractPropTypes<{
8
11
  /** `.draft` source. A change redraws the diagram and keeps the camera. */
@@ -30,15 +33,14 @@ export declare const DraftDiagram: import("vue").DefineComponent<import("vue").E
30
33
  type: StringConstructor;
31
34
  default: undefined;
32
35
  };
36
+ /** Every `mountViewer` option. A new object with the same values does nothing. */
33
37
  options: {
34
38
  type: PropType<ViewerOptions>;
35
39
  default: undefined;
36
40
  };
37
41
  }>, () => import("vue").VNode<import("vue").RendererNode, import("vue").RendererElement, {
38
42
  [key: string]: any;
39
- }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, {
40
- update: (_result: UpdateResult) => true;
41
- }, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
43
+ }>, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, import("./events.js").HostEmits, string, import("vue").PublicProps, Readonly<import("vue").ExtractPropTypes<{
42
44
  /** `.draft` source. A change redraws the diagram and keeps the camera. */
43
45
  source: {
44
46
  type: StringConstructor;
@@ -64,17 +66,67 @@ export declare const DraftDiagram: import("vue").DefineComponent<import("vue").E
64
66
  type: StringConstructor;
65
67
  default: undefined;
66
68
  };
69
+ /** Every `mountViewer` option. A new object with the same values does nothing. */
67
70
  options: {
68
71
  type: PropType<ViewerOptions>;
69
72
  default: undefined;
70
73
  };
71
74
  }>> & Readonly<{
72
- onUpdate?: ((_result: UpdateResult) => any) | undefined;
75
+ onAnchors?: ((detail: {
76
+ anchors: readonly import("@draftably/runtime/viewer").Anchor[];
77
+ }) => any) | undefined;
78
+ onUpdate?: ((detail: UpdateResult | import("@draftably/runtime/host").EditorUpdate) => any) | undefined;
79
+ onRender?: ((detail: {
80
+ revision: number;
81
+ value: Extract<import("@draftably/runtime").RenderResult, {
82
+ ok: true;
83
+ }>["value"];
84
+ geometry: import("@draftably/runtime").Geometry | null;
85
+ }) => any) | undefined;
86
+ onDiagnostics?: ((detail: {
87
+ revision: number;
88
+ diagnostics: readonly import("@draftably/runtime").Diagnostic[];
89
+ stale: boolean;
90
+ }) => any) | undefined;
91
+ onCamera?: ((detail: {
92
+ camera: import("@draftably/runtime/viewer").Camera;
93
+ reason: import("@draftably/runtime/viewer").CameraReason;
94
+ }) => any) | undefined;
95
+ onItemactivate?: ((detail: {
96
+ id: string;
97
+ kind: "node" | "group" | "edge";
98
+ input: "pointer" | "keyboard";
99
+ revision: number;
100
+ }) => any) | undefined;
101
+ onLink?: ((detail: Omit<{
102
+ itemId: string;
103
+ href: string;
104
+ target: "_self" | "_blank";
105
+ preventDefault(): void;
106
+ }, "preventDefault"> & {
107
+ preventDefault(): void;
108
+ }) => any) | undefined;
109
+ onContextmenu?: ((detail: Omit<{
110
+ id: string | null;
111
+ kind?: import("@draftably/runtime/viewer").ItemKind;
112
+ clientX: number;
113
+ clientY: number;
114
+ preventDefault(): void;
115
+ }, "preventDefault"> & {
116
+ preventDefault(): void;
117
+ }) => any) | undefined;
118
+ onSelect?: ((detail: {
119
+ ids: readonly string[];
120
+ reason: "user" | "api";
121
+ }) => any) | undefined;
122
+ onChange?: ((detail: {
123
+ source: string;
124
+ }) => any) | undefined;
73
125
  }>, {
126
+ options: ViewerOptions;
74
127
  label: string;
75
128
  state: string | null;
76
129
  outline: "visible" | "hidden";
77
130
  svg: string;
78
- options: ViewerOptions;
79
131
  }, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
80
132
  export default DraftDiagram;
package/dist/index.js CHANGED
@@ -1,6 +1,9 @@
1
- /** `<DraftDiagram>` for Vue 3: the `<draft-diagram>` element with Vue props and an `update` event. */
1
+ /** `<DraftDiagram>` for Vue 3: the `<draft-diagram>` element with Vue props and events. */
2
2
  import '@draftably/runtime/element';
3
- import { defineComponent, h, onBeforeUnmount, onMounted, ref, watchEffect, } from 'vue';
3
+ import { defineComponent, h, onMounted, ref } from 'vue';
4
+ import { hostEmits, listenHostEvents } from './events.js';
5
+ export { renderSvg } from '@draftably/runtime';
6
+ export { HOST_EVENTS } from '@draftably/runtime/host';
4
7
  export const DraftDiagram = defineComponent({
5
8
  name: 'DraftDiagram',
6
9
  props: {
@@ -14,18 +17,13 @@ export const DraftDiagram = defineComponent({
14
17
  outline: { type: String, default: undefined },
15
18
  /** Static SVG from `renderSvg(source, { target: 'inline', idPrefix })`, shown until the viewer draws. */
16
19
  svg: { type: String, default: undefined },
20
+ /** Every `mountViewer` option. A new object with the same values does nothing. */
17
21
  options: { type: Object, default: undefined },
18
22
  },
19
- emits: { update: (_result) => true },
20
- setup(props, { emit, expose }) {
23
+ emits: hostEmits,
24
+ setup(props, { emit, expose, slots }) {
21
25
  const el = ref();
22
- const listen = (event) => emit('update', event.detail);
23
- onMounted(() => el.value?.addEventListener('draft-update', listen));
24
- onBeforeUnmount(() => el.value?.removeEventListener('draft-update', listen));
25
- watchEffect(() => {
26
- if (el.value && props.options)
27
- el.value.options = props.options;
28
- });
26
+ onMounted(() => listenHostEvents(el.value, emit));
29
27
  expose({ element: el });
30
28
  return () => h('draft-diagram', {
31
29
  ref: el,
@@ -33,7 +31,11 @@ export const DraftDiagram = defineComponent({
33
31
  state: props.state ?? undefined,
34
32
  label: props.label,
35
33
  outline: props.outline,
36
- }, props.svg ? [h('div', { innerHTML: props.svg })] : []);
34
+ // Only when set, so options a host set through the ref survive.
35
+ ...(props.options === undefined ? {} : { '.options': props.options }),
36
+ },
37
+ // The default slot holds slot content: children marked `data-draft-slot`.
38
+ [...(props.svg ? [h('div', { innerHTML: props.svg })] : []), ...(slots.default?.() ?? [])]);
37
39
  },
38
40
  });
39
41
  export default DraftDiagram;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@draftably/vue",
3
- "version": "0.2.1",
3
+ "version": "0.5.0",
4
4
  "description": "Vue components for Draftably diagrams.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -23,8 +23,8 @@
23
23
  },
24
24
  "sideEffects": false,
25
25
  "dependencies": {
26
- "@draftably/runtime": "0.3.0",
27
- "@draftably/editor": "0.3.0"
26
+ "@draftably/runtime": "0.5.0",
27
+ "@draftably/editor": "0.5.0"
28
28
  },
29
29
  "peerDependencies": {
30
30
  "vue": "^3.4"