Tag

Tags represent a set of interactive keywords that help to label, organize, and categorize objects.

This is the API of the Tag component, all HTML attributes and JS properties correspond to the table below, you can use them to customize how your tag looks.

Passing complex data via HTML attributes

Any properties that aren't Functions can be given value via HTML attributes, simply by writing `data-ts.${propertyName}=${encodedValue}`.

When passing complex data structures as HTML attributes, you need to JSON.stringify() the value and then pass it to encodeURIComponent to get a string that can be safely used as an attribute.

We supply ts.ui.encode() as a helper function that does just this.

  • <script>
    	// Prepare the data that we want to pass to the component
    	const dataToBeEncoded = {
    		thisDataIs: 'complex',
    		because: ['it', 'is', 'not', 'simple']
    	};
    	// Encode it using `ts.ui.encode()`
    	const encodedData = ts.ui.encode(
    		dataToBeEncoded
    	);
    	// Of couse, you could just use `encodeURIComponent(JSON.stringify())`,
    	// which could be especially useful during server-side rendering.
    	assert encodedData === encodeURIComponent(JSON.stringify(dataToBeEncoded));
    	// encodedData: "%7B%22thisDataIs%22%3A%22complex%22%2C%22because%22%3A%5B%22it%22%2C%22is%22%2C%22not%22%2C%22simple%22%5D%7D"
    
    	// Insert the value as an attribute of the component.
    	// (You'd probably handle this with React, Angular or some other client-side framework.)
    	document.write(
    		`<example-ts-component data-ts.exampleProperty=${encodedData}></example-ts-component>`
    	);
    </script>

Now that you know how to pass complex data to components, it's time to go through the different kinds of things the tag can do.

Value-only

Any non-string passed to data will be converted into a Map, using non-unique keys will result in unreliable behavior!

Values are simple descriptors, with lighter font weights and lowercase characters. To render, use a Map-like Array with only a value and a falsy key.

  • <script>
    	const data = ts.ui.encode(
    		[
    			[null, 'Roll a d20']
    		]
    	);
    </script>
    <figure
    	data-ts="Tag"
    	data-ts.data="%5B%5Bnull%2C%22Roll%20a%20d20%22%5D%5D">
    </figure>

It's also possible to set data (and all other attributes) later, through the JS API.

  • <script>
    	ts.ui.ready(() => {
    		ts.ui.get('#tag-roll20', tag => {
    			tag.data = new Map([
    				[null, 'Roll a d20']
    			]);
    		});
    	});
    </script>
    <figure
    	id="tag-roll20"
    	data-ts="Tag">
    </figure>

Key-Value

If you need a key with a value attached to it, use the Map-like Array structure like before, but with a key and a value this time.

  • <script>
    	const data = ts.ui.encode(
    		[
    			['Area of Origin', 'The Sword Coast']
    		]
    	);
    </script>
    <figure
    	data-ts="Tag"
    	data-ts.data="%5B%5B%22Area%20of%20Origin%22%2C%22The%20Sword%20Coast%22%5D%5D">
    </figure>

Key with multiple values

You can have a single key with multiple values, just make the value part of the Map-like Array an Array.

  • <script>
    	const data = ts.ui.encode(
    		[
    			['The Teeming Hive of Evil', ['Skullport', 'Port of Shadows']]
    		]
    	);
    </script>
    <figure
    	data-ts="Tag"
    	data-ts.data="%5B%5B%22The%20Teeming%20Hive%20of%20Evil%22%2C%5B%22Skullport%22%2C%22Port%20of%20Shadows%22%5D%5D%5D">
    </figure>

Multiple keys with single value

Of course you could flip it around and use several keys for a single value.

  • <script>
    	const data = ts.ui.encode(
    		[
    			[['Facial Tentacles', 'Potent Psionics'], 'Mind Flayer']
    		]
    	);
    </script>
    <figure
    	data-ts="Tag"
    	data-ts.data="%5B%5B%5B%22Facial%20Tentacles%22%2C%22Potent%20Psionics%22%5D%2C%22Mind%20Flayer%22%5D%5D">
    </figure>

Multiple keys with multiple values

It comes naturally that you can have several keys together with several values.

  • <script>
    	const data = ts.ui.encode(
    		[
    			[['Magic-user', 'Undead'], ['Lich', 'Vampire']]
    		]
    	);
    </script>
    <figure
    	data-ts="Tag"
    	data-ts.data="%5B%5B%5B%22Magic-user%22%2C%22Undead%22%5D%2C%5B%22Lich%22%2C%22Vampire%22%5D%5D%5D">
    </figure>

Multiple sets of key-values

If you want to have several key/value sets in a single tag, just have more entries in your Map-like Array.

  • <script>
    	const data = ts.ui.encode(
    		[
    			['Acererak'],
    			['Alignment', ['Lawful', 'Evil']],
    			['Hobbies', 'Building Dungeons'],
    		]
    	);
    </script>
    <figure
    	data-ts="Tag"
    	data-ts.data="%5B%5B%22Acererak%22%5D%2C%5B%22Alignment%22%2C%5B%22Lawful%22%2C%22Evil%22%5D%5D%2C%5B%22Hobbies%22%2C%22Building%20Dungeons%22%5D%5D">
    </figure>

Clickable look & click handler

If you want to make your tag look like it can be clicked, once you've initialized your tag, set the onclick handler.

  • <script>
    	const data = ts.ui.encode(
    		[
    			['Vision', ['Blindsight', 'Truesight', 'Darkvision']]
    		]
    	);
    	ts.ui.ready(() => {
    		ts.ui.get('#tag-clickable', tag => {
    			tag.onclick = () => {
    				ts.ui.Notification.success('Do you see?');
    			};
    		});
    	});
    </script>
    <figure
    	id="tag-clickable"
    	data-ts="Tag"
    	data-ts.data="%5B%5B%22Vision%22%2C%5B%22Blindsight%22%2C%22Truesight%22%2C%22Darkvision%22%5D%5D%5D">
    </figure>

You can use your own click handler if you'd like.

Make sure to set data-ts.clickable="true", otherwise your tag won't look like it's clickable.

  • <script>
    	const data = ts.ui.encode(
    		[
    			['Vision', ['Blindsight', 'Truesight', 'Darkvision']]
    		]
    	);
    	ts.ui.ready(() => {
    		ts.ui.get('#tag-clickable-ownhandler', tag => {
    			tag.element.addEventListener('click', e => {
    				ts.ui.Notification.success('Do you see?');
    			});
    		});
    	});
    </script>
    <figure
    	id="tag-clickable-ownhandler"
    	data-ts="Tag"
    	data-ts.clickable="true"
    	data-ts.data="%5B%5B%22Vision%22%2C%5B%22Blindsight%22%2C%22Truesight%22%2C%22Darkvision%22%5D%5D%5D">
    </figure>

Delete button & delete handler

If you want to be able to remove a tag, once you've initialized the tag, set the ondelete handler. This will create a DEL element as the last child of the tag.

When the DEL element is clicked, the tag will be removed from the DOM after a setTimeout. Don't try to read anything through the DOM at this point.

  • <script>
    	const data = ts.ui.encode(
    		[
    			['Languages', ['Sylvan', 'Common', 'Draconic', 'Giant']]
    		]
    	);
    	ts.ui.ready(() => {
    		ts.ui.get('#tag-deletable', tag => {
    			tag.ondelete = () => {
    				ts.ui.Notification.info('Tag disintegrated!');
    			}
    		});
    	});
    </script>
    <figure
    	id="tag-deletable"
    	data-ts="Tag"
    	data-ts.data="%5B%5B%22Languages%22%2C%5B%22Sylvan%22%2C%22Common%22%2C%22Draconic%22%2C%22Giant%22%5D%5D%5D">
    </figure>

Just like with click, you can use your own click handler for deletion if you'd like.

You have to remember that only react when the user clicks on the DEL element by checking for e.target.localName.

  • <script>
    	const data = ts.ui.encode(
    		[
    			['Languages', ['Sylvan', 'Common', 'Draconic', 'Giant']]
    		]
    	);
    	ts.ui.ready(() => {
    		ts.ui.get('#tag-deletable-ownhandler', tag => {
    			tag.element.addEventListener('click', e => {
    				if (e.target.localName === 'del') {
    					ts.ui.Notification.info('Tag disintegrated!');
    				}
    			});
    		});
    	});
    </script>
    <figure
    	id="tag-deletable-ownhandler"
    	data-ts="Tag"
    	data-ts.deletable="true"
    	data-ts.data="%5B%5B%22Languages%22%2C%5B%22Sylvan%22%2C%22Common%22%2C%22Draconic%22%2C%22Giant%22%5D%5D%5D">
    </figure>

Delete & Remove

If you want the user to confirm before you remove the tag from the DOM. You need to set doremove to false first, and call remove on your confirm callback

  • <script>
    	const data = ts.ui.encode(
    		[
    			['Languages', ['Sylvan', 'Common', 'Draconic', 'Giant']]
    		]
    	);
    	ts.ui.ready(() => {
    		ts.ui.get('#tag-remove', tag => {
    			tag.doremove = false;
    			tag.ondelete = () => {
    				ts.ui.Dialog.confirm('Do you want to delete the tag?', {
    				onaccept: function() {
    					tag.doremove = true;
    					tag.remove();
    					ts.ui.Notification.success('Tag removed');
    				},
    				oncancel: function() {
    					ts.ui.Notification.success('No, I do not want to delete it');
    				}
    			});
    			};
    		});
    	});
    </script>
    <figure
    	id="tag-remove"
    	data-ts="Tag"
    	data-ts.data="%5B%5B%22Languages%22%2C%5B%22Sylvan%22%2C%22Common%22%2C%22Draconic%22%2C%22Giant%22%5D%5D%5D">
    </figure>

Click & delete

In case you want to handle clicks and have a delete button on the same tag, you can do that the same way as above.

  • <script>
    	const data = ts.ui.encode(
    		[
    			['Beholder', 'Xanathar']
    		]
    	);
    	ts.ui.ready(() => {
    		ts.ui.get('#tag-delete-click', tag => {
    			tag.onclick = () => {
    				ts.ui.Notification.info('Don\'t poke the beholder!');
    			};
    			tag.ondelete = () => {
    				ts.ui.Notification.warning('I hope you know what you\'re doing...');
    			}
    		});
    	});
    </script>
    <figure
    	id="tag-delete-click"
    	data-ts="Tag"
    	data-ts.data="%5B%5B%22Beholder%22%2C%22Xanathar%22%5D%5D">
    </figure>

You can always handle your own clicks if that's what you're into.

You still have to remember that only handle the delete click when the user clicks on the DEL element by checking for e.target.localName.

  • <script>
    	const data = ts.ui.encode(
    		[
    			['Beholder', 'Xanathar']
    		]
    	);
    	ts.ui.ready(() => {
    		ts.ui.get('#tag-delete-click-ownhandler', tag => {
    			tag.element.addEventListener('click', e => {
    				if (e.target.localName === 'del') {
    					ts.ui.Notification.warning('I hope you know what you\'re doing...');
    				} else {
    					ts.ui.Notification.info('Don\'t poke the beholder!');
    				}
    			});
    		});
    	});
    </script>
    <figure
    	id="tag-delete-click-ownhandler"
    	data-ts="Tag"
    	data-ts.clickable="true"
    	data-ts.deletable="true"
    	data-ts.data="%5B%5B%22Beholder%22%2C%22Xanathar%22%5D%5D">
    </figure>

Locked look

If you want to lock down a tag, use data-ts.locked="true".

A locked tag will have its DEL button hidden and all click-related styling deactivated.

  • <script>
    	const data = ts.ui.encode(
    		[
    			['Dungeon', 'Hidden Shrine of Tamoachan']
    		]
    	);
    </script>
    <figure
    	data-ts="Tag"
    	data-ts.locked="true"
    	data-ts.data="%5B%5B%22Dungeon%22%2C%22Hidden%20Shrine%20of%20Tamoachan%22%5D%5D">
    </figure>

If you find a bug or need a feature…

  • Create GitHub Issue…