@themarioga/grid-editor 8.4.0 → 8.6.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.
Files changed (105) hide show
  1. package/CHANGELOG.md +51 -0
  2. package/README.md +105 -5
  3. package/dist/grideditor.bundle.min.js +3 -3
  4. package/dist/grideditor.css +15 -4
  5. package/dist/grideditor.d.ts +76 -4
  6. package/dist/grideditor.esm.js +212 -38
  7. package/dist/grideditor.js +212 -38
  8. package/dist/grideditor.min.css +1 -1
  9. package/dist/grideditor.min.css.map +1 -1
  10. package/dist/grideditor.min.js +2 -2
  11. package/dist/grideditor.min.js.map +3 -3
  12. package/dist/locales/grideditor.es.esm.js +5 -0
  13. package/dist/locales/grideditor.es.js +5 -0
  14. package/dist/locales/grideditor.es.min.js +1 -1
  15. package/dist/locales/grideditor.es.min.js.map +2 -2
  16. package/dist/plugins/grideditor.accordion.esm.js +7 -0
  17. package/dist/plugins/grideditor.accordion.js +7 -0
  18. package/dist/plugins/grideditor.accordion.min.js +1 -1
  19. package/dist/plugins/grideditor.accordion.min.js.map +3 -3
  20. package/dist/plugins/grideditor.autosave.esm.js +309 -0
  21. package/dist/plugins/grideditor.autosave.js +316 -0
  22. package/dist/plugins/grideditor.autosave.min.js +1 -0
  23. package/dist/plugins/grideditor.autosave.min.js.map +7 -0
  24. package/dist/plugins/grideditor.card.esm.js +10 -0
  25. package/dist/plugins/grideditor.card.js +10 -0
  26. package/dist/plugins/grideditor.card.min.js +1 -1
  27. package/dist/plugins/grideditor.card.min.js.map +3 -3
  28. package/dist/plugins/grideditor.ckeditor.esm.js +30 -1
  29. package/dist/plugins/grideditor.ckeditor.js +30 -1
  30. package/dist/plugins/grideditor.ckeditor.min.js +1 -1
  31. package/dist/plugins/grideditor.ckeditor.min.js.map +3 -3
  32. package/dist/plugins/grideditor.clipboard.esm.js +3 -0
  33. package/dist/plugins/grideditor.clipboard.js +3 -0
  34. package/dist/plugins/grideditor.clipboard.min.js +1 -1
  35. package/dist/plugins/grideditor.clipboard.min.js.map +2 -2
  36. package/dist/plugins/grideditor.codemirror-inline.esm.js +11 -0
  37. package/dist/plugins/grideditor.codemirror-inline.js +11 -0
  38. package/dist/plugins/grideditor.codemirror-inline.min.js +1 -1
  39. package/dist/plugins/grideditor.codemirror-inline.min.js.map +3 -3
  40. package/dist/plugins/grideditor.elements.esm.js +78 -4
  41. package/dist/plugins/grideditor.elements.js +78 -4
  42. package/dist/plugins/grideditor.elements.min.js +1 -1
  43. package/dist/plugins/grideditor.elements.min.js.map +3 -3
  44. package/dist/plugins/grideditor.gutters.esm.js +5 -0
  45. package/dist/plugins/grideditor.gutters.js +5 -0
  46. package/dist/plugins/grideditor.gutters.min.js +1 -1
  47. package/dist/plugins/grideditor.gutters.min.js.map +3 -3
  48. package/dist/plugins/grideditor.inline-style.esm.js +18 -10
  49. package/dist/plugins/grideditor.inline-style.js +18 -10
  50. package/dist/plugins/grideditor.inline-style.min.js +3 -3
  51. package/dist/plugins/grideditor.inline-style.min.js.map +3 -3
  52. package/dist/plugins/grideditor.order.esm.js +5 -0
  53. package/dist/plugins/grideditor.order.js +5 -0
  54. package/dist/plugins/grideditor.order.min.js +1 -1
  55. package/dist/plugins/grideditor.order.min.js.map +3 -3
  56. package/dist/plugins/grideditor.popup.esm.js +17 -6
  57. package/dist/plugins/grideditor.popup.js +17 -6
  58. package/dist/plugins/grideditor.popup.min.js +1 -1
  59. package/dist/plugins/grideditor.popup.min.js.map +3 -3
  60. package/dist/plugins/grideditor.sections.esm.js +6 -3
  61. package/dist/plugins/grideditor.sections.js +6 -3
  62. package/dist/plugins/grideditor.sections.min.js +1 -1
  63. package/dist/plugins/grideditor.sections.min.js.map +3 -3
  64. package/dist/plugins/grideditor.summernote.esm.js +40 -1
  65. package/dist/plugins/grideditor.summernote.js +40 -1
  66. package/dist/plugins/grideditor.summernote.min.js +1 -1
  67. package/dist/plugins/grideditor.summernote.min.js.map +3 -3
  68. package/dist/plugins/grideditor.tabs.esm.js +7 -0
  69. package/dist/plugins/grideditor.tabs.js +7 -0
  70. package/dist/plugins/grideditor.tabs.min.js +1 -1
  71. package/dist/plugins/grideditor.tabs.min.js.map +3 -3
  72. package/dist/plugins/grideditor.tinymce.esm.js +32 -1
  73. package/dist/plugins/grideditor.tinymce.js +32 -1
  74. package/dist/plugins/grideditor.tinymce.min.js +1 -1
  75. package/dist/plugins/grideditor.tinymce.min.js.map +3 -3
  76. package/docs/events.md +8 -0
  77. package/docs/locale-keys.md +16 -1
  78. package/docs/plugins.md +60 -7
  79. package/example/autosave.html +39 -22
  80. package/example/index.html +1 -1
  81. package/example/plugins.html +10 -0
  82. package/package.json +1 -2
  83. package/src/js/grideditor.js +248 -28
  84. package/src/js/inline-style/display.js +4 -3
  85. package/src/js/inline-style/drawerflow.js +10 -5
  86. package/src/js/locales/grideditor.es.js +5 -0
  87. package/src/js/plugins/grideditor.accordion.js +6 -0
  88. package/src/js/plugins/grideditor.autosave.js +349 -0
  89. package/src/js/plugins/grideditor.card.js +6 -0
  90. package/src/js/plugins/grideditor.ckeditor.js +6 -0
  91. package/src/js/plugins/grideditor.clipboard.js +3 -0
  92. package/src/js/plugins/grideditor.codemirror-inline.js +7 -0
  93. package/src/js/plugins/grideditor.elements.js +98 -4
  94. package/src/js/plugins/grideditor.gutters.js +4 -0
  95. package/src/js/plugins/grideditor.inline-style.js +2 -0
  96. package/src/js/plugins/grideditor.order.js +4 -0
  97. package/src/js/plugins/grideditor.popup.js +15 -6
  98. package/src/js/plugins/grideditor.sections.js +4 -3
  99. package/src/js/plugins/grideditor.summernote.js +14 -0
  100. package/src/js/plugins/grideditor.tabs.js +6 -0
  101. package/src/js/plugins/grideditor.tinymce.js +8 -0
  102. package/src/js/text/grideditor.text.js +27 -1
  103. package/src/less/grideditor.less +16 -8
  104. package/types/grideditor.d.ts +76 -4
  105. package/AUTO_SAVE.md +0 -30
package/CHANGELOG.md CHANGED
@@ -5,6 +5,57 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
5
5
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
7
 
8
+ ## [8.6.0] - 2026-10-02
9
+
10
+ ### Added
11
+ - The autosave plugin, `grideditor.autosave.js`: what is edited is kept in
12
+ `localStorage` or `sessionStorage` a second after each change and when the
13
+ page is left, without closing the text being typed into. An editor that
14
+ starts from the html a draft was made from asks, in its confirm modal,
15
+ whether to restore it. Options `enabled`, `storage`, `key`, `delay` and
16
+ `maxAge`; methods `enableAutosave`, `disableAutosave`, `saveDraft`,
17
+ `getDraft` and `clearDraft`; events `after-autosave`, `after-restore-draft`
18
+ and `autosave-error`.
19
+ - `getHtml({ keepEditing: true })` and `getPlainHtml({ keepEditing: true })`:
20
+ the same markup, read on a copy of the canvas, so the canvas never leaves
21
+ editing - open texts, settings panels and the focus stay where they are.
22
+ `custom_filter` is run on the copy too.
23
+ - For plugins: `cleanMarkup(root, liveOf)`, what `unmark`, `onDeinit` and
24
+ `onBeforeDeinit` take off, taken off the copy - a plugin that has one of
25
+ those and no `cleanMarkup` makes `keepEditing` fall back on `getHtml`, with
26
+ a warning; `onDestroy`; and on the handle `ge.snapshotHtml()`,
27
+ `ge.setHtml(html)` and `ge.confirm(message, options, answer)`. A text editor
28
+ of `GridEditor.texts` has `read(contentArea)` and `cleanCopy(copy, area)`.
29
+ Every shipped plugin and text editor has them.
30
+ - A feature plugin's `methods` are the instance's, whatever their names: up
31
+ to now only the editor's own documented ones were.
32
+
33
+ ### Changed
34
+ - `example/autosave.html` is built on the plugin, with an on/off switch and
35
+ a key per imported file; `AUTO_SAVE.md` is gone, its content in the README.
36
+ - The toolbar's add buttons on the left - rows, containers, the feature
37
+ plugins' and the overflow menu's button - are grey (`btn-light`), and the
38
+ `toolbar_groups` tabs `btn-outline-secondary`. Blue is left to the buttons
39
+ on the right: source, preview, layout and the `align: 'end'` ones.
40
+
41
+ ## [8.5.0] - 2026-10-01
42
+
43
+ ### Added
44
+ - `elements.types`: the elements the toolbar offers, a button each. A type
45
+ has its `type`, its `html` - a string, or a function called for each one
46
+ made - a `label` or `labelKey`, an `iconClass` and a `group`. A button
47
+ makes the html's root, marked with `data-ge-element` and `data-ge-label`
48
+ the way a host marks its own elements, or a `div` around html with no
49
+ single root. Declaring a type turns `enabled: 'auto'` on.
50
+ - `toolbar_groups`: a third core category, *Elements*, after *Content*. The
51
+ element buttons go there unless they name another `group`.
52
+ - A feature plugin's `toolbar` item whose `create()` returns `null` adds
53
+ nothing, rather than failing.
54
+
55
+ ### Changed
56
+ - `toolbar_groups`: the *Text* buttons are in *Elements* now, not in
57
+ *Content*, which keeps the containers and the sections.
58
+
8
59
  ## [8.4.0] - 2026-10-01
9
60
 
10
61
  ### Added
package/README.md CHANGED
@@ -36,7 +36,7 @@ from any web server, or from GitHub Pages, with no build step.
36
36
  | [example/summernote.html](example/summernote.html) | Summernote instead of tinyMCE | [live](https://themarioga.github.io/grid-editor/example/summernote.html) |
37
37
  | [example/clipboard.html](example/clipboard.html) | Copy and paste, within an editor, between two, and between tabs | [live](https://themarioga.github.io/grid-editor/example/clipboard.html) |
38
38
  | [example/wrap_content.html](example/wrap_content.html) | Non-bootstrap markup wrapped into the grid | [live](https://themarioga.github.io/grid-editor/example/wrap_content.html) |
39
- | [example/autosave.html](example/autosave.html) | Saving the html as the user edits | [live](https://themarioga.github.io/grid-editor/example/autosave.html) |
39
+ | [example/autosave.html](example/autosave.html) | The autosave plugin: drafts kept as the user edits, offered back after a reload; import and export | [live](https://themarioga.github.io/grid-editor/example/autosave.html) |
40
40
  | [example/adapter.html](example/adapter.html) | A page written for 6.x, with jQuery, running unchanged through the adapter | [live](https://themarioga.github.io/grid-editor/example/adapter.html) |
41
41
 
42
42
  [example/angular](example/angular) is the editor in an Angular app, from the npm
@@ -142,6 +142,14 @@ which parts are elements, tabs, accordions or popups. That marking is how the
142
142
  editor reads its markup back, so **save `getHtml`** for anything that will be
143
143
  edited again.
144
144
 
145
+ `getHtml` takes the canvas out of editing to read it and puts it back, which
146
+ closes a text being typed into and a settings panel that is open.
147
+ `getHtml({ keepEditing: true })` gives the same markup without the canvas
148
+ leaving editing - it is read on a copy - which is what something that reads
149
+ it as the user works, an autosave, wants. A plugin that cannot be read on a
150
+ copy (see [docs/plugins.md](docs/plugins.md#reading-on-a-copy)) makes it fall
151
+ back on `getHtml`, with a warning in the console.
152
+
145
153
  `getPlainHtml` is the same markup with that marking taken off, and each
146
154
  `div.ge-content` replaced by what it holds. Bootstrap's own classes and
147
155
  attributes stay, so tabs, accordions and popups still work in a page that
@@ -166,8 +174,8 @@ has, with the options it was made with, and warns once.
166
174
 
167
175
  | Method | Arguments | Returns | What it does |
168
176
  | --- | --- | --- | --- |
169
- | `getHtml` | — | `String` | The clean html: no drawers, no editor classes, no inline styles |
170
- | `getPlainHtml` | — | `String` | `getHtml` without grid-editor's marking (`ge-*`, `column`, `data-ge-*`), for publishing. It cannot be edited again as it was |
177
+ | `getHtml` | `options?` | `String` | The clean html: no drawers, no editor classes, no inline styles. `{ keepEditing: true }` reads it without the canvas leaving editing |
178
+ | `getPlainHtml` | `options?` | `String` | `getHtml` without grid-editor's marking (`ge-*`, `column`, `data-ge-*`), for publishing. It cannot be edited again as it was. Takes the same `options` |
171
179
  | `init` | — | `this` | Run the editing pass over the canvas again. Safe to call after you inject markup |
172
180
  | `deinit` | — | `this` | Strip the editing furniture, leave the markup |
173
181
  | `reset` | — | `this` | `deinit()` then `init()` |
@@ -379,7 +387,7 @@ new GridEditor('#myGrid', {
379
387
  });
380
388
  ```
381
389
 
382
- __`toolbar_groups`:__ Whether the toolbar's add buttons are sorted into categories, with tabs at the start of the toolbar choosing which one shows. Default `false`. With `true` the core's categories are *Rows*, the add row buttons, and *Content*, the texts, the containers and the sections; a plugin's buttons go in the category they name with `group` (see [docs/plugins.md](docs/plugins.md)), or in one of their own. The tab chosen is kept while the editor lives, through `setLocale` too. With a single category there is nothing to choose and no tabs. The buttons on the right - the view, source and preview, paste - are never grouped. The more button of `toolbar_overflow` holds what doesn't fit of the category shown; the tabs themselves never go behind it, so with a great many categories they can run out of room.
390
+ __`toolbar_groups`:__ Whether the toolbar's add buttons are sorted into categories, with tabs at the start of the toolbar choosing which one shows. Default `false`. With `true` the core's categories are *Rows*, the add row buttons, *Content*, the containers and the sections, and *Elements*, the texts and the elements of `elements.types`; a plugin's buttons go in the category they name with `group` (see [docs/plugins.md](docs/plugins.md)), or in one of their own. The tab chosen is kept while the editor lives, through `setLocale` too. With a single category there is nothing to choose and no tabs. The buttons on the right - the view, source and preview, paste - are never grouped. The more button of `toolbar_overflow` holds what doesn't fit of the category shown; the tabs themselves never go behind it, so with a great many categories they can run out of room.
383
391
 
384
392
  ```javascript
385
393
  new GridEditor('#myGrid', {
@@ -397,7 +405,7 @@ ge.canvas.addEventListener('grideditor:target-change', function(e) {
397
405
  ```
398
406
 
399
407
  __`custom_filter`:__ Allows the execution of a custom function before initialization and after de-initialization. Accepts a functions or a function name as string.
400
- Gives the `canvas` element and `isInit` (true/false) as parameter.
408
+ Gives the `canvas` element and `isInit` (true/false) as parameter. `getHtml({ keepEditing: true })` runs it too, with `isInit` false, on a copy of the canvas rather than the canvas: work on the element it is given, not on one you kept.
401
409
 
402
410
  ```javascript
403
411
  new GridEditor('#myGrid', {
@@ -592,6 +600,32 @@ new GridEditor('#myGrid', {
592
600
  enabled: 'auto', // on when the page has any; true or false to decide yourself
593
601
  selector: '[data-ge-element]', // what counts as an element
594
602
  auto: false, // true treats every loose node of a column as one
603
+ types: [], // the elements the toolbar offers, see below
604
+ },
605
+ });
606
+ ```
607
+
608
+ __`elements.types`:__ The elements the toolbar offers, a button each, in this order. A click makes one in a row and column of its own at the end of the canvas - or at the end of the active target, with `active_target` - and a drag drops it in the column it lands in, as a text or a container. Declaring a type turns `enabled: 'auto'` on, so the buttons are there on a page with no elements yet; `enabled: false` takes them away too.
609
+
610
+ | Field | | |
611
+ | --- | --- | --- |
612
+ | `type` | required | what it is: its `data-ge-element` |
613
+ | `html` | required | its markup: html, or a function called for each one made, which returns html or a node |
614
+ | `label` / `labelKey` | | the button's label, as it is or as a locale key, which wins and follows `setLocale`. Without either, the `type` |
615
+ | `iconClass` | | the button shows the icon alone, with the label as its title |
616
+ | `group` | | its `toolbar_groups` category. Default `elements` |
617
+
618
+ What a button makes is the html's root, marked the way you mark your own elements - `data-ge-element`, and `data-ge-label` with the label unless the html has one - so `getHtml` gives it back as you would have written it. Html with more than one root, or none, is wrapped in a `div`. A `<script>` in html given as a string does not run. A type with no `type` or no `html` is left off the toolbar, and one whose function throws or gives nothing adds nothing; both with a warning in the console, as is a type whose markup does not match your `elements.selector`.
619
+
620
+ ```javascript
621
+ new GridEditor('#myGrid', {
622
+ elements: {
623
+ types: [
624
+ { type: 'quote', label: 'Pull quote', iconClass: 'bi bi-quote',
625
+ html: '<blockquote class="my-quote"><p>Quote</p></blockquote>' },
626
+ { type: 'figure', labelKey: 'element.figure', group: 'media',
627
+ html: function() { return '<figure id="fig-' + Date.now() + '"><img src="…"></figure>'; } },
628
+ ],
595
629
  },
596
630
  });
597
631
  ```
@@ -791,6 +825,72 @@ A paste is an add like any other: `before-add-*` and `after-add-*` with
791
825
  `source: 'paste'`, and canceling the first turns it away. A copy fires
792
826
  `grideditor:after-copy`.
793
827
 
828
+ ### Autosave
829
+
830
+ A plugin:
831
+
832
+ ```html
833
+ <script src="grid-editor/dist/plugins/grideditor.autosave.min.js"></script>
834
+ ```
835
+
836
+ What is being edited is kept in the browser's storage as it changes: a second
837
+ after the last change, and when the page is left or reloaded. It is read with
838
+ `getHtml({ keepEditing: true })`, so a text being typed into stays open, with
839
+ its cursor. When an editor starts and a draft of the same html is saved, the
840
+ editor's confirm modal asks whether to restore it; *Discard*, or closing the
841
+ modal, takes the draft away.
842
+
843
+ ```javascript
844
+ new GridEditor('#myGrid', {
845
+ autosave: {
846
+ enabled: true, // saving from the start; the methods turn it on and off
847
+ storage: 'local', // or 'session'
848
+ key: null, // where the draft is kept: one per document, see below
849
+ delay: 1000, // milliseconds after the last change
850
+ maxAge: null, // milliseconds a draft is offered for; null for ever
851
+ },
852
+ });
853
+ ```
854
+
855
+ A draft remembers the html its editor started from, and is only offered to an
856
+ editor that starts from the same html. A page that loads one document after
857
+ another into the editor is not offered one document's draft over another, but
858
+ with one key, editing the second overwrites the first's draft. Give each
859
+ document a key of its own - its file name, its id - and each keeps its draft:
860
+
861
+ ```javascript
862
+ new GridEditor('#myGrid', { autosave: { key: 'page-' + pageId } });
863
+ ```
864
+
865
+ The default key is `grideditor.autosave:` with the page's path and the canvas's
866
+ id. Two editors on a page with the same key would overwrite each other's
867
+ drafts, so the second saves nothing and says so in the console.
868
+
869
+ | Method | Returns | What it does |
870
+ | --- | --- | --- |
871
+ | `enableAutosave()` | `Boolean` | Starts saving, from what there is now |
872
+ | `disableAutosave()` | `Boolean` | Stops saving; the draft is kept |
873
+ | `saveDraft()` | `Boolean` | Saves now, if the html changed, even when disabled; `true` when it wrote |
874
+ | `getDraft()` | `Object` | `{ html, savedAt }`, or `null` |
875
+ | `clearDraft()` | `Boolean` | Takes the draft away, as a page does once it has saved to its server |
876
+
877
+ The methods are there only when the plugin is loaded, and answer `false` or
878
+ `null` when the browser gives the page no storage - a private window may not -
879
+ in which case nothing is saved and the console says so.
880
+
881
+ | Event | Payload | Fires |
882
+ | --- | --- | --- |
883
+ | `grideditor:after-autosave` | `canvas`, `html`, `savedAt`, `source` (`change`, `pagehide`, `api` or `destroy`) | after a draft is written |
884
+ | `grideditor:after-restore-draft` | `canvas`, `html`, `savedAt` | after the user chose to restore one |
885
+ | `grideditor:autosave-error` | `canvas`, `error` | when the storage refused a draft - it is full; the next change tries again |
886
+
887
+ A draft in `localStorage` outlives the browser being closed, on a computer
888
+ someone else may use next: `storage: 'session'` keeps it for the tab only. A
889
+ draft is offered only to an editor that starts from the same html *as this
890
+ version of grid-editor reads it*, so one saved before an upgrade that changes
891
+ what `getHtml` gives is not offered after it. Drafts are a safety net, not a
892
+ place to keep work.
893
+
794
894
  ### Containers
795
895
 
796
896
  Tabs, accordions, popups and cards. A tabs, accordion or popup container holds