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>
284 lines
7.6 KiB
Plaintext
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>
|
|
}
|
|
}
|