tool-bar icon indicating copy to clipboard operation
tool-bar copied to clipboard

ToolBarDropdownMenu

Open aminya opened this issue 4 years ago • 8 comments

Adding a dropdown menu that each option is a ToolBarButton.

It should be built on top of ToolBarItem.

We should use atom-select-list if possible.

References: https://www.w3schools.com/howto/howto_js_dropdown.asp Electron References if applicable: https://github.com/electron/electron/blob/v5.0.13/docs/api/menu-item.md https://github.com/electron/electron/blob/v5.0.13/docs/api/menu.md https://livebook.manning.com/book/electron-in-action/chapter-7/1

aminya avatar Apr 15 '20 16:04 aminya

I wrote this but I cannot make it show the list

const {ToolBarButtonView} = require("./tool-bar-button-view");
const SelectListView = require('atom-select-list');

class ToolBarDropdown {
  constructor (buttonOptions, listItems, group) {
    this.element = document.createElement('div');

    let dropdownButton = new ToolBarButtonView(buttonOptions, group);
    this.element.appendChild(dropdownButton.element);

    let list = document.createElement('div');
    list.classList.add('dropdown-content');
    let option = document.createElement('a');
    this.dropdownList = list.appendChild(option);
    this.element.appendChild(this.dropdownList);

    // or dropdownButton.element
    this.element.addEventListener('click', function (e)  {
        option.style.display = "block";
        if (e.preventDefault) {
          e.preventDefault();
          e.stopPropagation();
        }
      });
  }
}

inside toolbar.js:

const { ToolBarDropdown } = require('./items/tool-bar-dropdown');

exports.activate = function () {
  toolBarView = new ToolBarView();
  touchBarManager = new TouchBarManager();

  // test dropdown
  let toolBarManager = new ToolBarManager("Amin", toolBarView, touchBarManager);
  let mydropdown = new ToolBarDropdown({
    text: "hey",
    callback: () => console.log("a")
  },["option1", "option2"],"Amin");

  console.log(mydropdown.element);

  toolBarManager.addItem({element: mydropdown.element, priority: -1});
};

Maybe the callback executor of the button conflicts with the dropdown show.

aminya avatar Apr 16 '20 02:04 aminya

I got it working using atom-select-list:

dropdown

It isn't the most beautiful dropdown yet. Unfortunately, it changes the width of the toolbar itself.

The code

items/tool-bar-dropdown.js

const {ToolBarButtonView} = require("./tool-bar-button-view");
const SelectListView = require('atom-select-list');

class ToolBarDropdown {
  constructor (buttonOptions, listItems, group) {
    this.element = document.createElement('div');

    buttonOptions.callback = () => {
      if (mydropdown.list.selectList.element.hidden == true) {
        mydropdown.list.show();
      } else {
        mydropdown.list.selectList.element.hidden = true;
      }
    }
    this.button = new ToolBarButtonView(buttonOptions, group);
    this.element.appendChild(this.button.element);
    this.list= new DropdownSelector(listItems);
    this.element.appendChild(this.list.selectList.element);
  }
}


class DropdownSelector {
  // Make a selector object (should be called once)
  constructor(SelectorItems) {
    // Defining a SelectListView with methods - https://github.com/atom/atom-select-list
    this.selectList = new SelectListView({
      // an array containing the objects you want to show in the select list
      items: SelectorItems,

      // // called whenever an item needs to be displayed.
      elementForItem: (item) => {
        const element = document.createElement("li");
        element.textContent = item.text;
        return element;
      },

      // // called to retrieve a string property on each item and that will be used to filter them.
      filterKeyForItem: (item) => {
        return item.text;
      },

      // // called when the user clicks or presses Enter on an item. // use `=>` for `this`
      didConfirmSelection: (item) => {
        item.callback();
        this.selectList.element.hidden = true;
      },

      // // called when the user presses Esc or the list loses focus. // use `=>` for `this`
      didCancelSelection: () => {
        this.selectList.element.hidden = true;
      },
    });

    this.selectList.element.style.position = "relative";
    this.selectList.element.style.left = "20px";
	// initially hidden
    this.list.selectList.element.hidden = true;
  }

  // Show a selector object
  show() {
    // Show selector
    this.selectList.element.hidden = false;
    this.selectList.reset();
    this.selectList.focus();
  }

  // Dispose selector
  dispose() {
    this.selectList.element.hidden = true;
    this.selectList.destroy();
  }
}

module.exports.ToolBarDropdown = ToolBarDropdown;

tool-bar.js activate function:

const { ToolBarDropdown } = require('./items/tool-bar-dropdown');

exports.activate = function () {
  toolBarView = new ToolBarView();
  touchBarManager = new TouchBarManager();


  // test dropdown
  let mygroup = "Amin";
  let toolBarManager = new ToolBarManager(mygroup, toolBarView, touchBarManager);

  let items = [
    {text: "option1", callback: () => console.log("option1") },
    {text: "option1", callback: () => console.log("option2") }
  ];

  let mydropdown = new ToolBarDropdown({ icon: "star"}, items, mygroup);


  toolBarManager.addItem({element: mydropdown.element, priority: -1});
};

aminya avatar Apr 16 '20 04:04 aminya

It is more beautiful now using the actual button type of the Toolbar itself (without atom-select-list).

Still has the toolbar width changing issue:

dropdown2

The code

items/tool-bar-dropdown.js

const {ToolBarButtonView} = require("./tool-bar-button-view");

class ToolBarDropdown {

  /**
   *
   * @param {ButtonOptions} titleOptions
   * @param {ButtonOptions[]} listOptions
   * @param {string} group
   */
  constructor (titleOptions, listOptions, group) {
    this.element = document.createElement('div');
    this.createList(listOptions, group);
    this.createTitle(titleOptions, group);
    this.element.appendChild(this.title.element);
    this.element.appendChild(this.listElement);
  }

  /**
   *
   * @param {ButtonOptions} titleOptions
   * @param {string} group
   */
  createTitle (titleOptions, group) {
    // callback for titleButton
    titleOptions.callback = () => {
      if (this.isListHidden()) {
        this.showList();
      } else {
        // to close the list when clicked again
        this.hideList();
      }
    };

    // create the title button
    this.title = new ToolBarButtonView(titleOptions, group);
  }

  /**
   *
   * @param {ButtonOptions[]} listOptions
   * @param {string} group
   */
  createList(listOptions, group) {
    // list container
    this.listElement = document.createElement('div');

    // creating each list button and appending it to the list container
    for (let i =0, l = listOptions.length; i < l; i++) {
      this.listElement.appendChild((new ToolBarButtonView(listOptions[i], group)).element);
    }

    this.listElement.style.position = "relative";
    this.listElement.style.left = "20px";
    // this.listElement.style.cssFloat = "right";

    // initially hidden
    this.hideList();
  }

  isListHidden() {
    return this.listElement.hidden;
  }

  hideList() {
    this.listElement.hidden = true;
  }

  showList() {
    this.listElement.hidden = false;
  }
}

module.exports.ToolBarDropdown = ToolBarDropdown;

tool-bar.js inside activate:

const { ToolBarDropdown } = require('./items/tool-bar-dropdown');

exports.activate = function () {
  toolBarView = new ToolBarView();
  touchBarManager = new TouchBarManager();


  // test dropdown
  let mygroup = "Amin";
  let toolBarManager = new ToolBarManager(mygroup, toolBarView, touchBarManager);

  let listOptions = [
    {text: "option1", callback: () => console.log("option1") },
    {text: "option2", callback: () => console.log("option2") }
  ];
  let titleOption = {
    icon: "star"
  };
  let mydropdown = new ToolBarDropdown(titleOption,listOptions, mygroup);
  toolBarManager.addItem({element: mydropdown.element, priority: -1});
};

aminya avatar Apr 16 '20 19:04 aminya

@suda @ericcornelissen any solution for the tool-bar width changing issue? I cannot make the dropdown be a float thing separate from the tool-bar width.

aminya avatar Apr 16 '20 19:04 aminya

Don't really have the time to look into this right now, but the w3c solutions seems pretty nice to me.

Why doesn't the dropdown overlay whatever is below it? In fact, what is below it might not even be a toolbar, depending on the end-user setup.

ericcornelissen avatar Apr 17 '20 17:04 ericcornelissen

I think we need to add a floating dropdown and position it under the actual button and not appendChild it to the tool-bar.

aminya avatar Apr 17 '20 18:04 aminya

Not sure what you're trying to say @aminya :thinking:

I agree that it should be "floating" (though I don't think using CSS float is the solution) and position under1 the actual button. However, your current solution looks (didn't test it) like it won't work if there is no toolbar below the button (e.g. when toolbar position is top) without expanding the toolbar.

Regarding appendChild. This would depend on your implementation. Where do you plan to append it to? In the w3c solution it is inside a <div> after the <button>.


  1. However, if the toolbar is position at the bottom this doesn't work. Similarly, when it is position left or right, this might not work for buttons near the bottom.

ericcornelissen avatar Apr 18 '20 12:04 ericcornelissen

I found this video. It is maybe related. https://www.youtube.com/watch?v=IF6k0uZuypA&t=619s

There are other ones too: https://www.youtube.com/results?search_query=dropdown+menu

aminya avatar Apr 23 '20 20:04 aminya