Monarch API

Alright, adventurous ones: I have a Beta version of Monarch with the new components API! Manifest: https://github.com/zeel01/monarch/releases/download/v0.3.6/module.json API Docs: https://github.com/zeel01/monarch/blob/dynamic-components/readme.md#-monarch-api This API allows you to respond to a hook, and manipulate a set of data that describes info badges and controls that will display on the cards in each app. You can remove items (hide suit, value, and type?), add badges and controls, or even re-arrange stuff if you like. Controls are similar to header buttons. They each get a class to name them, and an onclick method which will be called when the user clicks. This callback will be passed the Card that the user was interacting with, along with the Cards container it's held within. By this method, you can replicate all the card interactions that core uses, or create just about anything you can come up with. I used this system to implement a "discard" button which can be tested by pasting the id of a pile into the setting under module settings. I'll probably change this later to be a drop down menu or something. @mattr64 @floradical @norc. @Lucas Ferreira @badideasbureau Any feedback on the system/documentation would be super appreciated. I think I have a pretty robust implementation, but no plan survives contact with the enemy 😉
GitHub
monarch/readme.md at dynamic-components · zeel01/monarch
A Foundry VTT card UI fit for royalty. Contribute to zeel01/monarch development by creating an account on GitHub.
No description
54 Replies
FloRadical
FloRadical3y ago
ohhhh I'll defo take a look during the weekend :O
BadIdeasBureau
Shiny! Looking at the docs (will poke the actual module ~this weekend), it looks like the badges only display for the currently selected card, is that right? Being able to show a badge on all cards that fit a given criterion would be handy for Torg (to show all cards in pool at a glance)
zeel
zeel3y ago
A badge will show on all cards, unless hide is set to true for one of them. Of course, since the card HUD isn't visible for cards you aren't hovering you won't see more than one at a time in the hand. Markers of some form are a future addition though, and will likely have some means of seeing which cards are marked all the time. The function you pass as text will be called for each card. All the badges/controls on the cards use the new system now. So if you load up thr module you will see what I mean.
BadIdeasBureau
Makes sense, thanks
zeel
zeel3y ago
Ah, I love it when you spend a day annoyed by something you did but can't for some reason come up with a better idea. Then the next day you immediately realize a better choice. I was bothered that I named the hook getMonarchHandCommands since it's commands and other stuff, but didn't want to make it multiple hooks. Then I looked at the title I gave my release: "Components API Beta" ...Components facepalmpicard So... breaking API change incoming (lmao): I'll be changing the name of the hook in the next pre-release. I'm pretty sure nobody wrote any code yet though, so 😆 So will be getMonarchHandComponents, getMonarchDeckComponents, etc. Hmm.... Query: Should I change the hook signature to pass all the component arrays in a "components" object, so that they can be referenced by name?
components = {
controls: Array<CardControl>,
badges: Array<CardBadge>,
markers: Array<CardMarker>,
...
}
components = {
controls: Array<CardControl>,
badges: Array<CardBadge>,
markers: Array<CardMarker>,
...
}
Then the order of the parameters won't matter, you would just do:
Hooks.on("getMonarchHandComponents", (monarch, components) => {
components.badges.push({
...
});
});
Hooks.on("getMonarchHandComponents", (monarch, components) => {
components.badges.push({
...
});
});
Thoughts? This would make it more robust against changes in the API later, as adding to this object would not cause the hook signature to change.
BadIdeasBureau
I like that approach
zeel
zeel3y ago
Okay, I made those changes and updated the links above: https://github.com/zeel01/monarch/releases/download/v0.3.1/module.json
zeel
zeel3y ago
@badideasbureau Markers!
No description
BadIdeasBureau
Yessss!
FloRadical
FloRadical3y ago
dumb question BUT are there TS types xD
zeel
zeel3y ago
There are JS Doc types 😜
FloRadical
FloRadical3y ago
I was asking because of the snippets above. Made it look like there are types I can probably throw smething together
zeel
zeel3y ago
Is there a way for me to define the types without using TS? I'm not opposed to maintaining a type definition, but I don't want to actually write my code in TS.
FloRadical
FloRadical3y ago
yeah you can just write a d.ts file by hand it's how the foundryvtt types are made, we write them by hand
zeel
zeel3y ago
I'll look into it Do you mind opening a Github issue so I can't forget?
FloRadical
FloRadical3y ago
distributing is the more "difficult" thing. You could just have it as a single downloadble file or make an npm package I'll ask the others how we should best define the hooks
zeel
zeel3y ago
Hooks man. So cool. But freaking impossible to properly define. JS Doc doesn't handle it right, I'm betting TS is all 🤷 about it too.
FloRadical
FloRadical3y ago
suprisngly, the boys have found a way
zeel
zeel3y ago
@floradical As a test, I tried just having it generate .d.ts from the JS docs, and set my release workflow to attach that file to my releases. If you get a chance to check it, is the difinition attached to this sufficient? https://github.com/zeel01/monarch/releases/tag/v0.3.2.t
FloRadical
FloRadical3y ago
MonarchApplicationMixin ?
zeel
zeel3y ago
Deep magic
FloRadical
FloRadical3y ago
I'll need to test it but I'm not actually sure that it'll work
zeel
zeel3y ago
So that my apps that don't share the same base can share functionality.
FloRadical
FloRadical3y ago
right
zeel
zeel3y ago
It's possible that I did one of thise "TS can not fathom this JS" things.
FloRadical
FloRadical3y ago
like break polymorphism? xD
zeel
zeel3y ago
Mixins don't break polymorphism! (much) It's not my fault this language doesn't support multiple inheritance.
FloRadical
FloRadical3y ago
nah mixins aren't a problem IIRC
zeel
zeel3y ago
Honestly the only things that you need types for are the typedefs in Components.js And the hook Which is still 🤷 to me
FloRadical
FloRadical3y ago
yeah the only stuff we need typed are the components.
zeel
zeel3y ago
And they aren't even full classes, just objects of stuff
FloRadical
FloRadical3y ago
yeah that's what interfaces are for
zeel
zeel3y ago
I maybe should type the callback functions though Actually, how does one do that.
FloRadical
FloRadical3y ago
If I can get the time I'll try and write up the stuff if you want or we'll drop the subject. either works for me type the callback functions?
zeel
zeel3y ago
Well I would like for system devs using TS not to be angry with using it Like:
/**
* @typedef {Object} CardControl An object defining a control to display on a card.
* @property {string|Function<string>} [tooltip] - The tooltip of the control, or a function that returns the tooltip
* @property {string|Function<string>} [aria] - The aria label (for screen readers) of the control, or a function that returns the aria label
* @property {string|Function<string>} [icon] - The icon to display for the control, or a function that returns the icon
* @property {string} [class] - The css class to apply to the control
* @property {boolean|Function<boolean>} [disabled] - Whether the control is disabled, or a function that returns whether the control is disabled
* @property {Function} [onclick] - The function to call when the control is clicked
* @property {Array<CardControl>} [controls] - An array of controls to display as a group
*/
/**
* @typedef {Object} CardControl An object defining a control to display on a card.
* @property {string|Function<string>} [tooltip] - The tooltip of the control, or a function that returns the tooltip
* @property {string|Function<string>} [aria] - The aria label (for screen readers) of the control, or a function that returns the aria label
* @property {string|Function<string>} [icon] - The icon to display for the control, or a function that returns the icon
* @property {string} [class] - The css class to apply to the control
* @property {boolean|Function<boolean>} [disabled] - Whether the control is disabled, or a function that returns whether the control is disabled
* @property {Function} [onclick] - The function to call when the control is clicked
* @property {Array<CardControl>} [controls] - An array of controls to display as a group
*/
How do I type hint the parameters that get passed to onclick or to tooltip? I know how to do Function<boolean> to hint the return value
FloRadical
FloRadical3y ago
I'm honestly not sure how to do that propely in JSDoc. TS is kinda self-documenting in that way. lemme try something does the onclick function get any inputs?
zeel
zeel3y ago
event, card, and container
FloRadical
FloRadical3y ago
/** An object defining a control to display on a card. */
interface CardControl {
/** The tooltip of the control, or a function that returns the tooltip */
tooltip: string | (() => string);
/** The aria label (for screen readers) of the control, or a function that returns the aria label */
aria: string | (() => string);
/** The icon to display for the control, or a function that returns the icon */
icon: string | (() => string);
/** The css class to apply to the control */
class: string;
/**
* Whether or not to disable the control. The control will appear grayed out. May be a function that returns a boolean.
* @defaultValue `false`
*/
disabled?: string | (() => string);
/** A function that will run when the control is clicked*/
onclick: (event: MouseEvent, card: Card, container: Cards) => void;
/**
* An array of controls to be added as a control group. When included, you may omit all other properties except `class`.
* Instead, each item in this `controls` array should be a complete CardControl object. Does not support nesting.
*/
controls?: CardControl[];
}
/** An object defining a control to display on a card. */
interface CardControl {
/** The tooltip of the control, or a function that returns the tooltip */
tooltip: string | (() => string);
/** The aria label (for screen readers) of the control, or a function that returns the aria label */
aria: string | (() => string);
/** The icon to display for the control, or a function that returns the icon */
icon: string | (() => string);
/** The css class to apply to the control */
class: string;
/**
* Whether or not to disable the control. The control will appear grayed out. May be a function that returns a boolean.
* @defaultValue `false`
*/
disabled?: string | (() => string);
/** A function that will run when the control is clicked*/
onclick: (event: MouseEvent, card: Card, container: Cards) => void;
/**
* An array of controls to be added as a control group. When included, you may omit all other properties except `class`.
* Instead, each item in this `controls` array should be a complete CardControl object. Does not support nesting.
*/
controls?: CardControl[];
}
zeel
zeel3y ago
/**
* @callback clickCallback
* @param {Event} event
* @param {Card} card
* @param {Cards} container
* @returns {string}
*/
/**
* @callback clickCallback
* @param {Event} event
* @param {Card} card
* @param {Cards} container
* @returns {string}
*/
This seems to perhaps do the trick?
export type clickCallback = (event: Event, card: Card, container: Cards) => string;
export type clickCallback = (event: Event, card: Card, container: Cards) => string;
FloRadical
FloRadical3y ago
^^ yay
zeel
zeel3y ago
Then if I use clickCallback as the type instead of Function it seems right?
FloRadical
FloRadical3y ago
yup since you defined the signature and return type of the function as a type so you just reference that
zeel
zeel3y ago
Okay, so now how does the hook get handled? I have a Components typedef. So the signature for the Hook callback is: (monarch: FormApplication, components: Components) => void
FloRadical
FloRadical3y ago
pretty much, yeah
zeel
zeel3y ago
components.contextMenu is an array of CardControl, so not much different from components.controls just displayed inside this nifty context menu. By default, I've added toggles for the seven colored markers I included in v0.3.2
Unknown User
Unknown User3y ago
Message Not Public
Sign In & Join Server To View
zeel
zeel3y ago
Possibly Additional built in buttons/settings will be added when the API is done.
Unknown User
Unknown User3y ago
Message Not Public
Sign In & Join Server To View
zeel
zeel3y ago
You can make it disabled too So the button is there, but can't be used. And you can disable it per-card. So if they have the mana for one card, but not another, you can control that. And you can always look through the controls array for the play button, and give it a disable function. Rather than replacing/removing the button.
Unknown User
Unknown User3y ago
Message Not Public
Sign In & Join Server To View
zeel
zeel3y ago
Yep Wooo! Finally: AppControls These componenets are used to add the buttons on the sheet for manipulating the whole application or pile. https://github.com/zeel01/monarch/releases/download/v0.3.4/module.json Found in components.appControls these work very similarly to card controls, but the callback signatures are a little different since they don't reference specific cards. This array is empty/ignored for the Card sheet, for it use controls and a CardControl since those are designed to reference a Card object. And that I believe is the end of the Dynamic Components portion of the API! I have a few more API things I want to add before I release this is a public build, plus some non-API issues to deal with. I would greatly appreciate any API feedback I can get before then though. https://github.com/zeel01/monarch/releases/download/v0.3.5/module.json Added some hooks for other events. https://github.com/zeel01/monarch/releases/download/v0.3.6/module.json
FloRadical
FloRadical3y ago
I'll give it a try this weekend