kit
kit copied to clipboard
Updating a style conditionally for a widget
Hi! 👋
I'm trying to build my first script using a widget API and am really impressed with how quickly I was able to come along! Thanks for building such a useful tool.
I'm running into one small issue. I'd like to update the background-color
of a widget after a timer I have elapses, but what I'd intuitively expect to work here isn't.
I've added the full code at the bottom of this issue, but below I've excerpted the notable part.
Note the classname bgColor
specified in the class list below, which I was expecting to be interpolated from state, but it's not. It does get picked up though, as reflected with the rendering of the inner text [Debug: {{bgColor}}]
which does render as expected.
let clock = await widget(`
<h1 class="text-xl {{bgColor}} p-5 whitespace-nowrap">
{{intent}} [Debug: {{bgColor}}]
<span class="ml-8 font-mono">{{timer}}</span>
</h1>`, {
transparent: true,
draggable: true,
hasShadow: false,
alwaysOnTop: true,
})
clock.setState({
timer: fmtMSS(remainingS),
bgColor: remainingS >= 0 ? "bg-rose-700" : "bg-emerald-600"
})
However, I don't get the background color as expected:

My hunch is that maybe non-innerText attributes don't doesn't get re-updated with state updates?
Appendix: Full Code
// Name: timebox
// Description: Set an intention and timebox it
import "@johnlindquist/kit"
let input = await arg("intention - #mins");
let [intent, mins] = input.split("-").map(s => s.trim());
if (mins === undefined) mins = 5; // default: 5 mins
let clock = await widget(`<h1 class="text-xl {{bgColor}} p-5 whitespace-nowrap">{{intent}} {{bgColor}} <span class="ml-8 font-mono">{{timer}}</span></h1>`, {
transparent: true,
draggable: true,
hasShadow: false,
alwaysOnTop: true,
})
clock.setState({
intent,
mins
})
let start = new Date();
let timer = setInterval(() => {
let elapsedS = Math.ceil((new Date().getTime() - start.getTime()) / 1000);
let remainingS = (+mins) * 60 - elapsedS;
let fmtMSS = (s) => {return(s-(s%=60))/60+(9<s?':':':0')+s};
clock.setState({
timer: fmtMSS(remainingS),
bgColor: remainingS >= 0 ? "bg-rose-700" : "bg-emerald-600"
})
}, 300)
Widgets use "petite-vue", so they require the :class
syntax to bind a property from the state
https://github.com/vuejs/petite-vue
let w = await widget(
`
<h1 class="text-xl p-5 whitespace-nowrap" :class="bgColor">
Hello {{bgColor}}!
</h1>`,
{
transparent: true,
draggable: true,
hasShadow: false,
alwaysOnTop: true,
state: {
bgColor: `bg-rose-700`,
},
width: 300,
height: 100,
}
)
let bg = ["rose", "emerald"]
let index = 0
setInterval(() => {
let bgColor = `bg-${bg[index]}-700`
w.setState({
bgColor,
})
index = index ? 0 : 1
}, 1000)
Thanks @johnlindquist! Appreciate the guidance here, and in the Discord as well.
I think it could be useful to add a note about petite-vue
when Widgets are introduced in the docs. The {{handlebar}}
syntax is introduced in the example, but I wasn't sure what was being used to implement that. I don't see any other mention of petite-vue
in the repo.
It probably is a more advanced use-case so I'd understand if you don't want to get into that level of implementation detail, but I think it could benefit other widget authors. What do you think? If you think it makes sense, and if you'd like me to do so, I could also offer to put up a suggestion PR.
I would love a PR! 👍
Great! I've put one up at #1115