{"version":3,"sources":["https://lively-kernel.org/lively4/composed-offset/src/components/widgets/lively-menu.js"],"names":["Morph","pt","html","Entry","fromDescription","desc","Array","isArray","fromArray","String","name","callbackOrChildren","right","icon","options","onSelect","onDeselect","onClick","entry","Function","callback","Promise","children","selectHandler","deselectHandler","asItem","menu","HTMLElement","append","innerHTML","replace","item","appendChild","addEventListener","evt","matchingItems","includes","selectItem","matchesFilter","filter","textContent","toLowerCase","selected","deselected","FILTER_KEY_BLACKLIST","LivelyMenu","initialize","setAttribute","registerKeys","moveInsideWindow","w","window","innerWidth","innerHeight","b","lively","getGlobalBounds","original","topLeft","bottom","y","x","left","top","delta","subPt","parentMenu","getExtent","moveBy","topLevelMenu","_filter","value","onKeyDown","key","get","items","forEach","classList","remove","nonMatchingItems","add","currentItem","length","from","querySelectorAll","matchFilter","onSpaceDown","warn","onUpDown","selectUpOrDown","onDownDown","onEscDown","closeWindow","offset","targetIndex","indexOf","onLeftDown","focusWithoutScroll","onRightDown","enterSubmenu","onTabDown","shiftKey","submenu","onEnterUp","openOn","optEvt","optPos","container","console","log","resolve","ea","match","inner","setPosition","focus","subitems","document","createElement","components","openIn","bounds","getBoundingClientRect","menuBounds"],"mappings":"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","file":"lively-menu.js","sourcesContent":["/*MD # Lively Menu\n\n![](lively-menu.png){height=200}\n\n\nMD*/\n\nimport Morph from 'src/components/widgets/lively-morph.js';\nimport { pt } from 'src/client/graphics.js';\nimport html from 'src/client/html.js';\n\nclass Entry {\n  static fromDescription(desc) {\n    if (Array.isArray(desc)) {\n      return this.fromArray(desc);\n    } else if (desc instanceof String) {\n      // #TODO: convert the String '---' into a <hl />\n    } else {\n      // #TODO: Object\n    }\n  }\n  \n  static fromArray([name, callbackOrChildren, right, icon, options = {}]) {\n    const { onSelect, onDeselect, onClick } = options;\n    const entry = new Entry();\n\n    entry.name = name;\n    entry.callbackOrChildren = callbackOrChildren;\n    if (callbackOrChildren instanceof Function) {\n      entry.callback = callbackOrChildren;\n    } else if(callbackOrChildren instanceof Array || callbackOrChildren instanceof Promise) {\n      entry.children = callbackOrChildren;\n    }\n    if(onClick) {\n      entry.callback = onClick;\n    }\n    entry.icon = icon;\n    entry.right = right;\n    entry.selectHandler = onSelect;\n    entry.deselectHandler = onDeselect;\n\n    return entry;\n  }\n\n  asItem(menu) {\n    const icon = <div class='icon'></div>;\n    if (this.icon instanceof HTMLElement) {\n      icon.append(this.icon)\n    } else {\n      icon.innerHTML = this.icon ||  \"\"\n    }\n\n    const right = <label></label>;\n    if (this.right) {\n      if (this.right instanceof HTMLElement) {\n        right.append(this.right)\n      } else {\n        right.innerHTML = typeof this.right === 'string' ? this.right.replace(\"CMD\", \"Ctrl\") : this.right;\n      }\n    }\n    if (this.children) {\n      right.append(<span class=\"submenuindicator\">►</span>);\n    }\n\n    const item = <li>{icon}{this.name}</li>;\n    item.entry = this;\n    item.appendChild(right);\n\n    if (this.callback) {\n      item.addEventListener(\"click\", evt => this.callback(evt, item));\n    }\n\n    item.addEventListener(\"mouseenter\", async evt => {\n      if (menu.matchingItems.includes(item)) {\n        menu.selectItem(item);\n      }\n    });\n\n    return item;\n  }\n\n  matchesFilter(filter) {\n    if (this.name instanceof HTMLElement) {\n      return this.name.textContent.toLowerCase().includes(filter.toLowerCase());\n    }\n    return typeof this.name === 'string' && this.name.toLowerCase().includes(filter.toLowerCase());\n  }\n\n  selected() {\n    if (this.selectHandler) {\n      this.selectHandler();\n    }\n  }\n  deselected() {\n    if (this.deselectHandler) {\n      this.deselectHandler();\n    }\n  }\n}\n\nconst FILTER_KEY_BLACKLIST = ['Control', 'Shift', 'Capslock', 'Alt', ' ', 'Enter', 'Escape', 'ArrowUp', 'ArrowRight', 'ArrowDown', 'ArrowLeft', 'Tab'];\n\nexport default class LivelyMenu extends Morph {\n  initialize() {\n    this.setAttribute(\"tabindex\", 0 // we want keyboard events\n    );html.registerKeys(this, \"Menu\", this, true);\n  }\n\n  moveInsideWindow() {\n    var w = pt(window.innerWidth - 12, window.innerHeight - 12);\n    var b = lively.getGlobalBounds(this);\n    var original = b.topLeft();\n\n    if (b.bottom() > w.y) {\n      b.y -= b.bottom() - w.y;\n    }\n    if (b.right() > w.x) {\n      b.x -= b.right() - w.x;\n    }\n    if (b.left() < 0) {\n      b.x -= b.left();\n    }\n    if (b.top() < 0) {\n      b.y -= b.top();\n    }\n\n    var delta = b.topLeft().subPt(original\n    // lively.moveBy(this.topLevelMenu(), delta)\n    );if (this.parentMenu) {\n      if (delta.x < 0) {\n        delta.x -= lively.getExtent(this.parentMenu).x;\n      }\n    }\n    lively.moveBy(this, delta);\n\n    return delta;\n  }\n\n  topLevelMenu() {\n    if (!this.parentMenu) {\n      return this;\n    } else {\n      return this.parentMenu.topLevelMenu();\n    }\n  }\n\n  // lazy filter property\n  get filter() {\n    return this._filter = this._filter || '';\n  }\n  set filter(value) {\n    return this._filter = value;\n  }\n\n  onKeyDown(evt) {\n    if (FILTER_KEY_BLACKLIST.includes(evt.key)) {\n      return;\n    }\n\n    if (['Backspace', 'Delete'].includes(evt.key)) {\n      this.filter = '';\n    } else {\n      this.filter += evt.key;\n    }\n\n    this.get('#filter-hint').innerHTML = this.filter;\n\n    // lively.warn(evt.key, this.filter)\n\n    this.items.forEach(item => item.classList.remove('filtered-out'));\n    this.nonMatchingItems.forEach(item => item.classList.add('filtered-out'));\n\n    // lively.notify(this.matchingItems.length, this.nonMatchingItems.length)\n    if (!this.currentItem || this.nonMatchingItems.includes(this.currentItem) && this.matchingItems.length > 0) {\n      this.selectItem(this.matchingItems[0]);\n    }\n  }\n\n  get items() {\n    return Array.from(this.get(\".container\").querySelectorAll(\"li\"));\n  }\n\n  matchFilter(item) {\n    return item && item.entry && item.entry.matchesFilter(this.filter);\n  }\n\n  get matchingItems() {\n    return this.items.filter(item => this.matchFilter(item));\n  }\n\n  get nonMatchingItems() {\n    return this.items.filter(item => !this.matchFilter(item));\n  }\n\n  onSpaceDown(evt) {\n    lively.warn('should toggle binary Preferences');\n  }\n\n  onUpDown(evt) {\n    this.selectUpOrDown(evt, -1);\n  }\n\n  onDownDown(evt) {\n    this.selectUpOrDown(evt, 1);\n  }\n\n  onEscDown(evt) {\n    // #TODO: check if we are in a submenu\n    if (this.parentMenu) {\n      this.parentMenu.onEscDown(evt);\n    }\n    this.closeWindow();\n  }\n\n  selectUpOrDown(evt, offset = 0) {\n    if (!this.currentItem) {\n      this.selectItem(this.items[0]);\n    } else {\n      var matchingItems = this.matchingItems;\n      var targetIndex = (matchingItems.indexOf(this.currentItem) + offset + matchingItems.length) % matchingItems.length; //cycling through menu items\n      this.selectItem(matchingItems[targetIndex]);\n    }\n  }\n\n  onLeftDown(evt) {\n    if (this.parentMenu) {\n      lively.focusWithoutScroll(this.parentMenu);\n      this.parentMenu.selectUpOrDown(evt);\n    }\n  }\n\n  async onRightDown(evt) {\n    if (!this.currentItem) {\n      return;\n    }\n\n    var entry = this.currentItem.entry;\n\n    if ((await entry.children) instanceof Array) {\n      this.enterSubmenu(evt);\n    }\n  }\n\n  onTabDown(evt) {\n    if (evt.shiftKey) {\n      this.onLeftDown(evt);\n    } else {\n      this.onRightDown(evt);\n    }\n  }\n\n  enterSubmenu(evt) {\n    lively.focusWithoutScroll(this.submenu);\n    this.submenu.selectUpOrDown(evt);\n  }\n\n  async onEnterUp(evt) {\n    if (!this.currentItem) return;\n\n    var entry = this.currentItem.entry;\n    if (entry.callback) {\n      entry.callback(evt, this.currentItem);\n    } else if ((await entry.children) instanceof Array) {\n      this.enterSubmenu(evt);\n    }\n  }\n\n  async openOn(items, optEvt, optPos) {\n    var container = this.get(\".container\");\n    container.innerHTML = \"\"; // clear\n    // create a radio button for each tool\n    if (!items) {\n      console.log(\"WARNING: no items to open\");\n      return Promise.resolve();\n    }\n    for (let ea of items) {\n      if (typeof ea === 'string') {\n        const match = ea.match(/^---(.+)---$/)\n        if (match) {\n          const inner = <span></span>;\n          inner.innerHTML = match[1];\n          container.append(<hr class='separator-with-text'>{inner}</hr>)\n        } else {\n          container.append(<hr class='separator'></hr>)\n        }\n        continue\n      }\n\n      const entry = Entry.fromDescription(ea);\n      const item = entry.asItem(this);\n      container.appendChild(item);\n    }\n    if (optPos) lively.setPosition(this, optPos);\n    this.moveInsideWindow();\n\n    return Promise.resolve(container);\n  }\n  \n  closeWindow() {\n    if(this.currentItem) {\n      this.currentItem.entry.deselected();\n    }\n    this.remove();\n  }\n\n  async selectItem(item) {\n    if (this.currentItem) {\n      this.currentItem.classList.remove(\"current\");\n      this.currentItem.entry.deselected();\n    }\n    if (!item) return;\n    // lively.showElement(item)\n    item.classList.add(\"current\");\n    this.currentItem = item;\n\n    // scroll item into view\n    item.setAttribute('tabindex', 0);\n    item.focus();\n\n    var ea = item.entry;\n    var menu = this.get(\".container\");\n    if (this.submenu) this.submenu.closeWindow();\n    item.entry.selected();\n    const subitems = await ea.children; // resolve Promise\n    if (subitems instanceof Array) {\n      this.submenu = document.createElement(\"lively-menu\");\n      this.submenu.parentMenu = this;\n      await lively.components.openIn(menu, this.submenu);\n      var bounds = item.getBoundingClientRect();\n      var menuBounds = menu.getBoundingClientRect();\n      this.submenu.openOn(subitems, null, pt(bounds.right, bounds.top).subPt(pt(menuBounds.left, menuBounds.top\n      // lively.moveBy(this, delta)\n      )));\n    }\n  }\n\n}"]}