Modal

The fullscreen Modal can be used for stuff that doesn't really work in an Aside.

The Modal component must be created with a child component Panel.

  • <dialog data-ts="Modal">
    	<div data-ts="Panel">
    		<p>Modal content.</p>
    	</div>
    </dialog>

You can create the Modal wherever you like, just make sure that it's positioned outside of the Main element when it opens.

  • <p>Main content.</p>
    <dialog data-ts="Modal" data-ts.title="Example Modal">
    	<div data-ts="Panel">
    		<p>Modal content.</p>
    	</div>
    </dialog>

Title and layout

The data-ts.title attribute configures the Modal title. You can also choose to add a Box element to center the content.

  • <dialog data-ts="Modal" id="example1">
    	<div data-ts="Panel">
    		<article data-ts="Box">
    			<h1>A Modal Example</h1>
    			<p></p>
    			<p>
    				<button onclick="ts.ui.get('#example1').close()" class="ts-primary">
    					<span>Close the Modal</span>
    				</button>
    			</p>
    		</article>
    	</div>
    </dialog>

Let's see how that looks. To open the Modal, you can either set the data-ts.open attribute to true (using jQuery or something like that) or you can use the following API.

ts.ui.get('#example1').open(); // you can pass an element instead of an ID

Micro Size

You can add ts-micro-modal class name to the modal element to have smaller modal with extra padding.

  • <dialog data-ts="Modal" id="exampleMicro" class="ts-micro-modal">
    	<div data-ts="Panel">
    		<article data-ts="Box">
    			<h1>A Micro Modal Example</h1>
    			<p></p>
    			<p>
    				<button onclick="ts.ui.get('#exampleMicro').close()" class="ts-primary">
    					<span>Close the Modal</span>
    				</button>
    			</p>
    		</article>
    	</div>
    </dialog>

And here you can try it out to see how it looks:

ts.ui.get('#exampleMicro').open(); // you can pass an element instead of an ID

Modal buttons

Buttons can be added as part of the content, but some Modals work better when the buttons are presented in a fixed footer, for example when there is a lot of content (and scrolling). These buttons can be added using the API we know from the Footer.

ts.ui.get('#example2', modal => {
	modal.buttons([{
		label: 'Close the Modal',
		type: 'ts-primary',
		onclick: function() {
			modal.close();
		}
	}]).open();
});

TODO: Enable keyboard focus in the Modal StatusBar.

Here"s a summary of the buttons collection and button model.

Modal footer

You can get a footer by following, then you can use the API we know from the Footer.

ts.ui.get('#example2').footer();
ts.ui.get('#example2', modal => {
	modal.buttons([{
		label: 'Clear the footer',
		type: 'ts-primary',
		onclick: function() {
			modal.footer().clear();
		}
	}]).open();
});

Modal header

You can get a header by following, then you can use the API we know from the Header.

ts.ui.get('#example2').header();

Modal tabs

There's two ways to add tabs and one of them is likely to fit your workflow. First, we will add some tabs to the example modal using the API we know from the Header.

ts.ui.get('#example1', modal => {
	modal.tabs([
		{label: 'One'},
		{label: 'Two'},
		{label: 'Three', onselect: function() {
			console.log('Tab three selected!');
		}},
	]).open();
});

The tabs don"t do anything by default, so what happens at onselect is completely up to you. If you decice to go with this approach, you should make sure to read the section on busy and done further down the page.

The alternative approach is to declare multiple Panel inside a containing Panels element. They should all have the data-ts.label attribute declared. This will generate the tabs automatically as seen in this example.

  • <dialog data-ts="Modal">
    	<ul data-ts="Panels">
    		<li data-ts="Panel" data-ts.label="Details">
    			<div data-ts="Box"></div>
    		</li>
    		<li data-ts="Panel" data-ts.label="More Details">
    			<div data-ts="Box"></div>
    		</li>
    	</li>
    </dialog>

Note that even if the tabs are automatically generated via Panel elements, it is still possible to manipulate the tabs programatically.

ts.ui.get('#mymodal', modal => {
	var tabs = modal.tabs();
	tabs[0].select();
	tabs.reverse();
});

Here"s a summary of the tabs collection and tab model.

Busy and done

If you are loading the Modal tabs incrementally (so that the content is not hardcoded inside Panels ), you may want to mark the Modal as busy() and done() to show a Spinner while loading. Importantly, if you are using Box to center the content, this will also instruct the Modal to center the the newly loaded content all over again.

ts.ui.get('#example4', modal => {
	function load(page) {
		modal.busy();
		setTimeout(function throttle() {
			$.get(page, function(html) {
				modal.html(html).done();
			});
		}, 200);
	}
	modal.tabs([
		{label: 'Page One', onselect: function() {
			load('page1.html');
		}},
		{label: 'Page Two',  onselect: function() {
			load('page2.html');
		}},
	]).open();
});

Note that you can also call modal.reflex() to center the Box at any point in time.

Tracking the state

You can implement the methods onopen, onopened, onclose and onclosed to do something whenever the Modal changes state.

ts.ui.get(myelement, modal => {
	Object.assign(modal, {
		onopen: () => console.log('Will open'),
		onopened: () => console.log('Did open'),
		onclose: () => console.log('Will close'),
		onclosed: () => console.log('Did close')
	});
});

If you return false in methods onopen and onclose, the Modal will respect that. You can also setup inline callbacks to be invoked when the Modal changes state.

  • <dialog data-ts="Modal"
    	data-ts.onopen="console.log('Will open')"
    	data-ts.onopened="console.log('Did open')"
    	data-ts.onclose="console.log('Will close')"
    	data-ts.onclosed="console.log('Did close')">
    	<div data-ts="Panel">
    		<p>Modal content.</p>
    	</div>
    </dialog>

Finally, you can also track the state with some custom DOM events if you like.

function debug(e) {
	console.log(e.type, e.target);
}
document.addEventListener('ts-open', debug);
document.addEventListener('ts-opened', debug);
document.addEventListener('ts-close', debug);
document.addEventListener('ts-closed', debug);

The events ts-open and ts-close can be blocked with e.preventDefault() to prevent the Modal from changing state.

Here's finally an overview of the Modal API.

If you find a bug or need a feature…

  • Create GitHub Issue…
  • A Modal Example

    Modal ipsum dolor amet jerky sausage pork belly tenderloin burgdoggen kevin prosciutto beef ribs shoulder tri-tip salami ribeye turducken rump. Rump prosciutto ham, kevin picanha drumstick chuck pork chop. Short ribs tail shank, alcatra kevin spare ribs meatloaf beef. Pork loin salami flank andouille prosciutto chuck bresaola sirloin ribeye. Bacon picanha salami filet mignon capicola beef ribs. Venison jowl meatloaf jerky porchetta, brisket shank picanha.

    Modal with fixed footer

    Bacon ipsum dolor amet jerky sausage pork belly tenderloin burgdoggen kevin prosciutto beef ribs shoulder tri-tip salami ribeye turducken rump. Rump prosciutto ham, kevin picanha drumstick chuck pork chop. Short ribs tail shank, alcatra kevin spare ribs meatloaf beef. Pork loin salami flank andouille prosciutto chuck bresaola sirloin ribeye. Bacon picanha salami filet mignon capicola beef ribs. Venison jowl meatloaf jerky porchetta, brisket shank picanha drumstick capicola frankfurter.

    A Micro Modal Example

    Micro Modal ipsum dolor amet jerky sausage pork belly tenderloin burgdoggen kevin prosciutto beef ribs shoulder tri-tip salami ribeye turducken rump. Rump prosciutto ham, kevin picanha drumstick chuck pork chop. Short ribs tail shank, alcatra kevin spare ribs meatloaf beef. Pork loin salami flank andouille prosciutto chuck bresaola sirloin ribeye. Bacon picanha salami filet mignon capicola beef ribs. Venison jowl meatloaf jerky porchetta, brisket shank picanha.