Skip to content

Commit

Permalink
Add <NumberFlowGroup> for Vue
Browse files Browse the repository at this point in the history
  • Loading branch information
barvian committed Nov 13, 2024
1 parent e21d924 commit ad220fd
Show file tree
Hide file tree
Showing 7 changed files with 90 additions and 29 deletions.
5 changes: 5 additions & 0 deletions .changeset/thin-hotels-chew.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@number-flow/vue': patch
---

Add <NumberFlowGroup>
38 changes: 38 additions & 0 deletions packages/vue/src/NumberFlowGroup.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<script lang="ts" setup>
import type { NumberFlowLite } from 'number-flow'
import { key, type RegisterWithGroup } from './group'
import { provide, watch, type Ref, nextTick, onUnmounted } from 'vue'
const flows = new Set<Ref<NumberFlowLite | undefined>>()
let updating = false
const registerWithGroup: RegisterWithGroup = (el, parts) => {
flows.add(el)
watch(
parts,
async () => {
if (updating) return
updating = true
flows.forEach((flow) => {
flow.value?.willUpdate()
})
await nextTick()
flows.forEach((flow) => {
flow.value?.didUpdate()
})
updating = false
}
// { flush: 'pre' } // default
)
onUnmounted(() => {
flows.delete(el)
})
}
provide(key, registerWithGroup)
</script>
<template>
<slot />
</template>
9 changes: 9 additions & 0 deletions packages/vue/src/group.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import type { NumberFlowLite, partitionParts } from 'number-flow'
import type { InjectionKey, Ref, ComputedRef, MaybeRefOrGetter } from 'vue'

export type RegisterWithGroup = (
el: Ref<NumberFlowLite | undefined>,
parts: ComputedRef<ReturnType<typeof partitionParts>>
) => void

export const key = Symbol() as InjectionKey<RegisterWithGroup | undefined>
2 changes: 2 additions & 0 deletions packages/vue/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import {
prefersReducedMotion
} from 'number-flow'

export { default as NumberFlowGroup } from './NumberFlowGroup.vue'

export type { Value, Format, Trend } from 'number-flow'
export { NumberFlowElement }

Expand Down
29 changes: 8 additions & 21 deletions packages/vue/src/index.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ import {
NumberFlowLite,
type Props as NumberFlowProps
} from 'number-flow'
import { computed, ref } from 'vue'
import { computed, inject, ref, watch } from 'vue'
import { key as groupKey } from './group'
type Props = Partial<NumberFlowProps> & {
locales?: Intl.LocalesArgument
Expand All @@ -18,7 +19,7 @@ type Props = Partial<NumberFlowProps> & {
// This is repetitive but I couldn't get it any cleaner using `withDefaults`,
// because then you can't destructure,
// and if you don't set defaults Vue will use its own for i.e. booleans.
// and if you don't set defaults Vue will use its own for e.g. booleans.
const {
locales,
format,
Expand All @@ -41,7 +42,6 @@ defineOptions({
inheritAttrs: false // set them manually to ensure `parts` updates last
})
// Technically the original animationsstart still emits but ah well:
const emit = defineEmits<{
(e: 'animationsstart'): void
(e: 'animationsfinish'): void
Expand All @@ -50,32 +50,19 @@ const emit = defineEmits<{
// You're supposed to cache these between uses:
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString
const formatter = computed(() => new Intl.NumberFormat(locales, format))
const parts = computed(() => partitionParts(value, formatter.value))
// Pre effects don't seem to batch to handle grouping like React:
// https://discord.com/channels/325477692906536972/1299071468533055541
//
// watch(
// [parts, () => isolate],
// ([_, isolate]) => {
// if (!isolate) el.value?.willUpdate()
// },
// { flush: 'pre' }
// )
// watch(
// [parts, () => isolate],
// ([_, isolate]) => {
// if (!isolate) el.value?.didUpdate()
// },
// { flush: 'post' }
// )
// Handle grouping. Keep as much logic in NumberFlowGroup.vue as possible
// for better tree-shaking:
const register = inject(groupKey, undefined)
register?.(el, parts)
</script>
<template>
<!-- Make sure parts is set last: -->
<number-flow-vue
ref="el"
v-bind="$attrs"
:manual="Boolean(register)"
:trend
:continuous
:animated
Expand Down
22 changes: 14 additions & 8 deletions site/src/pages/[...framework]/examples/_Group/vue/Component.vue
Original file line number Diff line number Diff line change
@@ -1,17 +1,23 @@
<script setup lang="ts">
import NumberFlow from '@number-flow/vue'
import NumberFlow, { NumberFlowGroup } from '@number-flow/vue'
const { value, diff } = defineProps<{
value: number
diff: number
}>()
</script>

<template>
<div
style="--number-flow-char-height: 0.85em"
class="~text-xl/4xl flex items-center gap-2 font-semibold"
>
<NumberFlow :value :format="{ style: 'currency', currency: 'USD' }" />
<NumberFlow :value="diff" :format="{ style: 'percent', maximumFractionDigits: 2 }" />
</div>
<NumberFlowGroup>
<div style="--number-flow-char-height: 0.85em" class="flex items-center gap-4 font-semibold">
<NumberFlow :value :format="{ style: 'currency', currency: 'USD' }" class="~text-2xl/4xl" />
<NumberFlow
:value="diff"
:format="{ style: 'percent', maximumFractionDigits: 2, signDisplay: 'always' }"
:class="[
'~text-lg/2xl transition-colors duration-300',
diff < 0 ? 'text-red-500' : 'text-emerald-500'
]"
/>
</div>
</NumberFlowGroup>
</template>
14 changes: 14 additions & 0 deletions site/src/pages/[...framework]/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import Type from '@/components/Type.astro'
import Heading from '@/components/Heading.astro'
import Union from '@/components/Union.astro'
import Link from '@/components/Link.astro'
import Group from './examples/_Group/index.astro'
export { getStaticPaths } from '@/lib/framework'
export const components = {a: Link, pre: Pre}

Expand Down Expand Up @@ -271,6 +272,19 @@ See [MDN](https://developer.mozilla.org/en-US/docs/Web/CSS/font-variant-numeric#

{/* <TabularNumsDemo client:visible /> */}

<Match vue svelte>
---

<Heading value="Grouping"/>

If you're using multiple `<NumberFlow>` components on one line, you can wrap
them in a `<NumberFlowGroup>` to properly sync their transitions:

<Group />

`<NumberFlowGroup>` doesn't render an element or accept any props.
</Match>

---

<Match>
Expand Down

0 comments on commit ad220fd

Please sign in to comment.