TLDR: https://stackblitz.com/edit/angular-ivy-86uxvs?file=src/app/app.component.html
The way I see it, each keyword is an object
with a word
and a tooltip
. So I'll define the schema for that.
export type keyword = {
word: string;
tooltip: string;
};
I'll define the keywords in an object for easy lookup:
keywords: { [key: string]: keyword } = {
disadvantage: {
word: 'disadvantage',
tooltip: 'advantage description',
},
incapacitated: {
word: 'incapacitated',
tooltip: 'incapacitated description',
},
};
I think the best way is to split the string into an array of strings and keywords, so we can print them to the html in order. Here's a function that does that - I put all this in a service because it seems fitting.
toDynamicText(text: string): (string | keyword)[] {
const res: (string | keyword)[] = [];
const tokens = text.split(' ');
let i = 0;
for (const token of tokens) {
let keyword = this.keywords[token.toLowerCase()]; //undefined if word is not a keyword
if (keyword) {
i = res.push(keyword);
} else {
if (!res[i]) res[i] = token;
else res[i] += ' ' + token;
}
}
return res;
}
output array looks like this:
[
"When you take the Dodge action, you focus entirely on avoiding attacks. Until the start of your next turn, any Attack roll made against you has",
{
"word": "disadvantage",
"tooltip": "advantage description"
},
"if you can see the attacker, and you make Dexterity Saving Throws with advantage. You lose this benefit if you are",
{
"word": "incapacitated",
"tooltip": "incapacitated description"
},
"(as explained in Conditions ) or if your speed drops to 0. "
]
and here's an example of how to use it:
text =
'When you take the Dodge action, you focus entirely on avoiding attacks. ' +
'Until the start of your next turn, any Attack roll made against you has disadvantage if you can see the attacker, ' +
'and you make Dexterity Saving Throws with advantage. ' +
'You lose this benefit if you are Incapacitated (as explained in Conditions ) or if your speed drops to 0. ';
dynamicText: (string | keyword)[] = [];
constructor(private dynamicTextService: DynamicTextService) {}
ngOnInit(): void {
this.dynamicText = this.dynamicTextService.toDynamicText(this.text);
}
isString(token: any) {
return typeof token === 'string';
}
//Typescript compiler wants a typecast, so we have two helper functions
getTooltip(token: string | keyword) {
return (token as keyword).tooltip;
}
getWord(token: string | keyword) {
return (token as keyword).word;
}
<p>
<ng-container *ngFor="let token of dynamicText">
<ng-container *ngIf="isString(token)">{{ token }}</ng-container>
<ng-container *ngIf="!isString(token)"
><b [matTooltip]="getTooltip(token)" style="cursor: pointer">
{{ getWord(token) }}
</b></ng-container
>
</ng-container>
</p>
You could make it more robust by ensuring that the tooltip
and word
properties exist before trying to return them.
I would make this a component with text
as an input variable.