Home Reference Source

cables_dev/cables_ui/src/ui/elements/tabpanel/tabpanel.js

  1. import { Events, Logger } from "cables-shared-client";
  2. import userSettings from "../../components/usersettings.js";
  3. import { getHandleBarHtml } from "../../utils/handlebars.js";
  4. import { notify, notifyError } from "../notification.js";
  5.  
  6.  
  7. /**
  8. * a tab panel, that can contain tabs
  9. *
  10. * @export
  11. * @class TabPanel
  12. * @extends {Events}
  13. */
  14. export default class TabPanel extends Events
  15. {
  16. constructor(eleId)
  17. {
  18. super();
  19. this._log = new Logger("TabPanel " + eleId);
  20.  
  21. this.id = CABLES.uuid();
  22. this._eleId = eleId;
  23. this._tabs = [];
  24. this._eleContentContainer = null;
  25. this._eleTabPanel = null;
  26. this.showTabListButton = false;
  27. this._dynCmds = [];
  28.  
  29. if (!this._eleTabPanel)
  30. {
  31. this._eleTabPanel = document.createElement("div");
  32. this._eleTabPanel.classList.add("tabpanel");
  33. this._eleTabPanel.innerHTML = "";
  34.  
  35. const el = ele.byId(this._eleId);
  36. if (!el)
  37. {
  38. console.error("could not find ele " + this._eleId);
  39. return;
  40. }
  41. el.appendChild(this._eleTabPanel);
  42.  
  43. this._eleContentContainer = document.createElement("div");
  44. this._eleContentContainer.classList.add("contentcontainer");
  45. this._eleContentContainer.innerHTML = "";
  46. el.appendChild(this._eleContentContainer);
  47. }
  48.  
  49. this.on("resize", () =>
  50. {
  51. for (let i = 0; i < this._tabs.length; i++) this._tabs[i].emitEvent("resize");
  52. });
  53. }
  54.  
  55.  
  56. getUniqueTitle(title)
  57. {
  58. const existingTab = this.getTabByTitle(title);
  59. let count = 0;
  60. while (existingTab)
  61. {
  62. count++;
  63. if (!this.getTabByTitle(title + " (" + count + ")")) break;
  64. }
  65.  
  66. if (count > 0)
  67. title = title + " (" + count + ")";
  68.  
  69. return title;
  70. }
  71.  
  72. updateHtml()
  73. {
  74. let html = "";
  75. html += getHandleBarHtml("tabpanel_bar", { "id": this.id, "tabs": this._tabs });
  76. this._eleTabPanel.innerHTML = html;
  77.  
  78. const editortabList = document.getElementById("editortabList" + this.id);
  79. if (!editortabList)
  80. {
  81. console.warn("no editortabList?!?");
  82. return;
  83. }
  84. if (!this.showTabListButton)
  85. {
  86. editortabList.style.display = "none";
  87. editortabList.parentElement.style["padding-left"] = "0";
  88. }
  89. else
  90. {
  91. editortabList.parentElement.style["padding-left"] = "34px";
  92.  
  93. editortabList.style.display = "block";
  94. editortabList.addEventListener(
  95. "mousedown",
  96. (e) =>
  97. {
  98. const items = [];
  99. for (let i = 0; i < this._tabs.length; i++)
  100. {
  101. const tab = this._tabs[i];
  102. items.push({
  103. "title": tab.options.name,
  104. "func": () => { this.activateTab(tab.id); }
  105. });
  106. }
  107. CABLES.contextMenu.show(
  108. {
  109. "items": items
  110. }, e.target);
  111. },
  112. );
  113. }
  114.  
  115.  
  116. for (let i = 0; i < this._dynCmds.length; i++) gui.cmdPallet.removeDynamic(this._dynCmds[i]);
  117.  
  118. for (let i = 0; i < this._tabs.length; i++)
  119. {
  120. if (window.gui && this._eleId == "maintabs")
  121. {
  122. const t = this._tabs[i];
  123.  
  124. const cmd = gui.cmdPallet.addDynamic("tab", "Tab " + t.title, () =>
  125. {
  126. gui.maintabPanel.show(true);
  127.  
  128. this.activateTab(t.id, true);
  129. }, t.icon || "edit");
  130. this._dynCmds.push(cmd);
  131. }
  132.  
  133. // ----------------
  134.  
  135.  
  136. document.getElementById("editortab" + this._tabs[i].id).addEventListener(
  137. "mousedown",
  138. function (e)
  139. {
  140. if (e.target.dataset.id) this.activateTab(e.target.dataset.id, true);
  141. }.bind(this),
  142. );
  143.  
  144. if (this._tabs[i].options.closable)
  145. {
  146. document.getElementById("editortab" + this._tabs[i].id).addEventListener(
  147. "mousedown",
  148. function (e)
  149. {
  150. if (e.button == 1) if (e.target.dataset.id) this.closeTab(e.target.dataset.id);
  151. }.bind(this),
  152. );
  153. }
  154.  
  155. if (document.getElementById("closetab" + this._tabs[i].id))
  156. {
  157. document.getElementById("closetab" + this._tabs[i].id).addEventListener(
  158. "mousedown",
  159. function (e)
  160. {
  161. this.closeTab(e.target.dataset.id);
  162. }.bind(this),
  163. );
  164. }
  165. }
  166.  
  167. this.scrollToActiveTab();
  168. }
  169.  
  170. activateTabByName(name)
  171. {
  172. name = name || "";
  173. let found = false;
  174. let tab = null;
  175. for (let i = 0; i < this._tabs.length; i++)
  176. {
  177. if (this._tabs[i].title.toLowerCase() === name.toLowerCase() ||
  178. (this._tabs[i].options.name || "").toLowerCase() === name.toLowerCase())
  179. {
  180. tab = this._tabs[i];
  181. this.activateTab(tab.id);
  182. found = true;
  183. }
  184. else this._tabs[i].deactivate();
  185. }
  186.  
  187. if (!found) console.log("[activateTabByName] could not find tab", name);
  188.  
  189. this.updateHtml();
  190. return tab;
  191. }
  192.  
  193. scrollToActiveTab()
  194. {
  195. const tab = this.getActiveTab();
  196. const w = this._eleTabPanel.clientWidth;
  197. if (!tab) return;
  198. let left = document.getElementById("editortab" + tab.id).offsetLeft;
  199. left += document.getElementById("editortab" + tab.id).clientWidth;
  200. left += 25;
  201.  
  202. const tabContainer = document.querySelector("#maintabs .tabs");
  203. if (tabContainer && left > w) tabContainer.scrollLeft = left;
  204. }
  205.  
  206. activateTab(id)
  207. {
  208. let found = false;
  209. for (let i = 0; i < this._tabs.length; i++)
  210. {
  211. if (this._tabs[i].id === id)
  212. {
  213. found = true;
  214. this.emitEvent("onTabActivated", this._tabs[i]);
  215. this._tabs[i].activate();
  216. }
  217. }
  218.  
  219. if (found)
  220. for (let i = 0; i < this._tabs.length; i++)
  221. if (this._tabs[i].id != id)
  222. this._tabs[i].deactivate();
  223.  
  224. this.updateHtml();
  225.  
  226. if (!found)
  227. {
  228. // console.log("could not find tab", id);
  229. }
  230.  
  231. if (CABLES.editorSession && CABLES.editorSession.loaded() && CABLES.UI.loaded) this.saveCurrentTabUsersettings();
  232. }
  233.  
  234. loadCurrentTabUsersettings()
  235. {
  236. let found = false;
  237. for (let i = 0; i < this._tabs.length; i++)
  238. {
  239. if (userSettings.get("tabsLastTitle_" + this._eleId) == this._tabs[i].title)
  240. {
  241. this.activateTab(this._tabs[i].id);
  242. found = true;
  243. break;
  244. }
  245. }
  246. }
  247.  
  248.  
  249. saveCurrentTabUsersettings()
  250. {
  251. const activeTab = this.getActiveTab();
  252.  
  253. if (!activeTab) return;
  254. userSettings.set("tabsLastTitle_" + this._eleId, activeTab.title);
  255. }
  256.  
  257. getTabByDataId(dataId)
  258. {
  259. for (let i = 0; i < this._tabs.length; i++) if (this._tabs[i].dataId == dataId) return this._tabs[i];
  260. }
  261.  
  262. getTabByTitle(title)
  263. {
  264. for (let i = 0; i < this._tabs.length; i++) if (this._tabs[i].title == title) return this._tabs[i];
  265. }
  266.  
  267. getTabById(id)
  268. {
  269. for (let i = 0; i < this._tabs.length; i++) if (this._tabs[i].id == id) return this._tabs[i];
  270. }
  271.  
  272. closeAllTabs()
  273. {
  274. while (this._tabs.length) this.closeTab(this._tabs[0].id);
  275. }
  276.  
  277. closeTab(id)
  278. {
  279. let tab = null;
  280. let idx = 0;
  281. for (let i = 0; i < this._tabs.length; i++)
  282. {
  283. if (this._tabs[i].id == id)
  284. {
  285. tab = this._tabs[i];
  286. // tab.emitEvent("close");
  287. this._tabs.splice(i, 1);
  288. idx = i;
  289. break;
  290. }
  291. }
  292. if (!tab) return;
  293.  
  294. this.emitEvent("onTabRemoved", tab);
  295. tab.remove();
  296.  
  297. if (idx > this._tabs.length - 1) idx = this._tabs.length - 1;
  298. if (this._tabs[idx]) this.activateTab(this._tabs[idx].id);
  299.  
  300. this.updateHtml();
  301. }
  302.  
  303. setChanged(id, changed)
  304. {
  305. if (this.getTabById(id)) this.getTabById(id).options.wasChanged = changed;
  306. this.updateHtml();
  307. }
  308.  
  309. setTabNum(num)
  310. {
  311. const tab = this._tabs[Math.min(this._tabs.length, num)];
  312. this.activateTab(tab.id);
  313. }
  314.  
  315. getNumTabs()
  316. {
  317. return this._tabs.length;
  318. }
  319.  
  320. cycleActiveTab()
  321. {
  322. if (this._tabs.length <= 1) return;
  323.  
  324. for (let i = 1; i < this._tabs.length; i++)
  325. if (this._tabs[i - 1].active)
  326. return this.activateTab(this._tabs[i].id);
  327.  
  328. return this.activateTab(this._tabs[0].id);
  329. }
  330.  
  331. getActiveTab()
  332. {
  333. for (let i = 0; i < this._tabs.length; i++) if (this._tabs[i].active) return this._tabs[i];
  334. }
  335.  
  336. updateSize()
  337. {
  338. for (let i = 0; i < this._tabs.length; i++) this._tabs[i].updateSize();
  339. }
  340.  
  341. getSaveButton()
  342. {
  343. const t = this.getActiveTab();
  344. if (!t) return;
  345.  
  346. const b = t.getSaveButton();
  347. if (b) return b;
  348. }
  349.  
  350. addTab(tab, activate)
  351. {
  352. if (tab.options.singleton)
  353. {
  354. const t = this.getTabByTitle(tab.title);
  355. if (t)
  356. {
  357. this.activateTab(t.id);
  358. this.emitEvent("onTabAdded", t, true);
  359.  
  360. if (activate) this.activateTab(t.id);
  361. return t;
  362. }
  363. }
  364.  
  365. tab.initHtml(this._eleContentContainer);
  366. this._tabs.push(tab);
  367.  
  368. if (activate) this.activateTab(tab.id);
  369.  
  370. // var tabEl=document.getElementById("editortab"+tab.id)
  371.  
  372. this.updateHtml();
  373. this.emitEvent("onTabAdded", tab, false);
  374.  
  375. // setTimeout(() => { this.updateSize(); console.log("update size of tab"); }, 200);
  376. return tab;
  377. }
  378.  
  379. addIframeTab(title, url, options, userInteraction)
  380. {
  381. const iframeTab = this.addTab(new CABLES.UI.Tab(title, options));
  382. const id = CABLES.uuid();
  383.  
  384. const html = "<div class=\"loading\" id=\"loading" + id + "\" style=\"position:absolute;left:45%;top:34%\"></div><iframe id=\"iframe" + id + "\" allow=\"clipboard-write\" style=\"border:none;width:100%;height:100%\" src=\"" + url + "\" onload=\"document.getElementById('loading" + id + "').style.display='none';\"></iframe";
  385. iframeTab.contentEle.innerHTML = html;
  386. iframeTab.contentEle.style.padding = "0px";
  387. if (options.gotoUrl)
  388. {
  389. iframeTab.toolbarEle.innerHTML = "<a class=\"button-small\" href=\"" + options.gotoUrl + "\" target=\"_blank\">Open in new tab</a>";
  390. }
  391. else
  392. {
  393. iframeTab.toolbarEle.innerHTML = "<a class=\"button-small\" href=\"" + url + "\" target=\"_blank\">Open in new tab</a>";
  394. }
  395.  
  396. const frame = document.getElementById("iframe" + id);
  397.  
  398. const talkerAPI = new CABLESUILOADER.TalkerAPI(frame.contentWindow);
  399.  
  400. talkerAPI.addEventListener("manualScreenshot", (opts, next) =>
  401. {
  402. CABLES.platform.setManualScreenshot(opts.manualScreenshot);
  403.  
  404. if (opts.manualScreenshot)
  405. {
  406. gui.patchView.store.saveScreenshot(true, () =>
  407. {
  408. talkerAPI.send("screenshotSaved");
  409. });
  410. }
  411. });
  412.  
  413. talkerAPI.addEventListener("notify", (opts, next) =>
  414. {
  415. notify(opts.msg, opts.text, opts.options);
  416. });
  417.  
  418. talkerAPI.addEventListener("notifyError", (opts, next) =>
  419. {
  420. notifyError(opts.msg, opts.text, opts.options);
  421. });
  422.  
  423. talkerAPI.addEventListener("updatePatchName", (opts, next) =>
  424. {
  425. gui.setProjectName(opts.name);
  426. CABLESUILOADER.talkerAPI.send("updatePatchName", opts, (err, r) => {});
  427. });
  428.  
  429. talkerAPI.addEventListener("updatePatchSummary", (opts, next) =>
  430. {
  431. gui.project().summary = opts;
  432. gui.patchParamPanel.show(true);
  433. });
  434.  
  435. talkerAPI.addEventListener("opsDeleted", (opts, next) =>
  436. {
  437. const opdocs = gui.opDocs.getAll();
  438. const deletedOps = opts.ops || [];
  439. for (let i = 0; i < deletedOps.length; i++)
  440. {
  441. const deletedOp = deletedOps[i];
  442. const opDocToDelete = opdocs.findIndex((opDoc) => { return opDoc.id === deletedOp.id; });
  443. if (opDocToDelete) opdocs.splice(opDocToDelete, 1);
  444. gui.opSelect().reload();
  445. }
  446. let plural = deletedOps.length > 1 ? "s" : "";
  447. if (deletedOps.length > 0) notify("deleted " + deletedOps.length + " op" + plural);
  448. this.closeTab(iframeTab.id);
  449. });
  450.  
  451. this.activateTab(iframeTab.id);
  452. gui.maintabPanel.show(userInteraction);
  453. return iframeTab;
  454. }
  455. }