This example shows how to give TabView buttons for adding and removing tabs.
+Note: be sure to add the yui3-skin-sam classname to the
+page's <body> element or to a parent element of the widget in order to apply
+the default CSS skin. See Understanding Skinning.
+
<body class="yui3-skin-sam"> <!-- You need this skin class -->+ + +
Plugin Template
+ +In order to make these addons reusable, we can build them as plugins. This
+allows the option for multiple tabviews that mix and match functionality.
+To get started, we will first fill in a basic Plugin template.
+The NAME property is required to prefix events, classNames, et cetera.
+The NS is the namespace where the plugin will live on the
+host. This is where its API can be accessed (e.g. "node.addable.destroy()").
+Adding the this._host alias provides a convenient way to get back to the TabView
+instance. Calling the superclass constructor kicks off the Base lifecycle,
+which will call the initializer.
+
+
var Addable = function(config) {
+ this._host = config.host;
+ Addable.superclass.constructor.apply(this, arguments);
+};
+
+Addable.NAME = 'addableTabs';
+Addable.NS = 'addable';
+
+Y.extend(Addable, Y.Plugin.Base, {
+ initializer: function(config) {
+ }
+});
+
+
+Addable Tab Plugin
+ +To simplify adding new tabs, we are going to add a button that
+users can click and that will prompt them for some details regarding the new tab.
+The main task we are trying to accomplish is to add some HTML to the
+TabView, listen for clicks on the button, prompt the user for input,
+and update the tabs accordingly.
HTML Template
+The first thing we need is a template for the markup to be generated. Adding
+this to the prototype allows separate customization for each TabView
+instance. For this example, we want it to look and feel like another Tab
+without actually being one.
Y.extend(Addable, Y.Plugin.Base, {
+ ADD_TEMPLATE: '<li class="yui3-tab" title="add a tab">' +
+ '<a class="yui3-tab-label yui3-tab-add">+</a></li>',
+
+ initializer: function(config) {
+ }
+});
+
+
+Adding the HTML
+Now that we have a markup template, we will need to add it to the TabView
+somehow. The render phase is the appropriate moment to do so. Listening
+for the render event will give us access to that moment. Listening
+for after('render') ensure that the rendering has actually happened. Then
+we just need to find the tab list and, using the template, add the new item.
+The contentBox provides a Node that can be used to manage
+the TabView HTML.
Y.extend(Addable, Y.Plugin.Base, {
+ ADD_TEMPLATE: '<li class="yui3-tab" title="add a tab">' +
+ '<a class="yui3-tab-label yui3-tab-add">+</a></li>',
+
+ initializer: function(config) {
+ var tabview = this.get('host');
+ tabview.after('render', this.afterRender, this);
+ },
+
+ afterRender: function(e) {
+ var tabview = this.get('host');
+ tabview.get('contentBox').one('> ul').append(this.ADD_TEMPLATE);
+ }
+});
+
+
+Handling the Click
+All that remains is to listen for clicks on the add button and prompt
+the user for the relevant Tab data. Again we can leverage
+the Node API, this time to delegate clicks on the add button.
+Stopping event propagation in our handler ensures that the event does
+not bubble any further, preventing the TabView from trying
+to handle it. To keep the example simple, a basic prompt is
+used to get the user input. This could be refined to use an
+Overlay or other custom control. The data is then handed off
+to TabView's add method.
Y.extend(Addable, Y.Plugin.Base, {
+ ADD_TEMPLATE: '<li class="yui3-tab" title="add a tab">' +
+ '<a class="yui3-tab-label yui3-tab-add">+</a></li>',
+
+ initializer: function(config) {
+ var tabview = this.get('host');
+ tabview.after('render', this.afterRender, this);
+
+ tabview.get('contentBox')
+ .delegate('click', this.onAddClick, '.yui3-tab-add', this);
+ },
+
+ afterRender: function(e) {
+ this.get('host').get('contentBox').one('> ul').append(this.ADD_TEMPLATE);
+ },
+
+ getTabInput: function() {
+ return {
+ label: window.prompt('label:', 'new tab'),
+ content: window.prompt('content:', '<p>new content</p>'),
+ index: Number(window.prompt('index:', this._host.size()))
+ }
+ },
+
+ onAddClick: function(e) {
+ e.stopPropagation();
+ var tabview = this.get('host');
+ input = this.getTabInput();
+ tabview.add(input, input.index);
+ }
+});
+
+
+Using the Plugin
+Now we can go ahead and plug in our functionality. This can be during
+construction with the plugins attribute, or subsequently
+via the plug method.
var tabview = new Y.TabView({
+ children: [{
+ label: 'foo',
+ content: '<p>foo content</p>'
+ }, {
+ label: 'bar',
+ content: '<p>bar content</p>'
+ }, {
+ label: 'baz',
+ content: '<p>baz content</p>'
+ }],
+ plugins: [Addable]
+});
+
+// or
+// tabview.plug(Addable);
+
+
+Removeable Tabs Plugin
+The process for creating a removeable plugin for TabView
+is very similar. The full source is provided below.
var Removeable = function(config) {
+ Removeable.superclass.constructor.apply(this, arguments);
+};
+
+Removeable.NAME = 'removeableTabs';
+Removeable.NS = 'removeable';
+
+Y.extend(Removeable, Y.Plugin.Base, {
+ REMOVE_TEMPLATE: '<a class="yui3-tab-remove" title="remove tab">x</a>',
+
+ initializer: function(config) {
+ var tabview = this.get('host'),
+ cb = tabview.get('contentBox');
+
+ cb.addClass('yui3-tabview-removeable');
+ cb.delegate('click', this.onRemoveClick, '.yui3-tab-remove', this);
+
+ // Tab events bubble to TabView
+ tabview.after('tab:render', this.afterTabRender, this);
+ },
+
+ afterTabRender: function(e) {
+ // boundingBox is the Tab's LI
+ e.target.get('boundingBox').append(this.REMOVE_TEMPLATE);
+ },
+
+ onRemoveClick: function(e) {
+ e.stopPropagation();
+ var tab = Y.Widget.getByNode(e.target);
+ tab.remove();
+ }
+});
+
+Complete Example Source
+
+Note: be sure to add the yui3-skin-sam classname to the
+page's <body> element or to a parent element of the widget in order to apply
+the default CSS skin. See Understanding Skinning.
+
<body class="yui3-skin-sam"> <!-- You need this skin class -->+ + +
<div id="demo">
+ <ul>
+ <li><a href="#foo">foo</a></li>
+ <li><a href="#bar">bar</a></li>
+ <li><a href="#baz">baz</a></li>
+ </ul>
+ <div>
+ <div id="foo">
+ <p>foo content</p>
+ </div>
+ <div id="bar">
+ <p>bar content</p>
+ </div>
+ <div id="baz">
+ <p>baz content</p>
+ </div>
+ </div>
+</div>
+
+<div id="demo2">
+</div>
+
+<div id="demo3">
+</div>
+
+<script type="text/javascript">
+YUI().use('tabview', 'escape', 'plugin', function(Y) {
+ var Addable = function(config) {
+ Addable.superclass.constructor.apply(this, arguments);
+ };
+
+ Addable.NAME = 'addableTabs';
+ Addable.NS = 'addable';
+
+ Y.extend(Addable, Y.Plugin.Base, {
+ ADD_TEMPLATE: '<li class="yui3-tab" title="add a tab">' +
+ '<a class="yui3-tab-label yui3-tab-add">+</a></li>',
+
+ initializer: function(config) {
+ var tabview = this.get('host');
+ tabview.after('render', this.afterRender, this);
+ tabview.get('contentBox')
+ .delegate('click', this.onAddClick, '.yui3-tab-add', this);
+ },
+
+ getTabInput: function() {
+ var tabview = this.get('host');
+ return {
+ label: Y.Escape.html(window.prompt('label:', 'new tab')),
+ content: '<p>' + Y.Escape.html(window.prompt('content:', 'new content')) + '</p>',
+ index: Number(window.prompt('index:', tabview.size()))
+ }
+ },
+
+ afterRender: function(e) {
+ var tabview = this.get('host');
+ tabview.get('contentBox').one('> ul').append(this.ADD_TEMPLATE);
+ },
+
+ onAddClick: function(e) {
+ e.stopPropagation();
+ var tabview = this.get('host'),
+ input = this.getTabInput();
+ tabview.add(input, input.index);
+ }
+ });
+
+ var Removeable = function(config) {
+ Removeable.superclass.constructor.apply(this, arguments);
+ };
+
+ Removeable.NAME = 'removeableTabs';
+ Removeable.NS = 'removeable';
+
+ Y.extend(Removeable, Y.Plugin.Base, {
+ REMOVE_TEMPLATE: '<a class="yui3-tab-remove" title="remove tab">x</a>',
+
+ initializer: function(config) {
+ var tabview = this.get('host'),
+ cb = tabview.get('contentBox');
+
+ cb.addClass('yui3-tabview-removeable');
+ cb.delegate('click', this.onRemoveClick, '.yui3-tab-remove', this);
+
+ // Tab events bubble to TabView
+ tabview.after('tab:render', this.afterTabRender, this);
+ },
+
+ afterTabRender: function(e) {
+ // boundingBox is the Tab's LI
+ e.target.get('boundingBox').append(this.REMOVE_TEMPLATE);
+ },
+
+ onRemoveClick: function(e) {
+ e.stopPropagation();
+ var tab = Y.Widget.getByNode(e.target);
+ tab.remove();
+ }
+ });
+
+ var tabview = new Y.TabView({
+ srcNode: '#demo',
+ plugins: [Addable]
+ });
+
+ var tabview2 = new Y.TabView({
+ children: [{
+ label: 'foo',
+ content: '<p>foo content</p>'
+ }, {
+ label: 'bar',
+ content: '<p>bar content</p>'
+ }, {
+ label: 'baz',
+ content: '<p>baz content</p>'
+ }],
+ plugins: [Removeable]
+ });
+
+ var tabview3 = new Y.TabView({
+ children: [{
+ label: 'foo',
+ content: '<p>foo content</p>'
+ }, {
+ label: 'bar',
+ content: '<p>bar content</p>'
+ }, {
+ label: 'baz',
+ content: '<p>bar content</p>'
+ }],
+ plugins: [Addable, Removeable]
+ });
+
+ tabview.render();
+ tabview2.render('#demo2');
+ tabview3.render('#demo3');
+});
+</script>
+
+