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>

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>