@openleaf-editor/vue 0.1.0-beta.2 → 0.1.0-beta.4

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 ADDED
@@ -0,0 +1,77 @@
1
+ # @openleaf-editor/vue
2
+
3
+ Vue 3 wrapper for the OpenLeaf custom element.
4
+
5
+ > **Editor output is untrusted input.** Whatever the editor produces — and
6
+ > whatever a user pasted into it — must be sanitized **on your server** before it
7
+ > is stored or rendered as HTML. Client-side sanitization is a user-experience
8
+ > feature, not a security control: anything the editor strips can be put back
9
+ > with developer tools, because the editor runs under the user's control.
10
+ >
11
+ > [`@openleaf-editor/sanitize`](https://github.com/PeytonNowlin/openleaf/tree/main/packages/sanitize) ships the
12
+ > canonical allowlist as data and generates configuration for DOMPurify, Python
13
+ > `bleach` and PHP HTMLPurifier from it, so client and server enforce the same
14
+ > rules. Read [SECURITY.md](https://github.com/PeytonNowlin/openleaf/blob/main/SECURITY.md) before you ship.
15
+
16
+ ## Install
17
+
18
+ ```sh
19
+ npm install @openleaf-editor/vue@beta
20
+ ```
21
+
22
+ Keep every `@openleaf-editor/*` package on the same version. They pin each other
23
+ exactly, so mixing versions installs two copies of the schema and the toolbar
24
+ registry -- and a node built by one is not a node type the other accepts.
25
+
26
+ ## Use it
27
+
28
+ ```vue
29
+ <script setup>
30
+ import { ref } from 'vue'
31
+ import { OpenLeafEditor } from '@openleaf-editor/vue'
32
+
33
+ const html = ref('<p>Hello</p>')
34
+ </script>
35
+
36
+ <template>
37
+ <OpenLeafEditor v-model="html" toolbar="bold italic | link" />
38
+ </template>
39
+ ```
40
+
41
+ ### If you use the custom element directly
42
+
43
+ This wrapper registers nothing with Vue's compiler, so it just works. Reaching
44
+ for `<openleaf-editor>` in a template instead means telling Vue that it is a
45
+ custom element, or you get `[Vue warn]: Failed to resolve component:
46
+ openleaf-editor`:
47
+
48
+ ```js
49
+ // vite.config.js
50
+ export default {
51
+ plugins: [
52
+ vue({
53
+ template: {
54
+ compilerOptions: { isCustomElement: (tag) => tag.startsWith('openleaf-') },
55
+ },
56
+ }),
57
+ ],
58
+ }
59
+ ```
60
+
61
+ ## It is a wrapper, not a port
62
+
63
+ The editor is the `<openleaf-editor>` custom element. This forwards attributes,
64
+ keeps a controlled value in sync, and re-emits `openleaf:change`. Nothing about
65
+ editing lives here.
66
+
67
+ That is deliberate: three framework ports would be three copies of the schema and
68
+ three sets of bugs, and a node built by one is not a node type another accepts. If
69
+ you are not using Vue, skip this package -- the element works on its own
70
+ and that is the supported path, not a fallback.
71
+
72
+ Importing this module does not touch the DOM, so it is safe in a server render.
73
+ The element upgrades on the client.
74
+
75
+ ## License
76
+
77
+ Apache-2.0.
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,0BAA0B,CAAA;AAIjC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4EA6CzB,CAAA"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,0BAA0B,CAAA;AAGjC,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;4EA4EzB,CAAA"}
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@
4
4
  * `v-model` maps onto the element's `value`. The custom element remains the
5
5
  * source of truth for editing; this wrapper does not reimplement the schema.
6
6
  */
7
- import { defineComponent, h, onMounted, ref, watch } from 'vue';
7
+ import { defineComponent, h, onBeforeUnmount, onMounted, ref, watch } from 'vue';
8
8
  import '@openleaf-editor/element';
9
9
  export const OpenLeafEditor = defineComponent({
10
10
  name: 'OpenLeafEditor',
@@ -20,15 +20,27 @@ export const OpenLeafEditor = defineComponent({
20
20
  emits: ['update:modelValue'],
21
21
  setup(props, { emit, attrs }) {
22
22
  const el = ref(null);
23
+ const onChange = (event) => {
24
+ const detail = event.detail;
25
+ // The detail carries the already-serialized document. Reading `.value`
26
+ // back off the element serializes it a second time, per keystroke.
27
+ emit('update:modelValue', detail?.value ?? el.value?.value ?? '');
28
+ };
23
29
  onMounted(() => {
24
30
  const host = el.value;
25
31
  if (!host)
26
32
  return;
27
- if (props.modelValue)
33
+ // `!== undefined`, not truthiness. `modelValue` defaults to `''`, and `''`
34
+ // is falsy -- so mounting with the declared default skipped the write
35
+ // entirely and left whatever stale `innerHTML` was in the template.
36
+ if (props.modelValue !== undefined)
28
37
  host.value = props.modelValue;
29
- host.addEventListener('openleaf:change', () => {
30
- emit('update:modelValue', host.value);
31
- });
38
+ host.addEventListener('openleaf:change', onChange);
39
+ });
40
+ // React and Angular both cleaned up; Vue did not, so every unmount left a
41
+ // listener holding a closure over the component's `emit` alive.
42
+ onBeforeUnmount(() => {
43
+ el.value?.removeEventListener('openleaf:change', onChange);
32
44
  });
33
45
  watch(() => props.modelValue, (html) => {
34
46
  const host = el.value;
@@ -40,12 +52,28 @@ export const OpenLeafEditor = defineComponent({
40
52
  return () => h('openleaf-editor', {
41
53
  ...attrs,
42
54
  ref: el,
43
- toolbar: props.toolbar,
44
- toolbar2: props.toolbar2,
45
- menubar: props.menubar === true ? '' : props.menubar === false ? undefined : props.menubar,
46
- formats: props.formats,
47
- lang: props.lang,
48
- for: props.for,
55
+ // `^` forces `setAttribute` rather than a property write.
56
+ //
57
+ // Vue's `shouldSetAsProp` ends in `return key in el`, and every one of
58
+ // these names is now a real accessor on the element -- so without the
59
+ // prefix Vue takes the property path. That path is where `toolbar` used
60
+ // to be a SILENT no-op: the element exposed a getter-only `toolbar`
61
+ // accessor for plugin authors, `patchDOMProp` does
62
+ // `try { el[key] = value } catch {}`, assigning to a getter-only
63
+ // accessor throws in strict mode, and the throw was swallowed.
64
+ // `<OpenLeafEditor toolbar="bold italic" />` rendered the full
65
+ // 22-button default bar with no error anywhere.
66
+ //
67
+ // The element now has setters that reflect, so the property path works
68
+ // too. Forcing attributes anyway keeps this wrapper correct against an
69
+ // older element than itself, and keeps one spelling of the truth: the
70
+ // attribute is what the element reads.
71
+ '^toolbar': props.toolbar,
72
+ '^toolbar2': props.toolbar2,
73
+ '^menubar': props.menubar === true ? '' : props.menubar === false ? undefined : props.menubar,
74
+ '^formats': props.formats,
75
+ '^lang': props.lang,
76
+ '^for': props.for,
49
77
  });
50
78
  },
51
79
  });
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,EAAE,eAAe,EAAE,CAAC,EAAE,SAAS,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,KAAK,CAAA;AAC/D,OAAO,0BAA0B,CAAA;AAIjC,MAAM,CAAC,MAAM,cAAc,GAAG,eAAe,CAAC;IAC5C,IAAI,EAAE,gBAAgB;IACtB,KAAK,EAAE;QACL,UAAU,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,EAAE,EAAE;QACzC,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE;QAC7C,QAAQ,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE;QAC9C,OAAO,EAAE,EAAE,IAAI,EAAE,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE;QACxD,OAAO,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE;QAC7C,IAAI,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE;QAC1C,GAAG,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE;KAC1C;IACD,KAAK,EAAE,CAAC,mBAAmB,CAAC;IAC5B,KAAK,CAAC,KAAK,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE;QAC1B,MAAM,EAAE,GAAG,GAAG,CAAc,IAAI,CAAC,CAAA;QAEjC,SAAS,CAAC,GAAG,EAAE;YACb,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAA;YACrB,IAAI,CAAC,IAAI;gBAAE,OAAM;YACjB,IAAI,KAAK,CAAC,UAAU;gBAAE,IAAI,CAAC,KAAK,GAAG,KAAK,CAAC,UAAU,CAAA;YACnD,IAAI,CAAC,gBAAgB,CAAC,iBAAiB,EAAE,GAAG,EAAE;gBAC5C,IAAI,CAAC,mBAAmB,EAAE,IAAI,CAAC,KAAK,CAAC,CAAA;YACvC,CAAC,CAAC,CAAA;QACJ,CAAC,CAAC,CAAA;QAEF,KAAK,CACH,GAAG,EAAE,CAAC,KAAK,CAAC,UAAU,EACtB,CAAC,IAAI,EAAE,EAAE;YACP,MAAM,IAAI,GAAG,EAAE,CAAC,KAAK,CAAA;YACrB,IAAI,CAAC,IAAI,IAAI,IAAI,KAAK,SAAS;gBAAE,OAAM;YACvC,IAAI,IAAI,CAAC,KAAK,KAAK,IAAI;gBAAE,IAAI,CAAC,KAAK,GAAG,IAAI,CAAA;QAC5C,CAAC,CACF,CAAA;QAED,OAAO,GAAG,EAAE,CACV,CAAC,CAAC,iBAAiB,EAAE;YACnB,GAAG,KAAK;YACR,GAAG,EAAE,EAAE;YACP,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,QAAQ,EAAE,KAAK,CAAC,QAAQ;YACxB,OAAO,EAAE,KAAK,CAAC,OAAO,KAAK,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO;YAC1F,OAAO,EAAE,KAAK,CAAC,OAAO;YACtB,IAAI,EAAE,KAAK,CAAC,IAAI;YAChB,GAAG,EAAE,KAAK,CAAC,GAAG;SACf,CAAC,CAAA;IACN,CAAC;CACF,CAAC,CAAA"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openleaf-editor/vue",
3
- "version": "0.1.0-beta.2",
3
+ "version": "0.1.0-beta.4",
4
4
  "description": "Vue 3 wrapper for the OpenLeaf custom element. The editor itself stays framework-free.",
5
5
  "license": "Apache-2.0",
6
6
  "publishConfig": {
@@ -16,6 +16,9 @@
16
16
  "url": "https://github.com/PeytonNowlin/openleaf/issues"
17
17
  },
18
18
  "type": "module",
19
+ "engines": {
20
+ "node": ">=20"
21
+ },
19
22
  "main": "./dist/index.js",
20
23
  "types": "./dist/index.d.ts",
21
24
  "exports": {
@@ -31,7 +34,7 @@
31
34
  "vue": "^3.4.0"
32
35
  },
33
36
  "dependencies": {
34
- "@openleaf-editor/element": "0.1.0-beta.2"
37
+ "@openleaf-editor/element": "0.1.0-beta.4"
35
38
  },
36
39
  "devDependencies": {
37
40
  "vue": "^3.5.13"