UNPKG

akurath

Version:

IDE Frontend for codepsaces.io

355 lines (287 loc) 10 kB
define([ "hr/utils", "hr/dom", "hr/hr", "models/command", "models/tab", "collections/tabs", "utils/dragdrop", "utils/keyboard", "utils/contextmenu", "views/grid", "views/tabs/tab", "views/tabs/base", "views/tabs/section" ], function(_, $, hr, Command, Tab , Tabs, dnd, Keyboard, ContextMenu, GridView, TabView, TabPanelView, TabsSectionView) { // Complete tabs system var TabsView = hr.View.extend({ className: "cb-tabs", defaults: { // Base layout layout: null, // Available layouts layouts: { "Auto Grid": 0, "Columns: 1": 1, "Columns: 2": 2, "Columns: 3": 3, "Columns: 4": 4 }, // Enable tab menu tabMenu: true, // Enable open new tab newTab: true, // Max number of tabs per sections (-1 for unlimited) maxTabsPerSection: -1, // Tabs are draggable draggable: true, // Enable keyboard shortcuts keyboardShortcuts: true }, events: {}, // Constructor initialize: function(options) { var that = this; TabsView.__super__.initialize.apply(this, arguments); // Current active tab id this.activeTab = null; this.activeSection = 0; // Has been restored this._restored = false; // Current layout this.layout = this.options.layout; // null: mode auto this.grid = new GridView({}, this); this.grid.$el.appendTo(this.$el); // Drag and drop of tabs this.drag = new dnd.DraggableType(); this.drag.toggle(this.options.draggable); this.drag.on("drop", function(section, tab) { if (!section && tab) tab.splitSection(); }) // Commands this.layoutCommand = new Command({}, { 'type': "menu", 'title': "Layout" }); _.each(this.options.layouts, function(layout, layoutName) { var command = new Command({}, { 'type': "action", 'title': layoutName, 'action': function() { that.setLayout(layout); } }); this.layoutCommand.menu.add(command); this.on("layout", function(_layout) { command.toggleFlag("active", layout == _layout); }); }, this); // Tabs collection this.tabs = new Tabs(); // Restorer this.restorer = {}; // Set base layout this.setLayout(this.layout); return this; }, // Return a tab by its id getById: function(id) { return this.tabs.getById(id); }, // Return a section by its id getSection: function(id) { var s = _.find(this.grid.views, function(section) { return section.sectionId == id; }); if (!s) { s = new TabsSectionView({ sectionId: id }, this); this.grid.addView(s); } return s; }, // Remove a section removeSection: function(id) { var s = this.getSection(id); this.grid.removeView(s); return this; }, // Render all tabs render: function() { return this.ready(); }, /* * Add a tab * @V : view class * @constructor : contructor options * @options : options */ add: function(V, construct, options) { var tab = null; options = _.defaults(options || {}, { // Tab type type: "unknown", // Don't trigger event silent: false, // Open after creation open: true, // Base title title: "untitled", // Unique id for this tab uniqueId: null, // Base section id section: this.activeSection }); if (options.uniqueId) { tab = this.tabs.getById(options.uniqueId) } else { options.uniqueId = _.uniqueId("tab"); } if (!tab) { tab = new Tab({ 'manager': this }, { 'type': options.type, 'id': options.uniqueId, 'title': options.title }); // Create tab object this.tabs.add(tab); // Create content view tab.view = new V(_.extend(construct || {}, { "tab": tab, }), this); tab.view.update(); // Add to section var sectionId = options.section; for (;;) { var section = this.getSection(sectionId); if (this.options.maxTabsPerSection > 0 && section.tabs.size() >= this.options.maxTabsPerSection) { sectionId = _.uniqueId("tabSection"); } else { section.addTab(tab); break; } } } if (options.open) tab.active(); this.saveTabs(); return tab.view; }, // Open default new tab openDefault: function() { this.trigger("tabs:opennew"); }, // Define tabs layout setLayout: function(l) { if (!_.contains(_.values(this.options.layouts), l)) return; this.grid.setLayout(l); this.trigger("layout", l); this.saveTabs(); }, // Check if tab is the active tab isActiveTab: function(tab) { return this.activeTab == tab.id; }, // Check sections // -> check that there is no empty sections checkSections: function() { _.each(this.grid.views, function(section) { // If empty remove it if (section.tabs.size() == 0) { this.grid.removeView(section); return; } // If no active tab if (section.tabs.getActive() == null) { section.tabs.first().active(); } }, this); this.saveTabs(); }, // Change tab section changeTabSection: function(tab, section, options) { if (_.isString(tab)) tab = this.tabs.getById(tab); if (!tab) return false; section = this.getSection(section); // Check limit if (this.options.maxTabsPerSection > 0 && section.tabs.size() >= this.options.maxTabsPerSection) return false; // Remove from old section tab.section.remove(tab); // Add to new section section.addTab(tab, options); // Active tab.active(); // Check sections to remove empty one this.checkSections(); return true; }, // Save tabs saveTabs: function() { if (!this._restored) return; var state = {}; // Snapshot sections and tabs state.sections = _.map(this.grid.views, function(section) { return { 'id': section.sectionId, 'tabs': section.tabs.map(function(tab) { return tab.snapshot(); }) }; }); // Snapshot layout state.layout = this.grid.columns; hr.Storage.set("tabs", state); }, // Add a restorer for tabs addRestorer: function(type, handler) { this.restorer[type] = handler; return this; }, // Load tabs saved in last session (return number of tabs restored) restoreTabs: function(state) { var n = 0, that = this; state = state || hr.Storage.get("tabs") || {}; // Set layout this.setLayout(state.layout); // Restore tabs return Q.all( _.chain(state.sections || []) .map(function(section) { that.getSection(section.id); return section.tabs; }) .flatten() .map(function(tab) { // Restore tab return Q() .then(function() { if (!that.restorer[tab.type]) return; return Q(that.restorer[tab.type](tab)); }) .then(function(_tab) { if (!_tab) return; // restore in right section _tab.changeSection(tab.section); n = n + 1; }) .fail(function() { return Q(); }); }) .value() ) .then(function() { that._restored = true; that.checkSections(); return n; }); } }, { Panel: TabPanelView }); // Register as a template component hr.View.Template.registerComponent("component.tabs", TabsView); return TabsView; });