ng-number-flow 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +178 -0
- package/fesm2022/ng-number-flow.mjs +187 -0
- package/fesm2022/ng-number-flow.mjs.map +1 -0
- package/package.json +51 -0
- package/types/ng-number-flow.d.ts +50 -0
package/README.md
ADDED
|
@@ -0,0 +1,178 @@
|
|
|
1
|
+
# ng-number-flow
|
|
2
|
+
|
|
3
|
+
An Angular (19+ standalone) animated number component. A thin, dependency-light wrapper around
|
|
4
|
+
the framework-agnostic [`number-flow`](https://github.com/barvian/number-flow) web component —
|
|
5
|
+
the same animation/formatting engine used by the official React, Vue, and Svelte packages.
|
|
6
|
+
|
|
7
|
+
- 🎞️ Smooth rolling-digit transitions (Web Animations API)
|
|
8
|
+
- ♿ Accessible out of the box (`role="img"` + `aria-label`)
|
|
9
|
+
- 🌍 Locale-aware formatting via `Intl.NumberFormat`
|
|
10
|
+
- đź§© Group directive to synchronize animations across multiple numbers
|
|
11
|
+
- 🅰️ Standalone, `OnPush`, signal-based, zoneless-compatible; works with Angular 19 → latest
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
npm i ng-number-flow number-flow
|
|
17
|
+
# or
|
|
18
|
+
pnpm add ng-number-flow number-flow
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
`number-flow` is a runtime dependency of the wrapper and installed automatically, but keeping it
|
|
22
|
+
in your own `package.json` is recommended so bundlers resolve it reliably.
|
|
23
|
+
|
|
24
|
+
## Quick start
|
|
25
|
+
|
|
26
|
+
`NumberFlowComponent` is a standalone component with the selector `number-flow`. Import it and
|
|
27
|
+
bind a `value`:
|
|
28
|
+
|
|
29
|
+
```ts
|
|
30
|
+
import { Component, signal } from '@angular/core';
|
|
31
|
+
import { NumberFlowComponent } from 'ng-number-flow';
|
|
32
|
+
|
|
33
|
+
@Component({
|
|
34
|
+
selector: 'app-demo',
|
|
35
|
+
standalone: true,
|
|
36
|
+
imports: [NumberFlowComponent],
|
|
37
|
+
template: `<number-flow [value]="count()" />`,
|
|
38
|
+
})
|
|
39
|
+
export class DemoComponent {
|
|
40
|
+
readonly count = signal(3500);
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Whenever `value` changes, the digits animate from the old value to the new one.
|
|
45
|
+
|
|
46
|
+
## Formatting
|
|
47
|
+
|
|
48
|
+
Pass a standard [`Intl.NumberFormat` options](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Intl/NumberFormat/NumberFormat#options)
|
|
49
|
+
object to `format`, and optionally `locales`, `prefix`, and `suffix`:
|
|
50
|
+
|
|
51
|
+
```html
|
|
52
|
+
<!-- Currency -->
|
|
53
|
+
<number-flow
|
|
54
|
+
[value]="price()"
|
|
55
|
+
[format]="{ style: 'currency', currency: 'USD' }"
|
|
56
|
+
/>
|
|
57
|
+
|
|
58
|
+
<!-- Percent -->
|
|
59
|
+
<number-flow
|
|
60
|
+
[value]="ratio()"
|
|
61
|
+
[format]="{ style: 'percent', maximumFractionDigits: 1 }"
|
|
62
|
+
/>
|
|
63
|
+
|
|
64
|
+
<!-- Fixed decimals with prefix/suffix and a locale -->
|
|
65
|
+
<number-flow
|
|
66
|
+
[value]="amount()"
|
|
67
|
+
locales="de-DE"
|
|
68
|
+
prefix="$"
|
|
69
|
+
suffix=" USD"
|
|
70
|
+
[format]="{ minimumFractionDigits: 2, maximumFractionDigits: 2 }"
|
|
71
|
+
/>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
`Intl.NumberFormat` instances are cached internally (keyed by `locales:format`), so re-renders
|
|
75
|
+
are cheap.
|
|
76
|
+
|
|
77
|
+
## Grouping (synchronized animations)
|
|
78
|
+
|
|
79
|
+
Wrap multiple `number-flow` elements with the `numberFlowGroup` directive so their animations
|
|
80
|
+
start on the **same frame**, even when only some values change:
|
|
81
|
+
|
|
82
|
+
```ts
|
|
83
|
+
import { Component, signal } from '@angular/core';
|
|
84
|
+
import { NumberFlowComponent, NumberFlowGroupDirective } from 'ng-number-flow';
|
|
85
|
+
|
|
86
|
+
@Component({
|
|
87
|
+
selector: 'app-stats',
|
|
88
|
+
standalone: true,
|
|
89
|
+
imports: [NumberFlowComponent, NumberFlowGroupDirective],
|
|
90
|
+
template: `
|
|
91
|
+
<div numberFlowGroup>
|
|
92
|
+
<number-flow [value]="wins()" /> / <number-flow [value]="total()" />
|
|
93
|
+
</div>
|
|
94
|
+
`,
|
|
95
|
+
})
|
|
96
|
+
export class StatsComponent {
|
|
97
|
+
readonly wins = signal(120);
|
|
98
|
+
readonly total = signal(480);
|
|
99
|
+
}
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
To exclude a single child from group batching (animate it independently), set `[isolate]="true"`
|
|
103
|
+
on that `number-flow`.
|
|
104
|
+
|
|
105
|
+
## API
|
|
106
|
+
|
|
107
|
+
### `NumberFlowComponent` — `<number-flow>`
|
|
108
|
+
|
|
109
|
+
#### Inputs
|
|
110
|
+
|
|
111
|
+
| Input | Type | Default | Description |
|
|
112
|
+
| --- | --- | --- | --- |
|
|
113
|
+
| `value` | `number \| bigint \| string` (`Value`) | — | The number to display. Changing it triggers the animation. |
|
|
114
|
+
| `locales` | `Intl.LocalesArgument` | runtime default | BCP 47 locale(s) for formatting. |
|
|
115
|
+
| `format` | `Format` (`Intl.NumberFormatOptions`) | — | Number formatting options. |
|
|
116
|
+
| `prefix` | `string` | — | Static text rendered before the number. |
|
|
117
|
+
| `suffix` | `string` | — | Static text rendered after the number. |
|
|
118
|
+
| `animated` | `boolean` | `true` | Enable/disable animation. |
|
|
119
|
+
| `respectMotionPreference` | `boolean` | `true` | Skip animation when the OS requests reduced motion. |
|
|
120
|
+
| `transformTiming` | `EffectTiming` | `linear(...)` ~900ms | Timing for digit movement. |
|
|
121
|
+
| `spinTiming` | `EffectTiming` | falls back to `transformTiming` | Timing for the spin/roll effect. |
|
|
122
|
+
| `opacityTiming` | `EffectTiming` | `ease-out` 450ms | Timing for fade in/out of digits. |
|
|
123
|
+
| `trend` | `Trend` (`number \| (oldValue, value) => number`) | `Math.sign(value - old)` | Direction digits roll. |
|
|
124
|
+
| `plugins` | `Plugin[]` | — | `number-flow` plugins. |
|
|
125
|
+
| `digits` | `Digits` | — | Per-place digit constraints, e.g. `{ 1: { max: 5 } }`. |
|
|
126
|
+
| `isolate` | `boolean` | `false` | When inside a group, opt this element out of batched updates. |
|
|
127
|
+
| `willChange` | `boolean` | `false` | Add `will-change` hints for smoother animation of frequently-updating numbers. |
|
|
128
|
+
| `nonce` | `string` | — | CSP nonce for the injected style. |
|
|
129
|
+
|
|
130
|
+
#### Outputs
|
|
131
|
+
|
|
132
|
+
| Output | Payload | Description |
|
|
133
|
+
| --- | --- | --- |
|
|
134
|
+
| `animationsStart` | `void` | Emitted when a transition begins. |
|
|
135
|
+
| `animationsFinish` | `void` | Emitted when all transitions complete. |
|
|
136
|
+
|
|
137
|
+
```html
|
|
138
|
+
<number-flow
|
|
139
|
+
[value]="count()"
|
|
140
|
+
(animationsStart)="onStart()"
|
|
141
|
+
(animationsFinish)="onFinish()"
|
|
142
|
+
/>
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
### `NumberFlowGroupDirective` — `[numberFlowGroup]`
|
|
146
|
+
|
|
147
|
+
A standalone attribute directive. Place it on any container element to batch the animations of
|
|
148
|
+
all descendant `number-flow` components. No inputs.
|
|
149
|
+
|
|
150
|
+
### Exported types
|
|
151
|
+
|
|
152
|
+
`Value`, `Format`, `Data`, `Trend`, `Digits`, `Props`, `Plugin`, and `NumberFlowElement`
|
|
153
|
+
(the underlying custom-element type) are re-exported for convenience.
|
|
154
|
+
|
|
155
|
+
## How it works
|
|
156
|
+
|
|
157
|
+
The component renders an internal custom element (`<number-flow-ng>`, registered once via
|
|
158
|
+
`number-flow`'s `define()`) inside a host with `display: contents`, mirroring the React/Vue
|
|
159
|
+
wrapper pattern. Inputs are signal `input()`s; an `afterRenderEffect` syncs them onto the element
|
|
160
|
+
whenever they change, setting every property first and assigning `data` **last** (the engine's
|
|
161
|
+
required ordering), computing `data` with the core `formatToData` helper.
|
|
162
|
+
|
|
163
|
+
## SSR
|
|
164
|
+
|
|
165
|
+
This release is **client-side only**. On the server the component renders nothing and initializes
|
|
166
|
+
in the browser — its render hooks (`afterNextRender` / `afterRenderEffect`) run on the client only.
|
|
167
|
+
Declarative-Shadow-DOM hydration is a planned follow-up.
|
|
168
|
+
|
|
169
|
+
## Compatibility
|
|
170
|
+
|
|
171
|
+
- **Angular:** peer dependency `>=19.0.0`. Uses signal `input()` / `output()` / `viewChild()` and
|
|
172
|
+
`afterRenderEffect` (the last of which requires Angular 19+). Built and tested with the latest
|
|
173
|
+
Angular tooling.
|
|
174
|
+
- **Zone / zoneless:** works with both. Uses `OnPush` and native element events.
|
|
175
|
+
|
|
176
|
+
## License
|
|
177
|
+
|
|
178
|
+
MIT — same as `number-flow`.
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import * as i0 from '@angular/core';
|
|
2
|
+
import { InjectionToken, viewChild, input, output, inject, DestroyRef, afterNextRender, afterRenderEffect, ChangeDetectionStrategy, CUSTOM_ELEMENTS_SCHEMA, Component, Directive } from '@angular/core';
|
|
3
|
+
import NumberFlowLite, { define, formatToData } from 'number-flow/lite';
|
|
4
|
+
|
|
5
|
+
const NUMBER_FLOW_GROUP = new InjectionToken('NUMBER_FLOW_GROUP');
|
|
6
|
+
|
|
7
|
+
const cache = new Map();
|
|
8
|
+
function getFormatter(locales, format) {
|
|
9
|
+
const key = `${locales ? JSON.stringify(locales) : ''}:${format ? JSON.stringify(format) : ''}`;
|
|
10
|
+
let formatter = cache.get(key);
|
|
11
|
+
if (!formatter) {
|
|
12
|
+
formatter = new Intl.NumberFormat(locales, format);
|
|
13
|
+
cache.set(key, formatter);
|
|
14
|
+
}
|
|
15
|
+
return formatter;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const NUMBER_FLOW_ELEMENT_NAME = 'number-flow-ng';
|
|
19
|
+
define(NUMBER_FLOW_ELEMENT_NAME, NumberFlowLite);
|
|
20
|
+
|
|
21
|
+
class NumberFlowComponent {
|
|
22
|
+
constructor() {
|
|
23
|
+
this.flowRef = viewChild.required('flow');
|
|
24
|
+
this.value = input(/* @ts-ignore */
|
|
25
|
+
...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
|
|
26
|
+
this.locales = input(/* @ts-ignore */
|
|
27
|
+
...(ngDevMode ? [undefined, { debugName: "locales" }] : /* istanbul ignore next */ []));
|
|
28
|
+
this.format = input(/* @ts-ignore */
|
|
29
|
+
...(ngDevMode ? [undefined, { debugName: "format" }] : /* istanbul ignore next */ []));
|
|
30
|
+
this.prefix = input(/* @ts-ignore */
|
|
31
|
+
...(ngDevMode ? [undefined, { debugName: "prefix" }] : /* istanbul ignore next */ []));
|
|
32
|
+
this.suffix = input(/* @ts-ignore */
|
|
33
|
+
...(ngDevMode ? [undefined, { debugName: "suffix" }] : /* istanbul ignore next */ []));
|
|
34
|
+
this.animated = input(/* @ts-ignore */
|
|
35
|
+
...(ngDevMode ? [undefined, { debugName: "animated" }] : /* istanbul ignore next */ []));
|
|
36
|
+
this.respectMotionPreference = input(/* @ts-ignore */
|
|
37
|
+
...(ngDevMode ? [undefined, { debugName: "respectMotionPreference" }] : /* istanbul ignore next */ []));
|
|
38
|
+
this.transformTiming = input(/* @ts-ignore */
|
|
39
|
+
...(ngDevMode ? [undefined, { debugName: "transformTiming" }] : /* istanbul ignore next */ []));
|
|
40
|
+
this.spinTiming = input(/* @ts-ignore */
|
|
41
|
+
...(ngDevMode ? [undefined, { debugName: "spinTiming" }] : /* istanbul ignore next */ []));
|
|
42
|
+
this.opacityTiming = input(/* @ts-ignore */
|
|
43
|
+
...(ngDevMode ? [undefined, { debugName: "opacityTiming" }] : /* istanbul ignore next */ []));
|
|
44
|
+
this.trend = input(/* @ts-ignore */
|
|
45
|
+
...(ngDevMode ? [undefined, { debugName: "trend" }] : /* istanbul ignore next */ []));
|
|
46
|
+
this.plugins = input(/* @ts-ignore */
|
|
47
|
+
...(ngDevMode ? [undefined, { debugName: "plugins" }] : /* istanbul ignore next */ []));
|
|
48
|
+
this.digits = input(/* @ts-ignore */
|
|
49
|
+
...(ngDevMode ? [undefined, { debugName: "digits" }] : /* istanbul ignore next */ []));
|
|
50
|
+
this.isolate = input(false, /* @ts-ignore */
|
|
51
|
+
...(ngDevMode ? [{ debugName: "isolate" }] : /* istanbul ignore next */ []));
|
|
52
|
+
this.willChange = input(false, /* @ts-ignore */
|
|
53
|
+
...(ngDevMode ? [{ debugName: "willChange" }] : /* istanbul ignore next */ []));
|
|
54
|
+
this.nonce = input(/* @ts-ignore */
|
|
55
|
+
...(ngDevMode ? [undefined, { debugName: "nonce" }] : /* istanbul ignore next */ []));
|
|
56
|
+
this.animationsStart = output();
|
|
57
|
+
this.animationsFinish = output();
|
|
58
|
+
this.group = inject(NUMBER_FLOW_GROUP, { optional: true });
|
|
59
|
+
this.destroyRef = inject(DestroyRef);
|
|
60
|
+
// One-time setup: wire events and register with the group. `afterNextRender`
|
|
61
|
+
// runs on the client only (never during SSR) and after the view exists, so the
|
|
62
|
+
// view child is guaranteed resolved.
|
|
63
|
+
afterNextRender(() => {
|
|
64
|
+
const el = this.flowRef().nativeElement;
|
|
65
|
+
const onStart = () => this.animationsStart.emit();
|
|
66
|
+
const onFinish = () => this.animationsFinish.emit();
|
|
67
|
+
el.addEventListener('animationsstart', onStart);
|
|
68
|
+
el.addEventListener('animationsfinish', onFinish);
|
|
69
|
+
const unregister = this.group?.register(el);
|
|
70
|
+
this.destroyRef.onDestroy(() => {
|
|
71
|
+
el.removeEventListener('animationsstart', onStart);
|
|
72
|
+
el.removeEventListener('animationsfinish', onFinish);
|
|
73
|
+
unregister?.();
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
// Reactive sync: push the current inputs onto the custom element whenever any of
|
|
77
|
+
// them change. Custom-element property assignment is DOM work, so it belongs in
|
|
78
|
+
// `afterRenderEffect` (client-only) rather than a plain `effect`.
|
|
79
|
+
afterRenderEffect({
|
|
80
|
+
write: () => {
|
|
81
|
+
const el = this.flowRef().nativeElement;
|
|
82
|
+
this.applyProps(el);
|
|
83
|
+
this.applyData(el);
|
|
84
|
+
}
|
|
85
|
+
});
|
|
86
|
+
}
|
|
87
|
+
applyProps(el) {
|
|
88
|
+
const d = NumberFlowLite.defaultProps;
|
|
89
|
+
el.animated = this.animated() ?? d.animated;
|
|
90
|
+
el.respectMotionPreference = this.respectMotionPreference() ?? d.respectMotionPreference;
|
|
91
|
+
el.transformTiming = this.transformTiming() ?? d.transformTiming;
|
|
92
|
+
el.spinTiming = this.spinTiming() ?? d.spinTiming;
|
|
93
|
+
el.opacityTiming = this.opacityTiming() ?? d.opacityTiming;
|
|
94
|
+
el.trend = this.trend() ?? d.trend;
|
|
95
|
+
el.plugins = this.plugins() ?? d.plugins;
|
|
96
|
+
el.digits = this.digits() ?? d.digits;
|
|
97
|
+
el.batched = !!this.group && !this.isolate();
|
|
98
|
+
if (this.willChange())
|
|
99
|
+
el.setAttribute('data-will-change', '');
|
|
100
|
+
else
|
|
101
|
+
el.removeAttribute('data-will-change');
|
|
102
|
+
}
|
|
103
|
+
applyData(el) {
|
|
104
|
+
const value = this.value();
|
|
105
|
+
if (value == null)
|
|
106
|
+
return;
|
|
107
|
+
const data = formatToData(value, getFormatter(this.locales(), this.format()), this.prefix(), this.suffix());
|
|
108
|
+
if (this.group && !this.isolate()) {
|
|
109
|
+
this.group.beginUpdate();
|
|
110
|
+
el.data = data;
|
|
111
|
+
this.group.endUpdate();
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
el.data = data;
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NumberFlowComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
|
|
118
|
+
static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.0.5", type: NumberFlowComponent, isStandalone: true, selector: "number-flow", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, locales: { classPropertyName: "locales", publicName: "locales", isSignal: true, isRequired: false, transformFunction: null }, format: { classPropertyName: "format", publicName: "format", isSignal: true, isRequired: false, transformFunction: null }, prefix: { classPropertyName: "prefix", publicName: "prefix", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, animated: { classPropertyName: "animated", publicName: "animated", isSignal: true, isRequired: false, transformFunction: null }, respectMotionPreference: { classPropertyName: "respectMotionPreference", publicName: "respectMotionPreference", isSignal: true, isRequired: false, transformFunction: null }, transformTiming: { classPropertyName: "transformTiming", publicName: "transformTiming", isSignal: true, isRequired: false, transformFunction: null }, spinTiming: { classPropertyName: "spinTiming", publicName: "spinTiming", isSignal: true, isRequired: false, transformFunction: null }, opacityTiming: { classPropertyName: "opacityTiming", publicName: "opacityTiming", isSignal: true, isRequired: false, transformFunction: null }, trend: { classPropertyName: "trend", publicName: "trend", isSignal: true, isRequired: false, transformFunction: null }, plugins: { classPropertyName: "plugins", publicName: "plugins", isSignal: true, isRequired: false, transformFunction: null }, digits: { classPropertyName: "digits", publicName: "digits", isSignal: true, isRequired: false, transformFunction: null }, isolate: { classPropertyName: "isolate", publicName: "isolate", isSignal: true, isRequired: false, transformFunction: null }, willChange: { classPropertyName: "willChange", publicName: "willChange", isSignal: true, isRequired: false, transformFunction: null }, nonce: { classPropertyName: "nonce", publicName: "nonce", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { animationsStart: "animationsStart", animationsFinish: "animationsFinish" }, host: { styleAttribute: "display: contents" }, viewQueries: [{ propertyName: "flowRef", first: true, predicate: ["flow"], descendants: true, isSignal: true }], ngImport: i0, template: `<number-flow-ng #flow></number-flow-ng>`, isInline: true, changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
|
|
119
|
+
}
|
|
120
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NumberFlowComponent, decorators: [{
|
|
121
|
+
type: Component,
|
|
122
|
+
args: [{
|
|
123
|
+
selector: 'number-flow',
|
|
124
|
+
standalone: true,
|
|
125
|
+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
|
|
126
|
+
changeDetection: ChangeDetectionStrategy.OnPush,
|
|
127
|
+
template: `<number-flow-ng #flow></number-flow-ng>`,
|
|
128
|
+
host: { style: 'display: contents' }
|
|
129
|
+
}]
|
|
130
|
+
}], ctorParameters: () => [], propDecorators: { flowRef: [{ type: i0.ViewChild, args: ['flow', { isSignal: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], locales: [{ type: i0.Input, args: [{ isSignal: true, alias: "locales", required: false }] }], format: [{ type: i0.Input, args: [{ isSignal: true, alias: "format", required: false }] }], prefix: [{ type: i0.Input, args: [{ isSignal: true, alias: "prefix", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], animated: [{ type: i0.Input, args: [{ isSignal: true, alias: "animated", required: false }] }], respectMotionPreference: [{ type: i0.Input, args: [{ isSignal: true, alias: "respectMotionPreference", required: false }] }], transformTiming: [{ type: i0.Input, args: [{ isSignal: true, alias: "transformTiming", required: false }] }], spinTiming: [{ type: i0.Input, args: [{ isSignal: true, alias: "spinTiming", required: false }] }], opacityTiming: [{ type: i0.Input, args: [{ isSignal: true, alias: "opacityTiming", required: false }] }], trend: [{ type: i0.Input, args: [{ isSignal: true, alias: "trend", required: false }] }], plugins: [{ type: i0.Input, args: [{ isSignal: true, alias: "plugins", required: false }] }], digits: [{ type: i0.Input, args: [{ isSignal: true, alias: "digits", required: false }] }], isolate: [{ type: i0.Input, args: [{ isSignal: true, alias: "isolate", required: false }] }], willChange: [{ type: i0.Input, args: [{ isSignal: true, alias: "willChange", required: false }] }], nonce: [{ type: i0.Input, args: [{ isSignal: true, alias: "nonce", required: false }] }], animationsStart: [{ type: i0.Output, args: ["animationsStart"] }], animationsFinish: [{ type: i0.Output, args: ["animationsFinish"] }] } });
|
|
131
|
+
|
|
132
|
+
class Coordinator {
|
|
133
|
+
constructor() {
|
|
134
|
+
this.flows = new Set();
|
|
135
|
+
this.updating = false;
|
|
136
|
+
this.endScheduled = false;
|
|
137
|
+
}
|
|
138
|
+
register(el) {
|
|
139
|
+
this.flows.add(el);
|
|
140
|
+
return () => {
|
|
141
|
+
this.flows.delete(el);
|
|
142
|
+
};
|
|
143
|
+
}
|
|
144
|
+
beginUpdate() {
|
|
145
|
+
if (this.updating)
|
|
146
|
+
return;
|
|
147
|
+
this.updating = true;
|
|
148
|
+
this.flows.forEach((f) => {
|
|
149
|
+
if (f.created)
|
|
150
|
+
f.willUpdate();
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
endUpdate() {
|
|
154
|
+
if (this.endScheduled)
|
|
155
|
+
return;
|
|
156
|
+
this.endScheduled = true;
|
|
157
|
+
queueMicrotask(() => {
|
|
158
|
+
this.endScheduled = false;
|
|
159
|
+
if (!this.updating)
|
|
160
|
+
return;
|
|
161
|
+
this.flows.forEach((f) => {
|
|
162
|
+
if (f.created)
|
|
163
|
+
f.didUpdate();
|
|
164
|
+
});
|
|
165
|
+
this.updating = false;
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
class NumberFlowGroupDirective {
|
|
170
|
+
static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NumberFlowGroupDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
|
|
171
|
+
static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.0.5", type: NumberFlowGroupDirective, isStandalone: true, selector: "[numberFlowGroup]", providers: [{ provide: NUMBER_FLOW_GROUP, useFactory: () => new Coordinator() }], ngImport: i0 }); }
|
|
172
|
+
}
|
|
173
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: NumberFlowGroupDirective, decorators: [{
|
|
174
|
+
type: Directive,
|
|
175
|
+
args: [{
|
|
176
|
+
selector: '[numberFlowGroup]',
|
|
177
|
+
standalone: true,
|
|
178
|
+
providers: [{ provide: NUMBER_FLOW_GROUP, useFactory: () => new Coordinator() }]
|
|
179
|
+
}]
|
|
180
|
+
}] });
|
|
181
|
+
|
|
182
|
+
/**
|
|
183
|
+
* Generated bundle index. Do not edit.
|
|
184
|
+
*/
|
|
185
|
+
|
|
186
|
+
export { NUMBER_FLOW_GROUP, NumberFlowComponent, NumberFlowGroupDirective };
|
|
187
|
+
//# sourceMappingURL=ng-number-flow.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"ng-number-flow.mjs","sources":["../../src/lib/number-flow.tokens.ts","../../src/lib/formatter-cache.ts","../../src/lib/number-flow.element.ts","../../src/lib/number-flow.component.ts","../../src/lib/number-flow-group.directive.ts","../../src/ng-number-flow.ts"],"sourcesContent":["import { InjectionToken } from '@angular/core';\nimport type NumberFlowLite from 'number-flow/lite';\n\nexport interface NumberFlowGroupCoordinator {\n\tregister(el: NumberFlowLite): () => void;\n\tbeginUpdate(): void;\n\tendUpdate(): void;\n}\n\nexport const NUMBER_FLOW_GROUP = new InjectionToken<NumberFlowGroupCoordinator | null>(\n\t'NUMBER_FLOW_GROUP'\n);\n","import type { Format, Value } from 'number-flow/lite';\n\nconst cache = new Map<string, Intl.NumberFormat>();\n\nexport function getFormatter(\n\tlocales?: Intl.LocalesArgument,\n\tformat?: Format\n): Intl.NumberFormat {\n\tconst key = `${locales ? JSON.stringify(locales) : ''}:${format ? JSON.stringify(format) : ''}`;\n\tlet formatter = cache.get(key);\n\tif (!formatter) {\n\t\tformatter = new Intl.NumberFormat(locales, format);\n\t\tcache.set(key, formatter);\n\t}\n\treturn formatter;\n}\n\nexport type { Format, Value };\n","import NumberFlowLite, { define } from 'number-flow/lite';\n\nexport const NUMBER_FLOW_ELEMENT_NAME = 'number-flow-ng';\n\ndefine(NUMBER_FLOW_ELEMENT_NAME, NumberFlowLite);\n","import {\n\tafterNextRender,\n\tafterRenderEffect,\n\tChangeDetectionStrategy,\n\tComponent,\n\tCUSTOM_ELEMENTS_SCHEMA,\n\tDestroyRef,\n\tElementRef,\n\tinject,\n\tinput,\n\toutput,\n\tviewChild\n} from '@angular/core';\nimport NumberFlowLite, {\n\tformatToData,\n\ttype Digits,\n\ttype Format,\n\ttype Trend,\n\ttype Value\n} from 'number-flow/lite';\nimport type { Plugin } from 'number-flow/plugins';\nimport { getFormatter } from './formatter-cache';\nimport { NUMBER_FLOW_GROUP } from './number-flow.tokens';\nimport './number-flow.element';\n\n@Component({\n\tselector: 'number-flow',\n\tstandalone: true,\n\tschemas: [CUSTOM_ELEMENTS_SCHEMA],\n\tchangeDetection: ChangeDetectionStrategy.OnPush,\n\ttemplate: `<number-flow-ng #flow></number-flow-ng>`,\n\thost: { style: 'display: contents' }\n})\nexport class NumberFlowComponent {\n\tprivate readonly flowRef = viewChild.required<ElementRef<NumberFlowLite>>('flow');\n\n\treadonly value = input<Value>();\n\treadonly locales = input<Intl.LocalesArgument>();\n\treadonly format = input<Format>();\n\treadonly prefix = input<string>();\n\treadonly suffix = input<string>();\n\treadonly animated = input<boolean>();\n\treadonly respectMotionPreference = input<boolean>();\n\treadonly transformTiming = input<EffectTiming>();\n\treadonly spinTiming = input<EffectTiming>();\n\treadonly opacityTiming = input<EffectTiming>();\n\treadonly trend = input<Trend>();\n\treadonly plugins = input<Plugin[]>();\n\treadonly digits = input<Digits>();\n\treadonly isolate = input(false);\n\treadonly willChange = input(false);\n\treadonly nonce = input<string>();\n\n\treadonly animationsStart = output<void>();\n\treadonly animationsFinish = output<void>();\n\n\tprivate readonly group = inject(NUMBER_FLOW_GROUP, { optional: true });\n\tprivate readonly destroyRef = inject(DestroyRef);\n\n\tconstructor() {\n\t\t// One-time setup: wire events and register with the group. `afterNextRender`\n\t\t// runs on the client only (never during SSR) and after the view exists, so the\n\t\t// view child is guaranteed resolved.\n\t\tafterNextRender(() => {\n\t\t\tconst el = this.flowRef().nativeElement;\n\n\t\t\tconst onStart = () => this.animationsStart.emit();\n\t\t\tconst onFinish = () => this.animationsFinish.emit();\n\t\t\tel.addEventListener('animationsstart', onStart);\n\t\t\tel.addEventListener('animationsfinish', onFinish);\n\n\t\t\tconst unregister = this.group?.register(el);\n\n\t\t\tthis.destroyRef.onDestroy(() => {\n\t\t\t\tel.removeEventListener('animationsstart', onStart);\n\t\t\t\tel.removeEventListener('animationsfinish', onFinish);\n\t\t\t\tunregister?.();\n\t\t\t});\n\t\t});\n\n\t\t// Reactive sync: push the current inputs onto the custom element whenever any of\n\t\t// them change. Custom-element property assignment is DOM work, so it belongs in\n\t\t// `afterRenderEffect` (client-only) rather than a plain `effect`.\n\t\tafterRenderEffect({\n\t\t\twrite: () => {\n\t\t\t\tconst el = this.flowRef().nativeElement;\n\t\t\t\tthis.applyProps(el);\n\t\t\t\tthis.applyData(el);\n\t\t\t}\n\t\t});\n\t}\n\n\tprivate applyProps(el: NumberFlowLite): void {\n\t\tconst d = NumberFlowLite.defaultProps;\n\t\tel.animated = this.animated() ?? d.animated;\n\t\tel.respectMotionPreference = this.respectMotionPreference() ?? d.respectMotionPreference;\n\t\tel.transformTiming = this.transformTiming() ?? d.transformTiming;\n\t\tel.spinTiming = this.spinTiming() ?? d.spinTiming;\n\t\tel.opacityTiming = this.opacityTiming() ?? d.opacityTiming;\n\t\tel.trend = this.trend() ?? d.trend;\n\t\tel.plugins = this.plugins() ?? d.plugins;\n\t\tel.digits = this.digits() ?? d.digits;\n\t\tel.batched = !!this.group && !this.isolate();\n\n\t\tif (this.willChange()) el.setAttribute('data-will-change', '');\n\t\telse el.removeAttribute('data-will-change');\n\t}\n\n\tprivate applyData(el: NumberFlowLite): void {\n\t\tconst value = this.value();\n\t\tif (value == null) return;\n\n\t\tconst data = formatToData(\n\t\t\tvalue,\n\t\t\tgetFormatter(this.locales(), this.format()),\n\t\t\tthis.prefix(),\n\t\t\tthis.suffix()\n\t\t);\n\n\t\tif (this.group && !this.isolate()) {\n\t\t\tthis.group.beginUpdate();\n\t\t\tel.data = data;\n\t\t\tthis.group.endUpdate();\n\t\t} else {\n\t\t\tel.data = data;\n\t\t}\n\t}\n}\n","import { Directive } from '@angular/core';\nimport type NumberFlowLite from 'number-flow/lite';\nimport { NUMBER_FLOW_GROUP, type NumberFlowGroupCoordinator } from './number-flow.tokens';\n\nclass Coordinator implements NumberFlowGroupCoordinator {\n\tprivate readonly flows = new Set<NumberFlowLite>();\n\tprivate updating = false;\n\tprivate endScheduled = false;\n\n\tregister(el: NumberFlowLite): () => void {\n\t\tthis.flows.add(el);\n\t\treturn () => {\n\t\t\tthis.flows.delete(el);\n\t\t};\n\t}\n\n\tbeginUpdate(): void {\n\t\tif (this.updating) return;\n\t\tthis.updating = true;\n\t\tthis.flows.forEach((f) => {\n\t\t\tif ((f as { created?: boolean }).created) f.willUpdate();\n\t\t});\n\t}\n\n\tendUpdate(): void {\n\t\tif (this.endScheduled) return;\n\t\tthis.endScheduled = true;\n\t\tqueueMicrotask(() => {\n\t\t\tthis.endScheduled = false;\n\t\t\tif (!this.updating) return;\n\t\t\tthis.flows.forEach((f) => {\n\t\t\t\tif ((f as { created?: boolean }).created) f.didUpdate();\n\t\t\t});\n\t\t\tthis.updating = false;\n\t\t});\n\t}\n}\n\n@Directive({\n\tselector: '[numberFlowGroup]',\n\tstandalone: true,\n\tproviders: [{ provide: NUMBER_FLOW_GROUP, useFactory: () => new Coordinator() }]\n})\nexport class NumberFlowGroupDirective {}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './public-api';\n"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ng-number-flow",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "An Angular (19+) standalone animated number component. A thin wrapper around number-flow.",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "phalla-doll",
|
|
7
|
+
"homepage": "https://github.com/phalla-doll/ng-number-flow",
|
|
8
|
+
"repository": {
|
|
9
|
+
"type": "git",
|
|
10
|
+
"url": "git+https://github.com/phalla-doll/ng-number-flow.git",
|
|
11
|
+
"directory": "packages/ng-number-flow"
|
|
12
|
+
},
|
|
13
|
+
"bugs": {
|
|
14
|
+
"url": "https://github.com/phalla-doll/ng-number-flow/issues"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"angular",
|
|
18
|
+
"ng",
|
|
19
|
+
"accessible",
|
|
20
|
+
"odometer",
|
|
21
|
+
"animation",
|
|
22
|
+
"number-format",
|
|
23
|
+
"number-animation",
|
|
24
|
+
"animated-number"
|
|
25
|
+
],
|
|
26
|
+
"sideEffects": [
|
|
27
|
+
"**/number-flow.element.ts"
|
|
28
|
+
],
|
|
29
|
+
"publishConfig": {
|
|
30
|
+
"access": "public"
|
|
31
|
+
},
|
|
32
|
+
"peerDependencies": {
|
|
33
|
+
"@angular/core": ">=19.0.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {
|
|
36
|
+
"number-flow": "^0.6.1",
|
|
37
|
+
"tslib": "^2.7.0"
|
|
38
|
+
},
|
|
39
|
+
"module": "fesm2022/ng-number-flow.mjs",
|
|
40
|
+
"typings": "types/ng-number-flow.d.ts",
|
|
41
|
+
"exports": {
|
|
42
|
+
"./package.json": {
|
|
43
|
+
"default": "./package.json"
|
|
44
|
+
},
|
|
45
|
+
".": {
|
|
46
|
+
"types": "./types/ng-number-flow.d.ts",
|
|
47
|
+
"default": "./fesm2022/ng-number-flow.mjs"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"type": "module"
|
|
51
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import NumberFlowLite, { Format, Trend, Digits } from 'number-flow/lite';
|
|
2
|
+
export { Data, Digits, Format, default as NumberFlowElement, Props, Trend, Value } from 'number-flow/lite';
|
|
3
|
+
import { Plugin } from 'number-flow/plugins';
|
|
4
|
+
export { Plugin } from 'number-flow/plugins';
|
|
5
|
+
import * as _angular_core from '@angular/core';
|
|
6
|
+
import { InjectionToken } from '@angular/core';
|
|
7
|
+
|
|
8
|
+
interface NumberFlowGroupCoordinator {
|
|
9
|
+
register(el: NumberFlowLite): () => void;
|
|
10
|
+
beginUpdate(): void;
|
|
11
|
+
endUpdate(): void;
|
|
12
|
+
}
|
|
13
|
+
declare const NUMBER_FLOW_GROUP: InjectionToken<NumberFlowGroupCoordinator | null>;
|
|
14
|
+
|
|
15
|
+
declare class NumberFlowComponent {
|
|
16
|
+
private readonly flowRef;
|
|
17
|
+
readonly value: _angular_core.InputSignal<number | undefined>;
|
|
18
|
+
readonly locales: _angular_core.InputSignal<Intl.LocalesArgument>;
|
|
19
|
+
readonly format: _angular_core.InputSignal<Format | undefined>;
|
|
20
|
+
readonly prefix: _angular_core.InputSignal<string | undefined>;
|
|
21
|
+
readonly suffix: _angular_core.InputSignal<string | undefined>;
|
|
22
|
+
readonly animated: _angular_core.InputSignal<boolean | undefined>;
|
|
23
|
+
readonly respectMotionPreference: _angular_core.InputSignal<boolean | undefined>;
|
|
24
|
+
readonly transformTiming: _angular_core.InputSignal<EffectTiming | undefined>;
|
|
25
|
+
readonly spinTiming: _angular_core.InputSignal<EffectTiming | undefined>;
|
|
26
|
+
readonly opacityTiming: _angular_core.InputSignal<EffectTiming | undefined>;
|
|
27
|
+
readonly trend: _angular_core.InputSignal<Trend | undefined>;
|
|
28
|
+
readonly plugins: _angular_core.InputSignal<Plugin[] | undefined>;
|
|
29
|
+
readonly digits: _angular_core.InputSignal<Digits | undefined>;
|
|
30
|
+
readonly isolate: _angular_core.InputSignal<boolean>;
|
|
31
|
+
readonly willChange: _angular_core.InputSignal<boolean>;
|
|
32
|
+
readonly nonce: _angular_core.InputSignal<string | undefined>;
|
|
33
|
+
readonly animationsStart: _angular_core.OutputEmitterRef<void>;
|
|
34
|
+
readonly animationsFinish: _angular_core.OutputEmitterRef<void>;
|
|
35
|
+
private readonly group;
|
|
36
|
+
private readonly destroyRef;
|
|
37
|
+
constructor();
|
|
38
|
+
private applyProps;
|
|
39
|
+
private applyData;
|
|
40
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<NumberFlowComponent, never>;
|
|
41
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<NumberFlowComponent, "number-flow", never, { "value": { "alias": "value"; "required": false; "isSignal": true; }; "locales": { "alias": "locales"; "required": false; "isSignal": true; }; "format": { "alias": "format"; "required": false; "isSignal": true; }; "prefix": { "alias": "prefix"; "required": false; "isSignal": true; }; "suffix": { "alias": "suffix"; "required": false; "isSignal": true; }; "animated": { "alias": "animated"; "required": false; "isSignal": true; }; "respectMotionPreference": { "alias": "respectMotionPreference"; "required": false; "isSignal": true; }; "transformTiming": { "alias": "transformTiming"; "required": false; "isSignal": true; }; "spinTiming": { "alias": "spinTiming"; "required": false; "isSignal": true; }; "opacityTiming": { "alias": "opacityTiming"; "required": false; "isSignal": true; }; "trend": { "alias": "trend"; "required": false; "isSignal": true; }; "plugins": { "alias": "plugins"; "required": false; "isSignal": true; }; "digits": { "alias": "digits"; "required": false; "isSignal": true; }; "isolate": { "alias": "isolate"; "required": false; "isSignal": true; }; "willChange": { "alias": "willChange"; "required": false; "isSignal": true; }; "nonce": { "alias": "nonce"; "required": false; "isSignal": true; }; }, { "animationsStart": "animationsStart"; "animationsFinish": "animationsFinish"; }, never, never, true, never>;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
declare class NumberFlowGroupDirective {
|
|
45
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<NumberFlowGroupDirective, never>;
|
|
46
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NumberFlowGroupDirective, "[numberFlowGroup]", never, {}, {}, never, never, true, never>;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export { NUMBER_FLOW_GROUP, NumberFlowComponent, NumberFlowGroupDirective };
|
|
50
|
+
export type { NumberFlowGroupCoordinator };
|