@@ -4,66 +4,65 @@ import {
44 SlottedTag ,
55 slottedStyles ,
66 partitionParts ,
7- type PartitionedParts ,
87 NumberFlowLite ,
98 prefersReducedMotion ,
109 canAnimate as _canAnimate ,
11- } from 'number-flow'
12- import { JSX } from 'solid-js/jsx-runtime'
13- import { createMemo , onMount , splitProps } from 'solid-js'
14- import { Dynamic } from 'solid-js/web'
15- export type { Value , Format , Trend } from 'number-flow'
10+ } from 'number-flow' ;
11+ import { JSX } from 'solid-js/jsx-runtime' ;
12+ import { createEffect , createMemo , createSignal , onMount , splitProps } from 'solid-js' ;
13+ import { Dynamic } from 'solid-js/web' ;
14+ export type { Value , Format , Trend } from 'number-flow' ;
1615
1716// Can't wait to not have to do this in React 19:
18- const OBSERVED_ATTRIBUTES = [ 'parts' ] as const
19- type ObservedAttribute = ( typeof OBSERVED_ATTRIBUTES ) [ number ]
17+ const OBSERVED_ATTRIBUTES = [ 'parts' ] as const ;
18+ type ObservedAttribute = ( typeof OBSERVED_ATTRIBUTES ) [ number ] ;
2019export class NumberFlowElement extends NumberFlowLite {
21- static observedAttributes = OBSERVED_ATTRIBUTES
20+ static observedAttributes = OBSERVED_ATTRIBUTES ;
2221 attributeChangedCallback ( attr : ObservedAttribute , _oldValue : string , newValue : string ) {
23- this [ attr ] = JSON . parse ( newValue )
22+ this [ attr ] = JSON . parse ( newValue ) ;
2423 }
2524}
2625
2726export type NumberFlowProps = JSX . HTMLAttributes < NumberFlowElement > & {
28- value : Value
29- locales ?: Intl . LocalesArgument
30- format ?: Format
31- isolate ?: boolean
32- animated ?: boolean
33- respectMotionPreference ?: boolean
34- willChange ?: boolean
27+ value : Value ;
28+ locales ?: Intl . LocalesArgument ;
29+ format ?: Format ;
30+ isolate ?: boolean ;
31+ animated ?: boolean ;
32+ respectMotionPreference ?: boolean ;
33+ willChange ?: boolean ;
3534 // animateDependencies?: React.DependencyList
36- onAnimationsStart ?: ( ) => void
37- onAnimationsFinish ?: ( ) => void
38- trend ?: ( typeof NumberFlowElement ) [ 'prototype' ] [ 'trend' ]
39- opacityTiming ?: ( typeof NumberFlowElement ) [ 'prototype' ] [ 'opacityTiming' ]
40- transformTiming ?: ( typeof NumberFlowElement ) [ 'prototype' ] [ 'transformTiming' ]
41- spinTiming ?: ( typeof NumberFlowElement ) [ 'prototype' ] [ 'spinTiming' ]
42- }
35+ onAnimationsStart ?: ( ) => void ;
36+ onAnimationsFinish ?: ( ) => void ;
37+ trend ?: ( typeof NumberFlowElement ) [ 'prototype' ] [ 'trend' ] ;
38+ opacityTiming ?: ( typeof NumberFlowElement ) [ 'prototype' ] [ 'opacityTiming' ] ;
39+ transformTiming ?: ( typeof NumberFlowElement ) [ 'prototype' ] [ 'transformTiming' ] ;
40+ spinTiming ?: ( typeof NumberFlowElement ) [ 'prototype' ] [ 'spinTiming' ] ;
41+ } ;
4342
4443// You're supposed to cache these between uses:
4544// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString
4645// Serialize to strings b/c React:
47- const formatters : Record < string , Intl . NumberFormat > = { }
46+ const formatters : Record < string , Intl . NumberFormat > = { } ;
4847
4948export default function NumberFlow ( props : NumberFlowProps ) {
5049 onMount ( ( ) => {
51- NumberFlowElement . define ( )
52- } )
50+ NumberFlowElement . define ( ) ;
51+ } ) ;
5352
5453 const localesString = createMemo (
5554 ( ) => ( props . locales ? JSON . stringify ( props . locales ) : '' ) ,
5655 [ props . locales ] ,
57- )
58- const formatString = createMemo ( ( ) => ( props . format ? JSON . stringify ( props . format ) : '' ) )
56+ ) ;
57+ const formatString = createMemo ( ( ) => ( props . format ? JSON . stringify ( props . format ) : '' ) ) ;
5958 const parts = createMemo ( ( ) => {
6059 const formatter = ( formatters [ `${ localesString } :${ formatString } ` ] ??= new Intl . NumberFormat (
6160 props . locales ,
6261 props . format ,
63- ) )
62+ ) ) ;
6463
65- return partitionParts ( props . value , formatter )
66- } )
64+ return partitionParts ( props . value , formatter ) ;
65+ } ) ;
6766
6867 const [ _used , others ] = splitProps ( props , [
6968 // For Root
@@ -80,13 +79,24 @@ export default function NumberFlow(props: NumberFlowProps) {
8079 'opacityTiming' ,
8180 'transformTiming' ,
8281 'spinTiming' ,
83- ] )
82+ ] ) ;
83+
84+ onMount ( ( ) => {
85+ // This is a workaround until this gets fixed: https://github.com/solidjs/solid/issues/2339
86+ const el = props . ref as unknown as HTMLElement ;
87+ const _parts = el . getAttribute ( 'attr:parts' ) ;
88+ if ( _parts ) {
89+ el . removeAttribute ( 'attr:parts' ) ;
90+ el . setAttribute ( 'parts' , _parts ) ;
91+ }
92+ } ) ;
8493
8594 return (
8695 < Dynamic
96+ ref = { props . ref }
8797 component = { 'number-flow' }
88- // https://docs.solidjs.com/reference/jsx-attributes/attr
8998 class = { props . class }
99+ // https://docs.solidjs.com/reference/jsx-attributes/attr
90100 attr :data-will-change = { props . willChange ? '' : undefined }
91101 { ...others }
92102 attr :parts = { JSON . stringify ( parts ( ) ) }
@@ -95,5 +105,36 @@ export default function NumberFlow(props: NumberFlowProps) {
95105 { parts ( ) . formatted }
96106 </ Dynamic >
97107 </ Dynamic >
98- )
108+ ) ;
109+ }
110+
111+ // SSR-safe canAnimate
112+ /** Unfinished and untested. */
113+ export function useCanAnimate (
114+ props : { respectMotionPreference : boolean } = { respectMotionPreference : true } ,
115+ ) {
116+ const [ canAnimate , setCanAnimate ] = createSignal ( _canAnimate ) ;
117+ const [ reducedMotion , setReducedMotion ] = createSignal ( false ) ;
118+
119+ // Handle SSR:
120+ onMount ( ( ) => {
121+ setCanAnimate ( _canAnimate ) ;
122+ setReducedMotion ( prefersReducedMotion ?. matches ?? false ) ;
123+ } ) ;
124+
125+ // Listen for reduced motion changes if needed:
126+ createEffect ( ( ) => {
127+ if ( ! props . respectMotionPreference ) return ;
128+ const onChange = ( { matches } : MediaQueryListEvent ) => {
129+ setReducedMotion ( matches ) ;
130+ } ;
131+ prefersReducedMotion ?. addEventListener ( 'change' , onChange ) ;
132+ return ( ) => {
133+ prefersReducedMotion ?. removeEventListener ( 'change' , onChange ) ;
134+ } ;
135+ } ) ;
136+
137+ return createMemo < boolean > ( ( ) => {
138+ return canAnimate ( ) && ( ! props . respectMotionPreference || ! reducedMotion ) ;
139+ } ) ;
99140}
0 commit comments