@canvist/vue 0.0.0 → 0.2.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/dist/mod.d.ts ADDED
@@ -0,0 +1,582 @@
1
+ /**
2
+ * Vue bindings for the canvist canvas editor.
3
+ *
4
+ * `CanvistEditor` renders an editor; `useCanvistEditor` gives you the
5
+ * editor itself, and `useEditorState` keeps a ref in step with something
6
+ * read from the editor. Commands, queries, and plugin APIs are typed from
7
+ * the extensions and plugins you pass, as in `@canvist/web`. `BubbleMenu`
8
+ * and `FloatingMenu` keep their slot next to the selection.
9
+ *
10
+ * ```ts
11
+ * const { element, editor } = useCanvistEditor({
12
+ * content: { format: "markdown", input: "Hello **world**" },
13
+ * });
14
+ * const bold = useEditorState(editor, (current) => current.can.toggleBold(), false);
15
+ * // <button :disabled="!bold" @click="editor?.commands.toggleBold()">Bold</button>
16
+ * // <div ref="element" />
17
+ * ```
18
+ *
19
+ * @module
20
+ */
21
+ import { type Editor, type EditorOptions, type Extensions, type Plugins, type StarterKit } from "@canvist/web";
22
+ import { type MenuOptions } from "@canvist/web/menus";
23
+ import { type Ref, type ShallowRef } from "vue";
24
+ /** What {@link useCanvistEditor} returns. */
25
+ export interface CanvistEditorHandle<E extends Extensions, P extends Plugins> {
26
+ /** Bind as the template ref of the element the editor renders into. */
27
+ readonly element: ShallowRef<HTMLElement | null>;
28
+ /** The editor, once it has loaded; `null` before that and after unmounting. */
29
+ readonly editor: ShallowRef<Editor<E, P> | null>;
30
+ }
31
+ /**
32
+ * Create an editor in `element` when the component mounts, and destroy it
33
+ * when it unmounts. `options` are read when the editor is created.
34
+ */
35
+ export declare function useCanvistEditor<const E extends Extensions = StarterKit, const P extends Plugins = []>(options?: EditorOptions<E, P>): CanvistEditorHandle<E, P>;
36
+ /**
37
+ * A ref holding `select(editor)`, updated after every edit or selection
38
+ * change. Until the editor exists, it holds `fallback`.
39
+ */
40
+ export declare function useEditorState<E extends Extensions, P extends Plugins, T>(editor: Readonly<Ref<Editor<E, P> | null>>, select: (editor: Editor<E, P>) => T, fallback: T): Readonly<ShallowRef<T>>;
41
+ /**
42
+ * Props for {@link CanvistEditor}: the editor's options, read once except
43
+ * `editable` and `placeholder`, which follow the props.
44
+ */
45
+ export interface CanvistEditorProps<E extends Extensions, P extends Plugins> extends EditorOptions<E, P> {
46
+ /**
47
+ * Server-rendered HTML (`.cv-doc` markup from canvist's renderer) to
48
+ * show until the editor takes over. The editor reads the document from
49
+ * it when `content` is not given.
50
+ */
51
+ readonly html?: string;
52
+ }
53
+ /**
54
+ * A canvist editor. Give it a width; without a height it grows with its
55
+ * document. It emits `ready` once the editor has loaded and `update`
56
+ * after every change, each with the editor.
57
+ */
58
+ export declare const CanvistEditor: new <const E extends Extensions = readonly [{
59
+ readonly commands: readonly [];
60
+ readonly name: "trailingNode";
61
+ readonly nodes: readonly [];
62
+ readonly queries: readonly [];
63
+ }, {
64
+ readonly commands: readonly [{
65
+ readonly args: readonly [{
66
+ readonly name: "level";
67
+ readonly type: {
68
+ readonly max: 6;
69
+ readonly min: 1;
70
+ readonly type: "int";
71
+ };
72
+ }];
73
+ readonly name: "toggleHeading";
74
+ }, {
75
+ readonly args: readonly [{
76
+ readonly name: "level";
77
+ readonly type: {
78
+ readonly max: 6;
79
+ readonly min: 1;
80
+ readonly type: "int";
81
+ };
82
+ }];
83
+ readonly name: "setHeading";
84
+ }];
85
+ readonly name: "heading";
86
+ readonly nodes: readonly [{
87
+ readonly attrs: readonly [{
88
+ readonly default: 1;
89
+ readonly name: "level";
90
+ readonly type: {
91
+ readonly max: 6;
92
+ readonly min: 1;
93
+ readonly type: "int";
94
+ };
95
+ }];
96
+ readonly kind: "block";
97
+ readonly name: "heading";
98
+ }];
99
+ readonly queries: readonly [{
100
+ readonly args: readonly [];
101
+ readonly name: "headingLevel";
102
+ readonly returns: {
103
+ readonly of: {
104
+ readonly max: 6;
105
+ readonly min: 1;
106
+ readonly type: "int";
107
+ };
108
+ readonly type: "nullable";
109
+ };
110
+ }];
111
+ }, {
112
+ readonly commands: readonly [{
113
+ readonly args: readonly [];
114
+ readonly name: "toggleBlockquote";
115
+ }];
116
+ readonly name: "blockquote";
117
+ readonly nodes: readonly [{
118
+ readonly attrs: readonly [];
119
+ readonly kind: "block";
120
+ readonly name: "blockquote";
121
+ }];
122
+ readonly queries: readonly [{
123
+ readonly args: readonly [];
124
+ readonly name: "isBlockquote";
125
+ readonly returns: {
126
+ readonly type: "bool";
127
+ };
128
+ }];
129
+ }, {
130
+ readonly commands: readonly [{
131
+ readonly args: readonly [{
132
+ readonly default: null;
133
+ readonly name: "language";
134
+ readonly type: {
135
+ readonly of: {
136
+ readonly type: "string";
137
+ };
138
+ readonly type: "nullable";
139
+ };
140
+ }];
141
+ readonly name: "toggleCodeBlock";
142
+ }, {
143
+ readonly args: readonly [{
144
+ readonly default: null;
145
+ readonly name: "language";
146
+ readonly type: {
147
+ readonly of: {
148
+ readonly type: "string";
149
+ };
150
+ readonly type: "nullable";
151
+ };
152
+ }];
153
+ readonly name: "setCodeBlock";
154
+ }];
155
+ readonly name: "codeBlock";
156
+ readonly nodes: readonly [{
157
+ readonly attrs: readonly [{
158
+ readonly default: null;
159
+ readonly name: "language";
160
+ readonly type: {
161
+ readonly of: {
162
+ readonly type: "string";
163
+ };
164
+ readonly type: "nullable";
165
+ };
166
+ }];
167
+ readonly kind: "block";
168
+ readonly name: "codeBlock";
169
+ }];
170
+ readonly queries: readonly [{
171
+ readonly args: readonly [];
172
+ readonly name: "isCodeBlock";
173
+ readonly returns: {
174
+ readonly type: "bool";
175
+ };
176
+ }, {
177
+ readonly args: readonly [];
178
+ readonly name: "codeBlockLanguage";
179
+ readonly returns: {
180
+ readonly of: {
181
+ readonly type: "string";
182
+ };
183
+ readonly type: "nullable";
184
+ };
185
+ }];
186
+ }, {
187
+ readonly commands: readonly [{
188
+ readonly args: readonly [];
189
+ readonly name: "setHardBreak";
190
+ }];
191
+ readonly name: "hardBreak";
192
+ readonly nodes: readonly [{
193
+ readonly attrs: readonly [];
194
+ readonly kind: "embed";
195
+ readonly name: "hardBreak";
196
+ }];
197
+ readonly queries: readonly [];
198
+ }, {
199
+ readonly commands: readonly [{
200
+ readonly args: readonly [];
201
+ readonly name: "setHorizontalRule";
202
+ }];
203
+ readonly name: "horizontalRule";
204
+ readonly nodes: readonly [{
205
+ readonly attrs: readonly [];
206
+ readonly kind: "block";
207
+ readonly name: "horizontalRule";
208
+ }];
209
+ readonly queries: readonly [];
210
+ }, {
211
+ readonly commands: readonly [{
212
+ readonly args: readonly [];
213
+ readonly name: "toggleBulletList";
214
+ }];
215
+ readonly name: "bulletList";
216
+ readonly nodes: readonly [{
217
+ readonly attrs: readonly [];
218
+ readonly kind: "block";
219
+ readonly name: "bulletList";
220
+ }];
221
+ readonly queries: readonly [{
222
+ readonly args: readonly [];
223
+ readonly name: "isBulletList";
224
+ readonly returns: {
225
+ readonly type: "bool";
226
+ };
227
+ }];
228
+ }, {
229
+ readonly commands: readonly [{
230
+ readonly args: readonly [];
231
+ readonly name: "toggleOrderedList";
232
+ }, {
233
+ readonly args: readonly [{
234
+ readonly name: "start";
235
+ readonly type: {
236
+ readonly type: "int";
237
+ };
238
+ }];
239
+ readonly name: "setOrderedListStart";
240
+ }];
241
+ readonly name: "orderedList";
242
+ readonly nodes: readonly [{
243
+ readonly attrs: readonly [{
244
+ readonly default: 1;
245
+ readonly name: "start";
246
+ readonly type: {
247
+ readonly type: "int";
248
+ };
249
+ }];
250
+ readonly kind: "block";
251
+ readonly name: "orderedList";
252
+ }];
253
+ readonly queries: readonly [{
254
+ readonly args: readonly [];
255
+ readonly name: "isOrderedList";
256
+ readonly returns: {
257
+ readonly type: "bool";
258
+ };
259
+ }, {
260
+ readonly args: readonly [];
261
+ readonly name: "orderedListStart";
262
+ readonly returns: {
263
+ readonly of: {
264
+ readonly type: "int";
265
+ };
266
+ readonly type: "nullable";
267
+ };
268
+ }];
269
+ }, {
270
+ readonly commands: readonly [{
271
+ readonly args: readonly [];
272
+ readonly name: "splitListItem";
273
+ }, {
274
+ readonly args: readonly [];
275
+ readonly name: "sinkListItem";
276
+ }, {
277
+ readonly args: readonly [];
278
+ readonly name: "liftListItem";
279
+ }];
280
+ readonly name: "listItem";
281
+ readonly nodes: readonly [{
282
+ readonly attrs: readonly [];
283
+ readonly kind: "block";
284
+ readonly name: "listItem";
285
+ }];
286
+ readonly queries: readonly [];
287
+ }, {
288
+ readonly commands: readonly [{
289
+ readonly args: readonly [];
290
+ readonly name: "toggleTaskList";
291
+ }, {
292
+ readonly args: readonly [];
293
+ readonly name: "toggleTask";
294
+ }, {
295
+ readonly args: readonly [{
296
+ readonly name: "checked";
297
+ readonly type: {
298
+ readonly type: "bool";
299
+ };
300
+ }];
301
+ readonly name: "setTaskChecked";
302
+ }, {
303
+ readonly args: readonly [];
304
+ readonly name: "splitTaskItem";
305
+ }, {
306
+ readonly args: readonly [];
307
+ readonly name: "sinkTaskItem";
308
+ }, {
309
+ readonly args: readonly [];
310
+ readonly name: "liftTaskItem";
311
+ }];
312
+ readonly name: "taskList";
313
+ readonly nodes: readonly [{
314
+ readonly attrs: readonly [];
315
+ readonly kind: "block";
316
+ readonly name: "taskList";
317
+ }, {
318
+ readonly attrs: readonly [{
319
+ readonly default: false;
320
+ readonly name: "checked";
321
+ readonly type: {
322
+ readonly type: "bool";
323
+ };
324
+ }];
325
+ readonly kind: "block";
326
+ readonly name: "taskItem";
327
+ }];
328
+ readonly queries: readonly [{
329
+ readonly args: readonly [];
330
+ readonly name: "isTaskList";
331
+ readonly returns: {
332
+ readonly type: "bool";
333
+ };
334
+ }, {
335
+ readonly args: readonly [];
336
+ readonly name: "isTaskChecked";
337
+ readonly returns: {
338
+ readonly of: {
339
+ readonly type: "bool";
340
+ };
341
+ readonly type: "nullable";
342
+ };
343
+ }];
344
+ }, {
345
+ readonly commands: readonly [{
346
+ readonly args: readonly [];
347
+ readonly name: "toggleBold";
348
+ }, {
349
+ readonly args: readonly [];
350
+ readonly name: "setBold";
351
+ }, {
352
+ readonly args: readonly [];
353
+ readonly name: "unsetBold";
354
+ }];
355
+ readonly name: "bold";
356
+ readonly nodes: readonly [{
357
+ readonly attrs: readonly [];
358
+ readonly kind: "mark";
359
+ readonly name: "bold";
360
+ }];
361
+ readonly queries: readonly [{
362
+ readonly args: readonly [];
363
+ readonly name: "isBold";
364
+ readonly returns: {
365
+ readonly type: "bool";
366
+ };
367
+ }];
368
+ }, {
369
+ readonly commands: readonly [{
370
+ readonly args: readonly [];
371
+ readonly name: "toggleItalic";
372
+ }, {
373
+ readonly args: readonly [];
374
+ readonly name: "setItalic";
375
+ }, {
376
+ readonly args: readonly [];
377
+ readonly name: "unsetItalic";
378
+ }];
379
+ readonly name: "italic";
380
+ readonly nodes: readonly [{
381
+ readonly attrs: readonly [];
382
+ readonly kind: "mark";
383
+ readonly name: "italic";
384
+ }];
385
+ readonly queries: readonly [{
386
+ readonly args: readonly [];
387
+ readonly name: "isItalic";
388
+ readonly returns: {
389
+ readonly type: "bool";
390
+ };
391
+ }];
392
+ }, {
393
+ readonly commands: readonly [{
394
+ readonly args: readonly [];
395
+ readonly name: "toggleUnderline";
396
+ }, {
397
+ readonly args: readonly [];
398
+ readonly name: "setUnderline";
399
+ }, {
400
+ readonly args: readonly [];
401
+ readonly name: "unsetUnderline";
402
+ }];
403
+ readonly name: "underline";
404
+ readonly nodes: readonly [{
405
+ readonly attrs: readonly [];
406
+ readonly kind: "mark";
407
+ readonly name: "underline";
408
+ }];
409
+ readonly queries: readonly [{
410
+ readonly args: readonly [];
411
+ readonly name: "isUnderline";
412
+ readonly returns: {
413
+ readonly type: "bool";
414
+ };
415
+ }];
416
+ }, {
417
+ readonly commands: readonly [{
418
+ readonly args: readonly [];
419
+ readonly name: "toggleStrike";
420
+ }, {
421
+ readonly args: readonly [];
422
+ readonly name: "setStrike";
423
+ }, {
424
+ readonly args: readonly [];
425
+ readonly name: "unsetStrike";
426
+ }];
427
+ readonly name: "strike";
428
+ readonly nodes: readonly [{
429
+ readonly attrs: readonly [];
430
+ readonly kind: "mark";
431
+ readonly name: "strike";
432
+ }];
433
+ readonly queries: readonly [{
434
+ readonly args: readonly [];
435
+ readonly name: "isStrike";
436
+ readonly returns: {
437
+ readonly type: "bool";
438
+ };
439
+ }];
440
+ }, {
441
+ readonly commands: readonly [{
442
+ readonly args: readonly [];
443
+ readonly name: "toggleCode";
444
+ }, {
445
+ readonly args: readonly [];
446
+ readonly name: "setCode";
447
+ }, {
448
+ readonly args: readonly [];
449
+ readonly name: "unsetCode";
450
+ }];
451
+ readonly name: "code";
452
+ readonly nodes: readonly [{
453
+ readonly attrs: readonly [];
454
+ readonly kind: "mark";
455
+ readonly name: "code";
456
+ }];
457
+ readonly queries: readonly [{
458
+ readonly args: readonly [];
459
+ readonly name: "isCode";
460
+ readonly returns: {
461
+ readonly type: "bool";
462
+ };
463
+ }];
464
+ }, {
465
+ readonly commands: readonly [{
466
+ readonly args: readonly [{
467
+ readonly name: "href";
468
+ readonly type: {
469
+ readonly type: "string";
470
+ };
471
+ }];
472
+ readonly name: "setLink";
473
+ }, {
474
+ readonly args: readonly [];
475
+ readonly name: "unsetLink";
476
+ }];
477
+ readonly name: "link";
478
+ readonly nodes: readonly [{
479
+ readonly attrs: readonly [{
480
+ readonly default: null;
481
+ readonly name: "href";
482
+ readonly type: {
483
+ readonly of: {
484
+ readonly type: "string";
485
+ };
486
+ readonly type: "nullable";
487
+ };
488
+ }];
489
+ readonly kind: "mark";
490
+ readonly name: "link";
491
+ }];
492
+ readonly queries: readonly [{
493
+ readonly args: readonly [];
494
+ readonly name: "linkHref";
495
+ readonly returns: {
496
+ readonly of: {
497
+ readonly type: "string";
498
+ };
499
+ readonly type: "nullable";
500
+ };
501
+ }];
502
+ }, {
503
+ readonly commands: readonly [{
504
+ readonly args: readonly [];
505
+ readonly name: "undo";
506
+ }, {
507
+ readonly args: readonly [];
508
+ readonly name: "redo";
509
+ }];
510
+ readonly name: "history";
511
+ readonly nodes: readonly [];
512
+ readonly queries: readonly [];
513
+ }], const P extends Plugins = []>(props: CanvistEditorProps<E, P> & {
514
+ onReady?: (...args: any[]) => any;
515
+ onUpdate?: (...args: any[]) => any;
516
+ } & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps) => import("vue").CreateComponentPublicInstanceWithMixins<CanvistEditorProps<E, P> & {
517
+ onReady?: (...args: any[]) => any;
518
+ onUpdate?: (...args: any[]) => any;
519
+ }, {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, ("ready" | "update")[], import("vue").PublicProps, {}, false, {}, {}, {}, {}, string, {}, any, import("vue").ComponentProvideOptions, {
520
+ P: {};
521
+ B: {};
522
+ D: {};
523
+ C: {};
524
+ M: {};
525
+ Defaults: {};
526
+ }, CanvistEditorProps<E, P> & {
527
+ onReady?: (...args: any[]) => any;
528
+ onUpdate?: (...args: any[]) => any;
529
+ }, {}, {}, {}, {}, {}>;
530
+ /** Props for {@link BubbleMenu} and {@link FloatingMenu}. */
531
+ export interface MenuProps<E extends Extensions, P extends Plugins> extends MenuOptions<E, P> {
532
+ /** The editor to follow; the menu stays hidden while it is `null`. */
533
+ readonly editor: Editor<E, P> | null;
534
+ }
535
+ /**
536
+ * A menu over selected text, as `bubbleMenu` from `@canvist/web/menus`
537
+ * places it. Pressing inside it keeps the editor's focus and selection.
538
+ */
539
+ export declare const BubbleMenu: new <E extends Extensions, P extends Plugins>(props: (MenuProps<E, P> & ({
540
+ [x: `on${Capitalize<string>}`]: ((...args: any[]) => any) | undefined;
541
+ } | {
542
+ [x: `on${Capitalize<string>}`]: ((...args: never) => any) | undefined;
543
+ })) & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps) => import("vue").CreateComponentPublicInstanceWithMixins<MenuProps<E, P> & ({
544
+ [x: `on${Capitalize<string>}`]: ((...args: any[]) => any) | undefined;
545
+ } | {
546
+ [x: `on${Capitalize<string>}`]: ((...args: never) => any) | undefined;
547
+ }), {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, import("vue").EmitsOptions, import("vue").PublicProps, {}, false, {}, {}, {}, {}, string, {}, any, import("vue").ComponentProvideOptions, {
548
+ P: {};
549
+ B: {};
550
+ D: {};
551
+ C: {};
552
+ M: {};
553
+ Defaults: {};
554
+ }, (MenuProps<E, P> & {
555
+ [x: `on${Capitalize<string>}`]: ((...args: any[]) => any) | undefined;
556
+ }) | (MenuProps<E, P> & {
557
+ [x: `on${Capitalize<string>}`]: ((...args: never) => any) | undefined;
558
+ }), {}, {}, {}, {}, {}>;
559
+ /**
560
+ * A menu beside the caret on an empty line, as `floatingMenu` from
561
+ * `@canvist/web/menus` places it.
562
+ */
563
+ export declare const FloatingMenu: new <E extends Extensions, P extends Plugins>(props: (MenuProps<E, P> & ({
564
+ [x: `on${Capitalize<string>}`]: ((...args: any[]) => any) | undefined;
565
+ } | {
566
+ [x: `on${Capitalize<string>}`]: ((...args: never) => any) | undefined;
567
+ })) & import("vue").VNodeProps & import("vue").AllowedComponentProps & import("vue").ComponentCustomProps) => import("vue").CreateComponentPublicInstanceWithMixins<MenuProps<E, P> & ({
568
+ [x: `on${Capitalize<string>}`]: ((...args: any[]) => any) | undefined;
569
+ } | {
570
+ [x: `on${Capitalize<string>}`]: ((...args: never) => any) | undefined;
571
+ }), {}, {}, {}, {}, import("vue").ComponentOptionsMixin, import("vue").ComponentOptionsMixin, import("vue").EmitsOptions, import("vue").PublicProps, {}, false, {}, {}, {}, {}, string, {}, any, import("vue").ComponentProvideOptions, {
572
+ P: {};
573
+ B: {};
574
+ D: {};
575
+ C: {};
576
+ M: {};
577
+ Defaults: {};
578
+ }, (MenuProps<E, P> & {
579
+ [x: `on${Capitalize<string>}`]: ((...args: any[]) => any) | undefined;
580
+ }) | (MenuProps<E, P> & {
581
+ [x: `on${Capitalize<string>}`]: ((...args: never) => any) | undefined;
582
+ }), {}, {}, {}, {}, {}>;
package/dist/mod.js ADDED
@@ -0,0 +1,153 @@
1
+ /**
2
+ * Vue bindings for the canvist canvas editor.
3
+ *
4
+ * `CanvistEditor` renders an editor; `useCanvistEditor` gives you the
5
+ * editor itself, and `useEditorState` keeps a ref in step with something
6
+ * read from the editor. Commands, queries, and plugin APIs are typed from
7
+ * the extensions and plugins you pass, as in `@canvist/web`. `BubbleMenu`
8
+ * and `FloatingMenu` keep their slot next to the selection.
9
+ *
10
+ * ```ts
11
+ * const { element, editor } = useCanvistEditor({
12
+ * content: { format: "markdown", input: "Hello **world**" },
13
+ * });
14
+ * const bold = useEditorState(editor, (current) => current.can.toggleBold(), false);
15
+ * // <button :disabled="!bold" @click="editor?.commands.toggleBold()">Bold</button>
16
+ * // <div ref="element" />
17
+ * ```
18
+ *
19
+ * @module
20
+ */
21
+ import { createEditor, } from "@canvist/web";
22
+ import { bubbleMenu, floatingMenu, } from "@canvist/web/menus";
23
+ import { defineComponent, h, onBeforeUnmount, onMounted, shallowRef, watch, } from "vue";
24
+ /**
25
+ * Create an editor in `element` when the component mounts, and destroy it
26
+ * when it unmounts. `options` are read when the editor is created.
27
+ */
28
+ export function useCanvistEditor(options = {}) {
29
+ const element = shallowRef(null);
30
+ const editor = shallowRef(null);
31
+ const mount = { live: true, editor };
32
+ onMounted(() => {
33
+ if (element.value !== null) {
34
+ void create(element.value, options, mount);
35
+ }
36
+ });
37
+ onBeforeUnmount(() => {
38
+ mount.live = false;
39
+ editor.value?.destroy();
40
+ editor.value = null;
41
+ });
42
+ return { element, editor };
43
+ }
44
+ /** Create the editor for `mount`, destroying it if the component unmounted while it loaded. */
45
+ async function create(element, options, mount) {
46
+ try {
47
+ const editor = await createEditor(element, options);
48
+ if (mount.live) {
49
+ mount.editor.value = editor;
50
+ }
51
+ else {
52
+ editor.destroy();
53
+ }
54
+ }
55
+ catch (error) {
56
+ if (mount.live) {
57
+ reportError(error);
58
+ }
59
+ }
60
+ }
61
+ /**
62
+ * A ref holding `select(editor)`, updated after every edit or selection
63
+ * change. Until the editor exists, it holds `fallback`.
64
+ */
65
+ export function useEditorState(editor, select, fallback) {
66
+ const state = shallowRef(fallback);
67
+ watch(editor, (current, _previous, onCleanup) => {
68
+ if (current === null) {
69
+ state.value = fallback;
70
+ return;
71
+ }
72
+ state.value = select(current);
73
+ onCleanup(current.subscribe(() => {
74
+ state.value = select(current);
75
+ }));
76
+ }, { immediate: true });
77
+ return state;
78
+ }
79
+ /** The option props {@link CanvistEditor} passes to the editor. */
80
+ const OPTION_PROPS = [
81
+ "extensions",
82
+ "plugins",
83
+ "content",
84
+ "theme",
85
+ "breaks",
86
+ "client",
87
+ "overscan",
88
+ "wasm",
89
+ "input",
90
+ "label",
91
+ "platform",
92
+ "editable",
93
+ "placeholder",
94
+ ];
95
+ /**
96
+ * A canvist editor. Give it a width; without a height it grows with its
97
+ * document. It emits `ready` once the editor has loaded and `update`
98
+ * after every change, each with the editor.
99
+ */
100
+ export const CanvistEditor = defineComponent((props, { emit }) => {
101
+ const { html, ...options } = props;
102
+ const { element, editor } = useCanvistEditor(options);
103
+ watch([editor, () => props.editable, () => props.placeholder], ([current, editable, placeholder]) => {
104
+ current?.setEditable(editable ?? true);
105
+ current?.setPlaceholder(placeholder ?? "");
106
+ });
107
+ watch(editor, (current, _previous, onCleanup) => {
108
+ if (current === null) {
109
+ return;
110
+ }
111
+ emit("ready", current);
112
+ onCleanup(current.subscribe(() => emit("update", current)));
113
+ });
114
+ // `innerHTML` is set once: Vue leaves the editor's elements alone
115
+ // while it is unchanged.
116
+ return () => h("div", { ref: element, innerHTML: html });
117
+ }, { props: ["html", ...OPTION_PROPS], emits: ["ready", "update"] });
118
+ /** A component that keeps its default slot where `attach` places it. */
119
+ function followingMenu(attach) {
120
+ return defineComponent((props, { slots }) => {
121
+ const element = shallowRef(null);
122
+ watch([
123
+ () => props.editor,
124
+ element,
125
+ () => props.placement,
126
+ () => props.offset,
127
+ () => props.padding,
128
+ () => props.shouldShow,
129
+ ], ([editor, current, placement, offset, padding, shouldShow], _, onCleanup) => {
130
+ if (editor === null || current === null) {
131
+ return;
132
+ }
133
+ const menu = attach(editor, current, {
134
+ ...(placement === undefined ? {} : { placement }),
135
+ ...(offset === undefined ? {} : { offset }),
136
+ ...(padding === undefined ? {} : { padding }),
137
+ ...(shouldShow === undefined ? {} : { shouldShow }),
138
+ });
139
+ onCleanup(() => menu.destroy());
140
+ }, { flush: "post" });
141
+ return () => h("div", { ref: element, hidden: true }, slots["default"]?.());
142
+ }, { props: ["editor", "placement", "offset", "padding", "shouldShow"] });
143
+ }
144
+ /**
145
+ * A menu over selected text, as `bubbleMenu` from `@canvist/web/menus`
146
+ * places it. Pressing inside it keeps the editor's focus and selection.
147
+ */
148
+ export const BubbleMenu = followingMenu(bubbleMenu);
149
+ /**
150
+ * A menu beside the caret on an empty line, as `floatingMenu` from
151
+ * `@canvist/web/menus` places it.
152
+ */
153
+ export const FloatingMenu = followingMenu(floatingMenu);
package/package.json CHANGED
@@ -1 +1,42 @@
1
- {"description":"Placeholder package for @canvist/vue","name":"@canvist/vue","repository":"https://github.com/ifiokjr/canvist","version":"0.0.0"}
1
+ {
2
+ "name": "@canvist/vue",
3
+ "version": "0.2.0",
4
+ "description": "Vue bindings for the canvist canvas editor: a component, and composables typed from the extensions you register.",
5
+ "license": "Unlicense",
6
+ "homepage": "https://github.com/canvist/canvist/tree/main/packages/canvist-vue#readme",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/canvist/canvist.git",
10
+ "directory": "packages/canvist-vue"
11
+ },
12
+ "bugs": {
13
+ "url": "https://github.com/canvist/canvist/issues",
14
+ "email": "support@canvist.dev"
15
+ },
16
+ "type": "module",
17
+ "exports": {
18
+ ".": {
19
+ "types": "./dist/mod.d.ts",
20
+ "default": "./dist/mod.js"
21
+ }
22
+ },
23
+ "files": [
24
+ "dist"
25
+ ],
26
+ "sideEffects": false,
27
+ "scripts": {
28
+ "check:types": "tsc --noEmit",
29
+ "build": "tsc -p tsconfig.build.json",
30
+ "prepack": "npm run build"
31
+ },
32
+ "dependencies": {
33
+ "@canvist/web": "^0.2.0"
34
+ },
35
+ "peerDependencies": {
36
+ "vue": "^3.4.0"
37
+ },
38
+ "devDependencies": {
39
+ "typescript": "^7.0.2",
40
+ "vue": "^3.5.0"
41
+ }
42
+ }
package/readme.md ADDED
@@ -0,0 +1,50 @@
1
+ <p align="center">
2
+ <a href="https://github.com/canvist/canvist">
3
+ <picture>
4
+ <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/canvist/canvist/main/assets/brand/vector/lockup-dark.svg">
5
+ <img width="220" src="https://raw.githubusercontent.com/canvist/canvist/main/assets/brand/vector/lockup.svg" alt="canvist" />
6
+ </picture>
7
+ </a>
8
+ </p>
9
+
10
+ <p align="center">
11
+ <a href="https://github.com/canvist/canvist/blob/main/docs/src/quick-start/vue.md"><strong>Quick start</strong></a> ·
12
+ <a href="https://github.com/canvist/canvist/blob/main/docs/src/readme.md"><strong>Docs</strong></a> ·
13
+ <a href="https://github.com/canvist/canvist/blob/main/docs/src/reference/contributing.md"><strong>Contributing</strong></a> ·
14
+ <a href="https://github.com/canvist/canvist"><strong>canvist</strong></a>
15
+ </p>
16
+
17
+ <p align="center">
18
+ <a href="https://www.npmjs.com/package/@canvist/vue"><img src="https://img.shields.io/npm/v/%40canvist%2Fvue?color=3B2CF5" alt="npm version" /></a>
19
+ <a href="https://github.com/canvist/canvist/actions/workflows/web.yml"><img src="https://github.com/canvist/canvist/actions/workflows/web.yml/badge.svg?branch=main" alt="web status" /></a>
20
+ <a href="https://unlicense.org"><img src="https://img.shields.io/badge/license-Unlicense-3B2CF5" alt="Unlicense" /></a>
21
+ </p>
22
+
23
+ # @canvist/vue
24
+
25
+ Vue bindings for [`@canvist/web`](../canvist-web), the canvist canvas editor. Commands, queries, and plugin APIs are typed from the extensions and plugins you pass.
26
+
27
+ ```sh
28
+ npm install @canvist/vue
29
+ ```
30
+
31
+ ```vue
32
+ <script setup lang="ts">
33
+ import { useCanvistEditor, useEditorState } from "@canvist/vue";
34
+
35
+ const { element, editor } = useCanvistEditor({ label: "Notes" });
36
+ const isBold = useEditorState(editor, (current) => current.query.isBold(), false);
37
+ </script>
38
+
39
+ <template>
40
+ <button :aria-pressed="isBold" @click="editor?.commands.toggleBold()">Bold</button>
41
+ <div ref="element" />
42
+ </template>
43
+ ```
44
+
45
+ - `useCanvistEditor(options)` creates the editor in `element` on mount and destroys it on unmount.
46
+ - `useEditorState(editor, select, fallback)` returns a ref updated after every change.
47
+ - `<CanvistEditor />` takes the editor's options as props, emits `ready` and `update`, and takes `html` for server-rendered HTML to hydrate. `editable` and `placeholder` follow their props.
48
+ - `<BubbleMenu :editor="editor">` and `<FloatingMenu :editor="editor">` keep their slot next to the selection.
49
+
50
+ See [Quick start: Vue](../../docs/src/quick-start/vue.md).
package/CHANGELOG.md DELETED
@@ -1,3 +0,0 @@
1
- ## 0.0.0
2
-
3
- - Placeholder release.
package/LICENSE DELETED
@@ -1,24 +0,0 @@
1
- This is free and unencumbered software released into the public domain.
2
-
3
- Anyone is free to copy, modify, publish, use, compile, sell, or
4
- distribute this software, either in source code form or as a compiled
5
- binary, for any purpose, commercial or non-commercial, and by any
6
- means.
7
-
8
- In jurisdictions that recognize copyright laws, the author or authors
9
- of this software dedicate any and all copyright interest in the
10
- software to the public domain. We make this dedication for the benefit
11
- of the public at large and to the detriment of our heirs and
12
- successors. We intend this dedication to be an overt act of
13
- relinquishment in perpetuity of all present and future rights to this
14
- software under copyright law.
15
-
16
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19
- IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20
- OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21
- ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22
- OTHER DEALINGS IN THE SOFTWARE.
23
-
24
- For more information, please refer to <http://unlicense.org/>
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # @canvist/vue
2
-
3
- This is a placeholder release published by monochange to bootstrap trusted publishing.