kit icon indicating copy to clipboard operation
kit copied to clipboard

Updating a style conditionally for a widget

Open pringshia opened this issue 2 years ago • 3 comments

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:

CleanShot 2023-01-19 at 22 51 44@2x

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)

pringshia avatar Jan 20 '23 03:01 pringshia

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)

Open widget-background-color in Script Kit

johnlindquist avatar Jan 20 '23 04:01 johnlindquist

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.

pringshia avatar Jan 20 '23 04:01 pringshia

I would love a PR! 👍

johnlindquist avatar Jan 20 '23 04:01 johnlindquist

Great! I've put one up at #1115

pringshia avatar Feb 11 '23 13:02 pringshia