@themarioga/grid-editor 8.8.2 → 8.9.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.
@@ -504,6 +504,57 @@
504
504
  --bs-offcanvas-width: 340px;
505
505
  --bs-offcanvas-height: 60vh;
506
506
  }
507
+ /* The sidebar: always there while editing, with the page making room for it
508
+ (html.ge-sidebar-open) where the window is wide enough. Narrower than
509
+ Bootstrap's sm it covers the page instead. */
510
+ .ge-settings-sidebar {
511
+ --bs-offcanvas-width: 340px;
512
+ }
513
+ .ge-settings-sidebar .ge-sidebar-empty {
514
+ margin: 0;
515
+ color: var(--bs-secondary-color, #6c757d);
516
+ }
517
+ html.ge-sidebar-open {
518
+ padding-right: 340px;
519
+ }
520
+ /* On the panel's left edge, centred; folded away, on the window's right edge.
521
+ Its own element, so the panel can slide out from under it. */
522
+ .ge-sidebar-toggle {
523
+ position: fixed;
524
+ top: 50%;
525
+ right: 340px;
526
+ z-index: 1045;
527
+ display: flex;
528
+ align-items: center;
529
+ justify-content: center;
530
+ width: 1.5rem;
531
+ height: 3rem;
532
+ padding: 0;
533
+ transform: translateY(-50%);
534
+ border: 1px solid var(--bs-border-color, #dee2e6);
535
+ border-right: 0;
536
+ border-radius: 0.375rem 0 0 0.375rem;
537
+ background: var(--bs-body-bg, #fff);
538
+ color: var(--bs-body-color, #212529);
539
+ transition: right 0.3s ease-in-out;
540
+ }
541
+ .ge-sidebar-toggle[aria-expanded="false"] {
542
+ right: 0;
543
+ }
544
+ .ge-sidebar-toggle.ge-under-dialog {
545
+ z-index: 1040;
546
+ }
547
+ @media (max-width: 575.98px) {
548
+ .ge-settings-sidebar {
549
+ --bs-offcanvas-width: 100vw;
550
+ }
551
+ .ge-sidebar-toggle[aria-expanded="true"] {
552
+ right: calc(100vw - 1.5rem);
553
+ border: 1px solid var(--bs-border-color, #dee2e6);
554
+ border-left: 0;
555
+ border-radius: 0 0.375rem 0.375rem 0;
556
+ }
557
+ }
507
558
  .ge-settings-popover {
508
559
  /* Popper would position it; without Popper that is the editor's job */
509
560
  position: absolute;
@@ -121,6 +121,18 @@ export interface InlineStyleOptions {
121
121
  visibility?: { drawer?: boolean };
122
122
  }
123
123
 
124
+ /** What the sidebar shows while nothing is selected, instead of its message. */
125
+ export interface SidebarContent {
126
+ /** Text, not html. Default the sidebar's own title. */
127
+ title?: string;
128
+ body: Element;
129
+ }
130
+
131
+ export interface SidebarSettings {
132
+ /** Called whenever the sidebar is left with nothing selected. Null gives the message. */
133
+ empty?: ((ge: GridEditor) => SidebarContent | null) | null;
134
+ }
135
+
124
136
  export type CustomFilter = ((canvas: HTMLElement, isInit: boolean) => void) | string;
125
137
 
126
138
  /** The settings. Every one is optional. */
@@ -183,7 +195,10 @@ export interface GridEditorOptions {
183
195
  locale_strings?: Record<string, string>;
184
196
  callbacks?: Partial<GridEditorCallbacks>;
185
197
  confirm_delete?: boolean;
186
- settings_panel?: 'offcanvas' | 'popover' | 'modal' | 'inline';
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. */
199
+ settings_panel?: 'offcanvas' | 'popover' | 'modal' | 'inline' | 'sidebar';
200
+ /** The 'sidebar' panel. */
201
+ sidebar?: SidebarSettings;
187
202
  drag?: { delay?: number; touch_delay?: number; threshold?: number; animation?: number; scroll?: boolean };
188
203
  tinymce?: { config?: Record<string, unknown> };
189
204
  ckeditor?: { config?: Record<string, unknown> };
@@ -317,6 +332,13 @@ export interface TargetChangePayload {
317
332
  from: HTMLElement | null;
318
333
  }
319
334
 
335
+ /** The selection changed, with settings_panel 'sidebar': the node whose settings the sidebar shows, or null. */
336
+ export interface SelectionChangePayload {
337
+ canvas: HTMLElement;
338
+ node: HTMLElement | null;
339
+ from: HTMLElement | null;
340
+ }
341
+
320
342
  /** The events, by name, with their payloads. Adding fires a specific name and the generic one. */
321
343
  export interface GridEditorEventMap {
322
344
  'grideditor:before-add': Payload;
@@ -357,6 +379,7 @@ export interface GridEditorEventMap {
357
379
  'grideditor:popup-orphan': PopupOrphanPayload;
358
380
  'grideditor:view-change': ViewChangePayload;
359
381
  'grideditor:target-change': TargetChangePayload;
382
+ 'grideditor:selection-change': SelectionChangePayload;
360
383
  'grideditor:after-autosave': AutosavePayload;
361
384
  'grideditor:after-restore-draft': RestoreDraftPayload;
362
385
  'grideditor:autosave-error': AutosaveErrorPayload;
@@ -468,6 +491,10 @@ export declare class GridEditor {
468
491
  getActiveTarget(): HTMLElement | null;
469
492
  /** Make a column or region, or the first a selector matches, where the toolbar adds; null for none. */
470
493
  setActiveTarget(target: HTMLElement | string | null): this;
494
+ /** The node whose settings the sidebar shows, or null. Null unless settings_panel is 'sidebar'. */
495
+ getSelected(): HTMLElement | null;
496
+ /** Select a node with settings, or the first a selector matches, or null for none. Does nothing unless settings_panel is 'sidebar'. */
497
+ setSelected(node: HTMLElement | string | null): this;
471
498
  getView(): View;
472
499
  setLocale(code: string): this;
473
500
  createRow(layout?: RowLayout, options?: Placement): HTMLElement | null;
@@ -328,7 +328,9 @@ var METHODS = {
328
328
  getUtility: { value: true },
329
329
  setUtility: { value: true },
330
330
  getActiveTarget: { value: true },
331
- setActiveTarget: {}
331
+ setActiveTarget: {},
332
+ getSelected: { value: true },
333
+ setSelected: {}
332
334
  };
333
335
  var PLACEMENTS = ["appendTo", "prependTo", "insertAfter", "insertBefore"];
334
336
  var BREAKPOINTS = [
@@ -412,6 +414,10 @@ var NESTED_SETTINGS = {
412
414
  tools: true
413
415
  // The indent tools in the column's drawer
414
416
  },
417
+ sidebar: {
418
+ empty: null
419
+ // function(ge) -> { title, body } shown while nothing is selected, or null for the message
420
+ },
415
421
  drag: {
416
422
  delay: 0,
417
423
  // Milliseconds to hold before a drag starts
@@ -428,6 +434,8 @@ var NESTED_SETTINGS = {
428
434
  var warned = {};
429
435
  var editorCounter = 0;
430
436
  var instances = /* @__PURE__ */ new WeakMap();
437
+ var sidebarHolder = null;
438
+ var EDITOR_CONTROLS = ".ge-mainControls, .ge-settings-panel, .ge-sidebar-toggle, .ge-dialog, .ge-confirm, .ge-size-picker, .modal, .modal-backdrop";
431
439
  function translate(settings, key, params) {
432
440
  var locales = GridEditor.locales;
433
441
  var locale = locales[settings.locale] || {};
@@ -633,7 +641,9 @@ function build(instance, baseElem, optionsOrMethod) {
633
641
  "confirm_delete": true,
634
642
  // Ask before deleting a row or a column
635
643
  "settings_panel": "offcanvas",
636
- // Where a node's settings open: 'offcanvas', 'popover', 'modal' or 'inline'
644
+ // Where a node's settings open: 'offcanvas', 'popover', 'modal', 'inline' or 'sidebar'
645
+ "sidebar": NESTED_SETTINGS.sidebar,
646
+ // The 'sidebar' panel: what it shows while nothing is selected
637
647
  "drag": NESTED_SETTINGS.drag
638
648
  // How a drag behaves, whatever drives it
639
649
  }, optionsOrMethod);
@@ -659,6 +669,17 @@ function build(instance, baseElem, optionsOrMethod) {
659
669
  var sortables = [];
660
670
  var instanceId = ++editorCounter;
661
671
  var destroyed = false;
672
+ var selected = null;
673
+ var sidebarCollapsed = null;
674
+ var pluginHandle = null;
675
+ if (settings.settings_panel === "sidebar") {
676
+ if (sidebarHolder) {
677
+ warnOnceHere("sidebar", 'settings_panel "sidebar" is in use by another editor on the page, which has one sidebar: using offcanvas');
678
+ settings.settings_panel = "offcanvas";
679
+ } else {
680
+ sidebarHolder = instance;
681
+ }
682
+ }
662
683
  var detailsFor = /* @__PURE__ */ new WeakMap();
663
684
  var moves = /* @__PURE__ */ new WeakMap();
664
685
  var resizes = /* @__PURE__ */ new WeakMap();
@@ -1096,7 +1117,8 @@ function build(instance, baseElem, optionsOrMethod) {
1096
1117
  e.preventDefault();
1097
1118
  }
1098
1119
  }, signal);
1099
- if (settings.active_target) {
1120
+ var sidebarMode = panelMode() === "sidebar";
1121
+ if (settings.active_target || sidebarMode) {
1100
1122
  var pressed = null;
1101
1123
  canvas.addEventListener("pointerdown", function(e) {
1102
1124
  pressed = { x: e.clientX, y: e.clientY };
@@ -1110,24 +1132,61 @@ function build(instance, baseElem, optionsOrMethod) {
1110
1132
  if (from && Math.abs(e.clientX - from.x) + Math.abs(e.clientY - from.y) > settings.drag.threshold) {
1111
1133
  return;
1112
1134
  }
1113
- var region = closest(e.target, targetSelector());
1114
- if (region && region === activeTarget && (e.target === region || e.target === child(region, ".ge-tools-drawer"))) {
1115
- changeTarget(null);
1116
- return;
1135
+ if (settings.active_target) {
1136
+ clickTarget(e);
1137
+ }
1138
+ if (sidebarMode && canvas.contains(e.target)) {
1139
+ changeSelection(settingsNodeAt(e.target));
1117
1140
  }
1118
- changeTarget(region && region !== canvas && canvas.contains(region) ? region : null);
1119
1141
  }, signal);
1120
1142
  document.addEventListener("keydown", function(e) {
1121
- if (e.key !== "Escape" || !activeTarget || openSettingsState || dialogState) {
1143
+ if (e.key !== "Escape" || openSettingsState || dialogState) {
1144
+ return;
1145
+ }
1146
+ if (confirmDialog && hasClass(confirmDialog, "show")) {
1122
1147
  return;
1123
1148
  }
1124
1149
  var focus = document.activeElement;
1125
1150
  if (focus && (is(focus, "input, textarea, select") || focus.isContentEditable)) {
1126
1151
  return;
1127
1152
  }
1128
- changeTarget(null);
1153
+ if (selected && isEditing()) {
1154
+ changeSelection(null);
1155
+ } else if (activeTarget) {
1156
+ changeTarget(null);
1157
+ }
1129
1158
  }, signal);
1130
1159
  }
1160
+ if (sidebarMode) {
1161
+ canvas.addEventListener("focusin", function(e) {
1162
+ var node = isEditing() ? settingsNodeAt(e.target) : null;
1163
+ if (node) {
1164
+ changeSelection(node);
1165
+ }
1166
+ }, signal);
1167
+ document.addEventListener("click", function(e) {
1168
+ var target = e.target;
1169
+ if (!selected || !isEditing() || !target || target.nodeType !== 1) {
1170
+ return;
1171
+ }
1172
+ if (!attached(target) || canvas.contains(target)) {
1173
+ return;
1174
+ }
1175
+ if (closest(target, EDITOR_CONTROLS) || canvas.querySelector(".ge-rte-active")) {
1176
+ return;
1177
+ }
1178
+ changeSelection(null);
1179
+ }, signal);
1180
+ window.addEventListener("resize", reserveSidebarRoom, signal);
1181
+ }
1182
+ function clickTarget(e) {
1183
+ var region = closest(e.target, targetSelector());
1184
+ if (region && region === activeTarget && (e.target === region || e.target === child(region, ".ge-tools-drawer"))) {
1185
+ changeTarget(null);
1186
+ return;
1187
+ }
1188
+ changeTarget(region && region !== canvas && canvas.contains(region) ? region : null);
1189
+ }
1131
1190
  }
1132
1191
  function textReady(block) {
1133
1192
  if (!is(block, ".ge-content") || !canvas.contains(block)) {
@@ -1898,6 +1957,7 @@ function build(instance, baseElem, optionsOrMethod) {
1898
1957
  function openSource() {
1899
1958
  deinit();
1900
1959
  changeTarget(null);
1960
+ changeSelection(null);
1901
1961
  htmlTextArea.style.height = 0.8 * document.documentElement.clientHeight + "px";
1902
1962
  htmlTextArea.value = canvas.innerHTML;
1903
1963
  show(htmlTextArea);
@@ -1943,6 +2003,9 @@ function build(instance, baseElem, optionsOrMethod) {
1943
2003
  if (openSettingsState && !attached(openSettingsState.node)) {
1944
2004
  closeSettings();
1945
2005
  }
2006
+ if (selected && !canvas.contains(selected)) {
2007
+ changeSelection(null);
2008
+ }
1946
2009
  if (dialogState && dialogState.home && !attached(dialogState.home)) {
1947
2010
  closeDialog();
1948
2011
  }
@@ -1967,9 +2030,11 @@ function build(instance, baseElem, optionsOrMethod) {
1967
2030
  } else if (activeTarget) {
1968
2031
  addClass(activeTarget, "ge-active-target");
1969
2032
  }
2033
+ showSidebar();
1970
2034
  }
1971
2035
  function deinit() {
1972
2036
  closeSettings();
2037
+ hideSidebar();
1973
2038
  removeClass(canvas, "ge-editing ge-drag-drawer ge-dropping");
1974
2039
  if (activeTarget) {
1975
2040
  removeClass(activeTarget, "ge-active-target");
@@ -2130,6 +2195,10 @@ function build(instance, baseElem, optionsOrMethod) {
2130
2195
  }
2131
2196
  deinit();
2132
2197
  activeTarget = null;
2198
+ selected = null;
2199
+ if (sidebarHolder === instance) {
2200
+ sidebarHolder = null;
2201
+ }
2133
2202
  removeConfirmModal();
2134
2203
  removeSettingsPanels();
2135
2204
  removeDialog();
@@ -2140,7 +2209,7 @@ function build(instance, baseElem, optionsOrMethod) {
2140
2209
  destroyed = true;
2141
2210
  }
2142
2211
  function loadPlugins() {
2143
- var api = pluginApi();
2212
+ var api = pluginHandle = pluginApi();
2144
2213
  registerFamily("grid", {}, widthFamily());
2145
2214
  if (settings.row_cols !== false) {
2146
2215
  registerFamily("grid", {}, rowColsFamily());
@@ -3261,9 +3330,11 @@ function build(instance, baseElem, optionsOrMethod) {
3261
3330
  function addSettingsTool(drawer, node, presets) {
3262
3331
  var details = createDetails(node, presets || []);
3263
3332
  detailsFor.set(node, details);
3264
- createTool(drawer, t("tool.settings"), "ge-settings", "bi bi-gear-fill", function() {
3265
- toggleSettings(node, details, this);
3266
- });
3333
+ if (panelMode() !== "sidebar") {
3334
+ createTool(drawer, t("tool.settings"), "ge-settings", "bi bi-gear-fill", function() {
3335
+ toggleSettings(node, details, this);
3336
+ });
3337
+ }
3267
3338
  var kind = kindOf(node);
3268
3339
  Object.keys(UTILITIES).forEach(function(name) {
3269
3340
  if (UTILITIES[name].drawerTools) {
@@ -3354,7 +3425,7 @@ function build(instance, baseElem, optionsOrMethod) {
3354
3425
  return;
3355
3426
  }
3356
3427
  var holder = element("div", { "class": "ge-panel-section", "data-ge-plugin": name });
3357
- if (mode === "offcanvas" || mode === "modal") {
3428
+ if (mode === "offcanvas" || mode === "modal" || mode === "sidebar") {
3358
3429
  holder.appendChild(sectionTitle(t(section.labelKey)));
3359
3430
  holder.appendChild(section.body);
3360
3431
  } else {
@@ -3378,7 +3449,7 @@ function build(instance, baseElem, optionsOrMethod) {
3378
3449
  });
3379
3450
  return sections;
3380
3451
  }
3381
- var PANEL_MODES = ["offcanvas", "popover", "modal", "inline"];
3452
+ var PANEL_MODES = ["offcanvas", "popover", "modal", "inline", "sidebar"];
3382
3453
  var settingsPanels = {};
3383
3454
  var openSettingsState = null;
3384
3455
  var modalState = /* @__PURE__ */ new WeakMap();
@@ -3393,7 +3464,14 @@ function build(instance, baseElem, optionsOrMethod) {
3393
3464
  return node && detailsFor.get(node) || null;
3394
3465
  }
3395
3466
  function settingsScope() {
3396
- return openSettingsState && openSettingsState.mode !== "inline" ? [canvas, openSettingsState.details] : [canvas];
3467
+ var scope = [canvas];
3468
+ if (openSettingsState && openSettingsState.mode !== "inline") {
3469
+ scope.push(openSettingsState.details);
3470
+ }
3471
+ if (sidebarShown) {
3472
+ scope.push(sidebarShown.details);
3473
+ }
3474
+ return scope;
3397
3475
  }
3398
3476
  function toggleSettings(node, details, gear) {
3399
3477
  var mode = panelMode();
@@ -3483,6 +3561,9 @@ function build(instance, baseElem, optionsOrMethod) {
3483
3561
  } else {
3484
3562
  hideModal(panel);
3485
3563
  }
3564
+ returnPanel(open);
3565
+ }
3566
+ function returnPanel(open) {
3486
3567
  removeClass(open.node, "ge-settings-target");
3487
3568
  open.details.style.removeProperty("display");
3488
3569
  dropEmptyStyle(open.details);
@@ -3515,6 +3596,11 @@ function build(instance, baseElem, optionsOrMethod) {
3515
3596
  panel = create('<div class="popover bs-popover-bottom ge-settings-panel ge-settings-popover" role="dialog"><div class="popover-arrow"></div><div class="popover-header"><span class="ge-settings-title"></span></div><div class="popover-body ge-settings-body"></div></div>');
3516
3597
  one(panel, ".popover-header").appendChild(closeButton());
3517
3598
  hide(panel);
3599
+ } else if (mode === "sidebar") {
3600
+ panel = create('<div class="offcanvas offcanvas-end ge-settings-panel ge-settings-sidebar" tabindex="-1" role="complementary"><div class="offcanvas-header"><h5 class="offcanvas-title ge-settings-title"></h5></div><div class="offcanvas-body ge-settings-body"></div></div>');
3601
+ panel.id = "ge-settings-sidebar-" + instanceId;
3602
+ one(panel, ".offcanvas-header").appendChild(closeButton());
3603
+ settingsPanels["sidebar-toggle"] = document.body.appendChild(sidebarToggle(panel));
3518
3604
  } else {
3519
3605
  panel = create('<div class="modal fade ge-settings-panel ge-settings-modal" tabindex="-1" role="dialog" aria-hidden="true"><div class="modal-dialog modal-dialog-centered modal-dialog-scrollable"><div class="modal-content"><div class="modal-header"><h5 class="modal-title ge-settings-title"></h5></div><div class="modal-body ge-settings-body"></div><div class="modal-footer"></div></div></div></div>');
3520
3606
  one(panel, ".modal-header").appendChild(closeButton());
@@ -3525,13 +3611,19 @@ function build(instance, baseElem, optionsOrMethod) {
3525
3611
  }
3526
3612
  delegate(panel, "click", ".ge-settings-close", function(e) {
3527
3613
  e.preventDefault();
3528
- closeSettings();
3614
+ if (mode === "sidebar") {
3615
+ changeSelection(null);
3616
+ } else {
3617
+ closeSettings();
3618
+ }
3529
3619
  });
3530
3620
  settingsPanels[mode] = document.body.appendChild(panel);
3531
3621
  return panel;
3532
3622
  }
3533
3623
  function removeSettingsPanels() {
3534
3624
  closeSettings();
3625
+ unshowSidebar();
3626
+ removeClass(document.documentElement, "ge-sidebar-open");
3535
3627
  Object.keys(settingsPanels).forEach(function(mode) {
3536
3628
  if (mode === "modal") {
3537
3629
  retireModal(settingsPanels[mode]);
@@ -3544,6 +3636,180 @@ function build(instance, baseElem, optionsOrMethod) {
3544
3636
  });
3545
3637
  settingsPanels = {};
3546
3638
  }
3639
+ var SIDEBAR_MIN_WIDTH = 576;
3640
+ var sidebarShown = null;
3641
+ var sidebarEmpty = null;
3642
+ function isEditing() {
3643
+ return hasClass(canvas, "ge-editing");
3644
+ }
3645
+ function sidebarToggle(panel) {
3646
+ var button = element("button", { type: "button", "class": "ge-sidebar-toggle", "aria-controls": panel.id });
3647
+ button.appendChild(element("i"));
3648
+ button.addEventListener("click", function() {
3649
+ foldSidebar(!sidebarCollapsed);
3650
+ });
3651
+ return button;
3652
+ }
3653
+ function foldSidebar(collapsed) {
3654
+ sidebarCollapsed = collapsed;
3655
+ applySidebar();
3656
+ }
3657
+ function applySidebar() {
3658
+ var panel = settingsPanels.sidebar;
3659
+ var toggle2 = settingsPanels["sidebar-toggle"];
3660
+ if (!panel) {
3661
+ return;
3662
+ }
3663
+ var open = !sidebarCollapsed;
3664
+ if (open && !hasClass(panel, "show")) {
3665
+ removeClass(panel, "hiding");
3666
+ panel.getBoundingClientRect();
3667
+ addClass(panel, "show");
3668
+ } else if (!open && hasClass(panel, "show")) {
3669
+ removeClass(panel, "show");
3670
+ addClass(panel, "hiding");
3671
+ window.setTimeout(function() {
3672
+ removeClass(panel, "hiding");
3673
+ }, 300);
3674
+ }
3675
+ var label = open ? t("panel.collapse") : t("panel.expand");
3676
+ toggle2.setAttribute("aria-expanded", open ? "true" : "false");
3677
+ toggle2.setAttribute("aria-label", label);
3678
+ toggle2.setAttribute("title", label);
3679
+ one(toggle2, "i").className = "bi " + (open ? "bi-chevron-right" : "bi-chevron-left");
3680
+ reserveSidebarRoom();
3681
+ }
3682
+ function reserveSidebarRoom() {
3683
+ toggleClass(
3684
+ document.documentElement,
3685
+ "ge-sidebar-open",
3686
+ !!settingsPanels.sidebar && isEditing() && !sidebarCollapsed && window.innerWidth >= SIDEBAR_MIN_WIDTH
3687
+ );
3688
+ }
3689
+ function showSidebar() {
3690
+ if (panelMode() !== "sidebar") {
3691
+ return;
3692
+ }
3693
+ var panel = settingsPanel("sidebar");
3694
+ if (sidebarCollapsed === null) {
3695
+ sidebarCollapsed = window.innerWidth < SIDEBAR_MIN_WIDTH;
3696
+ }
3697
+ panel.style.removeProperty("display");
3698
+ settingsPanels["sidebar-toggle"].style.removeProperty("display");
3699
+ renderSidebar();
3700
+ applySidebar();
3701
+ }
3702
+ function hideSidebar() {
3703
+ unshowDetails();
3704
+ var panel = settingsPanels.sidebar;
3705
+ if (!panel) {
3706
+ return;
3707
+ }
3708
+ panel.style.display = "none";
3709
+ settingsPanels["sidebar-toggle"].style.display = "none";
3710
+ removeClass(document.documentElement, "ge-sidebar-open");
3711
+ }
3712
+ function setSidebarTitle(text) {
3713
+ var panel = settingsPanels.sidebar;
3714
+ one(panel, ".ge-settings-title").textContent = text;
3715
+ panel.setAttribute("aria-label", text);
3716
+ }
3717
+ function unshowDetails() {
3718
+ var shown = sidebarShown;
3719
+ sidebarShown = null;
3720
+ if (shown) {
3721
+ returnPanel(shown);
3722
+ }
3723
+ }
3724
+ function unshowSidebar() {
3725
+ unshowDetails();
3726
+ if (sidebarEmpty) {
3727
+ sidebarEmpty.body.remove();
3728
+ sidebarEmpty = null;
3729
+ }
3730
+ }
3731
+ function renderSidebar() {
3732
+ var panel = settingsPanels.sidebar;
3733
+ if (!panel || !isEditing()) {
3734
+ return;
3735
+ }
3736
+ var details = selected ? detailsOf(selected) : null;
3737
+ if (details ? sidebarShown && sidebarShown.details === details : !sidebarShown && sidebarEmpty) {
3738
+ return;
3739
+ }
3740
+ unshowSidebar();
3741
+ var holder = one(panel, ".ge-settings-body");
3742
+ if (!details) {
3743
+ var content = emptyContent();
3744
+ var body = content ? content.body : element("p", { "class": "ge-sidebar-empty" }, t("panel.sidebar_empty"));
3745
+ setSidebarTitle(content ? content.title : t("panel.sidebar_title"));
3746
+ holder.appendChild(body);
3747
+ sidebarEmpty = { body };
3748
+ return;
3749
+ }
3750
+ sidebarShown = { node: selected, details, home: details.parentElement, next: details.nextElementSibling };
3751
+ setSidebarTitle(t("panel.title", { kind: kindLabel(selected) }));
3752
+ holder.appendChild(details);
3753
+ show(details);
3754
+ addClass(selected, "ge-settings-target");
3755
+ }
3756
+ function emptyContent() {
3757
+ var fromHost = settings.sidebar.empty;
3758
+ var found = null;
3759
+ if (typeof fromHost === "function") {
3760
+ found = emptyContentOf(fromHost(instance), function(result) {
3761
+ return result.title;
3762
+ });
3763
+ } else if (fromHost !== null && fromHost !== void 0) {
3764
+ warnOnceHere("sidebar.empty", "sidebar.empty is a function(ge) returning { title, body }, not " + typeof fromHost + ": ignored");
3765
+ }
3766
+ [CONTAINERS, FEATURES, UTILITIES].forEach(function(registry) {
3767
+ Object.keys(registry).forEach(function(name) {
3768
+ var plugin = registry[name];
3769
+ if (found || !plugin.sidebarEmpty) {
3770
+ return;
3771
+ }
3772
+ found = emptyContentOf(plugin.sidebarEmpty(pluginHandle), function(result) {
3773
+ return typeof result.titleKey === "string" && result.titleKey !== "" ? t(result.titleKey) : "";
3774
+ });
3775
+ });
3776
+ });
3777
+ return found;
3778
+ }
3779
+ function emptyContentOf(result, titleOf) {
3780
+ if (!result || !result.body || result.body.nodeType !== 1) {
3781
+ return null;
3782
+ }
3783
+ var title = titleOf(result);
3784
+ return { title: typeof title === "string" && title !== "" ? title : t("panel.sidebar_title"), body: result.body };
3785
+ }
3786
+ function settingsNodeAt(target) {
3787
+ var drawer = closest(target, ".ge-tools-drawer", canvas);
3788
+ if (drawer) {
3789
+ var home = drawer.parentElement;
3790
+ var owner = detailsFor.has(home) ? home : children(home).filter(function(each) {
3791
+ return detailsFor.has(each);
3792
+ })[0];
3793
+ if (owner) {
3794
+ return owner;
3795
+ }
3796
+ }
3797
+ for (var node = target; node && node !== canvas; node = node.parentElement) {
3798
+ if (detailsFor.has(node)) {
3799
+ return node;
3800
+ }
3801
+ }
3802
+ return null;
3803
+ }
3804
+ function changeSelection(node) {
3805
+ if (node === selected) {
3806
+ return;
3807
+ }
3808
+ var from = selected;
3809
+ selected = node;
3810
+ renderSidebar();
3811
+ emit("selection-change", { canvas, node, from });
3812
+ }
3547
3813
  function placePopover(panel, gear) {
3548
3814
  if (!attached(gear)) {
3549
3815
  return;
@@ -4658,6 +4924,23 @@ function build(instance, baseElem, optionsOrMethod) {
4658
4924
  }
4659
4925
  changeTarget(node);
4660
4926
  }
4927
+ function apiSetSelected(value) {
4928
+ if (panelMode() !== "sidebar") {
4929
+ warnOnceHere("sidebar-api", 'setSelected needs settings_panel "sidebar": without it, it does nothing');
4930
+ return;
4931
+ }
4932
+ if (value === null || value === void 0) {
4933
+ changeSelection(null);
4934
+ return;
4935
+ }
4936
+ var node = nodeFrom(value);
4937
+ if (!node || node === canvas || !canvas.contains(node) || !detailsFor.has(node)) {
4938
+ var named = typeof value === "string" ? value : node ? "<" + node.tagName.toLowerCase() + ">" : String(value);
4939
+ warn("setSelected: " + named + " is not a node of the canvas with settings");
4940
+ return;
4941
+ }
4942
+ changeSelection(node);
4943
+ }
4661
4944
  var own = {
4662
4945
  getHtml,
4663
4946
  getPlainHtml,
@@ -4696,7 +4979,11 @@ function build(instance, baseElem, optionsOrMethod) {
4696
4979
  getActiveTarget: function() {
4697
4980
  return settings.active_target ? activeTarget : null;
4698
4981
  },
4699
- setActiveTarget: apiSetActiveTarget
4982
+ setActiveTarget: apiSetActiveTarget,
4983
+ getSelected: function() {
4984
+ return panelMode() === "sidebar" ? selected : null;
4985
+ },
4986
+ setSelected: apiSetSelected
4700
4987
  };
4701
4988
  Object.keys(own).forEach(function(name) {
4702
4989
  var value = !!(METHODS[name] && METHODS[name].value);
@@ -4747,7 +5034,7 @@ GridEditor.t = translate;
4747
5034
  GridEditor._plainHtml = plainHtml;
4748
5035
  GridEditor.Sortable = null;
4749
5036
  GridEditor.bootstrap = null;
4750
- GridEditor.version = false ? "dev" : "8.8.2";
5037
+ GridEditor.version = false ? "dev" : "8.9.0";
4751
5038
  GridEditor.locales = {
4752
5039
  en: {
4753
5040
  "tool.move": "Move",
@@ -4780,6 +5067,10 @@ GridEditor.locales = {
4780
5067
  "panel.title": "{kind} settings",
4781
5068
  "panel.close": "Close",
4782
5069
  "panel.done": "Done",
5070
+ "panel.sidebar_title": "Settings",
5071
+ "panel.sidebar_empty": "Click an element to see its settings",
5072
+ "panel.collapse": "Hide settings",
5073
+ "panel.expand": "Show settings",
4783
5074
  "panel.id": "Id",
4784
5075
  "panel.classes": "Classes",
4785
5076
  "panel.section_general": "Id and classes",
@@ -47,7 +47,9 @@
47
47
  getUtility: { value: true },
48
48
  setUtility: { value: true },
49
49
  getActiveTarget: { value: true },
50
- setActiveTarget: {}
50
+ setActiveTarget: {},
51
+ getSelected: { value: true },
52
+ setSelected: {}
51
53
  };
52
54
  var TOOL_SETTINGS = [
53
55
  "row_tools",
@@ -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");var i=window.jQuery,T=function(a,o){return u.t({locale:"en"},a,o)},m={};function b(a,o){m[a]||(m[a]=!0,window.console&&window.console.warn&&window.console.warn("grid-editor: "+o))}!i||!i.fn?b("no-jquery",T("warning.adapter_no_jquery")):x();function x(){var a={getHtml:{value:!0},getPlainHtml:{value:!0},init:{},deinit:{},reset:{},destroy:{},changeView:{},getView:{value:!0},createRow:{value:!0},createColumn:{value:!0},createElement:{value:!0},createText:{value:!0},createSection:{value:!0},createContainer:{value:!0},addTab:{value:!0},addAccordionItem:{value:!0},setLocale:{},getUtility:{value:!0},setUtility:{value:!0},getActiveTarget:{value:!0},setActiveTarget:{}},o=["row_tools","col_tools","element_tools","container_tools","tab_tools","accordion_tools","text_tools"];function g(r){return!!r&&r.nodeType===1}function l(r){return r instanceof i?r[0]||null:r}function h(r){if(r instanceof i)return l(r);if(r&&typeof r=="object"&&!Array.isArray(r)&&!g(r)){var t={};return Object.keys(r).forEach(function(e){t[e]=l(r[e])}),t}return r}function w(r){return g(r)?i(r):r}var s=new WeakMap;function p(r){if(!r||typeof r!="object")return r;if(s.has(r))return s.get(r);var t=Object.assign({},r);return["node","parent","canvas"].forEach(function(e){e in r&&(t[e]=i(r[e]||[]))}),r.container&&(t.container=i(r.container)),Array.isArray(r.nodes)&&(t.node=i(r.nodes)),["from","to"].forEach(function(e){var n=r[e];n&&typeof n=="object"&&"parent"in n&&(t[e]=Object.assign({},n,{parent:i(n.parent||[])}))}),s.set(r,t),t}var v=i.event.dispatch;i.event.dispatch=function(r){return arguments.length===1&&r instanceof window.CustomEvent&&typeof r.type=="string"&&r.type.indexOf("grideditor:")===0?v.call(this,r,p(r.detail)):v.apply(this,arguments)};function y(r){return function(t){return r.call(this,i.event.fix(t))}}function _(r){if(!r||!r.on)return r;var t;return typeof r.on=="function"?t=y(r.on):(t={},Object.keys(r.on).forEach(function(e){t[e]=y(r.on[e])})),Object.assign({},r,{on:t})}function j(r){return function(t,e){var n=typeof r=="string"?window[r]:r;return n(i(t),e)}}function E(r){if(r=Object.assign({},r||{}),r.callbacks){var t={};Object.keys(r.callbacks).forEach(function(n){var c=r.callbacks[n];t[n]=typeof c=="function"?function(f){return c(p(f))}:c}),r.callbacks=t}if(r.custom_filter&&r.custom_filter.length){var e=typeof r.custom_filter=="string"||typeof r.custom_filter=="function"?[r.custom_filter]:Array.prototype.slice.call(r.custom_filter);r.custom_filter=e.map(j)}return o.forEach(function(n){Array.isArray(r[n])&&(r[n]=r[n].map(_))}),r.source_textarea=l(r.source_textarea),r}function A(r){var t={canvas:i(r.canvas)};return Object.keys(a).forEach(function(e){t[e]=function(){var n=r[e].apply(r,Array.prototype.map.call(arguments,h));return n===r?void 0:w(n)}}),Object.defineProperty(t,"settings",{enumerable:!0,get:function(){return r.settings}}),t}u._created.push(function(r){i.data(r.canvas,"grideditor",A(r));var t=r.destroy;r.destroy=function(){var e=t.apply(this,arguments);return i.removeData(r.canvas,"grideditor"),e}});function O(r,t,e){var n=a[t];if(!n)return b("method:"+t,'unknown method "'+t+'"'),r;if(e=e.map(h),n.value){var c=r.first();if(!c.length)return null;var f=u.get(c[0]);return f?w(f[t].apply(f,e)):t==="getHtml"?c.html():t==="getPlainHtml"?u._plainHtml(c.html()):null}return r.each(function(){var d=u.get(this);d&&d[t].apply(d,e)}),r}i.fn.gridEditor=function(r){if(typeof r=="string")return O(this,r,Array.prototype.slice.call(arguments,1));var t=E(r);return this.each(function(){u.create(this,t)}),this},i.fn.gridEditor.locales=u.locales,i.fn.gridEditor.t=u.t}})();
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");var i=window.jQuery,T=function(a,o){return u.t({locale:"en"},a,o)},m={};function b(a,o){m[a]||(m[a]=!0,window.console&&window.console.warn&&window.console.warn("grid-editor: "+o))}!i||!i.fn?b("no-jquery",T("warning.adapter_no_jquery")):x();function x(){var a={getHtml:{value:!0},getPlainHtml:{value:!0},init:{},deinit:{},reset:{},destroy:{},changeView:{},getView:{value:!0},createRow:{value:!0},createColumn:{value:!0},createElement:{value:!0},createText:{value:!0},createSection:{value:!0},createContainer:{value:!0},addTab:{value:!0},addAccordionItem:{value:!0},setLocale:{},getUtility:{value:!0},setUtility:{value:!0},getActiveTarget:{value:!0},setActiveTarget:{},getSelected:{value:!0},setSelected:{}},o=["row_tools","col_tools","element_tools","container_tools","tab_tools","accordion_tools","text_tools"];function g(r){return!!r&&r.nodeType===1}function l(r){return r instanceof i?r[0]||null:r}function h(r){if(r instanceof i)return l(r);if(r&&typeof r=="object"&&!Array.isArray(r)&&!g(r)){var t={};return Object.keys(r).forEach(function(e){t[e]=l(r[e])}),t}return r}function v(r){return g(r)?i(r):r}var s=new WeakMap;function w(r){if(!r||typeof r!="object")return r;if(s.has(r))return s.get(r);var t=Object.assign({},r);return["node","parent","canvas"].forEach(function(e){e in r&&(t[e]=i(r[e]||[]))}),r.container&&(t.container=i(r.container)),Array.isArray(r.nodes)&&(t.node=i(r.nodes)),["from","to"].forEach(function(e){var n=r[e];n&&typeof n=="object"&&"parent"in n&&(t[e]=Object.assign({},n,{parent:i(n.parent||[])}))}),s.set(r,t),t}var p=i.event.dispatch;i.event.dispatch=function(r){return arguments.length===1&&r instanceof window.CustomEvent&&typeof r.type=="string"&&r.type.indexOf("grideditor:")===0?p.call(this,r,w(r.detail)):p.apply(this,arguments)};function y(r){return function(t){return r.call(this,i.event.fix(t))}}function _(r){if(!r||!r.on)return r;var t;return typeof r.on=="function"?t=y(r.on):(t={},Object.keys(r.on).forEach(function(e){t[e]=y(r.on[e])})),Object.assign({},r,{on:t})}function j(r){return function(t,e){var n=typeof r=="string"?window[r]:r;return n(i(t),e)}}function E(r){if(r=Object.assign({},r||{}),r.callbacks){var t={};Object.keys(r.callbacks).forEach(function(n){var c=r.callbacks[n];t[n]=typeof c=="function"?function(f){return c(w(f))}:c}),r.callbacks=t}if(r.custom_filter&&r.custom_filter.length){var e=typeof r.custom_filter=="string"||typeof r.custom_filter=="function"?[r.custom_filter]:Array.prototype.slice.call(r.custom_filter);r.custom_filter=e.map(j)}return o.forEach(function(n){Array.isArray(r[n])&&(r[n]=r[n].map(_))}),r.source_textarea=l(r.source_textarea),r}function A(r){var t={canvas:i(r.canvas)};return Object.keys(a).forEach(function(e){t[e]=function(){var n=r[e].apply(r,Array.prototype.map.call(arguments,h));return n===r?void 0:v(n)}}),Object.defineProperty(t,"settings",{enumerable:!0,get:function(){return r.settings}}),t}u._created.push(function(r){i.data(r.canvas,"grideditor",A(r));var t=r.destroy;r.destroy=function(){var e=t.apply(this,arguments);return i.removeData(r.canvas,"grideditor"),e}});function O(r,t,e){var n=a[t];if(!n)return b("method:"+t,'unknown method "'+t+'"'),r;if(e=e.map(h),n.value){var c=r.first();if(!c.length)return null;var f=u.get(c[0]);return f?v(f[t].apply(f,e)):t==="getHtml"?c.html():t==="getPlainHtml"?u._plainHtml(c.html()):null}return r.each(function(){var d=u.get(this);d&&d[t].apply(d,e)}),r}i.fn.gridEditor=function(r){if(typeof r=="string")return O(this,r,Array.prototype.slice.call(arguments,1));var t=E(r);return this.each(function(){u.create(this,t)}),this},i.fn.gridEditor.locales=u.locales,i.fn.gridEditor.t=u.t}})();