Modal

The fullscreen Modal can be used for stuff that doesn't really work in an Aside. Note that this component is in development, the design and APIs may change.

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.

<main data-ts="Main">
	<p>Main content.</p>
</main>
<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 Main element to center the content.

<dialog data-ts="Modal" id="example1">
	<div data-ts="Panel">
		<main data-ts="Main">
			<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>
		</main>
	</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

Adding some buttons

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

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.

Adding some 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 TabBar.

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 elements with a data-ts.label attribute and this will generate the tabs automatically as seen in this example.

<dialog data-ts="Modal">
	<div data-ts="Panel" data-ts.label="Details">
		<main data-ts="Main"></main>
	</div>
	<div data-ts="Panel" data-ts.label="More Details">
		<main data-ts="Main"></main>
	</div>
</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 hide the content while loading. Importantly, if you are using Main 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 Main at any point in time.

Showing a spinner

If you expect the server to respond particularly slowly today, you can also use the equivalent methods spin() and stop() to display a spinner while loading.

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.

A Modal Example

The suggested "modal layout" markup structure and CSS has not been finalized yet.

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.

A Modal Example

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.

User details

More details