Files
WRNexusJS/.publish/ui/components/ToggleCount.wrn
T
ClintchizandClaude Opus 5 7a2b58652a
Quality / quality (ubuntu-latest) (push) Failing after 11m2s
Quality / quality (windows-latest) (push) Canceled after 0s
chore(release): prepare 0.8.6
Bumps all 47 packages, the root manifest and the VS Code extension to 0.8.6,
and rebuilds the editor compiler, language server and extension bundles that
embed the version.

The release carries the output delivery fix: camelCase outputs now reach
parent bindings, and 18 components emit through output.* instead of
hand-built CustomEvents. See the 0.8.6 migration entry for what changes for
consumers.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-09 01:54:44 +05:30

284 lines
7.6 KiB
Plaintext

component ToggleCount {
outputs {
change(payload: { value?: string | number | boolean | null; values?: Array<string | number | boolean | null | object>; sourceEvent?: Event; [key: string]: string | number | boolean | null | object } | string | number | boolean | null)
toggle(payload: { sourceEvent?: Event; [key: string]: string | number | boolean | null | object })
}
props {
size: string = "default"
color: string = "primary"
variant: string = "segmented"
class: string = ""
name: string = "billing-cycle"
value: string = "monthly"
firstValue: string = "monthly"
firstLabel: string = "Monthly"
secondValue: string = "annual"
secondLabel: string = "Annual"
ariaLabel: string = "Billing frequency"
items: unknown[] = []
currency: string = "$"
suffix: string = ""
firstValueKey: string = "monthly"
secondValueKey: string = "annual"
emptyValue: string = "—"
align: string = "end"
fullWidth: boolean = true
disabled: boolean = false
animate: boolean = true
animationDuration: number = 450
animationSteps: number = 18
}
state selectedValue = value
functions {
shared function isFirstSelected() {
return selectedValue === firstValue
}
shared function isSecondSelected() {
return selectedValue === secondValue
}
shared function displayValue(item) {
if (isSecondSelected()) {
return item[secondValueKey] !== undefined
? item[secondValueKey]
: emptyValue
}
return item[firstValueKey] !== undefined
? item[firstValueKey]
: emptyValue
}
client function dispatchToggleEvent(sourceEvent, previousValue, payload) {
payload = {
component: "ToggleCount",
name: name,
value: selectedValue,
previousValue: previousValue,
firstValue: firstValue,
secondValue: secondValue
}
output.change(payload)
output.toggle(payload)
}
client function selectValue(sourceEvent, nextValue, previousValue) {
if (disabled || selectedValue === nextValue) {
return
}
previousValue = selectedValue
selectedValue = nextValue
if (
animate &&
!window.matchMedia("(prefers-reduced-motion: reduce)").matches
) {
animateDisplayedValues(sourceEvent, previousValue)
}
dispatchToggleEvent(sourceEvent, previousValue)
}
client function animateDisplayedValues(sourceEvent, previousValue, root, nodes, animationToken) {
root = sourceEvent.currentTarget.closest("[data-wrn-toggle-count]")
if (!root) {
return
}
animationToken = String(Date.now()) + ":" + String(selectedValue)
root.setAttribute("data-animation-token", animationToken)
nodes = root.querySelectorAll("[data-toggle-count-value]")
animateValueAt(nodes, 0, previousValue, root, animationToken)
}
client function animateValueAt(
nodes,
index,
previousValue,
root,
animationToken,
node,
fromValue,
toValue
) {
if (index >= nodes.length) {
return
}
node = nodes[index]
fromValue = Number(
previousValue === secondValue
? node.getAttribute("data-second-value")
: node.getAttribute("data-first-value")
)
toValue = Number(
selectedValue === secondValue
? node.getAttribute("data-second-value")
: node.getAttribute("data-first-value")
)
if (!Number.isNaN(fromValue) && !Number.isNaN(toValue)) {
node.replaceChildren(String(fromValue))
scheduleValueFrame(
node,
fromValue,
toValue,
1,
Math.max(1, Number(animationSteps) || 1),
Math.max(1, Number(animationDuration) || 1),
root,
animationToken
)
}
animateValueAt(
nodes,
index + 1,
previousValue,
root,
animationToken
)
}
client function scheduleValueFrame(
node,
fromValue,
toValue,
frame,
totalFrames,
duration,
root,
animationToken,
nextValue
) {
if (frame > totalFrames) {
return
}
nextValue = Math.round(
fromValue +
(toValue - fromValue) * (frame / totalFrames)
)
setTimeout(
applyAnimatedValue,
Math.max(1, duration * frame / totalFrames),
node,
nextValue,
root,
animationToken
)
scheduleValueFrame(
node,
fromValue,
toValue,
frame + 1,
totalFrames,
duration,
root,
animationToken
)
}
client function applyAnimatedValue(
node,
nextValue,
root,
animationToken
) {
if (
root &&
root.getAttribute("data-animation-token") !== animationToken
) {
return
}
node.replaceChildren(String(nextValue))
}
client function toggleValue(sourceEvent) {
selectValue(
sourceEvent,
isFirstSelected() ? secondValue : firstValue
)
}
}
view {
<section
{...attrs}
data-wrn-toggle-count
data-variant="{variant}"
data-value="{selectedValue}"
data-disabled="{disabled ? 'true' : 'false'}"
class="wire-next wire-next--color-{color} wire-next--size-{size} wire-next--toggle-count wire-next--toggle-count-{variant} {fullWidth ? 'wire-next--toggle-count-full' : ''} {disabled ? 'wire-next--disabled' : ''} {class}"
>
<input type="hidden" name="{name}" value="{selectedValue}" />
<div class="wire-next__toggle-count-control wire-next__toggle-count-control--{align}">
{#if variant === "switch"}
<span data-selected="{isFirstSelected() ? 'true' : 'false'}">{firstLabel}</span>
<button
type="button"
role="switch"
aria-label="{ariaLabel}"
aria-checked="{isSecondSelected() ? 'true' : 'false'}"
disabled="{disabled}"
@click="toggleValue(event)"
class="wire-next__toggle-count-switch"
>
<span aria-hidden="true"></span>
</button>
<span data-selected="{isSecondSelected() ? 'true' : 'false'}">{secondLabel}</span>
{:else}
<div class="wire-next__toggle-count-segmented" role="group" aria-label="{ariaLabel}">
<button
type="button"
aria-pressed="{isFirstSelected() ? 'true' : 'false'}"
disabled="{disabled}"
@click="selectValue(event, firstValue)"
>{firstLabel}</button>
<button
type="button"
aria-pressed="{isSecondSelected() ? 'true' : 'false'}"
disabled="{disabled}"
@click="selectValue(event, secondValue)"
>{secondLabel}</button>
</div>
{/if}
</div>
{#if items.length > 0}
<div class="wire-next__toggle-count-items">
{#each items as item}
<article>
<span>{item.label || item.name}</span>
<strong>
{#if currency}<small>{currency}</small>{/if}
<span
data-toggle-count-value
data-first-value="{item[firstValueKey]}"
data-second-value="{item[secondValueKey]}"
>{displayValue(item)}</span>
{#if suffix}<small>{suffix}</small>{/if}
</strong>
{#if item.description}<small>{item.description}</small>{/if}
</article>
{/each}
</div>
{/if}
</section>
}
}