@themarioga/grid-editor 3.2.1 → 4.1.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 (65) hide show
  1. package/BUILDING.md +12 -3
  2. package/CHANGELOG.md +87 -0
  3. package/README.md +79 -7
  4. package/UPGRADING.md +79 -0
  5. package/dist/grideditor.css +188 -20
  6. package/dist/grideditor.min.css +1 -1
  7. package/dist/grideditor.min.css.map +1 -1
  8. package/dist/jquery.grideditor.bundle.min.js +10 -0
  9. package/dist/jquery.grideditor.js +924 -174
  10. package/dist/jquery.grideditor.min.js +1 -1
  11. package/dist/jquery.grideditor.min.js.map +1 -1
  12. package/dist/locales/grideditor.es.js +34 -0
  13. package/dist/locales/grideditor.es.min.js +1 -1
  14. package/dist/locales/grideditor.es.min.js.map +1 -1
  15. package/dist/plugins/grideditor.accordion.js +8 -0
  16. package/dist/plugins/grideditor.accordion.min.js +1 -1
  17. package/dist/plugins/grideditor.accordion.min.js.map +1 -1
  18. package/dist/plugins/grideditor.alignment.js +81 -0
  19. package/dist/plugins/grideditor.alignment.min.js +2 -0
  20. package/dist/plugins/grideditor.alignment.min.js.map +1 -0
  21. package/dist/plugins/grideditor.elements.js +5 -5
  22. package/dist/plugins/grideditor.elements.min.js +1 -1
  23. package/dist/plugins/grideditor.elements.min.js.map +1 -1
  24. package/dist/plugins/grideditor.gutters.js +145 -0
  25. package/dist/plugins/grideditor.gutters.min.js +2 -0
  26. package/dist/plugins/grideditor.gutters.min.js.map +1 -0
  27. package/dist/plugins/grideditor.order.js +206 -0
  28. package/dist/plugins/grideditor.order.min.js +2 -0
  29. package/dist/plugins/grideditor.order.min.js.map +1 -0
  30. package/dist/plugins/grideditor.spacing.js +299 -0
  31. package/dist/plugins/grideditor.spacing.min.js +2 -0
  32. package/dist/plugins/grideditor.spacing.min.js.map +1 -0
  33. package/dist/plugins/grideditor.tabs.js +8 -0
  34. package/dist/plugins/grideditor.tabs.min.js +1 -1
  35. package/dist/plugins/grideditor.tabs.min.js.map +1 -1
  36. package/dist/plugins/grideditor.visibility.js +172 -0
  37. package/dist/plugins/grideditor.visibility.min.js +2 -0
  38. package/dist/plugins/grideditor.visibility.min.js.map +1 -0
  39. package/docs/events.md +26 -8
  40. package/docs/locale-keys.md +54 -0
  41. package/docs/plugins.md +129 -10
  42. package/example/autosave.html +6 -1
  43. package/example/basic.html +8 -2
  44. package/example/breakpoints.html +6 -1
  45. package/example/ckeditor.html +6 -1
  46. package/example/containers.html +5 -1
  47. package/example/elements.html +6 -1
  48. package/example/index.html +24 -1
  49. package/example/locale.html +6 -1
  50. package/example/plugins.html +6 -1
  51. package/example/summernote.html +6 -1
  52. package/example/utilities.html +145 -0
  53. package/example/wrap_content.html +6 -1
  54. package/package.json +3 -2
  55. package/src/js/jquery.grideditor.js +924 -174
  56. package/src/js/locales/grideditor.es.js +34 -0
  57. package/src/js/plugins/grideditor.accordion.js +8 -0
  58. package/src/js/plugins/grideditor.alignment.js +81 -0
  59. package/src/js/plugins/grideditor.elements.js +5 -5
  60. package/src/js/plugins/grideditor.gutters.js +145 -0
  61. package/src/js/plugins/grideditor.order.js +206 -0
  62. package/src/js/plugins/grideditor.spacing.js +299 -0
  63. package/src/js/plugins/grideditor.tabs.js +8 -0
  64. package/src/js/plugins/grideditor.visibility.js +172 -0
  65. package/src/less/grideditor.less +210 -18
package/BUILDING.md CHANGED
@@ -19,8 +19,17 @@ and the files in `src/js/locales/` and `src/js/plugins/` one by one under
19
19
  `dist/locales/` and `dist/plugins/`. Neither of those two directories is part
20
20
  of the main bundle: the `src/js/*.js` glob does not descend, so a page loads
21
21
  the languages and the container plugins it actually wants. See
22
- [docs/plugins.md](docs/plugins.md) for what a container plugin is. Locale files are
23
- deliberately not part of the main bundle: a page loads only the languages it
22
+ [docs/plugins.md](docs/plugins.md) for what a container plugin is.
23
+
24
+ One more file comes out of it: `dist/jquery.grideditor.bundle.min.js`, the
25
+ minified editor concatenated with the copy of SortableJS in `node_modules`,
26
+ for pages that would rather load one file than two. It carries both MIT
27
+ notices, it is built after the minified editor because it is made of it, and a
28
+ page loads it *or* the editor and SortableJS separately, never both. The
29
+ version is pinned in `package.json`, so refreshing it is an `npm update
30
+ sortablejs` and a rebuild.
31
+
32
+ Locale files are deliberately not part of the main bundle: a page loads only the languages it
24
33
  offers. English is the exception and lives in the bundle, because it is the
25
34
  fallback every string lookup ends at. See `docs/locale-keys.md` for the keys,
26
35
  and `src/js/locales/grideditor.es.js` for what a locale file looks like.
@@ -44,7 +53,7 @@ suite, pass part of its name:
44
53
 
45
54
  A suite can also be run on its own with `node test/rte.js`.
46
55
 
47
- The fixture pages in `test/fixtures` load jQuery, jQuery UI and Bootstrap from
56
+ The fixture pages in `test/fixtures` load jQuery, SortableJS and Bootstrap from
48
57
  `test/vendor` rather than from a CDN, so most of the suite runs offline. The
49
58
  rich text editor suite is the exception: it loads tinyMCE, CKEditor and
50
59
  Summernote from their CDNs to test against the real editors, and the runner
package/CHANGELOG.md CHANGED
@@ -5,6 +5,93 @@ 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
+ ## [4.1.0] - 2026-09-22
9
+ ### Added
10
+ - Utility plugins, a third kind beside containers and features:
11
+ `$.fn.gridEditor.utilities`. A plugin declares families of Bootstrap's
12
+ responsive utility classes and the editor reads them through the cascade,
13
+ writes them per breakpoint (the all view writes the class with no infix and
14
+ clears the rest), shows them in a folded *Responsive* section of the settings
15
+ panel, and previews them in each breakpoint view. See docs/plugins.md.
16
+ - `getUtility` and `setUtility` methods, and the `utilities` setting for the
17
+ plugins' options.
18
+ - `before-utility`, `after-utility` and `view-change` events. `view-change` is
19
+ new for every host, not only for utility plugins.
20
+ - The plugin handle gains `kindOf`, `view`, `viewTiers`, `breakpoints`,
21
+ `getUtility` and `setUtility`, and a utility plugin may add tools beside any
22
+ drawer's gear and mark the canvas in `onRefresh`.
23
+ - The `visibility` utility plugin: hide a row, column, element or container at
24
+ some breakpoints with `d-{bp}-none` and show it at others. An eye in each
25
+ drawer toggles the view being edited with as few classes as that takes. A
26
+ hidden node stays on the canvas, faded, and in the all view a badge says at
27
+ which breakpoints it is hidden.
28
+ - The `order` utility plugin: a column's place in its row per breakpoint,
29
+ with `order-{bp}-*`. Two arrows in each column drawer move it one place
30
+ earlier or later in the view being edited without moving the markup, and
31
+ take the classes off again once the row is back in the markup's order. A
32
+ column ordered by class says so in a corner of the canvas.
33
+ - The `alignment` utility plugin: `justify-content-{bp}-*` and
34
+ `align-items-{bp}-*` on rows, `align-self-{bp}-*` on columns, as fields in
35
+ the settings panel.
36
+ - The `gutters` utility plugin: `g-{bp}-*`, `gx-{bp}-*` and `gy-{bp}-*` on
37
+ rows. A row with gutter classes is drawn with its real gutters instead of the
38
+ editor's frame, the preview settles `g` against `gx`/`gy` the way Bootstrap's
39
+ css does, and choosing `g` takes that breakpoint's `gx` and `gy` off.
40
+ `utilities.gutters.scale` follows a page that changed `$spacers`.
41
+ - The `spacing` utility plugin: padding and margin on rows, columns, elements
42
+ and containers, `{p,m}{,x,y,t,b,s,e}-{bp}-*`. The panel has a padding and a
43
+ margin group, each a side and a value, and the preview settles the fourteen
44
+ families the way Bootstrap's css does. `utilities.spacing.values` and
45
+ `.scale` narrow the steps and follow a changed `$spacers`.
46
+ - A utility plugin can build its own panel (`panel`, `ge.utilityField`,
47
+ families with `panel: false`) and preview a node as a whole (`preview`).
48
+ - `example/utilities.html`.
49
+
50
+ ## [4.0.0] - 2026-09-22
51
+ ### Added
52
+ - Editing from a touchscreen. Sorting, resizing a column and carrying a toolbar
53
+ button onto the canvas all work with a finger; a touch rests for
54
+ `drag.touch_delay` before it moves anything, so the page can still be
55
+ scrolled. `test/touch.js` holds every gesture to it, and none of them worked
56
+ in 3.x.
57
+ - `dist/jquery.grideditor.bundle.min.js`: the editor with SortableJS inside it,
58
+ for pages that would rather load one file than two. A page loads it or the
59
+ pair, never both.
60
+ - A `drag` setting: `delay`, `touch_delay`, `threshold`, `animation` and
61
+ `scroll`, named for the gesture rather than for the library underneath.
62
+ - A whole container can be dragged. Its move tool used to be a handle for a
63
+ list that did not accept containers, so dragging one did nothing.
64
+
65
+ ### Removed
66
+ - jQuery UI is no longer a dependency. A page loads jQuery, SortableJS and the
67
+ editor, which comes to 81.6 kB gzip against 132.8 kB before: 51 kB less, 39%
68
+ of the page.
69
+ - `sortable_options` and `resizable_options`. Both handed out the drag
70
+ library's own options; `drag` and `resize` replace them, and passing either
71
+ warns once naming the replacement.
72
+
73
+ ### Changed
74
+ - Resizing a column and dragging a toolbar button onto the canvas are the
75
+ editor's own pointer code, so jQuery UI is no longer used at all. A refused
76
+ `before-resize` now stops the gesture before it starts instead of refusing
77
+ every step of it, and the pointer is captured, so a fast drag that leaves the
78
+ column behind keeps resizing it. `.ui-resizable-*` become `.ge-resize-*` and
79
+ `.ge-resizing`; a palette button carries `.ge-palette-button`.
80
+ - Sorting is SortableJS instead of jQuery UI: rows, columns, blocks, tabs,
81
+ accordion items and elements all move through it, with touch support that
82
+ jQuery UI sortable never had. `.ui-sortable-helper` and
83
+ `.ui-sortable-placeholder` become `.ge-drag-helper` and
84
+ `.ge-drag-placeholder`.
85
+ - Every sortable list the editor makes, core's and a plugin's, goes through one
86
+ internal seam that names the drag toolkit in one place. Groundwork for 4.0,
87
+ which replaces jQuery UI with SortableJS.
88
+ - A plugin's `onSortable` hook receives that function instead of a jQuery UI
89
+ options object, and describes a list rather than making one. See
90
+ `UPGRADING.md`.
91
+ - Sortable groups are scoped to the editor instance, so two editors on one page
92
+ no longer drag into each other.
93
+
94
+
8
95
  ## [3.2.1] - 2026-09-22
9
96
  ### Changed
10
97
  - `example/containers.html` shows a card too: its own row, with two columns
package/README.md CHANGED
@@ -1,7 +1,7 @@
1
1
  Grid Editor
2
2
  ===========
3
3
 
4
- Grid Editor is a visual javascript editor for the [bootstrap 5 grid system](https://getbootstrap.com/docs/5.3/layout/grid/), written as a [jQuery](http://jquery.com/) plugin. You can create, drag, resize and delete rows and columns, indent them, and give each of bootstrap's six breakpoints its own layout — or edit them all at once. It also edits tabs, accordions, popups and cards, and any markup you mark as an element, and it tells your application about every change it makes.
4
+ Grid Editor is a visual javascript editor for the [bootstrap 5 grid system](https://getbootstrap.com/docs/5.3/layout/grid/), written as a [jQuery](http://jquery.com/) plugin. You can create, drag, resize and delete rows and columns, indent them, and give each of bootstrap's six breakpoints its own layout — or edit them all at once, with a mouse or with a finger. It also edits tabs, accordions, popups and cards, and any markup you mark as an element, and it tells your application about every change it makes.
5
5
 
6
6
  This is a fork of [Friendly-Pixel/grid-editor](https://github.com/Friendly-Pixel/grid-editor)
7
7
  by Simon Epskamp, carrying it on from 2.x. It is published as
@@ -30,6 +30,7 @@ from any web server, or from GitHub Pages, with no build step.
30
30
  | [example/plugins.html](example/plugins.html) | The plugin model, with one written in the page itself | [live](https://themarioga.github.io/grid-editor/example/plugins.html) |
31
31
  | [example/containers.html](example/containers.html) | Tabs, accordions, popups and cards, two levels deep | [live](https://themarioga.github.io/grid-editor/example/containers.html) |
32
32
  | [example/elements.html](example/elements.html) | Element level controls, including an element with no visual output | [live](https://themarioga.github.io/grid-editor/example/elements.html) |
33
+ | [example/utilities.html](example/utilities.html) | Bootstrap's responsive utilities, edited per breakpoint | [live](https://themarioga.github.io/grid-editor/example/utilities.html) |
33
34
  | [example/locale.html](example/locale.html) | The interface in Spanish, with a language switcher | [live](https://themarioga.github.io/grid-editor/example/locale.html) |
34
35
  | [example/ckeditor.html](example/ckeditor.html) | CKEditor instead of tinyMCE | [live](https://themarioga.github.io/grid-editor/example/ckeditor.html) |
35
36
  | [example/summernote.html](example/summernote.html) | Summernote instead of tinyMCE | [live](https://themarioga.github.io/grid-editor/example/summernote.html) |
@@ -39,7 +40,7 @@ from any web server, or from GitHub Pages, with no build step.
39
40
  Installation
40
41
  ------------
41
42
 
42
- * __Dependencies:__ Grid Editor depends on jQuery, jQuery UI, Bootstrap Icons, and Bootstrap 5, so make sure you have included those in the page.
43
+ * __Dependencies:__ Grid Editor depends on jQuery, [SortableJS](https://sortablejs.github.io/Sortable/), Bootstrap Icons, and Bootstrap 5, so make sure you have included those in the page.
43
44
  * If you want to use the tinyMCE integration, include tinyMCE 6 as well. The tinyMCE jQuery plugin is no longer needed, and no longer exists as of tinyMCE 6.
44
45
  * If you want to use the summernote integration, include summernote as well.
45
46
  * If you want to use the CKEditor integration... you get the point.
@@ -52,11 +53,20 @@ npm install @themarioga/grid-editor
52
53
  * Or [download the latest version of Grid Editor](https://github.com/themarioga/grid-editor/archive/master.zip) and include it in your page:
53
54
 
54
55
  ```html
55
- <!-- Make sure jQuery, jQuery UI, bootstrap icons, and bootstrap 5 are included. TinyMCE is optional. -->
56
+ <!-- Make sure jQuery, SortableJS, bootstrap icons, and bootstrap 5 are included. TinyMCE is optional. -->
56
57
  <link rel="stylesheet" type="text/css" href="grid-editor/dist/grideditor.min.css" />
58
+ <script src="https://cdn.jsdelivr.net/npm/sortablejs@1.15.6/Sortable.min.js"></script>
57
59
  <script src="grid-editor/dist/jquery.grideditor.min.js"></script>
58
60
  ```
59
61
 
62
+ Or, for a page that would rather load one file, the editor with SortableJS
63
+ inside it &mdash; one or the other, never both:
64
+
65
+ ```html
66
+ <link rel="stylesheet" type="text/css" href="grid-editor/dist/grideditor.min.css" />
67
+ <script src="grid-editor/dist/jquery.grideditor.bundle.min.js"></script>
68
+ ```
69
+
60
70
  Usage
61
71
  -----
62
72
  ```javascript
@@ -91,6 +101,8 @@ $('#myGrid').gridEditor('method', argument);
91
101
  | `createContainer` | `type`, `options?` | `jQuery` | `'tabs'`, `'accordion'` or `'popup'` |
92
102
  | `addTab` | `container`, `options?` | `jQuery` | Appends a tab, returns its pane |
93
103
  | `addAccordionItem` | `container`, `options?` | `jQuery` | Appends an item, returns its body |
104
+ | `getUtility` | `node`, `family`, `view?` | `String` | A utility plugin's value on a node in a view (the current one by default), or `null` |
105
+ | `setUtility` | `node`, `family`, `value`, `view?` | `Boolean` | Write it through the events; `null` is inherit. `false` if canceled or nothing changed |
94
106
 
95
107
  A method called on an element with no editor on it is a no-op that returns the
96
108
  set, so host code does not have to check first. `getHtml` is the exception: it
@@ -295,7 +307,19 @@ $('#myGrid').gridEditor({
295
307
 
296
308
  __`confirm_delete`:__ Whether to ask before deleting a row, column, element or container. Default `true`. The question is asked in a Bootstrap modal the editor builds outside your canvas, in the interface language; a page that loaded Bootstrap's css but not its javascript gets the browser's own confirm instead. Set it to `false` if you cancel `before-delete` and ask in your own way.
297
309
 
298
- __`sortable_options`:__ Merged into every jQuery UI sortable, for hosts that need `cancel`, `tolerance` or a custom `connectWith`.
310
+ __`drag`:__ How a drag behaves, wherever the editor drags something. Named for the gesture rather than for the library underneath, so it survives a change of library. Every gesture works from a touchscreen, which is what `touch_delay` is for: a touch drag that started instantly would take the page's scrolling with it, so a finger has to rest for a moment before it moves anything. Setting `delay` makes both gestures wait that long.
311
+
312
+ ```javascript
313
+ $('#myGrid').gridEditor({
314
+ drag: {
315
+ delay: 0, // ms to hold before a drag starts
316
+ touch_delay: 100, // the same for touch, where 0 eats the page's scrolling
317
+ threshold: 3, // px of movement before a gesture counts as a drag
318
+ animation: 150, // ms of reordering animation, 0 for none
319
+ scroll: true, // scroll the page when a drag reaches its edge
320
+ },
321
+ });
322
+ ```
299
323
 
300
324
  ### Breakpoints and sizing
301
325
 
@@ -315,13 +339,12 @@ __`resize`:__ Resizing a column by dragging its edge. Defaults:
315
339
  $('#myGrid').gridEditor({
316
340
  resize: {
317
341
  enabled: true,
318
- handles: 'e', // as jQuery UI names them; 'w' for a right to left page
342
+ handles: 'e', // 'w' for a right to left page, 'e, w' for both
319
343
  balance: 'next', // the following column absorbs the change; false lets the row wrap
320
344
  },
321
345
  });
322
346
  ```
323
347
 
324
- __`resizable_options`:__ Merged into every jQuery UI resizable.
325
348
 
326
349
  ### Elements
327
350
 
@@ -358,6 +381,55 @@ __`element_tools`:__ Extra tools on every element drawer, same shape as `row_too
358
381
  See [example/elements.html](example/elements.html), which also shows the
359
382
  pattern for an element with no visual output of its own.
360
383
 
384
+ ### Responsive utilities
385
+
386
+ Bootstrap's responsive utility classes, edited per breakpoint. Each family is
387
+ a plugin in a file of its own:
388
+
389
+ ```html
390
+ <script src="grid-editor/dist/plugins/grideditor.visibility.min.js"></script>
391
+ <script src="grid-editor/dist/plugins/grideditor.order.min.js"></script>
392
+ <script src="grid-editor/dist/plugins/grideditor.alignment.min.js"></script>
393
+ <script src="grid-editor/dist/plugins/grideditor.gutters.min.js"></script>
394
+ <script src="grid-editor/dist/plugins/grideditor.spacing.min.js"></script>
395
+ ```
396
+
397
+ | Plugin | Classes | On |
398
+ | --- | --- | --- |
399
+ | `visibility` | `d-{bp}-none`, `d-{bp}-block`, `d-{bp}-flex` | rows, columns, elements, containers |
400
+ | `order` | `order-{bp}-{first,0–5,last}` | columns |
401
+ | `alignment` | `justify-content-{bp}-*`, `align-items-{bp}-*`; `align-self-{bp}-*` | rows; columns |
402
+ | `gutters` | `g-{bp}-{0–5}`, `gx-{bp}-*`, `gy-{bp}-*` | rows |
403
+ | `spacing` | `{p,m}{,x,y,t,b,s,e}-{bp}-{0–5}`, and `auto` for margin | rows, columns, elements, containers |
404
+
405
+ A plugin puts a field in the *Responsive* section of each drawer's settings
406
+ panel, and some add a tool to the drawer. In a breakpoint view a change is
407
+ written for that breakpoint alone, and the field says what it inherits and from
408
+ where. In the all view it is written once, as the class with no breakpoint, and
409
+ replaces what the breakpoints said. While you edit, the canvas shows what the
410
+ classes mean in the view you are in; `getHtml` returns only the classes.
411
+
412
+ ```javascript
413
+ $('#myGrid').gridEditor('setUtility', column, 'visibility', 'none', 'md'); // d-md-none
414
+ $('#myGrid').gridEditor('getUtility', column, 'visibility', 'lg'); // 'none', inherited
415
+ ```
416
+
417
+ __`utilities`:__ Options for each plugin, under its name.
418
+
419
+ ```javascript
420
+ $('#myGrid').gridEditor({
421
+ utilities: {
422
+ visibility: { drawer: false }, // no eye in the drawers, the field only
423
+ order: { drawer: false }, // no arrows in the column drawers
424
+ gutters: { scale: ['0', '.25rem', '.5rem', '1rem', '1.5rem', '3rem'] }, // if you changed $spacers
425
+ spacing: { values: ['0', '2', '4'], scale: [/* the same */] }, // offer fewer steps
426
+ },
427
+ });
428
+ ```
429
+
430
+ See [example/utilities.html](example/utilities.html), and
431
+ [docs/plugins.md](docs/plugins.md#utility-plugins) for writing one.
432
+
361
433
  ### Containers
362
434
 
363
435
  Tabs, accordions, popups and cards. A tabs, accordion or popup container holds
@@ -379,7 +451,7 @@ it available:
379
451
  The toolbar offers a button per loaded plugin. See [docs/plugins.md](docs/plugins.md)
380
452
  for the contract, and for writing one of your own.
381
453
 
382
- __`plugins`:__ Which of the loaded plugins to use, containers and features alike. Every one by default; name them to use fewer than the page loaded.
454
+ __`plugins`:__ Which of the loaded plugins to use, containers, features and utilities alike. Every one by default; name them to use fewer than the page loaded.
383
455
 
384
456
  ```javascript
385
457
  $('#myGrid').gridEditor({ plugins: ['tabs', 'elements'] });
package/UPGRADING.md CHANGED
@@ -1,3 +1,82 @@
1
+ Upgrading from grid-editor `3.*` to `4.*`
2
+ =========================================
3
+
4
+ One change, and it is worth the major: jQuery UI is gone. The editor is 51 kB
5
+ lighter on the page and works from a touchscreen, which it never did. Four
6
+ things to do:
7
+
8
+ * __A plugin's `onSortable` takes a function, not an options object.__ It used
9
+ to receive the editor's shared jQuery UI options and call `.sortable()`
10
+ itself; it now receives `sortable(lists, { draggable, group })` and describes
11
+ the list instead. See [docs/plugins.md](docs/plugins.md). Nothing else in the
12
+ plugin handle changed.
13
+
14
+ ```javascript
15
+ // 3.x
16
+ onSortable: function(shared) {
17
+ ge.canvas.find('.ge-content').sortable($.extend({
18
+ items: '> .ge-element',
19
+ connectWith: '.ge-canvas .ge-content',
20
+ }, shared, ge.settings.sortable_options));
21
+ }
22
+
23
+ // 4.x
24
+ onSortable: function(sortable) {
25
+ sortable(ge.canvas.find('.ge-content'), {
26
+ draggable: '> .ge-element',
27
+ group: 'element',
28
+ });
29
+ }
30
+ ```
31
+
32
+ * __`sortable_options` and `resizable_options` are gone.__ They existed to hand
33
+ you the drag library's own options, and 4.0 stops promising there is one: the
34
+ group names, the handle, the filter and the callbacks are the editor's, and
35
+ overwriting them breaks the canvas rather than tuning it. What they were used
36
+ for is now `drag` — `delay`, `touch_delay`, `threshold`, `animation`,
37
+ `scroll` — and the `resize` block you already have. Passing either warns once
38
+ and names the replacement.
39
+
40
+ * __jQuery UI is not a dependency.__ Drop its `<script>` and add SortableJS;
41
+ resizing a column and carrying a toolbar button onto the canvas are the
42
+ editor's own pointer code now. A page that would rather load one file can
43
+ load `dist/jquery.grideditor.bundle.min.js`, the editor with SortableJS
44
+ inside it, instead of the two.
45
+
46
+ ```html
47
+ <!-- 3.x -->
48
+ <script src="jquery-ui.min.js"></script>
49
+ <script src="dist/jquery.grideditor.min.js"></script>
50
+
51
+ <!-- 4.x -->
52
+ <script src="Sortable.min.js"></script>
53
+ <script src="dist/jquery.grideditor.min.js"></script>
54
+ ```
55
+
56
+ Classes you may have styled:
57
+
58
+ | 3.x | 4.0 |
59
+ | --- | --- |
60
+ | `.ui-sortable-helper` | `.ge-drag-helper` |
61
+ | `.ui-sortable-placeholder` | `.ge-drag-placeholder` |
62
+ | `.ui-resizable-handle` | `.ge-resize-handle` |
63
+ | `.ui-resizable-e`, `.ui-resizable-w` | `.ge-resize-e`, `.ge-resize-w` |
64
+ | `.ui-resizable-resizing` | `.ge-resizing` |
65
+ | `.ui-draggable` on a toolbar button | `.ge-palette-button` |
66
+
67
+ * __A whole container can be dragged now.__ Its move tool was a handle for a
68
+ list that did not accept containers, so in 3.x dragging one did nothing. It
69
+ moves like a row does, and fires the same `before-move`/`after-move` with the
70
+ container's kind.
71
+
72
+ * __Two editors on one page no longer drag into each other.__ Lists were
73
+ connected by selector — `.ge-canvas .row` matches every canvas on the page —
74
+ so a column could be dragged from one editor into another, carrying the first
75
+ editor's furniture and firing its events in the wrong place. Lists are now
76
+ connected per instance. If you were relying on that, you were relying on a
77
+ bug.
78
+
79
+
1
80
  Upgrading from grid-editor `2.*` to `3.*`
2
81
  =========================================
3
82
 
@@ -76,7 +76,21 @@
76
76
  background: #2FA4E7;
77
77
  }
78
78
  /* The button following the pointer must not be what the pointer is over */
79
+ .ge-drag-helper {
80
+ opacity: 0.85;
81
+ pointer-events: none;
82
+ }
83
+ .ge-drag-helper .ge-tools-drawer {
84
+ pointer-events: none;
85
+ }
86
+ /* A toolbar button that can be carried onto the canvas */
87
+ .ge-palette-button {
88
+ cursor: move;
89
+ touch-action: none;
90
+ }
79
91
  .ge-toolbar-helper {
92
+ position: absolute;
93
+ z-index: 1000;
80
94
  pointer-events: none;
81
95
  opacity: 0.85;
82
96
  }
@@ -114,21 +128,21 @@
114
128
  /* A trigger pointing at a popup that is not there any more */
115
129
  /* Elements: the level below a column, inside a content area. Dashed
116
130
  rather than solid, so a page full of them still reads as content */
117
- /* Sorting */
131
+ /* Sorting. The dragged node stays in the list and shows where it would
132
+ land - that is the placeholder - while a copy of it follows the
133
+ pointer. */
118
134
  /* A size change should read as a movement rather than a jump, but not
119
135
  while the pointer is dragging the edge: there the column follows the
120
136
  pointer and a transition only lags behind it */
121
- /* Resizing. jQuery UI's own stylesheet is not a dependency, so the handle
122
- is styled here: a grip on the column's edge, clear of the drawer, which
123
- is what keeps the resize gesture and the sort gesture apart */
137
+ /* Resizing: a grip on the column's edge, clear of the drawer, which is
138
+ what keeps the resize gesture and the sort gesture apart */
124
139
  /* The units the column would land on, shown in its drawer while dragging */
125
140
  }
126
141
  .ge-canvas.ge-editing .ge-tools-drawer {
127
142
  /* Positioned, so a tool can hang a picker off itself. Deliberately
128
- not stacked above anything the rest of the time: jQuery UI's resize
129
- handle carries an inline z-index of 90 and runs down the column's
130
- edge, and a drawer sitting over it would take the clicks meant for
131
- the handle. */
143
+ not stacked above anything the rest of the time: the resize handle
144
+ runs down the column's edge, and a drawer sitting over it would take
145
+ the clicks meant for the handle. */
132
146
  position: relative;
133
147
  -webkit-touch-callout: none;
134
148
  /* While one of its tools is offering a choice, this drawer is the one
@@ -157,6 +171,7 @@
157
171
  padding: 5px;
158
172
  border-top: 1px solid rgba(0, 0, 0, 0.05);
159
173
  display: none;
174
+ /* The utility plugins' fields, folded under one toggle */
160
175
  }
161
176
  .ge-canvas.ge-editing .ge-tools-drawer .ge-details .btn-group a {
162
177
  color: #333;
@@ -180,6 +195,74 @@
180
195
  .ge-canvas.ge-editing .ge-tools-drawer .ge-details input.ge-classes {
181
196
  width: 200px;
182
197
  }
198
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities {
199
+ margin-top: 5px;
200
+ font-size: 11px;
201
+ /* The spacing plugin: a side and a value per group */
202
+ }
203
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities > .ge-utilities-toggle {
204
+ color: #333;
205
+ cursor: pointer;
206
+ text-decoration: none;
207
+ }
208
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities > .ge-utilities-toggle::before {
209
+ content: '\25B8';
210
+ display: inline-block;
211
+ width: 1em;
212
+ }
213
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities.ge-open > .ge-utilities-toggle::before {
214
+ content: '\25BE';
215
+ }
216
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities > .ge-utilities-body {
217
+ display: none;
218
+ flex-wrap: wrap;
219
+ gap: 4px 10px;
220
+ margin-top: 4px;
221
+ }
222
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities.ge-open > .ge-utilities-body {
223
+ display: flex;
224
+ }
225
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities .ge-utility {
226
+ display: inline-flex;
227
+ flex-direction: column;
228
+ margin: 0;
229
+ }
230
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities .ge-utility select {
231
+ border: 1px solid #7F7F7F;
232
+ border-radius: 4px;
233
+ font-size: 11px;
234
+ padding: 1px 2px;
235
+ }
236
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities .ge-utility-note {
237
+ color: #666;
238
+ font-size: 10px;
239
+ max-width: 160px;
240
+ }
241
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities .ge-spacing {
242
+ display: flex;
243
+ flex-wrap: wrap;
244
+ gap: 4px 10px;
245
+ }
246
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities .ge-spacing-group {
247
+ display: inline-flex;
248
+ flex-direction: column;
249
+ /* The group names the field once */
250
+ }
251
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities .ge-spacing-group > .ge-spacing-side {
252
+ border: 1px solid #7F7F7F;
253
+ border-radius: 4px;
254
+ font-size: 11px;
255
+ padding: 1px 2px;
256
+ margin-bottom: 2px;
257
+ }
258
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities .ge-spacing-group .ge-utility > .ge-utility-label {
259
+ display: none;
260
+ }
261
+ .ge-canvas.ge-editing .ge-tools-drawer .ge-details .ge-utilities .ge-spacing-gutter {
262
+ flex-basis: 100%;
263
+ color: #666;
264
+ font-size: 10px;
265
+ }
183
266
  .ge-canvas.ge-editing .row {
184
267
  background-color: #efefef;
185
268
  border: 1px solid #999999;
@@ -364,42 +447,48 @@
364
447
  .ge-canvas.ge-editing .ge-element > .ge-element-drawer > a.ge-element-info {
365
448
  cursor: default;
366
449
  }
367
- .ge-canvas.ge-editing .ui-sortable-placeholder {
450
+ .ge-canvas.ge-editing .ge-drag-placeholder {
368
451
  background: rgba(255, 255, 0, 0.2);
369
- visibility: visible !important;
452
+ opacity: 1;
370
453
  min-height: 50px;
371
454
  max-height: 100px;
372
455
  }
456
+ .ge-canvas.ge-editing .ge-drag-placeholder > * {
457
+ visibility: hidden;
458
+ }
373
459
  .ge-canvas.ge-editing .column {
374
460
  transition: width 120ms ease, margin-left 120ms ease;
375
461
  }
376
- .ge-canvas.ge-editing .column.ui-resizable-resizing {
462
+ .ge-canvas.ge-editing .column.ge-resizing {
377
463
  transition: none;
378
464
  /* Follow the pointer in pixels: the layout mode's max-width is what
379
465
  the column will snap back into once the drag lands */
380
466
  max-width: none;
381
467
  }
382
- .ge-canvas.ge-editing .column > .ui-resizable-handle {
468
+ .ge-canvas.ge-editing .column > .ge-resize-handle {
383
469
  position: absolute;
384
470
  display: block;
385
471
  z-index: 10;
472
+ /* The gesture is the editor's, not the page's: without this a touch
473
+ drag on the handle scrolls instead of resizing */
474
+ touch-action: none;
386
475
  }
387
- .ge-canvas.ge-editing .column > .ui-resizable-e,
388
- .ge-canvas.ge-editing .column > .ui-resizable-w {
476
+ .ge-canvas.ge-editing .column > .ge-resize-e,
477
+ .ge-canvas.ge-editing .column > .ge-resize-w {
389
478
  top: 0;
390
479
  bottom: 0;
391
480
  width: 8px;
392
481
  cursor: ew-resize;
393
482
  background: rgba(47, 164, 231, 0.3);
394
483
  }
395
- .ge-canvas.ge-editing .column > .ui-resizable-e:hover,
396
- .ge-canvas.ge-editing .column > .ui-resizable-w:hover {
484
+ .ge-canvas.ge-editing .column > .ge-resize-e:hover,
485
+ .ge-canvas.ge-editing .column > .ge-resize-w:hover {
397
486
  background: #2FA4E7;
398
487
  }
399
- .ge-canvas.ge-editing .column > .ui-resizable-e {
488
+ .ge-canvas.ge-editing .column > .ge-resize-e {
400
489
  right: -1px;
401
490
  }
402
- .ge-canvas.ge-editing .column > .ui-resizable-w {
491
+ .ge-canvas.ge-editing .column > .ge-resize-w {
403
492
  left: -1px;
404
493
  }
405
494
  .ge-canvas.ge-editing .ge-tools-drawer > .ge-resize-size {
@@ -409,11 +498,11 @@
409
498
  font-size: 11px;
410
499
  color: #178acc;
411
500
  }
412
- .ge-canvas.ge-editing .column.ui-resizable-resizing > .ge-tools-drawer > .ge-resize-size {
501
+ .ge-canvas.ge-editing .column.ge-resizing > .ge-tools-drawer > .ge-resize-size {
413
502
  display: block;
414
503
  }
415
504
  /* The handle belongs to editing, and never to the output */
416
- .ge-canvas:not(.ge-editing) .ui-resizable-handle {
505
+ .ge-canvas:not(.ge-editing) .ge-resize-handle {
417
506
  display: none;
418
507
  }
419
508
  /* Layout modes */
@@ -3873,3 +3962,82 @@
3873
3962
  .ge-canvas.ge-layout-xxl .offset-xxl-11 {
3874
3963
  margin-left: 91.66666667%;
3875
3964
  }
3965
+ /*
3966
+ * The visibility plugin. A node Bootstrap would hide is kept on the canvas -
3967
+ * there would be nothing left to click to show it again - and shown the way
3968
+ * the view sees it instead. The selectors outweigh Bootstrap's d-* classes,
3969
+ * which are !important too.
3970
+ */
3971
+ .ge-canvas.ge-editing {
3972
+ /* The all view: hidden at some breakpoints, said in a corner */
3973
+ }
3974
+ .ge-canvas.ge-editing .ge-visibility {
3975
+ display: block !important;
3976
+ }
3977
+ .ge-canvas.ge-editing .row.ge-visibility {
3978
+ display: flex !important;
3979
+ }
3980
+ .ge-canvas.ge-editing .ge-hidden-in-view {
3981
+ opacity: 0.45;
3982
+ background-image: repeating-linear-gradient(-45deg, transparent 0, transparent 6px, rgba(0, 0, 0, 0.07) 6px, rgba(0, 0, 0, 0.07) 12px);
3983
+ }
3984
+ .ge-canvas.ge-editing [data-ge-hidden-in] {
3985
+ position: relative;
3986
+ }
3987
+ .ge-canvas.ge-editing [data-ge-hidden-in]::after {
3988
+ content: attr(data-ge-hidden-in);
3989
+ position: absolute;
3990
+ right: 2px;
3991
+ bottom: 2px;
3992
+ z-index: 1;
3993
+ padding: 0 4px;
3994
+ border-radius: 3px;
3995
+ background: rgba(0, 0, 0, 0.6);
3996
+ color: #fff;
3997
+ font-size: 10px;
3998
+ line-height: 16px;
3999
+ pointer-events: none;
4000
+ }
4001
+ /* The order plugin: a column ordered by class in this view says where */
4002
+ .ge-canvas.ge-editing .column[data-ge-order]::before {
4003
+ content: attr(data-ge-order);
4004
+ position: absolute;
4005
+ left: 2px;
4006
+ bottom: 2px;
4007
+ z-index: 1;
4008
+ padding: 0 4px;
4009
+ border-radius: 3px;
4010
+ background: rgba(23, 138, 204, 0.85);
4011
+ color: #fff;
4012
+ font-size: 10px;
4013
+ line-height: 16px;
4014
+ pointer-events: none;
4015
+ }
4016
+ /*
4017
+ * The gutters plugin. A row with gutter classes is drawn with its real
4018
+ * gutters rather than the editor's frame: each column padded by half the
4019
+ * horizontal gutter, and the vertical gutter above each column. Bootstrap
4020
+ * takes the first line's back with a negative top margin on the row, which
4021
+ * here would pull it over the row above, so the row's drawer takes it back
4022
+ * instead and the gap shows between lines of columns only.
4023
+ */
4024
+ .ge-canvas.ge-editing .row.ge-gutters {
4025
+ margin-top: 0;
4026
+ }
4027
+ .ge-canvas.ge-editing .row.ge-gutters > .ge-tools-drawer {
4028
+ margin-top: 0;
4029
+ margin-bottom: calc(5px - var(--bs-gutter-y));
4030
+ /* Bootstrap pads every child of a row by half its gutter, the drawer
4031
+ too; it keeps the padding a row with no gutter class gives it */
4032
+ padding-left: 0.75rem;
4033
+ padding-right: 0.75rem;
4034
+ }
4035
+ .ge-canvas.ge-editing .row.ge-gutters > .column {
4036
+ padding-left: calc(var(--bs-gutter-x) * 0.5);
4037
+ padding-right: calc(var(--bs-gutter-x) * 0.5);
4038
+ }
4039
+ .ge-canvas.ge-editing .row.ge-gutters > .column > .ge-tools-drawer {
4040
+ margin-left: calc(var(--bs-gutter-x) * -0.5);
4041
+ margin-right: calc(var(--bs-gutter-x) * -0.5);
4042
+ width: calc(100% + var(--bs-gutter-x));
4043
+ }