@aquera/nile-elements 1.9.8 → 1.9.10
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 +7 -0
- package/dist/{index-df196058.cjs.js → index-50880eb7.cjs.js} +2 -2
- package/dist/index-50880eb7.cjs.js.map +1 -0
- package/dist/index-6832c52b.cjs.js +2 -0
- package/dist/index-6832c52b.cjs.js.map +1 -0
- package/dist/index-747d1880.cjs.js +2 -0
- package/dist/index-747d1880.cjs.js.map +1 -0
- package/dist/{index-249de90a.esm.js → index-7b1c9857.esm.js} +1 -1
- package/dist/index-a487d8b4.esm.js +1 -0
- package/dist/index-ba47645d.esm.js +1 -0
- package/dist/index.cjs.js +1 -1
- package/dist/index.esm.js +1 -1
- package/dist/index.js +1589 -1277
- package/dist/katex-44476ab4.esm.js +1 -0
- package/dist/katex-f4186f87.cjs.js +2 -0
- package/dist/katex-f4186f87.cjs.js.map +1 -0
- package/dist/nile-checkbox/nile-checkbox.cjs.js +1 -1
- package/dist/nile-checkbox/nile-checkbox.cjs.js.map +1 -1
- package/dist/nile-checkbox/nile-checkbox.esm.js +1 -1
- package/dist/nile-color-picker/index.cjs.js +1 -1
- package/dist/nile-color-picker/index.esm.js +1 -1
- package/dist/nile-color-picker/nile-color-picker.cjs.js +1 -1
- package/dist/nile-color-picker/nile-color-picker.cjs.js.map +1 -1
- package/dist/nile-color-picker/nile-color-picker.esm.js +1 -1
- package/dist/nile-combobox/index.cjs.js +1 -1
- package/dist/nile-combobox/index.esm.js +1 -1
- package/dist/nile-combobox/nile-combobox.cjs.js +1 -1
- package/dist/nile-combobox/nile-combobox.esm.js +1 -1
- package/dist/nile-detail/index.cjs.js +1 -1
- package/dist/nile-detail/index.esm.js +1 -1
- package/dist/nile-detail/nile-detail.cjs.js +1 -1
- package/dist/nile-detail/nile-detail.esm.js +1 -1
- package/dist/nile-nav-tab/nile-nav-tab.cjs.js +1 -1
- package/dist/nile-nav-tab/nile-nav-tab.cjs.js.map +1 -1
- package/dist/nile-nav-tab/nile-nav-tab.css.cjs.js +1 -1
- package/dist/nile-nav-tab/nile-nav-tab.css.cjs.js.map +1 -1
- package/dist/nile-nav-tab/nile-nav-tab.css.esm.js +11 -0
- package/dist/nile-nav-tab/nile-nav-tab.esm.js +1 -1
- package/dist/nile-nav-tab-group/nile-nav-tab-group.css.cjs.js +1 -1
- package/dist/nile-nav-tab-group/nile-nav-tab-group.css.cjs.js.map +1 -1
- package/dist/nile-nav-tab-group/nile-nav-tab-group.css.esm.js +1 -2
- package/dist/nile-wysiwyg-editor/engine/commands.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/commands.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/commands.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/create-state.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/create-state.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/index.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/index.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/inputrules.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/keymap.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/keymap.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/code-highlight.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embed-view.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embeds.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/embeds.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/find-replace.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/image.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/image.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/math-view.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/math-view.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/math-view.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/placeholder.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/style-block-view.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/style-block-view.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/style-block-view.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/suggestions.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/tables.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/plugins/tables.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/sanitize.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/sanitize.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/schema.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/schema.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/schema.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/engine/selection-state.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/engine/selection-state.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/index.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/index.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/math/katex-renderer.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/math/katex-renderer.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/math/katex-renderer.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.css.esm.js +22 -3
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-editor.esm.js +7 -6
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.esm.js +33 -54
- package/dist/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.esm.js +83 -95
- package/dist/nile-wysiwyg-editor/types.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/types.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/types.esm.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/bubble-item.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/bubble-item.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/bubble-item.esm.js +1 -0
- package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/bubble-menu.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/bubble-menu.esm.js +23 -15
- package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/code-block-menu.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/code-block-menu.esm.js +3 -3
- package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/embed-panel.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/embed-panel.esm.js +2 -2
- package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/math-panel.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/math-panel.esm.js +0 -3
- package/dist/nile-wysiwyg-editor/ui/preview.cjs.js +2 -0
- package/dist/nile-wysiwyg-editor/ui/preview.cjs.js.map +1 -0
- package/dist/nile-wysiwyg-editor/ui/preview.esm.js +8 -0
- package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/source-view.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/source-view.esm.js +225 -24
- package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/suggest-list.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/suggest-list.esm.js +9 -9
- package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/table-grid-picker.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/table-grid-picker.esm.js +115 -13
- package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js +1 -1
- package/dist/nile-wysiwyg-editor/ui/table-menu.cjs.js.map +1 -1
- package/dist/nile-wysiwyg-editor/ui/table-menu.esm.js +13 -12
- package/dist/src/index.d.ts +5 -0
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/nile-checkbox/nile-checkbox.js +1 -0
- package/dist/src/nile-checkbox/nile-checkbox.js.map +1 -1
- package/dist/src/nile-color-picker/nile-color-picker.d.ts +1 -0
- package/dist/src/nile-color-picker/nile-color-picker.js +1 -0
- package/dist/src/nile-color-picker/nile-color-picker.js.map +1 -1
- package/dist/src/nile-nav-tab/nile-nav-tab.css.js +11 -0
- package/dist/src/nile-nav-tab/nile-nav-tab.css.js.map +1 -1
- package/dist/src/nile-nav-tab/nile-nav-tab.d.ts +2 -0
- package/dist/src/nile-nav-tab/nile-nav-tab.js +13 -0
- package/dist/src/nile-nav-tab/nile-nav-tab.js.map +1 -1
- package/dist/src/nile-nav-tab-group/nile-nav-tab-group.css.js +1 -2
- package/dist/src/nile-nav-tab-group/nile-nav-tab-group.css.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/index.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/index.js +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/index.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embed-view.js +3 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embed-view.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.d.ts +19 -4
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.js +23 -18
- package/dist/src/nile-wysiwyg-editor/engine/plugins/embeds.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.d.ts +8 -3
- package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.js +41 -4
- package/dist/src/nile-wysiwyg-editor/engine/plugins/math-view.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/plugins/style-block-view.d.ts +17 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/style-block-view.js +174 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/style-block-view.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js +33 -2
- package/dist/src/nile-wysiwyg-editor/engine/plugins/tables.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/pm.test.js +49 -8
- package/dist/src/nile-wysiwyg-editor/engine/pm.test.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/sanitize.d.ts +12 -0
- package/dist/src/nile-wysiwyg-editor/engine/sanitize.js +76 -8
- package/dist/src/nile-wysiwyg-editor/engine/sanitize.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/engine/schema.d.ts +2 -0
- package/dist/src/nile-wysiwyg-editor/engine/schema.js +154 -2
- package/dist/src/nile-wysiwyg-editor/engine/schema.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/index.d.ts +5 -0
- package/dist/src/nile-wysiwyg-editor/index.js +3 -0
- package/dist/src/nile-wysiwyg-editor/index.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/math/katex-renderer.d.ts +38 -0
- package/dist/src/nile-wysiwyg-editor/math/katex-renderer.js +40 -0
- package/dist/src/nile-wysiwyg-editor/math/katex-renderer.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js +22 -3
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.d.ts +36 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js +125 -8
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js +5 -2
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.d.ts +2 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.js +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.d.ts +55 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.js +79 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.js +33 -54
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.d.ts +50 -2
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.js +158 -102
- package/dist/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/types.d.ts +4 -2
- package/dist/src/nile-wysiwyg-editor/types.js +2 -2
- package/dist/src/nile-wysiwyg-editor/types.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/bubble-item.d.ts +48 -0
- package/dist/src/nile-wysiwyg-editor/ui/bubble-item.js +69 -0
- package/dist/src/nile-wysiwyg-editor/ui/bubble-item.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.d.ts +17 -2
- package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js +128 -35
- package/dist/src/nile-wysiwyg-editor/ui/bubble-menu.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/embed-panel.d.ts +2 -1
- package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js +4 -3
- package/dist/src/nile-wysiwyg-editor/ui/embed-panel.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/math-panel.js +0 -3
- package/dist/src/nile-wysiwyg-editor/ui/math-panel.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/preview.d.ts +20 -0
- package/dist/src/nile-wysiwyg-editor/ui/preview.js +44 -0
- package/dist/src/nile-wysiwyg-editor/ui/preview.js.map +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/source-view.d.ts +35 -3
- package/dist/src/nile-wysiwyg-editor/ui/source-view.js +337 -24
- package/dist/src/nile-wysiwyg-editor/ui/source-view.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/suggest-list.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js +9 -0
- package/dist/src/nile-wysiwyg-editor/ui/suggest-list.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.d.ts +13 -2
- package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js +166 -10
- package/dist/src/nile-wysiwyg-editor/ui/table-grid-picker.js.map +1 -1
- package/dist/src/nile-wysiwyg-editor/ui/table-menu.d.ts +1 -0
- package/dist/src/nile-wysiwyg-editor/ui/table-menu.js +10 -8
- package/dist/src/nile-wysiwyg-editor/ui/table-menu.js.map +1 -1
- package/dist/src/version.js +1 -1
- package/dist/src/version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/xml-25c4108a.esm.js +1 -0
- package/dist/xml-90fd974e.cjs.js +2 -0
- package/dist/xml-90fd974e.cjs.js.map +1 -0
- package/package.json +2 -1
- package/rollup.config.js +1 -0
- package/src/index.ts +8 -0
- package/src/nile-checkbox/nile-checkbox.ts +1 -0
- package/src/nile-color-picker/nile-color-picker.ts +1 -0
- package/src/nile-nav-tab/nile-nav-tab.css.ts +11 -0
- package/src/nile-nav-tab/nile-nav-tab.ts +17 -0
- package/src/nile-nav-tab-group/nile-nav-tab-group.css.ts +1 -2
- package/src/nile-wysiwyg-editor/engine/index.ts +1 -0
- package/src/nile-wysiwyg-editor/engine/plugins/embed-view.ts +3 -0
- package/src/nile-wysiwyg-editor/engine/plugins/embeds.ts +26 -20
- package/src/nile-wysiwyg-editor/engine/plugins/math-view.ts +45 -4
- package/src/nile-wysiwyg-editor/engine/plugins/style-block-view.ts +178 -0
- package/src/nile-wysiwyg-editor/engine/plugins/tables.ts +34 -2
- package/src/nile-wysiwyg-editor/engine/pm.test.ts +58 -8
- package/src/nile-wysiwyg-editor/engine/sanitize.ts +71 -8
- package/src/nile-wysiwyg-editor/engine/schema.ts +158 -7
- package/src/nile-wysiwyg-editor/index.ts +5 -0
- package/src/nile-wysiwyg-editor/math/katex-renderer.ts +59 -0
- package/src/nile-wysiwyg-editor/math/katex.d.ts +17 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.css.ts +22 -3
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.test.ts +5 -2
- package/src/nile-wysiwyg-editor/nile-wysiwyg-editor.ts +142 -7
- package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/index.ts +2 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item.ts +68 -0
- package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.css.ts +33 -54
- package/src/nile-wysiwyg-editor/nile-wysiwyg-toolbar/nile-wysiwyg-toolbar.ts +202 -108
- package/src/nile-wysiwyg-editor/types.ts +6 -4
- package/src/nile-wysiwyg-editor/ui/bubble-item.ts +60 -0
- package/src/nile-wysiwyg-editor/ui/bubble-menu.ts +147 -40
- package/src/nile-wysiwyg-editor/ui/embed-panel.ts +4 -3
- package/src/nile-wysiwyg-editor/ui/math-panel.ts +0 -3
- package/src/nile-wysiwyg-editor/ui/preview.ts +42 -0
- package/src/nile-wysiwyg-editor/ui/source-view.ts +339 -31
- package/src/nile-wysiwyg-editor/ui/suggest-list.ts +10 -0
- package/src/nile-wysiwyg-editor/ui/table-grid-picker.ts +160 -10
- package/src/nile-wysiwyg-editor/ui/table-menu.ts +10 -8
- package/vscode-html-custom-data.json +75 -6
- package/dist/index-353172ed.esm.js +0 -1
- package/dist/index-b387a9f4.cjs.js +0 -2
- package/dist/index-b387a9f4.cjs.js.map +0 -1
- package/dist/index-cc1385e0.cjs.js +0 -2
- package/dist/index-cc1385e0.cjs.js.map +0 -1
- package/dist/index-dedc38a6.esm.js +0 -1
- package/dist/index-df196058.cjs.js.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { Schema, NodeSpec, MarkSpec } from 'prosemirror-model';
|
|
1
|
+
import { Schema, NodeSpec, MarkSpec, Attrs, DOMOutputSpec, ParseRule } from 'prosemirror-model';
|
|
2
2
|
import { addListNodes } from 'prosemirror-schema-list';
|
|
3
3
|
import { tableNodes } from 'prosemirror-tables';
|
|
4
4
|
import OrderedMap from 'orderedmap';
|
|
5
|
+
import { EMBED_SANDBOX, EMBED_REFERRER_POLICY } from './plugins/embeds';
|
|
5
6
|
|
|
6
7
|
export type Alignment = 'left' | 'center' | 'right' | 'justify';
|
|
7
8
|
|
|
@@ -54,6 +55,100 @@ function blockStyle(attrs: {
|
|
|
54
55
|
return parts.length ? parts.join('; ') : null;
|
|
55
56
|
}
|
|
56
57
|
|
|
58
|
+
/* -------------------------------------------------------------------------
|
|
59
|
+
* Author class / id passthrough
|
|
60
|
+
*
|
|
61
|
+
* Content elements keep the `class` and `id` an author gave them, so class/id
|
|
62
|
+
* CSS (e.g. a scoped author <style> block) can target the document. Values that
|
|
63
|
+
* collide with the editor's own internal names are dropped so content can never
|
|
64
|
+
* impersonate or restyle the chrome; the editor's own classes (emitted in
|
|
65
|
+
* toDOM) are always preserved and merged with the author's.
|
|
66
|
+
* ------------------------------------------------------------------------- */
|
|
67
|
+
|
|
68
|
+
const RESERVED_NAME = /^(?:nile-wysiwyg|ProseMirror)/;
|
|
69
|
+
|
|
70
|
+
export function cleanClass(value: string | null): string | null {
|
|
71
|
+
if (!value) return null;
|
|
72
|
+
const kept = value.split(/\s+/).filter(token => token && !RESERVED_NAME.test(token));
|
|
73
|
+
return kept.length ? kept.join(' ') : null;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function cleanId(value: string | null): string | null {
|
|
77
|
+
return value && !RESERVED_NAME.test(value) ? value : null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const HTML_ATTR_SPEC = { htmlClass: { default: null }, htmlId: { default: null } };
|
|
81
|
+
|
|
82
|
+
function readHtmlAttrs(dom: HTMLElement): { htmlClass: string | null; htmlId: string | null } {
|
|
83
|
+
return {
|
|
84
|
+
htmlClass: cleanClass(dom.getAttribute('class')),
|
|
85
|
+
htmlId: cleanId(dom.getAttribute('id')),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/** Wraps a tag-based parse rule so it also captures author class/id. Rules
|
|
90
|
+
* keyed by `style` (whose getAttrs receives a value string, not an element)
|
|
91
|
+
* pass through untouched. */
|
|
92
|
+
function augmentParseRule(rule: ParseRule): ParseRule {
|
|
93
|
+
if (!('tag' in rule) || 'style' in rule) return rule;
|
|
94
|
+
const inner = rule.getAttrs;
|
|
95
|
+
return {
|
|
96
|
+
...rule,
|
|
97
|
+
getAttrs(dom: HTMLElement) {
|
|
98
|
+
const base = inner ? inner(dom) : null;
|
|
99
|
+
if (base === false) return false;
|
|
100
|
+
return { ...(base ?? {}), ...readHtmlAttrs(dom) };
|
|
101
|
+
},
|
|
102
|
+
} as ParseRule;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Wraps a toDOM so stored class/id are written back onto the element, merged
|
|
106
|
+
* with any class the spec emits itself. Handles the `[tag]`, `[tag, attrs]`,
|
|
107
|
+
* `[tag, 0]`, and `[tag, attrs, …children]` output shapes. */
|
|
108
|
+
function augmentToDOM(
|
|
109
|
+
toDOM?: (node: { attrs: Attrs }, ...rest: unknown[]) => DOMOutputSpec
|
|
110
|
+
): ((node: { attrs: Attrs }, ...rest: unknown[]) => DOMOutputSpec) | undefined {
|
|
111
|
+
if (!toDOM) return toDOM;
|
|
112
|
+
return (node, ...rest) => {
|
|
113
|
+
const out = toDOM(node, ...rest);
|
|
114
|
+
const cls = node.attrs.htmlClass as string | null;
|
|
115
|
+
const id = node.attrs.htmlId as string | null;
|
|
116
|
+
if (!Array.isArray(out) || (!cls && !id)) return out;
|
|
117
|
+
const [tag, ...tail] = out as unknown[];
|
|
118
|
+
let attrs: Record<string, unknown> = {};
|
|
119
|
+
let children = tail;
|
|
120
|
+
if (tail.length && tail[0] && typeof tail[0] === 'object' && !Array.isArray(tail[0])) {
|
|
121
|
+
attrs = { ...(tail[0] as Record<string, unknown>) };
|
|
122
|
+
children = tail.slice(1);
|
|
123
|
+
}
|
|
124
|
+
if (cls) attrs.class = attrs.class ? `${attrs.class as string} ${cls}` : cls;
|
|
125
|
+
if (id) attrs.id = id;
|
|
126
|
+
return [tag, attrs, ...children] as DOMOutputSpec;
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Adds `htmlClass`/`htmlId` attrs plus parse/serialize support to a spec. */
|
|
131
|
+
function withHtmlAttrs<S extends NodeSpec | MarkSpec>(spec: S): S {
|
|
132
|
+
return {
|
|
133
|
+
...spec,
|
|
134
|
+
attrs: { ...(spec.attrs ?? {}), ...HTML_ATTR_SPEC },
|
|
135
|
+
parseDOM: spec.parseDOM?.map(augmentParseRule),
|
|
136
|
+
toDOM: augmentToDOM(spec.toDOM as never) as never,
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/** Content nodes/marks that carry author class/id. Excludes structural leaves
|
|
141
|
+
* (doc, text), atoms with bespoke DOM (image, embeds, mentions, math, …) and
|
|
142
|
+
* style-only marks (their spans are covered by `html_span`). */
|
|
143
|
+
const HTML_ATTR_NODES = [
|
|
144
|
+
'paragraph', 'heading', 'blockquote', 'code_block',
|
|
145
|
+
'bullet_list', 'ordered_list', 'list_item',
|
|
146
|
+
'table', 'table_row', 'table_cell', 'table_header',
|
|
147
|
+
];
|
|
148
|
+
const HTML_ATTR_MARKS = [
|
|
149
|
+
'link', 'bold', 'italic', 'underline', 'strike', 'code', 'subscript', 'superscript',
|
|
150
|
+
];
|
|
151
|
+
|
|
57
152
|
/**
|
|
58
153
|
* Block nodes. `align` is modeled as an attribute on textblocks rather than a
|
|
59
154
|
* wrapper node so alignment survives block-type changes (heading <-> paragraph).
|
|
@@ -368,6 +463,8 @@ const nodes: Record<string, NodeSpec> = {
|
|
|
368
463
|
frameborder: '0',
|
|
369
464
|
allowfullscreen: 'true',
|
|
370
465
|
allow: 'accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture',
|
|
466
|
+
sandbox: EMBED_SANDBOX,
|
|
467
|
+
referrerpolicy: EMBED_REFERRER_POLICY,
|
|
371
468
|
};
|
|
372
469
|
if (title) iframeAttrs.title = title;
|
|
373
470
|
const wrapperAttrs: Record<string, string> = {
|
|
@@ -389,6 +486,29 @@ const nodes: Record<string, NodeSpec> = {
|
|
|
389
486
|
},
|
|
390
487
|
},
|
|
391
488
|
|
|
489
|
+
/**
|
|
490
|
+
* Author-supplied `<style>` block, entered via the HTML source view. The CSS
|
|
491
|
+
* is sanitized on the way in (see sanitizeCss) — `@import`, `expression()`
|
|
492
|
+
* and external `url()` are neutralized — then stored verbatim and serialized
|
|
493
|
+
* straight back to a `<style>` element. A node view renders it live in the
|
|
494
|
+
* editor with a small chip so the otherwise-invisible block stays selectable.
|
|
495
|
+
*/
|
|
496
|
+
styleBlock: {
|
|
497
|
+
group: 'block',
|
|
498
|
+
atom: true,
|
|
499
|
+
selectable: true,
|
|
500
|
+
attrs: { css: { default: '' } },
|
|
501
|
+
parseDOM: [
|
|
502
|
+
{
|
|
503
|
+
tag: 'style',
|
|
504
|
+
getAttrs: (dom: HTMLElement) => ({ css: dom.textContent ?? '' }),
|
|
505
|
+
},
|
|
506
|
+
],
|
|
507
|
+
toDOM(node) {
|
|
508
|
+
return ['style', node.attrs.css];
|
|
509
|
+
},
|
|
510
|
+
},
|
|
511
|
+
|
|
392
512
|
/**
|
|
393
513
|
* Bookmark: a named in-text anchor (`<a id="…">`) for intra-document
|
|
394
514
|
* navigation. Invisible in serialized output; the editor draws a flag
|
|
@@ -705,6 +825,30 @@ const marks: Record<string, MarkSpec> = {
|
|
|
705
825
|
},
|
|
706
826
|
},
|
|
707
827
|
|
|
828
|
+
/** Carries an author `class`/`id` on an inline `<span>` that has no other
|
|
829
|
+
* meaning, so class/id CSS can target runs of text. Low priority so styled
|
|
830
|
+
* spans (color/font marks) and atom spans (mention/merge/math) win. */
|
|
831
|
+
html_span: {
|
|
832
|
+
attrs: { htmlClass: { default: null }, htmlId: { default: null } },
|
|
833
|
+
parseDOM: [
|
|
834
|
+
{
|
|
835
|
+
tag: 'span',
|
|
836
|
+
priority: 5,
|
|
837
|
+
getAttrs: (dom: HTMLElement) => {
|
|
838
|
+
const htmlClass = cleanClass(dom.getAttribute('class'));
|
|
839
|
+
const htmlId = cleanId(dom.getAttribute('id'));
|
|
840
|
+
return htmlClass || htmlId ? { htmlClass, htmlId } : false;
|
|
841
|
+
},
|
|
842
|
+
},
|
|
843
|
+
],
|
|
844
|
+
toDOM(mark) {
|
|
845
|
+
const attrs: Record<string, string> = {};
|
|
846
|
+
if (mark.attrs.htmlClass) attrs.class = mark.attrs.htmlClass;
|
|
847
|
+
if (mark.attrs.htmlId) attrs.id = mark.attrs.htmlId;
|
|
848
|
+
return ['span', attrs, 0];
|
|
849
|
+
},
|
|
850
|
+
},
|
|
851
|
+
|
|
708
852
|
text_color: {
|
|
709
853
|
attrs: { color: {} },
|
|
710
854
|
parseDOM: [
|
|
@@ -843,16 +987,23 @@ const orderedListSpec: NodeSpec = {
|
|
|
843
987
|
|
|
844
988
|
/** Builds the editor schema. List nodes are added via prosemirror-schema-list. */
|
|
845
989
|
export function createSchema(): Schema {
|
|
846
|
-
|
|
847
|
-
OrderedMap.from(nodes),
|
|
848
|
-
'paragraph block*',
|
|
849
|
-
'block'
|
|
850
|
-
)
|
|
990
|
+
let nodeMap = addListNodes(OrderedMap.from(nodes), 'paragraph block*', 'block')
|
|
851
991
|
.update('bullet_list', bulletListSpec)
|
|
852
992
|
.update('ordered_list', orderedListSpec)
|
|
853
993
|
.append(tableNodeSpecs)
|
|
854
994
|
.update('table', tableSpec);
|
|
855
|
-
|
|
995
|
+
// Thread author class/id through the content nodes and marks (applied last so
|
|
996
|
+
// it also wraps the list/table specs pulled in from external packages).
|
|
997
|
+
for (const name of HTML_ATTR_NODES) {
|
|
998
|
+
const spec = nodeMap.get(name);
|
|
999
|
+
if (spec) nodeMap = nodeMap.update(name, withHtmlAttrs(spec));
|
|
1000
|
+
}
|
|
1001
|
+
let markMap = OrderedMap.from<MarkSpec>(marks);
|
|
1002
|
+
for (const name of HTML_ATTR_MARKS) {
|
|
1003
|
+
const spec = markMap.get(name);
|
|
1004
|
+
if (spec) markMap = markMap.update(name, withHtmlAttrs(spec));
|
|
1005
|
+
}
|
|
1006
|
+
return new Schema({ nodes: nodeMap, marks: markMap });
|
|
856
1007
|
}
|
|
857
1008
|
|
|
858
1009
|
/** A shared default schema instance. */
|
|
@@ -1,3 +1,8 @@
|
|
|
1
1
|
export { NileWysiwygEditor, default } from './nile-wysiwyg-editor';
|
|
2
|
+
export { NileWysiwygToolbarItem } from './nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item';
|
|
3
|
+
export { NileWysiwygBubbleItem } from './ui/bubble-item';
|
|
4
|
+
export type { AuthoredToolbarItem } from './nile-wysiwyg-toolbar/nile-wysiwyg-toolbar';
|
|
2
5
|
export type { WysiwygChangeDetail, WysiwygCommandDetail, ToolbarConfig } from './types';
|
|
3
6
|
export { DEFAULT_TOOLBAR } from './types';
|
|
7
|
+
export { createKatexRenderer } from './math/katex-renderer';
|
|
8
|
+
export type { KatexLike, KatexRendererOptions } from './math/katex-renderer';
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import type { MathRenderer } from '../engine/plugins/math-view';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* The slice of the KaTeX module this helper needs. Declared locally so the
|
|
5
|
+
* design system never imports `katex` itself — you bundle KaTeX from your own
|
|
6
|
+
* app's install and pass the instance in.
|
|
7
|
+
*/
|
|
8
|
+
export interface KatexLike {
|
|
9
|
+
renderToString(latex: string, options?: Record<string, unknown>): string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface KatexRendererOptions {
|
|
13
|
+
/**
|
|
14
|
+
* Extra options merged into every `katex.renderToString` call (after the
|
|
15
|
+
* `displayMode` / `throwOnError` defaults). See the KaTeX options reference.
|
|
16
|
+
*/
|
|
17
|
+
katexOptions?: Record<string, unknown>;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Builds a {@link MathRenderer} backed by KaTeX for the editor's `mathRenderer`
|
|
22
|
+
* property. KaTeX is **not** bundled by the design system — install it in your
|
|
23
|
+
* app and hand the instance to this helper so your bundler ships it (no CDN):
|
|
24
|
+
*
|
|
25
|
+
* ```ts
|
|
26
|
+
* import katex from 'katex';
|
|
27
|
+
* import 'katex/dist/katex.min.css';
|
|
28
|
+
* import { createKatexRenderer } from '@aquera/nile-elements';
|
|
29
|
+
*
|
|
30
|
+
* editor.mathRenderer = createKatexRenderer(katex);
|
|
31
|
+
* // or with overrides:
|
|
32
|
+
* editor.mathRenderer = createKatexRenderer(katex, {
|
|
33
|
+
* katexOptions: { macros: { '\\RR': '\\mathbb{R}' } },
|
|
34
|
+
* });
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* Called with no instance it falls back to a global `katex` (e.g. a CDN
|
|
38
|
+
* `<script>`), which is handy for quick demos. If no engine is available at
|
|
39
|
+
* render time the raw LaTeX is returned, so content stays visible.
|
|
40
|
+
*/
|
|
41
|
+
export function createKatexRenderer(
|
|
42
|
+
katex?: KatexLike,
|
|
43
|
+
options: KatexRendererOptions = {}
|
|
44
|
+
): MathRenderer {
|
|
45
|
+
return (latex, display) => {
|
|
46
|
+
const engine = katex ?? (globalThis as { katex?: KatexLike }).katex;
|
|
47
|
+
if (!engine) return latex;
|
|
48
|
+
try {
|
|
49
|
+
return engine.renderToString(latex, {
|
|
50
|
+
displayMode: display === 'block',
|
|
51
|
+
throwOnError: false,
|
|
52
|
+
...options.katexOptions,
|
|
53
|
+
});
|
|
54
|
+
} catch {
|
|
55
|
+
// Malformed LaTeX (with throwOnError overridden true, say) → show source.
|
|
56
|
+
return latex;
|
|
57
|
+
}
|
|
58
|
+
};
|
|
59
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal ambient types for KaTeX — the package ships no declarations and we
|
|
3
|
+
* only use `renderToString`. Keeps the built-in MathML renderer in
|
|
4
|
+
* `engine/plugins/math-view.ts` type-safe without pulling in @types/katex.
|
|
5
|
+
*/
|
|
6
|
+
declare module 'katex' {
|
|
7
|
+
interface KatexOptions {
|
|
8
|
+
displayMode?: boolean;
|
|
9
|
+
output?: 'html' | 'mathml' | 'htmlAndMathml';
|
|
10
|
+
throwOnError?: boolean;
|
|
11
|
+
[option: string]: unknown;
|
|
12
|
+
}
|
|
13
|
+
const katex: {
|
|
14
|
+
renderToString(tex: string, options?: KatexOptions): string;
|
|
15
|
+
};
|
|
16
|
+
export default katex;
|
|
17
|
+
}
|
|
@@ -292,8 +292,10 @@ export const styles = css`
|
|
|
292
292
|
text-align: left;
|
|
293
293
|
}
|
|
294
294
|
|
|
295
|
-
|
|
296
|
-
.ProseMirror
|
|
295
|
+
|
|
296
|
+
.ProseMirror td > *,
|
|
297
|
+
.ProseMirror th > * {
|
|
298
|
+
margin-top: 0;
|
|
297
299
|
margin-bottom: 0;
|
|
298
300
|
}
|
|
299
301
|
|
|
@@ -605,11 +607,28 @@ export const styles = css`
|
|
|
605
607
|
font-weight: 600;
|
|
606
608
|
}
|
|
607
609
|
|
|
608
|
-
/* ---
|
|
610
|
+
/* --- Author <style> block (from HTML source) ---
|
|
611
|
+
Applied live by StyleBlockView (a hidden <style> host, scoped to the
|
|
612
|
+
content); the block renders nothing in the editor. */
|
|
613
|
+
.nile-wysiwyg__style-block {
|
|
614
|
+
display: none;
|
|
615
|
+
}
|
|
616
|
+
|
|
617
|
+
/* --- Sticky toolbar ---
|
|
618
|
+
Pins to the top of the scroll container. Note: sticky anchors to the
|
|
619
|
+
container's PADDING box, so if the scroll container has top padding, a
|
|
620
|
+
band of content shows above the toolbar — set --nile-wysiwyg-sticky-top
|
|
621
|
+
(or remove the container's top padding) to compensate. The opaque
|
|
622
|
+
background makes the pinned bar self-backing so content can never bleed
|
|
623
|
+
through it. */
|
|
609
624
|
:host([sticky-toolbar]) nile-wysiwyg-toolbar {
|
|
610
625
|
position: sticky;
|
|
611
626
|
top: var(--nile-wysiwyg-sticky-top, 0);
|
|
612
627
|
z-index: 12;
|
|
628
|
+
background: var(
|
|
629
|
+
--nile-wysiwyg-bg,
|
|
630
|
+
var(--nile-colors-white-base, var(--ng-colors-bg-primary, #ffffff))
|
|
631
|
+
);
|
|
613
632
|
}
|
|
614
633
|
|
|
615
634
|
/* --- Mentions & suggestions --- */
|
|
@@ -381,9 +381,12 @@ describe('nile-wysiwyg-editor', () => {
|
|
|
381
381
|
const source = el.shadowRoot!.querySelector('nile-wysiwyg-source-view')!;
|
|
382
382
|
expect(source).to.exist;
|
|
383
383
|
await source.updateComplete;
|
|
384
|
-
expect(source.
|
|
384
|
+
expect(source.currentValue).to.equal('<p>original</p>');
|
|
385
385
|
|
|
386
|
-
|
|
386
|
+
// Simulate editing the textarea (its input event drives the draft).
|
|
387
|
+
const textarea = source.shadowRoot!.querySelector('textarea')!;
|
|
388
|
+
textarea.value = '<p>edited</p><script>alert(1)</script>';
|
|
389
|
+
textarea.dispatchEvent(new Event('input'));
|
|
387
390
|
el.toggleSourceView();
|
|
388
391
|
await el.updateComplete;
|
|
389
392
|
expect(el.shadowRoot!.querySelector('nile-wysiwyg-source-view')).to.not.exist;
|
|
@@ -30,6 +30,7 @@ import {
|
|
|
30
30
|
ImageView,
|
|
31
31
|
FigureView,
|
|
32
32
|
EmbedView,
|
|
33
|
+
StyleBlockView,
|
|
33
34
|
MathView,
|
|
34
35
|
isOfficeHtml,
|
|
35
36
|
cleanOfficeHtml,
|
|
@@ -64,8 +65,11 @@ import { DEFAULT_TOOLBAR } from './types';
|
|
|
64
65
|
import { EMOJI } from './emoji';
|
|
65
66
|
|
|
66
67
|
import './nile-wysiwyg-toolbar/nile-wysiwyg-toolbar';
|
|
68
|
+
import './nile-wysiwyg-toolbar/nile-wysiwyg-toolbar-item';
|
|
69
|
+
import type { AuthoredToolbarItem } from './nile-wysiwyg-toolbar/nile-wysiwyg-toolbar';
|
|
67
70
|
import './ui/link-editor';
|
|
68
71
|
import './ui/bubble-menu';
|
|
72
|
+
import './ui/bubble-item';
|
|
69
73
|
import './ui/image-panel';
|
|
70
74
|
import './ui/table-menu';
|
|
71
75
|
import './ui/suggest-list';
|
|
@@ -177,6 +181,22 @@ export class NileWysiwygEditor extends NileElement {
|
|
|
177
181
|
/** Toolbar config string; `"none"` hides the toolbar. */
|
|
178
182
|
@property({ type: String }) toolbar = DEFAULT_TOOLBAR;
|
|
179
183
|
|
|
184
|
+
/**
|
|
185
|
+
* Toolbar built from authored `<nile-wysiwyg-toolbar-item>` light-DOM
|
|
186
|
+
* children. When present it takes precedence over the `toolbar` string.
|
|
187
|
+
*/
|
|
188
|
+
@state() private authoredToolbar?: AuthoredToolbarItem[];
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Bubble menu built from authored `<nile-wysiwyg-bubble-item>` light-DOM
|
|
192
|
+
* children. When present it replaces the built-in bold/italic/underline/
|
|
193
|
+
* strike/link set.
|
|
194
|
+
*/
|
|
195
|
+
@state() private authoredBubble?: AuthoredToolbarItem[];
|
|
196
|
+
|
|
197
|
+
/** Watches authored toolbar/bubble children so edits re-render them. */
|
|
198
|
+
private toolbarObserver?: MutationObserver;
|
|
199
|
+
|
|
180
200
|
/** Shows a balloon toolbar over text selections. */
|
|
181
201
|
@property({ type: Boolean, attribute: 'bubble-menu' }) bubbleMenu = false;
|
|
182
202
|
|
|
@@ -459,11 +479,34 @@ export class NileWysiwygEditor extends NileElement {
|
|
|
459
479
|
}
|
|
460
480
|
// Close floating panels when the user clicks anywhere outside them.
|
|
461
481
|
document.addEventListener('pointerdown', this.onOutsidePointerDown, true);
|
|
482
|
+
// Format painter applies on pointer release so a drag-selection is painted
|
|
483
|
+
// whole, not on the first mid-drag selection change.
|
|
484
|
+
document.addEventListener('mouseup', this.onPainterPointerUp);
|
|
485
|
+
|
|
486
|
+
// Discover an author-supplied toolbar / bubble menu (light-DOM
|
|
487
|
+
// <nile-wysiwyg-toolbar-item> / <nile-wysiwyg-bubble-item> children) and
|
|
488
|
+
// keep them in sync as they change.
|
|
489
|
+
this.syncAuthoredToolbar();
|
|
490
|
+
this.syncAuthoredBubble();
|
|
491
|
+
if (!this.toolbarObserver) {
|
|
492
|
+
this.toolbarObserver = new MutationObserver(() => {
|
|
493
|
+
this.syncAuthoredToolbar();
|
|
494
|
+
this.syncAuthoredBubble();
|
|
495
|
+
});
|
|
496
|
+
}
|
|
497
|
+
this.toolbarObserver.observe(this, {
|
|
498
|
+
childList: true,
|
|
499
|
+
subtree: true,
|
|
500
|
+
attributes: true,
|
|
501
|
+
attributeFilter: ['name', 'label', 'icon', 'divider', 'overflow'],
|
|
502
|
+
});
|
|
462
503
|
}
|
|
463
504
|
|
|
464
505
|
disconnectedCallback(): void {
|
|
465
506
|
super.disconnectedCallback();
|
|
466
507
|
document.removeEventListener('pointerdown', this.onOutsidePointerDown, true);
|
|
508
|
+
document.removeEventListener('mouseup', this.onPainterPointerUp);
|
|
509
|
+
this.toolbarObserver?.disconnect();
|
|
467
510
|
if (this.changeTimer) clearTimeout(this.changeTimer);
|
|
468
511
|
if (this.autosaveTimer) clearInterval(this.autosaveTimer);
|
|
469
512
|
if (this.previousBodyOverflow !== null) {
|
|
@@ -476,9 +519,57 @@ export class NileWysiwygEditor extends NileElement {
|
|
|
476
519
|
|
|
477
520
|
protected firstUpdated(): void {
|
|
478
521
|
this.defaultValue = this.value;
|
|
522
|
+
// Re-read now that light-DOM children are guaranteed parsed (the initial
|
|
523
|
+
// connectedCallback read can run mid-parse before children exist).
|
|
524
|
+
this.syncAuthoredToolbar();
|
|
525
|
+
this.syncAuthoredBubble();
|
|
479
526
|
this.mountView();
|
|
480
527
|
}
|
|
481
528
|
|
|
529
|
+
/**
|
|
530
|
+
* Reads `<nile-wysiwyg-toolbar-item>` children into a structured toolbar and
|
|
531
|
+
* stores it on {@link authoredToolbar}. Absent items clear the override
|
|
532
|
+
* (falling back to the `toolbar` string). No-ops when nothing changed to
|
|
533
|
+
* avoid needless re-renders.
|
|
534
|
+
*/
|
|
535
|
+
private syncAuthoredToolbar(): void {
|
|
536
|
+
const nodes = this.querySelectorAll('nile-wysiwyg-toolbar-item');
|
|
537
|
+
const next: AuthoredToolbarItem[] | undefined = nodes.length
|
|
538
|
+
? Array.from(nodes)
|
|
539
|
+
.map(el => ({
|
|
540
|
+
name: el.hasAttribute('divider') ? '|' : el.getAttribute('name') || '',
|
|
541
|
+
label: el.getAttribute('label') || undefined,
|
|
542
|
+
icon: el.getAttribute('icon') || undefined,
|
|
543
|
+
overflow: el.hasAttribute('overflow') || undefined,
|
|
544
|
+
}))
|
|
545
|
+
.filter(item => item.name)
|
|
546
|
+
: undefined;
|
|
547
|
+
if (JSON.stringify(next) !== JSON.stringify(this.authoredToolbar)) {
|
|
548
|
+
this.authoredToolbar = next;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
/**
|
|
553
|
+
* Reads `<nile-wysiwyg-bubble-item>` children into a structured bubble-menu
|
|
554
|
+
* item list stored on {@link authoredBubble}. Absent items clear the override
|
|
555
|
+
* (falling back to the built-in default). No-ops when nothing changed.
|
|
556
|
+
*/
|
|
557
|
+
private syncAuthoredBubble(): void {
|
|
558
|
+
const nodes = this.querySelectorAll('nile-wysiwyg-bubble-item');
|
|
559
|
+
const next: AuthoredToolbarItem[] | undefined = nodes.length
|
|
560
|
+
? Array.from(nodes)
|
|
561
|
+
.map(el => ({
|
|
562
|
+
name: el.hasAttribute('divider') ? '|' : el.getAttribute('name') || '',
|
|
563
|
+
label: el.getAttribute('label') || undefined,
|
|
564
|
+
icon: el.getAttribute('icon') || undefined,
|
|
565
|
+
}))
|
|
566
|
+
.filter(item => item.name)
|
|
567
|
+
: undefined;
|
|
568
|
+
if (JSON.stringify(next) !== JSON.stringify(this.authoredBubble)) {
|
|
569
|
+
this.authoredBubble = next;
|
|
570
|
+
}
|
|
571
|
+
}
|
|
572
|
+
|
|
482
573
|
protected updated(changed: PropertyValues): void {
|
|
483
574
|
if (changed.has('value') && this.view && !this.syncingValue) {
|
|
484
575
|
const current = docToHtml(this.view.state.doc);
|
|
@@ -654,23 +745,60 @@ export class NileWysiwygEditor extends NileElement {
|
|
|
654
745
|
new FigureView(node, view, getPos as () => number | undefined),
|
|
655
746
|
embed: (node, view, getPos) =>
|
|
656
747
|
new EmbedView(node, view, getPos as () => number | undefined),
|
|
748
|
+
styleBlock: node => new StyleBlockView(node),
|
|
657
749
|
math: node => new MathView(node, this.mathRenderer),
|
|
658
750
|
},
|
|
659
751
|
});
|
|
660
752
|
|
|
753
|
+
this.view.dom.addEventListener('mousedown', this.onPainterPointerDown);
|
|
754
|
+
|
|
661
755
|
this.toolbarState = this.snapshotToolbar(this.view.state);
|
|
662
756
|
this.counter = countWords(this.view.state.doc);
|
|
663
757
|
this.syncFormValue(this.view.state.doc);
|
|
664
758
|
}
|
|
665
759
|
|
|
760
|
+
/**
|
|
761
|
+
* True while a drag-selection is in progress with the format painter armed.
|
|
762
|
+
* Suppresses the mid-drag auto-apply in {@link handleTransaction} so the
|
|
763
|
+
* copied formatting lands on the final, complete selection (on mouse-up).
|
|
764
|
+
*/
|
|
765
|
+
private painterDragActive = false;
|
|
766
|
+
|
|
767
|
+
private onPainterPointerDown = (): void => {
|
|
768
|
+
if (this.view && isFormatPainterArmed(this.view.state)) {
|
|
769
|
+
this.painterDragActive = true;
|
|
770
|
+
}
|
|
771
|
+
};
|
|
772
|
+
|
|
773
|
+
private onPainterPointerUp = (): void => {
|
|
774
|
+
if (!this.painterDragActive) return;
|
|
775
|
+
this.painterDragActive = false;
|
|
776
|
+
// Defer a frame so ProseMirror has synced the final DOM selection.
|
|
777
|
+
requestAnimationFrame(() => {
|
|
778
|
+
if (!this.view) return;
|
|
779
|
+
if (this.view.state.selection.empty || !isFormatPainterArmed(this.view.state)) return;
|
|
780
|
+
const applyTr = applyFormatPainter(this.view.state);
|
|
781
|
+
if (applyTr) this.view.dispatch(applyTr);
|
|
782
|
+
});
|
|
783
|
+
};
|
|
784
|
+
|
|
666
785
|
private handleTransaction(tr: Transaction): void {
|
|
667
786
|
if (!this.view) return;
|
|
668
787
|
const newState = this.view.state.apply(tr);
|
|
669
788
|
this.view.updateState(newState);
|
|
670
789
|
|
|
671
790
|
// Format painter: once armed, the next non-empty selection the user makes
|
|
672
|
-
// receives the copied formatting, then the painter disarms.
|
|
673
|
-
|
|
791
|
+
// receives the copied formatting, then the painter disarms. While a pointer
|
|
792
|
+
// drag is building the selection we defer to the mouse-up handler
|
|
793
|
+
// (onPainterPointerUp) so the whole range is painted, not just the first
|
|
794
|
+
// mid-drag character. Single-shot selections (double-click, Shift+End,
|
|
795
|
+
// programmatic) still apply here.
|
|
796
|
+
if (
|
|
797
|
+
tr.selectionSet &&
|
|
798
|
+
!newState.selection.empty &&
|
|
799
|
+
isFormatPainterArmed(newState) &&
|
|
800
|
+
!this.painterDragActive
|
|
801
|
+
) {
|
|
674
802
|
const applyTr = applyFormatPainter(newState);
|
|
675
803
|
if (applyTr) {
|
|
676
804
|
this.view.dispatch(applyTr);
|
|
@@ -1389,7 +1517,7 @@ export class NileWysiwygEditor extends NileElement {
|
|
|
1389
1517
|
/** Toggles raw-HTML source editing. Content re-sanitizes on the way back. */
|
|
1390
1518
|
public toggleSourceView(): void {
|
|
1391
1519
|
if (this.sourceMode) {
|
|
1392
|
-
this.applySourceView(this.sourceViewEl?.
|
|
1520
|
+
this.applySourceView(this.sourceViewEl?.currentValue ?? null);
|
|
1393
1521
|
} else {
|
|
1394
1522
|
this.sourceMode = true;
|
|
1395
1523
|
this.updateComplete.then(() => {
|
|
@@ -1924,7 +2052,13 @@ export class NileWysiwygEditor extends NileElement {
|
|
|
1924
2052
|
};
|
|
1925
2053
|
|
|
1926
2054
|
public render(): TemplateResult {
|
|
1927
|
-
const
|
|
2055
|
+
const hasAuthoredToolbar = (this.authoredToolbar?.length ?? 0) > 0;
|
|
2056
|
+
// Keep the toolbar mounted in source mode too, so the main menu stays put
|
|
2057
|
+
// above the split source/preview view instead of being replaced by it. It
|
|
2058
|
+
// is disabled there — formatting commands would target the hidden rich-text
|
|
2059
|
+
// view — and the source view's own Cancel/Done bar handles exiting.
|
|
2060
|
+
const showToolbar =
|
|
2061
|
+
(this.toolbar !== 'none' || hasAuthoredToolbar) && !this.readonly;
|
|
1928
2062
|
return html`
|
|
1929
2063
|
<div
|
|
1930
2064
|
class="nile-wysiwyg ${this.showBlocks ? 'nile-wysiwyg--show-blocks' : ''}"
|
|
@@ -1937,9 +2071,10 @@ export class NileWysiwygEditor extends NileElement {
|
|
|
1937
2071
|
? html`
|
|
1938
2072
|
<nile-wysiwyg-toolbar
|
|
1939
2073
|
part="toolbar"
|
|
1940
|
-
exportparts="button, select, color"
|
|
2074
|
+
exportparts="button, select, color, overflow, overflow-panel"
|
|
1941
2075
|
config=${this.toolbar}
|
|
1942
|
-
|
|
2076
|
+
.items=${this.authoredToolbar}
|
|
2077
|
+
?disabled=${this.disabled || this.sourceMode}
|
|
1943
2078
|
.toolbarState=${this.toolbarState}
|
|
1944
2079
|
></nile-wysiwyg-toolbar>
|
|
1945
2080
|
`
|
|
@@ -1984,7 +2119,7 @@ export class NileWysiwygEditor extends NileElement {
|
|
|
1984
2119
|
this.closeShortcutHelp();
|
|
1985
2120
|
}}
|
|
1986
2121
|
></nile-wysiwyg-shortcut-help>
|
|
1987
|
-
<nile-wysiwyg-bubble-menu></nile-wysiwyg-bubble-menu>
|
|
2122
|
+
<nile-wysiwyg-bubble-menu .items=${this.authoredBubble}></nile-wysiwyg-bubble-menu>
|
|
1988
2123
|
<nile-wysiwyg-table-menu></nile-wysiwyg-table-menu>
|
|
1989
2124
|
<nile-wysiwyg-code-menu></nile-wysiwyg-code-menu>
|
|
1990
2125
|
<nile-wysiwyg-image-menu></nile-wysiwyg-image-menu>
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import { LitElement } from 'lit';
|
|
2
|
+
import { customElement, property } from 'lit/decorators.js';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Declarative, light-DOM marker for a single toolbar control.
|
|
6
|
+
*
|
|
7
|
+
* Author these inside a `<nile-wysiwyg-toolbar>` container within a
|
|
8
|
+
* `<nile-wysiwyg-editor>` to build the toolbar by hand instead of (or in
|
|
9
|
+
* addition to) the `toolbar` config string:
|
|
10
|
+
*
|
|
11
|
+
* ```html
|
|
12
|
+
* <nile-wysiwyg-editor>
|
|
13
|
+
* <nile-wysiwyg-toolbar>
|
|
14
|
+
* <nile-wysiwyg-toolbar-item name="bold" label="Bold"></nile-wysiwyg-toolbar-item>
|
|
15
|
+
* <nile-wysiwyg-toolbar-item name="italic"></nile-wysiwyg-toolbar-item>
|
|
16
|
+
* <nile-wysiwyg-toolbar-item divider></nile-wysiwyg-toolbar-item>
|
|
17
|
+
* <nile-wysiwyg-toolbar-item name="heading"></nile-wysiwyg-toolbar-item>
|
|
18
|
+
* <!-- goes into the 3-dot "more" menu, wherever it sits in the list -->
|
|
19
|
+
* <nile-wysiwyg-toolbar-item name="sourceView" overflow></nile-wysiwyg-toolbar-item>
|
|
20
|
+
* </nile-wysiwyg-toolbar>
|
|
21
|
+
* </nile-wysiwyg-editor>
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* The container is optional — items may also be direct children of the editor.
|
|
25
|
+
*
|
|
26
|
+
* `name` maps to a built-in control (the same keys the `toolbar` string
|
|
27
|
+
* accepts, e.g. `bold`, `italic`, `heading`, `textColor`, `table`). `label`
|
|
28
|
+
* and `icon` override that control's built-in tooltip/glyph; set `divider` to
|
|
29
|
+
* insert a group separator, or `overflow` to tuck this control into the
|
|
30
|
+
* toolbar's collapsible 3-dot "more" menu.
|
|
31
|
+
*
|
|
32
|
+
* The element renders nothing itself — the editor discovers it and renders the
|
|
33
|
+
* real control inside its own shadow DOM.
|
|
34
|
+
*
|
|
35
|
+
* @tag nile-wysiwyg-toolbar-item
|
|
36
|
+
*/
|
|
37
|
+
@customElement('nile-wysiwyg-toolbar-item')
|
|
38
|
+
export class NileWysiwygToolbarItem extends LitElement {
|
|
39
|
+
protected createRenderRoot(): this {
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
protected shouldUpdate(): boolean {
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/** Built-in control name — same keys accepted by the `toolbar` string. */
|
|
47
|
+
@property({ type: String, reflect: true }) name = '';
|
|
48
|
+
|
|
49
|
+
/** Overrides the control's tooltip / accessible label. */
|
|
50
|
+
@property({ type: String, reflect: true }) label = '';
|
|
51
|
+
|
|
52
|
+
/** Overrides the control's glyph (a `nile-glyph` name). */
|
|
53
|
+
@property({ type: String, reflect: true }) icon = '';
|
|
54
|
+
|
|
55
|
+
/** Renders a group divider instead of a control. */
|
|
56
|
+
@property({ type: Boolean, reflect: true }) divider = false;
|
|
57
|
+
|
|
58
|
+
/** When set, this control is tucked into the toolbar's collapsible 3-dot
|
|
59
|
+
* "more" menu instead of the main bar — regardless of where it sits among
|
|
60
|
+
* the items. */
|
|
61
|
+
@property({ type: Boolean, reflect: true }) overflow = false;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
declare global {
|
|
65
|
+
interface HTMLElementTagNameMap {
|
|
66
|
+
'nile-wysiwyg-toolbar-item': NileWysiwygToolbarItem;
|
|
67
|
+
}
|
|
68
|
+
}
|