@themarioga/grid-editor 8.10.0 → 9.0.0-beta.1

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.
@@ -193,6 +193,11 @@ GridEditor.features.clipboard = function(ge) {
193
193
  // the add buttons make
194
194
  align: "end",
195
195
  source: "paste",
196
+ // A toolbar built again - another plugin's buttons changed -
197
+ // shows only what fits, as refresh would
198
+ hidden: function() {
199
+ return !fits(read(), [item.kind]);
200
+ },
196
201
  // What the button showed, even if another tab has copied
197
202
  // something else since
198
203
  create: function() {
@@ -197,6 +197,11 @@
197
197
  // the add buttons make
198
198
  align: "end",
199
199
  source: "paste",
200
+ // A toolbar built again - another plugin's buttons changed -
201
+ // shows only what fits, as refresh would
202
+ hidden: function() {
203
+ return !fits(read(), [item.kind]);
204
+ },
200
205
  // What the button showed, even if another tab has copied
201
206
  // something else since
202
207
  create: function() {
@@ -1 +1 @@
1
- (()=>{var u=window.GridEditor;if(!u||typeof u.get!="function")throw new Error("grid-editor: load grideditor.js (or grideditor.bundle.min.js) before its plugins, its locales and grideditor.jquery.js");function b(t,e){return t?Array.prototype.slice.call(t.querySelectorAll(e)):[]}function w(t,e){return t?t.querySelector(e):null}function j(t,e){return t?Array.prototype.filter.call(t.children,function(i){return!e||i.matches(e)}):[]}function E(t,e){return j(t,e)[0]||null}function T(t,e){return C(e).forEach(function(i){t.classList.add(i)}),t}function A(t,e){return C(e).forEach(function(i){t.classList.remove(i)}),t}function C(t){return String(t||"").split(/\s+/).filter(Boolean)}var l={};function k(t){var e=t.nodeName;if(!l[e]){var i=document.body.appendChild(document.createElement(e));l[e]=getComputedStyle(i).display,i.remove(),l[e]==="none"&&(l[e]="block")}return l[e]}function I(t){return t.style.removeProperty("display"),getComputedStyle(t).display==="none"&&(t.style.display=k(t)),t}function H(t){return t.style.display="none",t}function s(t,e){return e===void 0&&(e=!R(t)),e?I(t):H(t)}function R(t){return!!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)}Object.assign(u.locales.en,{"tool.copy":"Copy","tool.paste":"Paste","clipboard.paste_row":"Paste row","clipboard.paste_section":"Paste section"});var h="grideditor.clipboard",v="grideditor-clipboard",S=1,p=null,G={column:["row","text","container","element"],row:["column"],section:["row"]};function g(){var t;try{t=window.localStorage.getItem(h)}catch(i){return p}if(t===null)return p;try{var e=JSON.parse(t);return e&&e.version===S&&e.html?e:null}catch(i){return null}}function _(t){try{window.localStorage.setItem(h,JSON.stringify(t)),p=null}catch(e){p=t}O()}function O(){document.dispatchEvent(new CustomEvent(v))}window.addEventListener("storage",function(t){t.key===h&&O()});u.features.clipboard=function(t){var e=null,i=!1;function d(n){return!t.settings.plugins||t.settings.plugins.indexOf(n)!==-1}function L(n){return n.category==="container"?!!u.containers[n.kind]&&d(n.kind):n.category==="section"?!!u.features.sections&&d("sections"):n.category==="element"?!!u.features.elements&&d("elements"):!0}function f(n,r){return!!n&&r.indexOf(n.category)!==-1&&L(n)}function y(n){return u.containers[n]?"container":["row","column","section","text","element"].indexOf(n)!==-1?n:null}function B(n,r){var o=t.nodeHtml(n);_({version:S,category:y(r),kind:r,html:o}),t.emit("after-copy",t.payloadFor(r,n,{source:"tool"}));var a=E(t.drawerOf(n),".ge-copy");if(a){var c=w(a,"i");T(a,"ge-copied"),c&&c.setAttribute("class","bi bi-check2"),setTimeout(function(){A(a,"ge-copied"),c&&c.setAttribute("class","bi bi-copy")},1200)}}function N(n,r){var o=g();f(o,r)&&t.place(t.freshNode(o.html),o.kind,{appendTo:n,source:"paste"})}function m(){var n=g();b(t.canvas,".ge-paste").forEach(function(r){s(r,f(n,r.getAttribute("data-ge-paste").split(" ")))}),t.toolbarItems("clipboard").forEach(function(r){var o=x[parseInt(r.getAttribute("data-ge-item"),10)];s(r,f(n,[o.kind]))}),e=n}var x=[{kind:"row",labelKey:"clipboard.paste_row"},{kind:"section",labelKey:"clipboard.paste_section"}].map(function(n){return Object.assign(n,{iconClass:"bi bi-clipboard-plus",align:"end",source:"paste",create:function(){return t.freshNode(e.html)}})});return{drawerTools:function(n,r,o){y(o)&&t.createTool(n,t.t("tool.copy"),"ge-copy","bi bi-copy",function(){B(r,o)});var a=G[o];if(a){var c=t.createTool(n,t.t("tool.paste"),"ge-paste","bi bi-clipboard-plus",function(){N(r,a)});c.setAttribute("data-ge-paste",a.join(" ")),s(c,f(g(),a))}},toolbar:x,onInit:function(){m(),i||(document.addEventListener(v,m),i=!0)},onDeinit:function(){document.removeEventListener(v,m),i=!1},cleanMarkup:function(){}}};})();
1
+ (()=>{var u=window.GridEditor;if(!u||typeof u.get!="function")throw new Error("grid-editor: load grideditor.js (or grideditor.bundle.min.js) before its plugins, its locales and grideditor.jquery.js");function b(t,e){return t?Array.prototype.slice.call(t.querySelectorAll(e)):[]}function w(t,e){return t?t.querySelector(e):null}function k(t,e){return t?Array.prototype.filter.call(t.children,function(i){return!e||i.matches(e)}):[]}function E(t,e){return k(t,e)[0]||null}function T(t,e){return C(e).forEach(function(i){t.classList.add(i)}),t}function A(t,e){return C(e).forEach(function(i){t.classList.remove(i)}),t}function C(t){return String(t||"").split(/\s+/).filter(Boolean)}var f={};function j(t){var e=t.nodeName;if(!f[e]){var i=document.body.appendChild(document.createElement(e));f[e]=getComputedStyle(i).display,i.remove(),f[e]==="none"&&(f[e]="block")}return f[e]}function I(t){return t.style.removeProperty("display"),getComputedStyle(t).display==="none"&&(t.style.display=j(t)),t}function H(t){return t.style.display="none",t}function s(t,e){return e===void 0&&(e=!R(t)),e?I(t):H(t)}function R(t){return!!(t.offsetWidth||t.offsetHeight||t.getClientRects().length)}Object.assign(u.locales.en,{"tool.copy":"Copy","tool.paste":"Paste","clipboard.paste_row":"Paste row","clipboard.paste_section":"Paste section"});var h="grideditor.clipboard",v="grideditor-clipboard",S=1,d=null,G={column:["row","text","container","element"],row:["column"],section:["row"]};function p(){var t;try{t=window.localStorage.getItem(h)}catch(i){return d}if(t===null)return d;try{var e=JSON.parse(t);return e&&e.version===S&&e.html?e:null}catch(i){return null}}function _(t){try{window.localStorage.setItem(h,JSON.stringify(t)),d=null}catch(e){d=t}O()}function O(){document.dispatchEvent(new CustomEvent(v))}window.addEventListener("storage",function(t){t.key===h&&O()});u.features.clipboard=function(t){var e=null,i=!1;function m(n){return!t.settings.plugins||t.settings.plugins.indexOf(n)!==-1}function L(n){return n.category==="container"?!!u.containers[n.kind]&&m(n.kind):n.category==="section"?!!u.features.sections&&m("sections"):n.category==="element"?!!u.features.elements&&m("elements"):!0}function l(n,r){return!!n&&r.indexOf(n.category)!==-1&&L(n)}function y(n){return u.containers[n]?"container":["row","column","section","text","element"].indexOf(n)!==-1?n:null}function B(n,r){var o=t.nodeHtml(n);_({version:S,category:y(r),kind:r,html:o}),t.emit("after-copy",t.payloadFor(r,n,{source:"tool"}));var a=E(t.drawerOf(n),".ge-copy");if(a){var c=w(a,"i");T(a,"ge-copied"),c&&c.setAttribute("class","bi bi-check2"),setTimeout(function(){A(a,"ge-copied"),c&&c.setAttribute("class","bi bi-copy")},1200)}}function N(n,r){var o=p();l(o,r)&&t.place(t.freshNode(o.html),o.kind,{appendTo:n,source:"paste"})}function g(){var n=p();b(t.canvas,".ge-paste").forEach(function(r){s(r,l(n,r.getAttribute("data-ge-paste").split(" ")))}),t.toolbarItems("clipboard").forEach(function(r){var o=x[parseInt(r.getAttribute("data-ge-item"),10)];s(r,l(n,[o.kind]))}),e=n}var x=[{kind:"row",labelKey:"clipboard.paste_row"},{kind:"section",labelKey:"clipboard.paste_section"}].map(function(n){return Object.assign(n,{iconClass:"bi bi-clipboard-plus",align:"end",source:"paste",hidden:function(){return!l(p(),[n.kind])},create:function(){return t.freshNode(e.html)}})});return{drawerTools:function(n,r,o){y(o)&&t.createTool(n,t.t("tool.copy"),"ge-copy","bi bi-copy",function(){B(r,o)});var a=G[o];if(a){var c=t.createTool(n,t.t("tool.paste"),"ge-paste","bi bi-clipboard-plus",function(){N(r,a)});c.setAttribute("data-ge-paste",a.join(" ")),s(c,l(p(),a))}},toolbar:x,onInit:function(){g(),i||(document.addEventListener(v,g),i=!0)},onDeinit:function(){document.removeEventListener(v,g),i=!1},cleanMarkup:function(){}}};})();
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/js/register.js", "../../src/js/dom.js", "../../src/js/plugins/grideditor.clipboard.js"],
4
- "sourcesContent": ["/**\n * What a plugin's or a locale's classic script imports in place of the\n * editor: the GridEditor the page already loaded.\n *\n * The build points `import { GridEditor } from '../grideditor.js'` here for\n * the classic scripts, so each registers on the editor the page has rather\n * than bringing its own. Loaded before the editor there is nothing to\n * register on, and saying so is better than a TypeError three calls later.\n */\nvar GridEditor = window.GridEditor;\n\nif (!GridEditor || typeof GridEditor.get !== 'function') {\n throw new Error('grid-editor: load grideditor.js (or grideditor.bundle.min.js) before its plugins, ' +\n 'its locales and grideditor.jquery.js');\n}\n\nexport { GridEditor };\nexport default GridEditor;\n", "/**\n * The DOM, the way grid-editor uses it.\n *\n * Small helpers over the browser's own API, for the core and the shipped\n * plugins. Not a public API: a plugin of a host's own is free to write its\n * DOM code however it likes, and this file makes no promise to it.\n *\n * Stateless on purpose. The build puts a copy of this file in each plugin's\n * classic script, so anything kept here would be kept once per copy; a\n * module that needs to remember something about a node keeps its own\n * WeakMap.\n */\n\n/** Every element under `root` matching `selector`, as an array. */\nexport function all(root, selector) {\n return root ? Array.prototype.slice.call(root.querySelectorAll(selector)) : [];\n}\n\n/** The first element under `root` matching `selector`, or null. */\nexport function one(root, selector) {\n return root ? root.querySelector(selector) : null;\n}\n\n/** `root` itself when it matches, then every element under it that does. */\nexport function selfAndAll(root, selector) {\n if (!root) { return []; }\n\n var found = all(root, selector);\n if (root.nodeType === 1 && root.matches(selector)) { found.unshift(root); }\n return found;\n}\n\n/** The element children of `node`, those matching `selector` if one is given. */\nexport function children(node, selector) {\n if (!node) { return []; }\n\n return Array.prototype.filter.call(node.children, function(each) {\n return !selector || each.matches(selector);\n });\n}\n\n/** The first element child matching `selector`, or null. */\nexport function child(node, selector) {\n return children(node, selector)[0] || null;\n}\n\n/** The nearest ancestor-or-self matching `selector` below `stopAt`, or null. */\nexport function closest(node, selector, stopAt) {\n var found = node && node.nodeType === 1 ? node.closest(selector) : null;\n\n if (found && stopAt && found !== stopAt && !stopAt.contains(found)) { return null; }\n return found;\n}\n\n/** Whether `node` is an element matching `selector`. */\nexport function is(node, selector) {\n return !!node && node.nodeType === 1 && node.matches(selector);\n}\n\n/** Whether `node` is in the document. */\nexport function attached(node) {\n return !!node && document.documentElement.contains(node);\n}\n\n/** The element siblings after `node` that match `selector`. */\nexport function nextAll(node, selector) {\n var found = [];\n\n for (var next = node.nextElementSibling; next; next = next.nextElementSibling) {\n if (!selector || next.matches(selector)) { found.push(next); }\n }\n return found;\n}\n\n/** The ancestors of `node` up to, and not including, `stop`, nearest first. */\nexport function parentsUntil(node, stop) {\n var found = [];\n\n for (var parent = node.parentElement; parent && parent !== stop; parent = parent.parentElement) {\n found.push(parent);\n }\n return found;\n}\n\n/**\n * The nodes the html parses into, detached. Parsed the way innerHTML parses,\n * so a <script> in it never runs, here or once it is in the page.\n */\nexport function parse(html) {\n var template = document.createElement('template');\n template.innerHTML = html;\n return Array.prototype.slice.call(template.content.childNodes);\n}\n\n/** The one element the html is, detached. */\nexport function create(html) {\n var nodes = parse(html.trim());\n\n for (var i = 0; i < nodes.length; i++) {\n if (nodes[i].nodeType === 1) { return nodes[i]; }\n }\n return null;\n}\n\n/** An element of `tag`, with `attributes` and `text`. */\nexport function element(tag, attributes, text) {\n var node = document.createElement(tag);\n\n Object.keys(attributes || {}).forEach(function(name) {\n var value = attributes[name];\n if (value !== null && value !== undefined && value !== false) { node.setAttribute(name, value); }\n });\n if (text !== undefined && text !== null) { node.textContent = text; }\n\n return node;\n}\n\n/** Replace what `node` holds with `html`. A <script> in it does not run. */\nexport function setHtml(node, html) {\n node.innerHTML = html === undefined || html === null ? '' : String(html);\n return node;\n}\n\n/** Add one or several space separated classes. */\nexport function addClass(node, names) {\n split(names).forEach(function(name) { node.classList.add(name); });\n return node;\n}\n\nexport function removeClass(node, names) {\n split(names).forEach(function(name) { node.classList.remove(name); });\n return node;\n}\n\n/** Each class on, or off, or flipped when `on` is not given. */\nexport function toggleClass(node, names, state) {\n split(names).forEach(function(name) {\n if (state === undefined) {\n node.classList.toggle(name);\n } else {\n node.classList.toggle(name, !!state);\n }\n });\n return node;\n}\n\nexport function hasClass(node, name) {\n return !!node && node.nodeType === 1 && node.classList.contains(name);\n}\n\nfunction split(names) {\n return String(names || '').split(/\\s+/).filter(Boolean);\n}\n\n/** Take the class attribute off when there is nothing left in it. */\nexport function dropEmptyClass(node) {\n if (!node.getAttribute('class')) { node.removeAttribute('class'); }\n return node;\n}\n\n/** Take the style attribute off when there is nothing left in it. */\nexport function dropEmptyStyle(node) {\n if (!node.getAttribute('style')) { node.removeAttribute('style'); }\n return node;\n}\n\n/** Set attributes; a null or undefined value takes the attribute off. */\nexport function attr(node, attributes) {\n Object.keys(attributes).forEach(function(name) {\n var value = attributes[name];\n\n if (value === null || value === undefined) {\n node.removeAttribute(name);\n } else {\n node.setAttribute(name, value);\n }\n });\n return node;\n}\n\n/**\n * Inline styles, by css property name. A number is pixels for the properties\n * that take a length, as jQuery had it; an empty string takes one off.\n */\nexport function css(node, styles) {\n Object.keys(styles).forEach(function(property) {\n var value = styles[property];\n\n if (typeof value === 'number' && !UNITLESS[property]) { value = value + 'px'; }\n node.style.setProperty(property, value === null || value === undefined ? '' : String(value));\n });\n return node;\n}\n\nvar UNITLESS = { opacity: true, 'z-index': true, 'flex-grow': true, 'flex-shrink': true, order: true };\n\n/** What an element of this tag shows as when nothing hides it. */\nvar defaultDisplays = {};\n\nfunction defaultDisplay(node) {\n var tag = node.nodeName;\n\n if (!defaultDisplays[tag]) {\n var probe = document.body.appendChild(document.createElement(tag));\n defaultDisplays[tag] = getComputedStyle(probe).display;\n probe.remove();\n if (defaultDisplays[tag] === 'none') { defaultDisplays[tag] = 'block'; }\n }\n return defaultDisplays[tag];\n}\n\n/** Show a node: its inline display:none off, and a display of its own if a stylesheet hides it. */\nexport function show(node) {\n node.style.removeProperty('display');\n if (getComputedStyle(node).display === 'none') { node.style.display = defaultDisplay(node); }\n return node;\n}\n\nexport function hide(node) {\n node.style.display = 'none';\n return node;\n}\n\n/** Show or hide; flip when `on` is not given. */\nexport function toggle(node, state) {\n if (state === undefined) { state = !visible(node); }\n return state ? show(node) : hide(node);\n}\n\n/** Whether a node takes up room: what jQuery's :visible meant. */\nexport function visible(node) {\n return !!(node.offsetWidth || node.offsetHeight || node.getClientRects().length);\n}\n\n/** The border box's width, fractions included. */\nexport function outerWidth(node) {\n return node.getBoundingClientRect().width;\n}\n\nexport function outerHeight(node) {\n return node.getBoundingClientRect().height;\n}\n\n/** The content box's height, which is what jQuery's height() gave. */\nexport function contentHeight(node) {\n var style = getComputedStyle(node);\n return node.clientHeight - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom);\n}\n\n/** Where a node is on the page, not in the window. */\nexport function offset(node) {\n var box = node.getBoundingClientRect();\n return { top: box.top + window.pageYOffset, left: box.left + window.pageXOffset };\n}\n\n/** Its index among its parent's element children, those matching `selector` if given. */\nexport function indexIn(node, selector) {\n return children(node.parentElement, selector).indexOf(node);\n}\n\n/** Put `node` right after `reference`. */\nexport function insertAfter(node, reference) {\n reference.parentNode.insertBefore(node, reference.nextSibling);\n return node;\n}\n\n/** Replace `node` with what it holds. */\nexport function unwrap(node) {\n var parent = node.parentNode;\n\n while (node.firstChild) { parent.insertBefore(node.firstChild, node); }\n node.remove();\n}\n\n/** Put `node` inside `wrapper`, where `node` was. */\nexport function wrap(node, wrapper) {\n node.parentNode.insertBefore(wrapper, node);\n wrapper.appendChild(node);\n return wrapper;\n}\n\n/**\n * Listen on `root` for `types` (space separated) on descendants that match\n * `selector`, with `this` the matched element, as jQuery's delegated\n * handlers had it. Hands back the listener, for removeEventListener; a\n * signal in `options` removes it too.\n */\nexport function delegate(root, types, selector, handler, options) {\n var listener = function(event) {\n var match = closest(event.target, selector, root);\n if (match && root.contains(match)) { return handler.call(match, event); }\n return undefined;\n };\n\n split(types).forEach(function(type) { root.addEventListener(type, listener, options); });\n return listener;\n}\n\n/** Listen for several space separated event types with one handler. */\nexport function on(target, types, handler, options) {\n split(types).forEach(function(type) { target.addEventListener(type, handler, options); });\n return handler;\n}\n\nexport function off(target, types, handler, options) {\n split(types).forEach(function(type) { target.removeEventListener(type, handler, options); });\n}\n\n/** A mouse event of `type` sent to `node`, the way a pointer would. */\nexport function fire(node, type) {\n node.dispatchEvent(new MouseEvent(type, { bubbles: type !== 'mouseenter' && type !== 'mouseleave' }));\n}\n\n/* ------------------------------------------------------------------\n * Taking a node away, visibly.\n *\n * The durations are jQuery's: 400 ms unless the caller says otherwise. `done` runs once the node is out of sight, which is\n * when the caller removes it; a node that is already out of sight is done at\n * once, on the next task, as jQuery had it.\n * ------------------------------------------------------------------ */\n\nvar DEFAULT_DURATION = 400;\n\nfunction animateAway(node, frames, duration, done) {\n duration = duration === undefined ? DEFAULT_DURATION : duration;\n\n var finish = function() {\n hide(node);\n if (done) { done(); }\n };\n\n if (!visible(node) || !node.animate || duration <= 0) {\n window.setTimeout(finish, 0);\n return;\n }\n\n var animation = node.animate(frames(getComputedStyle(node)), {\n duration: duration,\n easing: 'ease-in-out',\n });\n var called = false;\n var once = function() {\n if (called) { return; }\n called = true;\n finish();\n };\n\n animation.onfinish = once;\n animation.oncancel = once;\n}\n\n/** Fold a node up to nothing, then hide it. */\nexport function slideUp(node, duration, done) {\n if (typeof duration === 'function') {\n done = duration;\n duration = undefined;\n }\n\n node.style.overflow = 'hidden';\n animateAway(node, function(style) {\n return [\n {\n height: style.height,\n paddingTop: style.paddingTop,\n paddingBottom: style.paddingBottom,\n marginTop: style.marginTop,\n marginBottom: style.marginBottom,\n },\n { height: '0px', paddingTop: '0px', paddingBottom: '0px', marginTop: '0px', marginBottom: '0px' },\n ];\n }, duration, function() {\n node.style.removeProperty('overflow');\n if (done) { done(); }\n });\n}\n\n/** Fade a node out, then hide it. */\nexport function fadeOut(node, duration, done) {\n animateAway(node, function(style) {\n return [{ opacity: style.opacity }, { opacity: 0 }];\n }, duration, done);\n}\n\n/** Fade and fold a node, in height and, with `width`, sideways too, then hide it. */\nexport function shrinkAway(node, duration, done, width) {\n node.style.overflow = 'hidden';\n animateAway(node, function(style) {\n var from = { opacity: style.opacity, height: style.height };\n var to = { opacity: 0, height: '0px' };\n\n if (width) {\n from.width = style.width;\n to.width = '0px';\n }\n return [from, to];\n }, duration, function() {\n node.style.removeProperty('overflow');\n if (done) { done(); }\n });\n}\n", "/**\n * Copy and paste for grid-editor.\n *\n * A feature plugin: load this file after the editor and every row, column,\n * section, text block, container and element gets a copy tool, and every\n * place one of them can go gets a paste tool while there is something copied\n * that fits.\n * What it can ask the editor for is the handle its factory is called with,\n * described in docs/plugins.md.\n *\n * <script src=\"dist/grideditor.min.js\"></script>\n * <script src=\"dist/plugins/grideditor.clipboard.min.js\"></script>\n *\n * What is copied is kept in localStorage, so it survives a reload and can be\n * pasted in another tab or another editor of the same site. A browser that\n * will not give the page its storage keeps it in memory, for this page only.\n *\n * A paste is an add like any other - before-add-* and after-add-*, with\n * `source: 'paste'` - so a host can turn one away.\n */\nimport { GridEditor } from '../grideditor.js';\nimport * as dom from '../dom.js';\n\nObject.assign(GridEditor.locales.en, {\n 'tool.copy': 'Copy',\n 'tool.paste': 'Paste',\n 'clipboard.paste_row': 'Paste row',\n 'clipboard.paste_section': 'Paste section',\n});\n\nvar STORAGE_ITEM = 'grideditor.clipboard';\nvar CHANGE = 'grideditor-clipboard';\nvar VERSION = 1;\n\n/** Where nothing can be stored, what was copied lives here, for this page. */\nvar memory = null;\n\n/**\n * What each drawer takes when something is pasted into it, by the kind of the\n * node the drawer belongs to. A category is what was copied: a row, a column,\n * a section, a text block, a container of any type, an element.\n */\nvar TARGETS = {\n column: ['row', 'text', 'container', 'element'],\n row: ['column'],\n section: ['row'],\n};\n\nfunction read() {\n var raw;\n\n try {\n raw = window.localStorage.getItem(STORAGE_ITEM);\n } catch (error) {\n return memory;\n }\n if (raw === null) { return memory; }\n\n try {\n var clip = JSON.parse(raw);\n return clip && clip.version === VERSION && clip.html ? clip : null;\n } catch (error) {\n return null;\n }\n}\n\nfunction write(clip) {\n try {\n window.localStorage.setItem(STORAGE_ITEM, JSON.stringify(clip));\n memory = null;\n } catch (error) {\n memory = clip;\n }\n\n announce();\n}\n\n/** Every editor on the page looks at the clipboard again. */\nfunction announce() {\n document.dispatchEvent(new CustomEvent(CHANGE));\n}\n\n// Another tab copied something: every editor here looks again\nwindow.addEventListener('storage', function(e) {\n if (e.key === STORAGE_ITEM) { announce(); }\n});\n\nGridEditor.features.clipboard = function(ge) {\n\n var shown = null; // What the toolbar's paste buttons stand for\n var listening = false; // Whether refresh is on the document's change event\n\n function used(name) {\n return !ge.settings.plugins || ge.settings.plugins.indexOf(name) !== -1;\n }\n\n /** A category this editor can take: the plugin that makes it is here. */\n function available(clip) {\n if (clip.category === 'container') {\n return !!GridEditor.containers[clip.kind] && used(clip.kind);\n }\n if (clip.category === 'section') { return !!GridEditor.features.sections && used('sections'); }\n if (clip.category === 'element') { return !!GridEditor.features.elements && used('elements'); }\n\n return true;\n }\n\n function fits(clip, categories) {\n return !!clip && categories.indexOf(clip.category) !== -1 && available(clip);\n }\n\n function categoryOf(kind) {\n if (GridEditor.containers[kind]) { return 'container'; }\n\n return ['row', 'column', 'section', 'text', 'element'].indexOf(kind) !== -1 ? kind : null;\n }\n\n function copy(node, kind) {\n var html = ge.nodeHtml(node);\n\n write({ version: VERSION, category: categoryOf(kind), kind: kind, html: html });\n ge.emit('after-copy', ge.payloadFor(kind, node, { source: 'tool' }));\n\n // The canvas came back with new drawers: the one to flash is the\n // node's new copy tool\n var tool = dom.child(ge.drawerOf(node), '.ge-copy');\n if (!tool) { return; }\n\n var icon = dom.one(tool, 'i');\n dom.addClass(tool, 'ge-copied');\n if (icon) { icon.setAttribute('class', 'bi bi-check2'); }\n setTimeout(function() {\n dom.removeClass(tool, 'ge-copied');\n if (icon) { icon.setAttribute('class', 'bi bi-copy'); }\n }, 1200);\n }\n\n function paste(target, categories) {\n var clip = read();\n if (!fits(clip, categories)) { return; }\n\n ge.place(ge.freshNode(clip.html), clip.kind, { appendTo: target, source: 'paste' });\n }\n\n /** Show the paste tools, and the toolbar's paste buttons, that fit what is copied. */\n function refresh() {\n var clip = read();\n\n dom.all(ge.canvas, '.ge-paste').forEach(function(tool) {\n dom.toggle(tool, fits(clip, tool.getAttribute('data-ge-paste').split(' ')));\n });\n\n ge.toolbarItems('clipboard').forEach(function(button) {\n var item = TOOLBAR[parseInt(button.getAttribute('data-ge-item'), 10)];\n dom.toggle(button, fits(clip, [item.kind]));\n });\n\n shown = clip;\n }\n\n var TOOLBAR = [\n { kind: 'row', labelKey: 'clipboard.paste_row' },\n { kind: 'section', labelKey: 'clipboard.paste_section' },\n ].map(function(item) {\n return Object.assign(item, {\n iconClass: 'bi bi-clipboard-plus',\n // On the right, as an icon: pasting is not one of the things\n // the add buttons make\n align: 'end',\n source: 'paste',\n // What the button showed, even if another tab has copied\n // something else since\n create: function() { return ge.freshNode(shown.html); },\n });\n });\n\n return {\n drawerTools: function(drawer, node, kind) {\n if (categoryOf(kind)) {\n ge.createTool(drawer, ge.t('tool.copy'), 'ge-copy', 'bi bi-copy', function() {\n copy(node, kind);\n });\n }\n\n var categories = TARGETS[kind];\n if (!categories) { return; }\n\n var tool = ge.createTool(drawer, ge.t('tool.paste'), 'ge-paste', 'bi bi-clipboard-plus', function() {\n paste(node, categories);\n });\n tool.setAttribute('data-ge-paste', categories.join(' '));\n dom.toggle(tool, fits(read(), categories));\n },\n\n toolbar: TOOLBAR,\n\n onInit: function() {\n refresh();\n if (!listening) {\n document.addEventListener(CHANGE, refresh);\n listening = true;\n }\n },\n\n onDeinit: function() {\n document.removeEventListener(CHANGE, refresh);\n listening = false;\n },\n\n // Nothing of its own on the canvas: the tools are in the drawers\n cleanMarkup: function() {},\n };\n};\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["/**\n * What a plugin's or a locale's classic script imports in place of the\n * editor: the GridEditor the page already loaded.\n *\n * The build points `import { GridEditor } from '../grideditor.js'` here for\n * the classic scripts, so each registers on the editor the page has rather\n * than bringing its own. Loaded before the editor there is nothing to\n * register on, and saying so is better than a TypeError three calls later.\n */\nvar GridEditor = window.GridEditor;\n\nif (!GridEditor || typeof GridEditor.get !== 'function') {\n throw new Error('grid-editor: load grideditor.js (or grideditor.bundle.min.js) before its plugins, ' +\n 'its locales and grideditor.jquery.js');\n}\n\nexport { GridEditor };\nexport default GridEditor;\n", "/**\n * The DOM, the way grid-editor uses it.\n *\n * Small helpers over the browser's own API, for the core and the shipped\n * plugins. Not a public API: a plugin of a host's own is free to write its\n * DOM code however it likes, and this file makes no promise to it.\n *\n * Stateless on purpose. The build puts a copy of this file in each plugin's\n * classic script, so anything kept here would be kept once per copy; a\n * module that needs to remember something about a node keeps its own\n * WeakMap.\n */\n\n/** Every element under `root` matching `selector`, as an array. */\nexport function all(root, selector) {\n return root ? Array.prototype.slice.call(root.querySelectorAll(selector)) : [];\n}\n\n/** The first element under `root` matching `selector`, or null. */\nexport function one(root, selector) {\n return root ? root.querySelector(selector) : null;\n}\n\n/** `root` itself when it matches, then every element under it that does. */\nexport function selfAndAll(root, selector) {\n if (!root) { return []; }\n\n var found = all(root, selector);\n if (root.nodeType === 1 && root.matches(selector)) { found.unshift(root); }\n return found;\n}\n\n/** The element children of `node`, those matching `selector` if one is given. */\nexport function children(node, selector) {\n if (!node) { return []; }\n\n return Array.prototype.filter.call(node.children, function(each) {\n return !selector || each.matches(selector);\n });\n}\n\n/** The first element child matching `selector`, or null. */\nexport function child(node, selector) {\n return children(node, selector)[0] || null;\n}\n\n/** The nearest ancestor-or-self matching `selector` below `stopAt`, or null. */\nexport function closest(node, selector, stopAt) {\n var found = node && node.nodeType === 1 ? node.closest(selector) : null;\n\n if (found && stopAt && found !== stopAt && !stopAt.contains(found)) { return null; }\n return found;\n}\n\n/** Whether `node` is an element matching `selector`. */\nexport function is(node, selector) {\n return !!node && node.nodeType === 1 && node.matches(selector);\n}\n\n/** Whether `node` is in the document. */\nexport function attached(node) {\n return !!node && document.documentElement.contains(node);\n}\n\n/** The element siblings after `node` that match `selector`. */\nexport function nextAll(node, selector) {\n var found = [];\n\n for (var next = node.nextElementSibling; next; next = next.nextElementSibling) {\n if (!selector || next.matches(selector)) { found.push(next); }\n }\n return found;\n}\n\n/** The ancestors of `node` up to, and not including, `stop`, nearest first. */\nexport function parentsUntil(node, stop) {\n var found = [];\n\n for (var parent = node.parentElement; parent && parent !== stop; parent = parent.parentElement) {\n found.push(parent);\n }\n return found;\n}\n\n/**\n * The nodes the html parses into, detached. Parsed the way innerHTML parses,\n * so a <script> in it never runs, here or once it is in the page.\n */\nexport function parse(html) {\n var template = document.createElement('template');\n template.innerHTML = html;\n return Array.prototype.slice.call(template.content.childNodes);\n}\n\n/** The one element the html is, detached. */\nexport function create(html) {\n var nodes = parse(html.trim());\n\n for (var i = 0; i < nodes.length; i++) {\n if (nodes[i].nodeType === 1) { return nodes[i]; }\n }\n return null;\n}\n\n/** An element of `tag`, with `attributes` and `text`. */\nexport function element(tag, attributes, text) {\n var node = document.createElement(tag);\n\n Object.keys(attributes || {}).forEach(function(name) {\n var value = attributes[name];\n if (value !== null && value !== undefined && value !== false) { node.setAttribute(name, value); }\n });\n if (text !== undefined && text !== null) { node.textContent = text; }\n\n return node;\n}\n\n/** Replace what `node` holds with `html`. A <script> in it does not run. */\nexport function setHtml(node, html) {\n node.innerHTML = html === undefined || html === null ? '' : String(html);\n return node;\n}\n\n/** Add one or several space separated classes. */\nexport function addClass(node, names) {\n split(names).forEach(function(name) { node.classList.add(name); });\n return node;\n}\n\nexport function removeClass(node, names) {\n split(names).forEach(function(name) { node.classList.remove(name); });\n return node;\n}\n\n/** Each class on, or off, or flipped when `on` is not given. */\nexport function toggleClass(node, names, state) {\n split(names).forEach(function(name) {\n if (state === undefined) {\n node.classList.toggle(name);\n } else {\n node.classList.toggle(name, !!state);\n }\n });\n return node;\n}\n\nexport function hasClass(node, name) {\n return !!node && node.nodeType === 1 && node.classList.contains(name);\n}\n\nfunction split(names) {\n return String(names || '').split(/\\s+/).filter(Boolean);\n}\n\n/** Take the class attribute off when there is nothing left in it. */\nexport function dropEmptyClass(node) {\n if (!node.getAttribute('class')) { node.removeAttribute('class'); }\n return node;\n}\n\n/** Take the style attribute off when there is nothing left in it. */\nexport function dropEmptyStyle(node) {\n if (!node.getAttribute('style')) { node.removeAttribute('style'); }\n return node;\n}\n\n/** Set attributes; a null or undefined value takes the attribute off. */\nexport function attr(node, attributes) {\n Object.keys(attributes).forEach(function(name) {\n var value = attributes[name];\n\n if (value === null || value === undefined) {\n node.removeAttribute(name);\n } else {\n node.setAttribute(name, value);\n }\n });\n return node;\n}\n\n/**\n * Inline styles, by css property name. A number is pixels for the properties\n * that take a length, as jQuery had it; an empty string takes one off.\n */\nexport function css(node, styles) {\n Object.keys(styles).forEach(function(property) {\n var value = styles[property];\n\n if (typeof value === 'number' && !UNITLESS[property]) { value = value + 'px'; }\n node.style.setProperty(property, value === null || value === undefined ? '' : String(value));\n });\n return node;\n}\n\nvar UNITLESS = { opacity: true, 'z-index': true, 'flex-grow': true, 'flex-shrink': true, order: true };\n\n/** What an element of this tag shows as when nothing hides it. */\nvar defaultDisplays = {};\n\nfunction defaultDisplay(node) {\n var tag = node.nodeName;\n\n if (!defaultDisplays[tag]) {\n var probe = document.body.appendChild(document.createElement(tag));\n defaultDisplays[tag] = getComputedStyle(probe).display;\n probe.remove();\n if (defaultDisplays[tag] === 'none') { defaultDisplays[tag] = 'block'; }\n }\n return defaultDisplays[tag];\n}\n\n/** Show a node: its inline display:none off, and a display of its own if a stylesheet hides it. */\nexport function show(node) {\n node.style.removeProperty('display');\n if (getComputedStyle(node).display === 'none') { node.style.display = defaultDisplay(node); }\n return node;\n}\n\nexport function hide(node) {\n node.style.display = 'none';\n return node;\n}\n\n/** Show or hide; flip when `on` is not given. */\nexport function toggle(node, state) {\n if (state === undefined) { state = !visible(node); }\n return state ? show(node) : hide(node);\n}\n\n/** Whether a node takes up room: what jQuery's :visible meant. */\nexport function visible(node) {\n return !!(node.offsetWidth || node.offsetHeight || node.getClientRects().length);\n}\n\n/** The border box's width, fractions included. */\nexport function outerWidth(node) {\n return node.getBoundingClientRect().width;\n}\n\nexport function outerHeight(node) {\n return node.getBoundingClientRect().height;\n}\n\n/** The content box's height, which is what jQuery's height() gave. */\nexport function contentHeight(node) {\n var style = getComputedStyle(node);\n return node.clientHeight - parseFloat(style.paddingTop) - parseFloat(style.paddingBottom);\n}\n\n/** Where a node is on the page, not in the window. */\nexport function offset(node) {\n var box = node.getBoundingClientRect();\n return { top: box.top + window.pageYOffset, left: box.left + window.pageXOffset };\n}\n\n/** Its index among its parent's element children, those matching `selector` if given. */\nexport function indexIn(node, selector) {\n return children(node.parentElement, selector).indexOf(node);\n}\n\n/** Put `node` right after `reference`. */\nexport function insertAfter(node, reference) {\n reference.parentNode.insertBefore(node, reference.nextSibling);\n return node;\n}\n\n/** Replace `node` with what it holds. */\nexport function unwrap(node) {\n var parent = node.parentNode;\n\n while (node.firstChild) { parent.insertBefore(node.firstChild, node); }\n node.remove();\n}\n\n/** Put `node` inside `wrapper`, where `node` was. */\nexport function wrap(node, wrapper) {\n node.parentNode.insertBefore(wrapper, node);\n wrapper.appendChild(node);\n return wrapper;\n}\n\n/**\n * Listen on `root` for `types` (space separated) on descendants that match\n * `selector`, with `this` the matched element, as jQuery's delegated\n * handlers had it. Hands back the listener, for removeEventListener; a\n * signal in `options` removes it too.\n */\nexport function delegate(root, types, selector, handler, options) {\n var listener = function(event) {\n var match = closest(event.target, selector, root);\n if (match && root.contains(match)) { return handler.call(match, event); }\n return undefined;\n };\n\n split(types).forEach(function(type) { root.addEventListener(type, listener, options); });\n return listener;\n}\n\n/** Listen for several space separated event types with one handler. */\nexport function on(target, types, handler, options) {\n split(types).forEach(function(type) { target.addEventListener(type, handler, options); });\n return handler;\n}\n\nexport function off(target, types, handler, options) {\n split(types).forEach(function(type) { target.removeEventListener(type, handler, options); });\n}\n\n/** A mouse event of `type` sent to `node`, the way a pointer would. */\nexport function fire(node, type) {\n node.dispatchEvent(new MouseEvent(type, { bubbles: type !== 'mouseenter' && type !== 'mouseleave' }));\n}\n\n/* ------------------------------------------------------------------\n * Taking a node away, visibly.\n *\n * The durations are jQuery's: 400 ms unless the caller says otherwise. `done` runs once the node is out of sight, which is\n * when the caller removes it; a node that is already out of sight is done at\n * once, on the next task, as jQuery had it.\n * ------------------------------------------------------------------ */\n\nvar DEFAULT_DURATION = 400;\n\nfunction animateAway(node, frames, duration, done) {\n duration = duration === undefined ? DEFAULT_DURATION : duration;\n\n var finish = function() {\n hide(node);\n if (done) { done(); }\n };\n\n if (!visible(node) || !node.animate || duration <= 0) {\n window.setTimeout(finish, 0);\n return;\n }\n\n var animation = node.animate(frames(getComputedStyle(node)), {\n duration: duration,\n easing: 'ease-in-out',\n });\n var called = false;\n var once = function() {\n if (called) { return; }\n called = true;\n finish();\n };\n\n animation.onfinish = once;\n animation.oncancel = once;\n}\n\n/** Fold a node up to nothing, then hide it. */\nexport function slideUp(node, duration, done) {\n if (typeof duration === 'function') {\n done = duration;\n duration = undefined;\n }\n\n node.style.overflow = 'hidden';\n animateAway(node, function(style) {\n return [\n {\n height: style.height,\n paddingTop: style.paddingTop,\n paddingBottom: style.paddingBottom,\n marginTop: style.marginTop,\n marginBottom: style.marginBottom,\n },\n { height: '0px', paddingTop: '0px', paddingBottom: '0px', marginTop: '0px', marginBottom: '0px' },\n ];\n }, duration, function() {\n node.style.removeProperty('overflow');\n if (done) { done(); }\n });\n}\n\n/** Fade a node out, then hide it. */\nexport function fadeOut(node, duration, done) {\n animateAway(node, function(style) {\n return [{ opacity: style.opacity }, { opacity: 0 }];\n }, duration, done);\n}\n\n/** Fade and fold a node, in height and, with `width`, sideways too, then hide it. */\nexport function shrinkAway(node, duration, done, width) {\n node.style.overflow = 'hidden';\n animateAway(node, function(style) {\n var from = { opacity: style.opacity, height: style.height };\n var to = { opacity: 0, height: '0px' };\n\n if (width) {\n from.width = style.width;\n to.width = '0px';\n }\n return [from, to];\n }, duration, function() {\n node.style.removeProperty('overflow');\n if (done) { done(); }\n });\n}\n", "/**\n * Copy and paste for grid-editor.\n *\n * A feature plugin: load this file after the editor and every row, column,\n * section, text block, container and element gets a copy tool, and every\n * place one of them can go gets a paste tool while there is something copied\n * that fits.\n * What it can ask the editor for is the handle its factory is called with,\n * described in docs/plugins.md.\n *\n * <script src=\"dist/grideditor.min.js\"></script>\n * <script src=\"dist/plugins/grideditor.clipboard.min.js\"></script>\n *\n * What is copied is kept in localStorage, so it survives a reload and can be\n * pasted in another tab or another editor of the same site. A browser that\n * will not give the page its storage keeps it in memory, for this page only.\n *\n * A paste is an add like any other - before-add-* and after-add-*, with\n * `source: 'paste'` - so a host can turn one away.\n */\nimport { GridEditor } from '../grideditor.js';\nimport * as dom from '../dom.js';\n\nObject.assign(GridEditor.locales.en, {\n 'tool.copy': 'Copy',\n 'tool.paste': 'Paste',\n 'clipboard.paste_row': 'Paste row',\n 'clipboard.paste_section': 'Paste section',\n});\n\nvar STORAGE_ITEM = 'grideditor.clipboard';\nvar CHANGE = 'grideditor-clipboard';\nvar VERSION = 1;\n\n/** Where nothing can be stored, what was copied lives here, for this page. */\nvar memory = null;\n\n/**\n * What each drawer takes when something is pasted into it, by the kind of the\n * node the drawer belongs to. A category is what was copied: a row, a column,\n * a section, a text block, a container of any type, an element.\n */\nvar TARGETS = {\n column: ['row', 'text', 'container', 'element'],\n row: ['column'],\n section: ['row'],\n};\n\nfunction read() {\n var raw;\n\n try {\n raw = window.localStorage.getItem(STORAGE_ITEM);\n } catch (error) {\n return memory;\n }\n if (raw === null) { return memory; }\n\n try {\n var clip = JSON.parse(raw);\n return clip && clip.version === VERSION && clip.html ? clip : null;\n } catch (error) {\n return null;\n }\n}\n\nfunction write(clip) {\n try {\n window.localStorage.setItem(STORAGE_ITEM, JSON.stringify(clip));\n memory = null;\n } catch (error) {\n memory = clip;\n }\n\n announce();\n}\n\n/** Every editor on the page looks at the clipboard again. */\nfunction announce() {\n document.dispatchEvent(new CustomEvent(CHANGE));\n}\n\n// Another tab copied something: every editor here looks again\nwindow.addEventListener('storage', function(e) {\n if (e.key === STORAGE_ITEM) { announce(); }\n});\n\nGridEditor.features.clipboard = function(ge) {\n\n var shown = null; // What the toolbar's paste buttons stand for\n var listening = false; // Whether refresh is on the document's change event\n\n function used(name) {\n return !ge.settings.plugins || ge.settings.plugins.indexOf(name) !== -1;\n }\n\n /** A category this editor can take: the plugin that makes it is here. */\n function available(clip) {\n if (clip.category === 'container') {\n return !!GridEditor.containers[clip.kind] && used(clip.kind);\n }\n if (clip.category === 'section') { return !!GridEditor.features.sections && used('sections'); }\n if (clip.category === 'element') { return !!GridEditor.features.elements && used('elements'); }\n\n return true;\n }\n\n function fits(clip, categories) {\n return !!clip && categories.indexOf(clip.category) !== -1 && available(clip);\n }\n\n function categoryOf(kind) {\n if (GridEditor.containers[kind]) { return 'container'; }\n\n return ['row', 'column', 'section', 'text', 'element'].indexOf(kind) !== -1 ? kind : null;\n }\n\n function copy(node, kind) {\n var html = ge.nodeHtml(node);\n\n write({ version: VERSION, category: categoryOf(kind), kind: kind, html: html });\n ge.emit('after-copy', ge.payloadFor(kind, node, { source: 'tool' }));\n\n // The canvas came back with new drawers: the one to flash is the\n // node's new copy tool\n var tool = dom.child(ge.drawerOf(node), '.ge-copy');\n if (!tool) { return; }\n\n var icon = dom.one(tool, 'i');\n dom.addClass(tool, 'ge-copied');\n if (icon) { icon.setAttribute('class', 'bi bi-check2'); }\n setTimeout(function() {\n dom.removeClass(tool, 'ge-copied');\n if (icon) { icon.setAttribute('class', 'bi bi-copy'); }\n }, 1200);\n }\n\n function paste(target, categories) {\n var clip = read();\n if (!fits(clip, categories)) { return; }\n\n ge.place(ge.freshNode(clip.html), clip.kind, { appendTo: target, source: 'paste' });\n }\n\n /** Show the paste tools, and the toolbar's paste buttons, that fit what is copied. */\n function refresh() {\n var clip = read();\n\n dom.all(ge.canvas, '.ge-paste').forEach(function(tool) {\n dom.toggle(tool, fits(clip, tool.getAttribute('data-ge-paste').split(' ')));\n });\n\n ge.toolbarItems('clipboard').forEach(function(button) {\n var item = TOOLBAR[parseInt(button.getAttribute('data-ge-item'), 10)];\n dom.toggle(button, fits(clip, [item.kind]));\n });\n\n shown = clip;\n }\n\n var TOOLBAR = [\n { kind: 'row', labelKey: 'clipboard.paste_row' },\n { kind: 'section', labelKey: 'clipboard.paste_section' },\n ].map(function(item) {\n return Object.assign(item, {\n iconClass: 'bi bi-clipboard-plus',\n // On the right, as an icon: pasting is not one of the things\n // the add buttons make\n align: 'end',\n source: 'paste',\n // A toolbar built again - another plugin's buttons changed -\n // shows only what fits, as refresh would\n hidden: function() { return !fits(read(), [item.kind]); },\n // What the button showed, even if another tab has copied\n // something else since\n create: function() { return ge.freshNode(shown.html); },\n });\n });\n\n return {\n drawerTools: function(drawer, node, kind) {\n if (categoryOf(kind)) {\n ge.createTool(drawer, ge.t('tool.copy'), 'ge-copy', 'bi bi-copy', function() {\n copy(node, kind);\n });\n }\n\n var categories = TARGETS[kind];\n if (!categories) { return; }\n\n var tool = ge.createTool(drawer, ge.t('tool.paste'), 'ge-paste', 'bi bi-clipboard-plus', function() {\n paste(node, categories);\n });\n tool.setAttribute('data-ge-paste', categories.join(' '));\n dom.toggle(tool, fits(read(), categories));\n },\n\n toolbar: TOOLBAR,\n\n onInit: function() {\n refresh();\n if (!listening) {\n document.addEventListener(CHANGE, refresh);\n listening = true;\n }\n },\n\n onDeinit: function() {\n document.removeEventListener(CHANGE, refresh);\n listening = false;\n },\n\n // Nothing of its own on the canvas: the tools are in the drawers\n cleanMarkup: function() {},\n };\n};\n"],
5
+ "mappings": "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",
6
6
  "names": ["GridEditor", "all", "root", "selector", "one", "children", "node", "selector", "each", "child", "addClass", "node", "names", "split", "name", "removeClass", "split", "names", "defaultDisplays", "defaultDisplay", "node", "tag", "probe", "show", "hide", "toggle", "state", "visible", "GridEditor", "STORAGE_ITEM", "CHANGE", "VERSION", "memory", "TARGETS", "read", "raw", "error", "clip", "write", "announce", "e", "ge", "shown", "listening", "used", "name", "available", "fits", "categories", "categoryOf", "kind", "copy", "node", "html", "tool", "child", "icon", "one", "addClass", "removeClass", "paste", "target", "refresh", "all", "toggle", "button", "item", "TOOLBAR", "drawer"]
7
7
  }
package/docs/plugins.md CHANGED
@@ -377,7 +377,10 @@ rather than remembering anything about it.
377
377
  preview buttons, always as an icon - `bi bi-plus` when it has none.
378
378
  `label`, a string or a function called whenever the toolbar is built, stands
379
379
  in for `labelKey` when the label takes parameters; `className` goes on the
380
- button. With `inColumn: true` what it makes belongs in a column, as a
380
+ button. `hidden`, a boolean or a function called whenever the toolbar is
381
+ built, builds the button hidden: a plugin that shows and hides its buttons
382
+ gives it, so a toolbar built again - `ge.refreshToolbar()`, `setLocale` -
383
+ shows them as they are. With `inColumn: true` what it makes belongs in a column, as a
381
384
  container or a text does: a click puts it in a row and a column of its own
382
385
  at the end of the canvas, and a drop anywhere but in a column does the
383
386
  same where it was dropped.
package/package.json CHANGED
@@ -32,7 +32,7 @@
32
32
  "publishConfig": {
33
33
  "access": "public"
34
34
  },
35
- "version": "8.10.0",
35
+ "version": "9.0.0-beta.1",
36
36
  "main": "dist/grideditor.js",
37
37
  "module": "dist/grideditor.esm.js",
38
38
  "types": "dist/grideditor.d.ts",
@@ -403,7 +403,7 @@ function build(instance, baseElem, optionsOrMethod) {
403
403
  'toolbar_overflow' : 'menu', // What doesn't fit on one line: 'menu' behind a button, 'wrap' onto another line
404
404
  'drawer_overflow' : 'menu', // The same for the drawers' tools: 'menu' behind a button, 'wrap' onto another line
405
405
  'toolbar_groups' : false, // The add buttons in categories, one shown at a time, picked with tabs
406
- 'active_target' : false, // A click in a column makes it where the toolbar's buttons add
406
+ 'active_target' : false, // A click in a column makes it where the toolbar's buttons add. The 'sidebar' panel turns it on
407
407
  'element_tools' : [], // Host tools on element drawers, same shape as row_tools
408
408
  'element_classes' : [], // Preset class toggles on an element's settings panel
409
409
  // content_types, text_tools and text_classes are the text editor
@@ -442,6 +442,16 @@ function build(instance, baseElem, optionsOrMethod) {
442
442
  settings[name] = Object.assign({}, NESTED_SETTINGS[name], settings[name]);
443
443
  });
444
444
 
445
+ // The sidebar shows what is selected and the toolbar adds to the
446
+ // target: both follow the node last clicked, so one asks for the other.
447
+ // Read from what the host gave, as the default is already in settings
448
+ if (settings.settings_panel === 'sidebar') {
449
+ if (optionsOrMethod && optionsOrMethod.active_target === false) {
450
+ throw new TypeError('grid-editor: settings_panel "sidebar" needs active_target; leave it out or set it to true');
451
+ }
452
+ settings.active_target = true;
453
+ }
454
+
445
455
 
446
456
  // Elems
447
457
  var canvas,
@@ -1605,6 +1615,9 @@ function build(instance, baseElem, optionsOrMethod) {
1605
1615
  var button = labelButton(dom.element('a', { 'class': 'btn btn-sm ' + colour + ' ge-add-container ge-add-feature' }), label, iconClass);
1606
1616
 
1607
1617
  dom.addClass(button, item.className || '');
1618
+ // Hidden as it is now: a plugin that shows and hides its buttons
1619
+ // only hears of what changes, not of the toolbar being built again
1620
+ if (typeof item.hidden === 'function' ? item.hidden() : item.hidden) { dom.hide(button); }
1608
1621
  dom.attr(button, {
1609
1622
  'data-ge-toolbar': 'feature',
1610
1623
  'data-ge-feature': name,
@@ -168,6 +168,9 @@ GridEditor.features.clipboard = function(ge) {
168
168
  // the add buttons make
169
169
  align: 'end',
170
170
  source: 'paste',
171
+ // A toolbar built again - another plugin's buttons changed -
172
+ // shows only what fits, as refresh would
173
+ hidden: function() { return !fits(read(), [item.kind]); },
171
174
  // What the button showed, even if another tab has copied
172
175
  // something else since
173
176
  create: function() { return ge.freshNode(shown.html); },
@@ -161,7 +161,7 @@ export interface GridEditorOptions {
161
161
  drawer_overflow?: 'menu' | 'wrap';
162
162
  /** The add buttons in categories - rows, content, and a plugin's own - one shown at a time, chosen with tabs. Default false. */
163
163
  toolbar_groups?: boolean;
164
- /** A click in a column or a section makes it where the toolbar's buttons add. Default false. */
164
+ /** A click in a column or a section makes it where the toolbar's buttons add. Default false, and true with settings_panel 'sidebar', which throws if it is given false. */
165
165
  active_target?: boolean;
166
166
  /** The plugins to use, of any kind; null is every one loaded. */
167
167
  plugins?: string[] | null;
@@ -195,7 +195,7 @@ export interface GridEditorOptions {
195
195
  locale_strings?: Record<string, string>;
196
196
  callbacks?: Partial<GridEditorCallbacks>;
197
197
  confirm_delete?: boolean;
198
- /** Where a node's settings open. 'sidebar' is a panel that stays on the right while editing, showing the selected node's; one editor per page has it. */
198
+ /** Where a node's settings open. 'sidebar' is a panel that stays on the right while editing, showing the selected node's; one editor per page has it. It turns active_target on, and the constructor throws a TypeError if active_target is false. */
199
199
  settings_panel?: 'offcanvas' | 'popover' | 'modal' | 'inline' | 'sidebar';
200
200
  /** The 'sidebar' panel. */
201
201
  sidebar?: SidebarSettings;
@@ -487,7 +487,7 @@ export declare class GridEditor {
487
487
  /** Take the editor off: the markup stays, everything else goes. */
488
488
  destroy(): this;
489
489
  changeView(view: View): this;
490
- /** The column or region the toolbar adds to, or null. Null without active_target. */
490
+ /** The column or region the toolbar adds to, or null. Null without active_target, which settings_panel 'sidebar' turns on. */
491
491
  getActiveTarget(): HTMLElement | null;
492
492
  /** Make a column or region, or the first a selector matches, where the toolbar adds; null for none. */
493
493
  setActiveTarget(target: HTMLElement | string | null): this;