Calling a TW macro from a JS widget?

Hello everyone,

Once again I’m trying to do something above my pay grade: I’m customizing Projectify and I would like to use the pikaday widget in such a way that when the user selects a date these two things happen:

  • the date is stored in a specific field (already done)
  • a predefined TW macro is called.

My question is about the second point: how can I call a TW macro from a JS widget? (if that’s even possible?)

The current code for storing the date is like this (from here):

AbstractDatePickerWidget.prototype.setValue = function (date) {
	let updateFields = {
		title: this.title,
		[this.field]: date ? this.formatDate(date) : undefined
	};

	this.wiki.addTiddler(
		new $tw.Tiddler(
			this.wiki.getCreationFields(),
			this.getTiddler(),
			updateFields,
			this.wiki.getModificationFields()
		)
	);

	$tw.rootWidget.dispatchEvent({type: "tm-auto-save-wiki"});
};

Thank you!

do you mean a javascript macro or a wikitext macro?

what needs to be done by the macro?

I mean a wikitext macro.

I want to have some quite complex wikitext code my-macro (set some field, create a tiddler if condition is satisfied, …) to execute when the user performs specific actions. There are at least two different scenarios where mymacro should be called: 1) the user clicks a button 2) the user selects a date using the pikaday widget.

So in case 2), I would like to call <<my-macro>> from the JS code.

If this is not possible, I can have the user first select the date and then click another button to trigger the macro. Of course it’s not as good, because then the user would need to do two actions instead of one.

One way to do that would be to use an action string - have a look at $:/core/modules/widgets/select.js to see how it is done.

1 Like

Thank you, it works perfectly :slight_smile:

I didn’t realize that it’s the same logic as the actions arguments with the standard widgets, this makes sense.

For anyone interested, a wikitext macro can be called in a JS widget with invokeActionString like below:

AbstractDatePickerWidget.prototype.setValue = function (date) {
	let updateFields = {
		title: this.title,
		[this.field]: date ? this.formatDate(date) : undefined
	};

	this.wiki.addTiddler(
		new $tw.Tiddler(
			this.wiki.getCreationFields(),
			this.getTiddler(),
		        updateFields,
		    	this.wiki.getModificationFields()
		)
	);

    	this.invokeActionString(this.actions, this, "click");	
	$tw.rootWidget.dispatchEvent({type: "tm-auto-save-wiki"});
};

2 Likes

@erwan I am glad you found your answer. I too have modified and enhanced projectify substantially. Your workaround here highlights the date picker issues we find in tiddlywiki, mostly because we dont have an established solution.

  • I will just let you know I and others have found or written date picker solutions. Eg tiddlytools so its worth some search and research

If macro is written in JS, then very easy, require it and use its exported .run function

// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
const dateDurationMacro = require('$:/plugins/linonetwo/tw-calendar/date-duration-macro');
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
const getDateDuration = dateDurationMacro.run as (startDateString: string, endDateString: string) => string;

// use it
const startDate = $tw.utils.formatDateString(argument.event.start, '[UTC]YYYY0MM0DD0hh0mm0ss0XXX');
        const endDate = $tw.utils.formatDateString(argument.event.end, '[UTC]YYYY0MM0DD0hh0mm0ss0XXX');
        const durationText = getDateDuration(startDate, endDate);

If it is written in wikitext, then wikify it by makeChildWidget, like this

        const childTree = $tw.wiki.parseText('text/vnd.tiddlywiki', tiddler.fields.caption).tree;
        const astNode = { type: 'tiddler', children: childTree };
        const newWidgetNode = context.widget.makeChildWidget(astNode, { variables: context.widget.variables });
        // render tw content needs a temp real dom element, can't use vdom from `createElement`
        const temporaryEle = context.widget.document.createElement('div');
        // eslint-disable-next-line unicorn/no-null
        newWidgetNode.render(temporaryEle, null);
        captionResult = temporaryEle.textContent;

The variables here is very important, you can get it from parentWidget.variables.

Examples are from Calendar and Agenda plugin tw-calendar with Mobile friendly agenda page layout

1 Like