number_flow 0.1.2 → 0.2.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a4f1f61448b85a4aa44d8fa3956899b594d490f6af920018652e149ae35ebd5d
4
- data.tar.gz: b5e694d0edad03f5cd73dc17f5b3f0e3518bbcf809748f403f1ec1763d303b55
3
+ metadata.gz: 4b0cf284b80b9ebdd51127edf90746a9bf77e4f6c323f8dcd3917757ea82600f
4
+ data.tar.gz: 3f254dfe37f23d253477ab5bb574f0f76f982e82de16f36c6385102946991dc0
5
5
  SHA512:
6
- metadata.gz: 97efbebd67b86c36be60d33a557ea03009cbda3939f828ec1e328674a51643ab47369789bbb3337ad27016d7f9dd4f8cbd494c432572ae6ac8b8244fac86ea04
7
- data.tar.gz: c3a9afd1531ba668775bfa1876ead4899ac0225810327f5da99c2d2e6932c73c7edcb18885b7760c9af23c59193bd265221d7b34929e6ef465c235f979c09e07
6
+ metadata.gz: 69d1fdc102f5c9b2a751c8988a4276e263bf8f9a289fb517db2d54750676fbda4fcf6eda6d70aaa1f3a2786f56938949023f13927918d2d4822007d3e5bbd31a
7
+ data.tar.gz: 6ef6f8f6f2f3a4627a1ebbc0192180df55a9a6387bff5649f12aac5d0e1eec65051636cdf5669b84b9fa3790b8f5b865804b8270848f3e7fbfd9c50b23e6e3c5
data/CHANGELOG.md CHANGED
@@ -7,6 +7,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.2.0] - 2026-07-13
11
+
12
+ ### Added
13
+ - Decimal and fractional number animations with configurable precision and rollover behavior.
14
+ - Locale-aware number formatting powered by `Intl.NumberFormat`.
15
+ - Optional `NumberFlow::Component` integration and a Vite installer generator.
16
+ - Chromium/Cuprite browser coverage and continuous integration for browser behavior.
17
+
18
+ ### Changed
19
+ - Improved negative-number and reduced-motion animation behavior.
20
+ - Documented the decision to keep Number Flow distributed as a Ruby gem.
21
+
10
22
  ## [0.1.2] - 2026-07-12
11
23
 
12
24
  ### Changed
data/README.md CHANGED
@@ -1,7 +1,8 @@
1
1
  # NumberFlow
2
2
 
3
- Rails helper + Stimulus controller for smooth integer digit transitions inspired by [number-flow](https://github.com/barvian/number-flow).
3
+ Rails helper + Stimulus controller for smooth number digit transitions (integers, decimals, locale-aware) inspired by [number-flow](https://github.com/barvian/number-flow).
4
4
 
5
+ [![CI](https://github.com/dpaluy/number_flow/actions/workflows/ci.yml/badge.svg)](https://github.com/dpaluy/number_flow/actions/workflows/ci.yml)
5
6
  [![Gem Version](https://badge.fury.io/rb/number_flow.svg)](https://badge.fury.io/rb/number_flow)
6
7
 
7
8
  `number_flow` ships both CSS and JavaScript assets inside the gem, so you do not need a separate npm package.
@@ -37,6 +38,8 @@ Add this in your layout (or import into your main stylesheet):
37
38
 
38
39
  ### 2) Register the Stimulus controller
39
40
 
41
+ #### Sprockets / Importmap
42
+
40
43
  With importmap (`config/importmap.rb`):
41
44
 
42
45
  ```ruby
@@ -52,25 +55,56 @@ import NumberFlowController from "number_flow/controller"
52
55
  application.register("number-flow", NumberFlowController)
53
56
  ```
54
57
 
58
+ #### Vite Rails
59
+
60
+ Use the built-in generator to copy assets into `app/frontend/`:
61
+
62
+ ```sh
63
+ rails g number_flow:install --vite
64
+ ```
65
+
66
+ This copies:
67
+ - `controller.js` → `app/frontend/controllers/number_flow_controller.js`
68
+ - `number_flow.css` → `app/frontend/stylesheets/number_flow.css`
69
+
70
+ If `app/frontend/entrypoints/application.js` exists, the controller import and registration are appended automatically (idempotent — re-running won't duplicate).
71
+
72
+ Without `--vite`, the generator prints the Sprockets/importmap setup instructions above.
73
+
55
74
  ## Usage
56
75
 
57
- Render a value:
76
+ Render an integer:
58
77
 
59
78
  ```erb
60
79
  <%= number_flow_tag(12_345) %>
61
80
  ```
62
81
 
82
+ Render a decimal with precision:
83
+
84
+ ```erb
85
+ <%= number_flow_tag(12.34, precision: 2) %>
86
+ ```
87
+
88
+ Locale-aware formatting (uses `Intl.NumberFormat` on the client):
89
+
90
+ ```erb
91
+ <%= number_flow_tag(1234.5, precision: 1, locale: "de-DE", grouping: true) %>
92
+ <!-- renders as 1.234,5 -->
93
+ ```
94
+
63
95
  Render with options:
64
96
 
65
97
  ```erb
66
98
  <%= number_flow_tag(
67
- 12_345,
99
+ 12_345.67,
100
+ precision: 2,
68
101
  class: "kpi-value",
69
102
  duration: 550,
70
103
  easing: "ease-out",
71
104
  stagger: 30,
72
105
  grouping: true,
73
- aria_label: "Current total users"
106
+ locale: "en-US",
107
+ aria_label: "Current total revenue"
74
108
  ) %>
75
109
  ```
76
110
 
@@ -80,7 +114,7 @@ Update from JavaScript:
80
114
  const element = document.querySelector("[data-controller='number-flow']")
81
115
  element.dispatchEvent(
82
116
  new CustomEvent("number-flow:update", {
83
- detail: { value: 12987 }
117
+ detail: { value: 12987.50 }
84
118
  })
85
119
  )
86
120
  ```
@@ -96,9 +130,21 @@ Options:
96
130
  - `easing:` CSS easing function (default: `cubic-bezier(0.2, 0, 0, 1)`)
97
131
  - `stagger:` per-digit delay in milliseconds (default: `20`)
98
132
  - `grouping:` show thousands separators (default: `false`)
133
+ - `precision:` number of decimal digits (default: `0` — integer mode)
134
+ - `locale:` BCP 47 locale tag for formatting (default: `nil` → browser default)
99
135
  - `aria_label:` explicit `aria-label`
100
136
  - `data:` additional data attributes merged into the root element
101
137
 
138
+ ## Optional ViewComponent Wrapper
139
+
140
+ If your app uses [ViewComponent](https://github.com/github/view_component), `NumberFlow::Component` is available as a thin wrapper:
141
+
142
+ ```erb
143
+ <%= render(NumberFlow::Component.new(value: 42, precision: 2)) %>
144
+ ```
145
+
146
+ The component is only defined when the `view_component` gem is present. The gem does **not** add `view_component` as a runtime dependency. Add it to your app's Gemfile to use the component API.
147
+
102
148
  ## Accessibility
103
149
 
104
150
  - Root element uses `role="status"` and `aria-live="polite"`.
@@ -106,7 +152,17 @@ Options:
106
152
 
107
153
  ## Error Handling
108
154
 
109
- `number_flow_tag` raises `ArgumentError` when `value` cannot be coerced into an integer.
155
+ `number_flow_tag` raises `ArgumentError` when `value` cannot be coerced into a numeric value.
156
+
157
+ ## Why No npm Package?
158
+
159
+ **Decision: gem-only distribution.** The Stimulus controller consumes data attributes (`data-number-flow-value-value`, `data-number-flow-precision-value`, etc.) that are emitted by the Ruby helper. The JS is not standalone — it depends on the gem's markup contract. Publishing to npm would:
160
+
161
+ 1. **Double maintenance** for no standalone value — two version tracks, two release workflows, two changelogs.
162
+ 2. **Not solve a real problem** — Vite/importmap integration (via the install generator) already covers asset resolution without npm.
163
+ 3. **Conflict with positioning** — the gem already markets gem-only as a feature.
164
+
165
+ **Revisit trigger:** if a framework-agnostic core (pure formatting + DOM, no Rails-helper coupling) is extracted, or if community demand materializes for importing the controller from `@dpaluy/number_flow` in a non-Rails context. Until then: won't ship.
110
166
 
111
167
  ## Development
112
168
 
@@ -115,6 +171,12 @@ bundle exec rake test
115
171
  bundle exec rubocop
116
172
  ```
117
173
 
174
+ Browser system tests require Chrome/Chromium:
175
+
176
+ ```sh
177
+ RUN_SYSTEM_TESTS=true bundle exec rake test
178
+ ```
179
+
118
180
  ## Contributing
119
181
 
120
182
  Bug reports and pull requests are welcome on [GitHub](https://github.com/dpaluy/number_flow/issues).
@@ -8,7 +8,9 @@ export default class extends Controller {
8
8
  duration: Number,
9
9
  easing: String,
10
10
  stagger: Number,
11
- grouping: Boolean
11
+ grouping: Boolean,
12
+ precision: Number,
13
+ locale: String
12
14
  }
13
15
 
14
16
  connect() {
@@ -41,41 +43,33 @@ export default class extends Controller {
41
43
  return
42
44
  }
43
45
 
44
- this.updateTo(Math.trunc(nextValue))
46
+ this.updateTo(nextValue)
45
47
  }
46
48
 
47
49
  updateTo(nextValue) {
48
- const normalized = Math.trunc(nextValue)
49
- if (normalized === this.currentValue) return
50
+ if (nextValue === this.currentValue) return
51
+ if (!this.currentParts) return // Guard: not yet connected
50
52
 
51
53
  this.applyTimingVariables()
52
- this.render(normalized, !this.prefersReducedMotion())
53
- this.currentValue = normalized
54
- this.valueValue = normalized
54
+ this.render(nextValue, !this.prefersReducedMotion())
55
+ this.currentValue = nextValue
56
+ this.valueValue = nextValue
55
57
  }
56
58
 
57
59
  render(value, animate) {
58
- const previousDigits = this.currentParts.filter((part) => part.kind === "digit").map((part) => part.value)
60
+ const previousParts = this.currentParts
59
61
  const nextParts = this.partsFor(value)
60
- const nextDigits = nextParts.filter((part) => part.kind === "digit").map((part) => part.value)
61
-
62
- let digitIndex = 0
63
- const totalDigits = nextDigits.length
64
62
  const fragment = document.createDocumentFragment()
65
63
 
66
- nextParts.forEach((part) => {
64
+ nextParts.forEach((part, index) => {
67
65
  if (part.kind === "separator") {
68
66
  fragment.appendChild(this.buildSeparatorNode(part.value))
69
67
  return
70
68
  }
71
69
 
72
- const remaining = totalDigits - digitIndex
73
- const previousIndex = previousDigits.length - remaining
74
- const fromDigit = previousIndex >= 0 ? previousDigits[previousIndex] : 0
75
- const toDigit = part.value
76
- const indexFromRight = totalDigits - digitIndex - 1
77
- fragment.appendChild(this.buildDigitNode(fromDigit, toDigit, indexFromRight, animate))
78
- digitIndex += 1
70
+ const { fromDigit } = this.alignDigit(previousParts, nextParts, index)
71
+ const indexFromRight = nextParts.length - index - 1
72
+ fragment.appendChild(this.buildDigitNode(fromDigit, part.value, indexFromRight, animate))
79
73
  })
80
74
 
81
75
  this.element.replaceChildren(fragment)
@@ -92,6 +86,20 @@ export default class extends Controller {
92
86
  this.currentParts = nextParts
93
87
  }
94
88
 
89
+ alignDigit(previousParts, nextParts, currentIndex) {
90
+ const nextDigits = nextParts.filter((p) => p.kind === "digit")
91
+ const previousDigits = previousParts.filter((p) => p.kind === "digit")
92
+
93
+ const currentDigitIndex = nextParts.slice(0, currentIndex).filter((p) => p.kind === "digit").length
94
+ const remaining = nextDigits.length - currentDigitIndex
95
+ const previousIndex = previousDigits.length - remaining
96
+
97
+ const fromDigit = previousIndex >= 0 ? previousDigits[previousIndex].value : 0
98
+ const toDigit = nextParts[currentIndex].value
99
+
100
+ return { fromDigit, toDigit }
101
+ }
102
+
95
103
  partsFor(value) {
96
104
  return this.formattedString(value).split("").map((char) => {
97
105
  if (/\d/.test(char)) {
@@ -103,10 +111,14 @@ export default class extends Controller {
103
111
  }
104
112
 
105
113
  formattedString(value) {
106
- const sign = value < 0 ? "-" : ""
107
- const digits = Math.abs(value).toString()
108
- const groupedDigits = this.groupingValue ? digits.replace(/\B(?=(\d{3})+(?!\d))/g, ",") : digits
109
- return `${sign}${groupedDigits}`
114
+ const formatter = new Intl.NumberFormat(this.localeValue || undefined, {
115
+ useGrouping: this.groupingValue,
116
+ minimumFractionDigits: this.precisionValue,
117
+ maximumFractionDigits: this.precisionValue
118
+ })
119
+
120
+ const formatted = formatter.format(Math.abs(value))
121
+ return value < 0 ? `-${formatted}` : formatted
110
122
  }
111
123
 
112
124
  buildDigitNode(fromDigit, toDigit, indexFromRight, animate) {
@@ -150,19 +162,19 @@ export default class extends Controller {
150
162
 
151
163
  initialValue() {
152
164
  if (this.hasValueValue && Number.isFinite(this.valueValue)) {
153
- return Math.trunc(this.valueValue)
165
+ return this.valueValue
154
166
  }
155
167
 
156
168
  const inlineValue = Number(this.element.getAttribute("data-number-flow-value-value"))
157
169
  if (Number.isFinite(inlineValue)) {
158
- return Math.trunc(inlineValue)
170
+ return inlineValue
159
171
  }
160
172
 
161
173
  return 0
162
174
  }
163
175
 
164
176
  prefersReducedMotion() {
165
- return this.reducedMotionMedia.matches
177
+ return this.reducedMotionMedia?.matches === true
166
178
  }
167
179
 
168
180
  durationOrDefault() {
@@ -170,7 +182,7 @@ export default class extends Controller {
170
182
  }
171
183
 
172
184
  easingOrDefault() {
173
- return this.hasEasingValue ? this.easingValue : "cubic-bezier(0.2, 0, 0, 1)"
185
+ return this.hasEasingValue ? this.easingValue : "cubic-bezier(0.2, 0, 1)"
174
186
  }
175
187
 
176
188
  staggerOrDefault() {
@@ -181,4 +193,3 @@ export default class extends Controller {
181
193
  return typeof document !== "undefined" && document.location?.hostname === "localhost"
182
194
  }
183
195
  }
184
-
@@ -0,0 +1,30 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'number_flow/helper'
4
+
5
+ begin
6
+ require 'view_component'
7
+ rescue LoadError
8
+ # ViewComponent is not installed — Component is not defined.
9
+ end
10
+
11
+ module NumberFlow
12
+ # Optional ViewComponent wrapper around the +number_flow_tag+ helper.
13
+ # Only defined when the +view_component+ gem is available, so the gem
14
+ # never forces a hard dependency.
15
+ if defined?(::ViewComponent)
16
+ class Component < ::ViewComponent::Base
17
+ include NumberFlow::Helper
18
+
19
+ def initialize(value:, **options)
20
+ @value = value
21
+ @options = options
22
+ super()
23
+ end
24
+
25
+ def call
26
+ number_flow_tag(@value, **@options)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails/generators'
4
+ require 'rails/generators/base'
5
+
6
+ module NumberFlow
7
+ module Generators
8
+ class InstallGenerator < Rails::Generators::Base
9
+ desc 'Install NumberFlow for your asset pipeline (Sprockets, importmap, or Vite Rails).'
10
+
11
+ class_option :vite, type: :boolean, default: false,
12
+ desc: 'Copy controller and stylesheet into app/frontend for Vite Rails'
13
+
14
+ source_root File.expand_path('../../../..', __dir__)
15
+
16
+ CONTROLLER_SOURCE = 'app/assets/javascripts/number_flow/controller.js'
17
+ CSS_SOURCE = 'app/assets/stylesheets/number_flow.css'
18
+ VITE_CONTROLLER_TARGET = 'app/frontend/controllers/number_flow_controller.js'
19
+ VITE_CSS_TARGET = 'app/frontend/stylesheets/number_flow.css'
20
+ VITE_ENTRYPOINT = 'app/frontend/entrypoints/application.js'
21
+
22
+ def install
23
+ if options[:vite]
24
+ install_vite
25
+ else
26
+ install_standard
27
+ end
28
+ end
29
+
30
+ private
31
+
32
+ def install_vite
33
+ say_status('info', 'Installing NumberFlow for Vite Rails', :blue)
34
+
35
+ copy_file(CONTROLLER_SOURCE, VITE_CONTROLLER_TARGET)
36
+ copy_file(CSS_SOURCE, VITE_CSS_TARGET)
37
+ register_stimulus_controller
38
+ end
39
+
40
+ def install_standard
41
+ say_status('info', 'NumberFlow assets are served from the gem (Sprockets/importmap).', :blue)
42
+ say('')
43
+ say('1. Load the stylesheet in your layout:')
44
+ say(' <%= stylesheet_link_tag "number_flow", "data-turbo-track": "reload" %>')
45
+ say('')
46
+ say('2. Pin the controller (config/importmap.rb):')
47
+ say(' pin "number_flow/controller", to: "number_flow/controller.js"')
48
+ say('')
49
+ say('3. Register in app/javascript/controllers/index.js:')
50
+ say(' import NumberFlowController from "number_flow/controller"')
51
+ say(' application.register("number-flow", NumberFlowController)')
52
+ end
53
+
54
+ def register_stimulus_controller
55
+ return unless File.exist?(File.expand_path(VITE_ENTRYPOINT, destination_root))
56
+
57
+ entrypoint_path = File.expand_path(VITE_ENTRYPOINT, destination_root)
58
+ content = File.read(entrypoint_path)
59
+
60
+ import_line = 'import NumberFlowController from "../controllers/number_flow_controller"'
61
+ register_line = 'application.register("number-flow", NumberFlowController)'
62
+
63
+ return if content.include?(import_line)
64
+
65
+ inject_into_file(VITE_ENTRYPOINT, " #{import_line}\n", after: /import application from.*\n/)
66
+
67
+ unless content.include?(register_line)
68
+ inject_into_file(VITE_ENTRYPOINT, " #{register_line}\n",
69
+ after: /application = Application\.start.*\n/)
70
+ end
71
+
72
+ say_status('create', "#{VITE_ENTRYPOINT} (updated)", :green)
73
+ end
74
+ end
75
+ end
76
+ end
@@ -14,12 +14,14 @@ module NumberFlow
14
14
  easing: DEFAULT_EASING,
15
15
  stagger: DEFAULT_STAGGER,
16
16
  grouping: false,
17
+ precision: 0,
18
+ locale: nil,
17
19
  aria_label: nil,
18
20
  data: {},
19
21
  **html_options
20
22
  )
21
- normalized_value = Integer(value)
22
- formatted_value = format_integer(normalized_value, grouping: grouping)
23
+ normalized_value = coerce_number(value)
24
+ formatted_value = format_number(normalized_value, precision: precision, grouping: grouping, locale: locale)
23
25
  css_class = html_options.delete(:class)
24
26
  extra_data = html_options.delete(:data)
25
27
 
@@ -38,7 +40,9 @@ module NumberFlow
38
40
  number_flow_duration_value: Integer(duration),
39
41
  number_flow_easing_value: easing.to_s,
40
42
  number_flow_stagger_value: Integer(stagger),
41
- number_flow_grouping_value: grouping == true
43
+ number_flow_grouping_value: grouping == true,
44
+ number_flow_precision_value: precision.positive? ? Integer(precision) : nil,
45
+ number_flow_locale_value: locale
42
46
  },
43
47
  normalize_data_hash(data),
44
48
  normalize_data_hash(extra_data)
@@ -49,11 +53,22 @@ module NumberFlow
49
53
  safe_join(formatted_value.chars.map { |char| build_character_fragment(char) })
50
54
  end
51
55
  rescue ArgumentError, TypeError
52
- raise ArgumentError, 'number_flow_tag expects an integer-compatible value'
56
+ raise ArgumentError, 'number_flow_tag expects a numeric value'
53
57
  end
54
58
 
55
59
  private
56
60
 
61
+ def coerce_number(value)
62
+ case value
63
+ when Integer, Float
64
+ value
65
+ when String
66
+ Float(value)
67
+ else
68
+ value.respond_to?(:to_f) ? Float(value.to_f) : (raise ArgumentError)
69
+ end
70
+ end
71
+
57
72
  def build_character_fragment(char)
58
73
  if char.match?(/\d/)
59
74
  build_digit_fragment(char.to_i)
@@ -80,11 +95,40 @@ module NumberFlow
80
95
  end
81
96
  end
82
97
 
83
- def format_integer(value, grouping:)
98
+ def format_number(value, precision:, grouping:, locale:)
84
99
  sign = value.negative? ? '-' : ''
85
- digits = value.abs.to_s
86
- grouped = grouping ? digits.reverse.scan(/.{1,3}/).join(',').reverse : digits
87
- "#{sign}#{grouped}"
100
+ abs_value = value.abs
101
+ decimal_sep = locale_decimal_separator(locale)
102
+ group_sep = locale_group_separator(locale)
103
+
104
+ if precision.positive?
105
+ integer_part = Integer(abs_value.floor)
106
+ fraction = format('%.0f', (abs_value - integer_part) * (10**precision)).rjust(precision, '0')
107
+ number_str = "#{group_digits(integer_part, grouping, group_sep)}#{decimal_sep}#{fraction}"
108
+ else
109
+ number_str = group_digits(Integer(abs_value), grouping, group_sep)
110
+ end
111
+
112
+ "#{sign}#{number_str}"
113
+ end
114
+
115
+ def group_digits(integer, grouping, separator)
116
+ digits = integer.to_s
117
+ return digits unless grouping
118
+
119
+ digits.reverse.scan(/.{1,3}/).join(separator).reverse
120
+ end
121
+
122
+ def locale_decimal_separator(locale)
123
+ return ',' if locale && !locale.start_with?('en')
124
+
125
+ '.'
126
+ end
127
+
128
+ def locale_group_separator(locale)
129
+ return '.' if locale && !locale.start_with?('en')
130
+
131
+ ','
88
132
  end
89
133
 
90
134
  def merged_data_attributes(*hashes)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module NumberFlow
4
- VERSION = '0.1.2'
4
+ VERSION = '0.2.0'
5
5
  end
data/lib/number_flow.rb CHANGED
@@ -7,3 +7,19 @@ require_relative 'number_flow/engine'
7
7
  module NumberFlow
8
8
  class Error < StandardError; end
9
9
  end
10
+
11
+ # Load optional ViewComponent wrapper if the gem is present.
12
+ begin
13
+ require 'view_component'
14
+ require_relative '../app/components/number_flow/component'
15
+ rescue LoadError
16
+ # ViewComponent is not installed — Component stays undefined.
17
+ end
18
+
19
+ # Load Rails generators when Rails is available.
20
+ begin
21
+ require 'rails'
22
+ require_relative 'generators/number_flow/install/install_generator'
23
+ rescue LoadError
24
+ # Rails is not available — generators are not loaded.
25
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: number_flow
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - dpaluy
@@ -37,8 +37,8 @@ dependencies:
37
37
  - - ">="
38
38
  - !ruby/object:Gem::Version
39
39
  version: 8.1.0
40
- description: NumberFlow provides a Rails helper and Stimulus controller for smooth
41
- integer digit transitions using gem-shipped CSS and JavaScript assets.
40
+ description: NumberFlow provides a Rails helper and Stimulus controller for smooth,
41
+ locale-aware number transitions using gem-shipped CSS and JavaScript assets.
42
42
  email:
43
43
  - dpaluy@users.noreply.github.com
44
44
  executables: []
@@ -53,6 +53,8 @@ files:
53
53
  - README.md
54
54
  - app/assets/javascripts/number_flow/controller.js
55
55
  - app/assets/stylesheets/number_flow.css
56
+ - app/components/number_flow/component.rb
57
+ - lib/generators/number_flow/install/install_generator.rb
56
58
  - lib/number_flow.rb
57
59
  - lib/number_flow/engine.rb
58
60
  - lib/number_flow/helper.rb
@@ -83,5 +85,5 @@ required_rubygems_version: !ruby/object:Gem::Requirement
83
85
  requirements: []
84
86
  rubygems_version: 3.6.9
85
87
  specification_version: 4
86
- summary: Rails helper + Stimulus digit flow transitions for integers.
88
+ summary: Rails helper + Stimulus transitions for animated numbers.
87
89
  test_files: []