rails_tipjar 0.2.2 โ†’ 1.0.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: dd4604e05718d8a32b4384bd139441e1b2e46bf3716fc2daa2247a28755308ad
4
- data.tar.gz: 386776445d6478db9a2b871cf4fd67f2d999699092eb001d0f875d83717650b3
3
+ metadata.gz: 7f5dec9fdb53e29e9d8cd8a8265faad3fbb11d2aa01e29ace84f630400379f2c
4
+ data.tar.gz: ce6ac3c9a93e85d5839da4dd757490ae4b0dab9026ad46acd4dfff7643db3b66
5
5
  SHA512:
6
- metadata.gz: 22b84d3ed5cd38428af7ef20f7405a3d45e963ccb7f0400f8356e99aa8aff17c17f8165ea716c08d2a4cc31a8c3171109cdebd58c249020dcb85c7c00bb0b81c
7
- data.tar.gz: ea37e3133dd9190777f33aac89d36b90193c895475e2e5474f36dc9f36c05cd6411aa3ef4b26fd4ac259d488d83ca84ef3cce396288543e487885ea23f2eac08
6
+ metadata.gz: b4c4ea41cd2857f19736af2e337558f5a250bdcc426998888c5b7f6e17eb449fffd0b7ae640b315b9b3c10f20942acec33676f57e46227020c2aaff4539e4ed3
7
+ data.tar.gz: 015757b7e6d7991c95d7f5a0c91391e06713bdd1bd5bef600e06d5fccdf3ddc922c45300e684bc1052c7a8e5bd0e45e2a1f72c78508c30e52b2e22e81d3b5650
data/README.md CHANGED
@@ -1,76 +1,121 @@
1
1
  # RailsTipjar
2
2
 
3
- A reusable, customizable tip jar feature for Rails applications using Stripe Payment Links. Add a beautiful tip jar to any Rails app with just a few lines of code!
3
+ A reusable, customizable tip jar feature for Rails applications. Add a beautiful tip jar to any Rails app with just a few lines of code! Works with any payment provider - Stripe, PayPal, Buy Me a Coffee, Ko-fi, or any custom payment link.
4
4
 
5
5
  ## Features
6
6
 
7
- - ๐ŸŽจ Customizable floating button with multiple icon options
8
- - ๐Ÿ’ณ Stripe Payment Links integration (no backend payment processing needed)
9
- - ๐Ÿ“ฑ Fully responsive design
10
- - ๐ŸŽฏ Multiple positioning options
11
- - ๐ŸŒ™ Light/Dark theme support
7
+ - ๐ŸŽจ Customizable floating button with multiple icon options (coffee, heart, star, dollar, jar)
8
+ - ๐ŸŽจ Custom color theming with configurable background and text colors
9
+ - ๐Ÿ’ซ Optional pulsing animation effect
10
+ - ๐Ÿ’ณ Works with any payment provider (Stripe, PayPal, Ko-fi, Buy Me a Coffee, etc.)
11
+ - ๐Ÿ“ฑ Fully responsive design with inline styles (no CSS dependencies)
12
+ - ๐ŸŽฏ Multiple positioning options (bottom-right, bottom-left, top-right, top-left)
13
+ - ๐ŸŒ™ Light/Dark theme support for modal
12
14
  - ๐Ÿ“Š Built-in analytics tracking
13
15
  - โšก Stimulus.js powered interactions
14
16
  - ๐Ÿ”ง Zero configuration to get started
17
+ - ๐Ÿš€ Simple mode: Direct link without modal
18
+ - โœจ Fully self-contained (v0.2.0+)
15
19
 
16
- ## Why Stripe Payment Links?
20
+ ## Why Use Payment Links?
17
21
 
18
22
  - **No payment code** in your Rails apps
19
- - **PCI compliant** automatically
20
- - **No monthly fees** - pay only 2.9% + 30ยข per transaction
21
- - **International support** with multi-currency
22
- - **Professional checkout** experience
23
- - **Automatic receipts** and tax handling
23
+ - **No PCI compliance concerns** - handled by your payment provider
24
+ - **Multiple provider options** - use Stripe, PayPal, Ko-fi, Buy Me a Coffee, or any service
25
+ - **International support** - most providers support multiple currencies
26
+ - **Professional checkout** experience managed by the provider
27
+ - **Automatic receipts** and tax handling by the payment provider
24
28
 
25
29
  ## Installation
26
30
 
27
31
  Add this gem to your Rails application's Gemfile:
28
32
 
29
33
  ```ruby
30
- # From GitHub (recommended during development)
31
- gem 'rails_tipjar', git: 'https://github.com/justinpaulson/rails_tipjar'
32
-
33
- # Or from RubyGems (when published)
34
+ # From RubyGems
34
35
  gem 'rails_tipjar'
36
+
37
+ # Or from GitHub for latest features
38
+ gem 'rails_tipjar', git: 'https://github.com/justinpaulson/rails_tipjar'
35
39
  ```
36
40
 
37
41
  Then execute:
38
42
 
39
43
  ```bash
40
44
  bundle install
45
+ ```
46
+
47
+ Optionally run the generator (only needed for backwards compatibility with v0.3.0 and earlier):
48
+ ```bash
41
49
  rails generate tipjar:install
42
50
  ```
43
51
 
44
52
  ## Quick Start
45
53
 
46
- ### 1. Create Stripe Payment Links
54
+ ### 1. Create Payment Links
47
55
 
48
- 1. Go to [Stripe Dashboard](https://dashboard.stripe.com/payment-links)
49
- 2. Create payment links for different tip amounts
50
- 3. Set them to "Donation" mode
51
- 4. Copy the generated URLs
56
+ Choose your preferred payment provider and create payment links:
52
57
 
53
- ### 2. Configure the Gem
58
+ **Popular Options:**
59
+ - **Stripe**: [dashboard.stripe.com/payment-links](https://dashboard.stripe.com/payment-links)
60
+ - **PayPal**: [paypal.com/paypalme](https://paypal.com/paypalme)
61
+ - **Ko-fi**: [ko-fi.com](https://ko-fi.com)
62
+ - **Buy Me a Coffee**: [buymeacoffee.com](https://buymeacoffee.com)
63
+ - **GitHub Sponsors**: [github.com/sponsors](https://github.com/sponsors)
54
64
 
55
- Edit `config/initializers/tipjar.rb`:
65
+ Create links for different amounts or use a single link that accepts custom amounts.
56
66
 
57
- ```ruby
58
- RailsTipjar.configure do |config|
59
- config.payment_links = {
60
- small: "https://buy.stripe.com/your-5-dollar-link",
61
- medium: "https://buy.stripe.com/your-10-dollar-link",
62
- large: "https://buy.stripe.com/your-25-dollar-link"
63
- }
64
-
65
- config.position = :bottom_right
66
- config.icon = :coffee
67
- config.button_text = "Buy me a coffee"
68
- end
67
+ ### 2. Add to Your Views
68
+
69
+ No configuration file needed! Simply use the helper in your views with all options:
70
+
71
+ #### Simple Button (Direct Link)
72
+
73
+ Add directly to any view:
74
+
75
+ ```erb
76
+ <!-- Basic usage with default coffee icon -->
77
+ <%= tip_jar payment_link: 'https://your.payment.link' %>
78
+
79
+ <!-- With all customization options -->
80
+ <%= tip_jar payment_link: 'https://your.payment.link',
81
+ message: 'Support This Project', # Text shown on hover
82
+ icon: :heart, # :coffee, :heart, :star, :dollar, :jar, or custom SVG
83
+ position: :bottom_right, # :bottom_right, :bottom_left, :top_right, :top_left
84
+ color: '#8b5cf6', # Button background color
85
+ text_color: '#ffffff', # Button text color
86
+ pulse: true, # Enable pulsing animation
87
+ z_index: 1000, # Stack order
88
+ class: 'my-custom-class' %> # Additional CSS classes
89
+ ```
90
+
91
+ #### Modal Mode (Multiple Payment Options)
92
+
93
+ ```erb
94
+ <!-- Modal with multiple payment amounts -->
95
+ <%= tip_jar payment_links: {
96
+ small: 'https://your-provider.com/tip-5',
97
+ medium: 'https://your-provider.com/tip-10',
98
+ large: 'https://your-provider.com/tip-25'
99
+ },
100
+ modal_title: 'Support my work',
101
+ modal_description: 'Your support helps me continue creating.',
102
+ custom_amounts: [
103
+ { amount: 5, label: '$5', default: false },
104
+ { amount: 10, label: '$10', default: true },
105
+ { amount: 25, label: '$25', default: false }
106
+ ],
107
+ theme: :light, # :light, :dark, :auto
108
+ analytics_enabled: true %>
109
+
110
+ <!-- Force modal mode with single link -->
111
+ <%= tip_jar payment_link: 'https://your.payment.link',
112
+ use_modal: true,
113
+ modal_title: 'Choose your support amount' %>
69
114
  ```
70
115
 
71
- ### 3. Add to Your Layout
116
+ #### In Layouts
72
117
 
73
- In `app/views/layouts/application.html.erb`:
118
+ Add to `app/views/layouts/application.html.erb`:
74
119
 
75
120
  ```erb
76
121
  <!DOCTYPE html>
@@ -81,81 +126,61 @@ In `app/views/layouts/application.html.erb`:
81
126
  <body>
82
127
  <%= yield %>
83
128
 
84
- <!-- Add tip jar button -->
85
- <%= tipjar_button %>
129
+ <!-- Add floating tip jar button -->
130
+ <%= tip_jar payment_link: 'https://your.payment.link',
131
+ position: :bottom_right %>
86
132
  </body>
87
133
  </html>
88
134
  ```
89
135
 
90
136
  That's it! You now have a working tip jar on your site.
91
137
 
92
- ## Configuration Options
138
+ ## Helper Options
93
139
 
94
- ```ruby
95
- RailsTipjar.configure do |config|
96
- # Payment Links (required)
97
- config.payment_links = {
98
- small: "...",
99
- medium: "...",
100
- large: "...",
101
- custom: "..." # Optional: for custom amounts
102
- }
103
-
104
- # Button position
105
- # Options: :bottom_right, :bottom_left, :top_right, :top_left
106
- config.position = :bottom_right
107
-
108
- # Button icon
109
- # Options: :coffee, :heart, :star, :dollar, or custom SVG string
110
- config.icon = :coffee
111
-
112
- # Theme
113
- # Options: :light, :dark, :auto
114
- config.theme = :light
115
-
116
- # Button text (shown on hover)
117
- config.button_text = "Buy me a coffee"
118
-
119
- # Modal customization
120
- config.modal_title = "Support my work"
121
- config.modal_description = "Your support helps me continue creating."
122
-
123
- # Tip amounts shown in modal
124
- config.custom_amounts = [
125
- { amount: 5, label: "$5", default: false },
126
- { amount: 10, label: "$10", default: true },
127
- { amount: 25, label: "$25", default: false },
128
- { amount: 50, label: "$50", default: false }
129
- ]
130
-
131
- # Additional styling
132
- config.button_class = "custom-class"
133
- config.z_index = 1000
134
-
135
- # Analytics
136
- config.analytics_enabled = true # Works with GA4 and Plausible
137
- end
138
- ```
140
+ All configuration is done directly in the view helper:
139
141
 
140
- ## Usage
142
+ ### Common Options
141
143
 
142
- ### Basic Button
144
+ | Option | Description | Default | Values |
145
+ |--------|-------------|---------|--------|
146
+ | `payment_link` | Single payment URL for simple mode | - | URL string |
147
+ | `payment_links` | Hash of payment URLs for modal mode | - | Hash with keys like `:small`, `:medium`, `:large` |
148
+ | `message` / `button_text` | Text shown on button (on hover) | `"Buy me a coffee"` | String |
149
+ | `icon` | Button icon | `:coffee` | `:coffee`, `:heart`, `:star`, `:dollar`, `:jar`, or custom SVG |
150
+ | `position` | Button position on screen | `:bottom_right` | `:bottom_right`, `:bottom_left`, `:top_right`, `:top_left` |
151
+ | `color` | Button background color | `"#3b82f6"` | Hex color string |
152
+ | `text_color` | Button text color | `"#ffffff"` | Hex color string |
153
+ | `pulse` | Enable pulsing animation | `false` | `true` / `false` |
154
+ | `z_index` | CSS z-index for stacking | `1000` | Integer |
155
+ | `class` | Additional CSS classes | `""` | String |
156
+ | `use_modal` | Force modal mode | `false` | `true` / `false` |
143
157
 
144
- ```erb
145
- <!-- Default button with all config options -->
146
- <%= tipjar_button %>
158
+ ### Modal-Specific Options
147
159
 
148
- <!-- Override position for specific button -->
149
- <%= tipjar_button position: :top_left %>
160
+ | Option | Description | Default |
161
+ |--------|-------------|----------|
162
+ | `modal_title` | Modal header text | `"Support my work"` |
163
+ | `modal_description` | Modal description text | `"Your support helps..."` |
164
+ | `theme` | Modal color theme | `:light` |
165
+ | `custom_amounts` | Array of amount options | See example below |
166
+ | `analytics_enabled` | Track user interactions | `false` |
150
167
 
151
- <!-- Add custom CSS classes -->
152
- <%= tipjar_button class: "my-custom-class" %>
153
- ```
168
+ ### Custom Amounts Example
169
+
170
+ ```ruby
171
+ custom_amounts: [
172
+ { amount: 5, label: "$5", default: false },
173
+ { amount: 10, label: "$10", default: true }, # This will be pre-selected
174
+ { amount: 25, label: "$25", default: false },
175
+ { amount: 50, label: "$50", default: false }
176
+ ]
154
177
 
155
- ### Custom Links
178
+ ## Additional Helpers
179
+
180
+ ### Custom Tip Links
156
181
 
157
182
  ```erb
158
- <!-- Create a custom tip link -->
183
+ <!-- Create a custom tip link (requires initializer config) -->
159
184
  <%= tipjar_link "Support this project", amount: :large %>
160
185
 
161
186
  <!-- With custom styling -->
@@ -169,10 +194,33 @@ end
169
194
  <%= tipjar_modal %>
170
195
  ```
171
196
 
197
+ ## Backwards Compatibility
198
+
199
+ If you're upgrading from v0.3.0 or earlier and want to keep using the initializer-based configuration:
200
+
201
+ ```erb
202
+ <!-- Use the deprecated tipjar_button helper -->
203
+ <%= tipjar_button %>
204
+ ```
205
+
206
+ This requires having a `config/initializers/tipjar.rb` file. We recommend migrating to the new `tip_jar` helper with inline options for easier customization.
207
+
172
208
  ## Styling
173
209
 
174
- The gem includes default styles that work with most Rails applications. You can override styles by targeting these CSS classes:
210
+ As of v0.2.0, the gem is fully self-contained with inline styles - no external CSS required! The button works out of the box without any app-side styling.
211
+
212
+ ### Customization Options
213
+
214
+ 1. **Via Helper Options** (Recommended):
215
+ ```erb
216
+ <%= tip_jar payment_link: 'https://your.link',
217
+ color: '#10b981', # Green background
218
+ text_color: '#000000', # Black text
219
+ pulse: true, # Enable pulsing animation
220
+ class: 'custom' %> # Add custom CSS classes
221
+ ```
175
222
 
223
+ 2. **Via CSS Classes** (for modal mode):
176
224
  ```css
177
225
  /* Button */
178
226
  .tipjar-button { }
@@ -217,6 +265,26 @@ To test in a Rails app, add to your Gemfile:
217
265
  gem 'rails_tipjar', path: '../path/to/rails_tipjar'
218
266
  ```
219
267
 
268
+ ## Icon Options
269
+
270
+ The gem includes 5 built-in icons:
271
+ - `:coffee` - Coffee cup (default)
272
+ - `:heart` - Heart shape
273
+ - `:star` - Star
274
+ - `:dollar` - Dollar sign in circle
275
+ - `:jar` - Tip jar with animated coin
276
+
277
+ You can also provide a custom SVG string for complete customization.
278
+
279
+ ## Version History
280
+
281
+ - **v1.0.0** - Stable release with all configuration in view helper options (no initializer needed!)
282
+ - **v0.3.0** - Made payment provider agnostic (works with any payment link service)
283
+ - **v0.2.2** - Added color customization and pulse animation control
284
+ - **v0.2.1** - Fixed button circular shape when icon only displayed
285
+ - **v0.2.0** - Made gem fully self-contained with inline styles
286
+ - **v0.1.0** - Initial release
287
+
220
288
  ## Troubleshooting
221
289
 
222
290
  ### Button not appearing
@@ -225,15 +293,15 @@ gem 'rails_tipjar', path: '../path/to/rails_tipjar'
225
293
  2. Verify Stimulus is working in your app
226
294
  3. Check browser console for JavaScript errors
227
295
 
228
- ### Modal not opening
296
+ ### Modal not opening (modal mode only)
229
297
 
230
298
  1. Ensure Stimulus controller is registered
231
- 2. Check that tipjar.css is being loaded
232
- 3. Verify no z-index conflicts with your existing styles
299
+ 2. Verify no z-index conflicts with your existing styles
300
+ 3. Make sure `use_modal = true` if using single payment_link
233
301
 
234
302
  ### Payment links not working
235
303
 
236
- 1. Ensure your Stripe Payment Links are active
304
+ 1. Ensure your payment links are active with your provider
237
305
  2. Check that URLs are correctly configured in initializer
238
306
  3. Test links directly in browser
239
307
 
@@ -1,95 +1,184 @@
1
1
  module TipjarHelper
2
- def tipjar_button(options = {})
3
- config = RailsTipjar.config
2
+ def tip_jar(options = {})
3
+ # Extract all options with sensible defaults
4
+ payment_link = options[:payment_link] || options[:link]
5
+ payment_links = options[:payment_links]
6
+ use_modal = options[:use_modal] || false
7
+
8
+ # Button configuration
9
+ message = options[:message] || options[:button_text] || "Buy me a coffee"
10
+ icon = options[:icon] || :coffee
11
+ position = options[:position] || :bottom_right
12
+ color = options[:color] || "#3b82f6"
13
+ text_color = options[:text_color] || "#ffffff"
14
+ pulse = options[:pulse] || false
15
+ z_index = options[:z_index] || 1000
16
+ custom_class = options[:class] || ""
4
17
 
5
- position = options[:position] || config.position
6
- custom_class = options[:class] || config.button_class
7
- z_index = options[:z_index] || config.z_index
18
+ # Modal configuration (if using modal)
19
+ modal_title = options[:modal_title] || "Support my work"
20
+ modal_description = options[:modal_description] || "Your support helps me continue creating and maintaining this project."
21
+ theme = options[:theme] || :light
22
+ custom_amounts = options[:custom_amounts] || [
23
+ { amount: 5, label: "$5", default: false },
24
+ { amount: 10, label: "$10", default: true },
25
+ { amount: 25, label: "$25", default: false },
26
+ { amount: 50, label: "$50", default: false }
27
+ ]
28
+ analytics_enabled = options[:analytics_enabled] || false
8
29
 
9
- # Inject CSS styles into the page once
10
- tipjar_styles + tipjar_button_html(config, position, custom_class, z_index)
30
+ # Ensure color has # prefix
31
+ color = "##{color}" unless color.start_with?('#')
32
+ text_color = "##{text_color}" unless text_color.start_with?('#')
33
+
34
+ # If payment_links is provided or use_modal is true, use modal mode
35
+ if payment_links || use_modal
36
+ # Modal mode
37
+ tipjar_modal_button(
38
+ payment_links: payment_links || { default: payment_link },
39
+ position: position,
40
+ icon: icon,
41
+ theme: theme,
42
+ button_text: message,
43
+ modal_title: modal_title,
44
+ modal_description: modal_description,
45
+ custom_amounts: custom_amounts,
46
+ button_class: custom_class,
47
+ z_index: z_index,
48
+ analytics_enabled: analytics_enabled,
49
+ color: color,
50
+ text_color: text_color,
51
+ pulse: pulse
52
+ )
53
+ elsif payment_link
54
+ # Simple direct link mode
55
+ tipjar_simple_button(
56
+ payment_link: payment_link,
57
+ message: message,
58
+ icon: icon,
59
+ position: position,
60
+ color: color,
61
+ text_color: text_color,
62
+ pulse: pulse,
63
+ z_index: z_index,
64
+ custom_class: custom_class
65
+ )
66
+ else
67
+ # No payment method specified
68
+ raise ArgumentError, "You must provide either :payment_link or :payment_links option to tip_jar helper"
69
+ end
11
70
  end
12
71
 
13
- private
14
-
15
- def tipjar_button_html(config, position, custom_class, z_index)
16
- # Simple direct link mode when payment_link is set
17
- if config.payment_link && !config.use_modal
18
- position_style = position_inline_styles(position)
19
- button_style = "#{position_style} z-index: #{z_index}; display: flex; align-items: center; padding: 0.75rem 1rem; background-color: #3b82f6; color: white; border-radius: 9999px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; cursor: pointer; border: none; font-size: 0.875rem; font-weight: 500; text-decoration: none; animation: tipjar-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;"
20
-
21
- link_to config.payment_link,
22
- target: "_blank",
23
- rel: "noopener",
24
- class: "tipjar-button #{custom_class}",
25
- style: button_style,
26
- onmouseover: "this.style.backgroundColor='#2563eb'; this.style.transform='scale(1.05)'; this.querySelector('.tipjar-button-text').style.maxWidth='200px'; this.querySelector('.tipjar-button-text').style.marginLeft='0.5rem';",
27
- onmouseout: "this.style.backgroundColor='#3b82f6'; this.style.transform='scale(1)'; this.querySelector('.tipjar-button-text').style.maxWidth='0'; this.querySelector('.tipjar-button-text').style.marginLeft='0';" do
28
- content_tag(:span, style: "display: flex; align-items: center; justify-content: center; width: 1.5rem; height: 1.5rem;") do
29
- raw config.icon_svg
30
- end +
31
- content_tag(:span, config.button_text, class: "tipjar-button-text", style: "white-space: nowrap; max-width: 0; overflow: hidden; transition: all 0.3s ease; margin-left: 0;")
32
- end
72
+ # Deprecated - kept for backwards compatibility
73
+ def tipjar_button(options = {})
74
+ config = RailsTipjar.config if defined?(RailsTipjar)
75
+
76
+ if config && (config.payment_link || config.payment_links.any?)
77
+ # Use config from initializer for backwards compatibility
78
+ tip_jar(
79
+ payment_link: config.payment_link,
80
+ payment_links: config.payment_links.any? ? config.payment_links : nil,
81
+ use_modal: config.use_modal,
82
+ position: options[:position] || config.position,
83
+ icon: config.icon,
84
+ button_text: config.button_text,
85
+ color: config.color,
86
+ text_color: config.text_color,
87
+ pulse: config.pulse,
88
+ z_index: options[:z_index] || config.z_index,
89
+ class: options[:class] || config.button_class,
90
+ modal_title: config.modal_title,
91
+ modal_description: config.modal_description,
92
+ custom_amounts: config.custom_amounts,
93
+ theme: config.theme,
94
+ analytics_enabled: config.analytics_enabled
95
+ )
33
96
  else
34
- # Original modal mode - would need more work to be fully inline
35
- content_tag :div,
36
- class: "tipjar-container",
37
- data: {
38
- controller: "tipjar",
39
- tipjar_payment_links_value: config.payment_links.to_json,
40
- tipjar_theme_value: config.theme,
41
- tipjar_modal_title_value: config.modal_title,
42
- tipjar_modal_description_value: config.modal_description,
43
- tipjar_custom_amounts_value: config.custom_amounts.to_json,
44
- tipjar_analytics_enabled_value: config.analytics_enabled
45
- } do
46
- render partial: "tipjar/button", locals: {
47
- position: position,
48
- custom_class: custom_class,
49
- z_index: z_index,
50
- config: config
51
- }
52
- end
97
+ raise ArgumentError, "tipjar_button requires configuration. Use tip_jar helper with options instead."
98
+ end
99
+ end
100
+
101
+ private
102
+
103
+ def tipjar_simple_button(options = {})
104
+ payment_link = options[:payment_link]
105
+ message = options[:message]
106
+ icon = options[:icon]
107
+ position = options[:position]
108
+ color = options[:color]
109
+ text_color = options[:text_color]
110
+ pulse = options[:pulse]
111
+ z_index = options[:z_index]
112
+ custom_class = options[:custom_class]
113
+
114
+ position_style = position_inline_styles(position)
115
+ animation_style = pulse ? "animation: tipjar-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;" : ""
116
+
117
+ # Default to collapsed (icon-only) that expands on hover - 48x48px total
118
+ button_style = "#{position_style} z-index: #{z_index}; display: inline-flex; align-items: center; justify-content: center; padding: 12px; background-color: #{color}; color: #{text_color}; border-radius: 9999px; box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); transition: all 0.3s ease; cursor: pointer; border: none; font-size: 0.875rem; font-weight: 500; text-decoration: none; box-sizing: border-box; #{animation_style}"
119
+
120
+ hover_color = darken_color(color)
121
+
122
+ # Inject styles if needed
123
+ styles = tipjar_styles
124
+
125
+ # Create the link with inline styles - collapsed by default, expands on hover
126
+ link_html = link_to payment_link,
127
+ target: "_blank",
128
+ rel: "noopener",
129
+ class: "tipjar-button #{custom_class}",
130
+ style: button_style,
131
+ onmouseover: "this.style.backgroundColor='#{hover_color}'; this.style.transform='scale(1.05)'; this.style.padding='12px 20px'; this.querySelector('.tipjar-text').style.maxWidth='200px'; this.querySelector('.tipjar-text').style.marginLeft='0.5rem';",
132
+ onmouseout: "this.style.backgroundColor='#{color}'; this.style.transform='scale(1)'; this.style.padding='12px'; this.querySelector('.tipjar-text').style.maxWidth='0'; this.querySelector('.tipjar-text').style.marginLeft='0';" do
133
+ content_tag(:span, style: "display: inline-flex; align-items: center; justify-content: center; width: 1.5rem; height: 1.5rem;") do
134
+ raw icon_svg(icon)
135
+ end +
136
+ content_tag(:span, message, class: "tipjar-text", style: "white-space: nowrap; max-width: 0; overflow: hidden; transition: all 0.3s ease; margin-left: 0;")
53
137
  end
138
+
139
+ styles + link_html
54
140
  end
55
141
 
142
+ def tipjar_modal_button(options = {})
143
+ # Modal mode - renders button with modal
144
+ content_tag :div,
145
+ class: "tipjar-container",
146
+ data: {
147
+ controller: "tipjar",
148
+ tipjar_payment_links_value: options[:payment_links].to_json,
149
+ tipjar_theme_value: options[:theme],
150
+ tipjar_modal_title_value: options[:modal_title],
151
+ tipjar_modal_description_value: options[:modal_description],
152
+ tipjar_custom_amounts_value: options[:custom_amounts].to_json,
153
+ tipjar_analytics_enabled_value: options[:analytics_enabled]
154
+ } do
155
+ render partial: "tipjar/button", locals: {
156
+ position: options[:position],
157
+ custom_class: options[:button_class],
158
+ z_index: options[:z_index],
159
+ icon: options[:icon],
160
+ button_text: options[:button_text],
161
+ color: options[:color],
162
+ text_color: options[:text_color],
163
+ pulse: options[:pulse]
164
+ }
165
+ end
166
+ end
167
+
56
168
  def tipjar_styles
57
169
  return "" if @tipjar_styles_injected
58
170
  @tipjar_styles_injected = true
59
-
171
+
60
172
  content_tag :style do
61
173
  <<~CSS.html_safe
62
174
  @keyframes tipjar-pulse {
63
175
  0%, 100% { opacity: 1; }
64
176
  50% { opacity: 0.8; }
65
177
  }
66
-
67
- @keyframes tipjar-coin-drop {
68
- 0% {
69
- transform: translateY(0) rotate(0deg);
70
- opacity: 0;
71
- }
72
- 20% {
73
- opacity: 1;
74
- }
75
- 80% {
76
- transform: translateY(7px) rotate(180deg);
77
- opacity: 1;
78
- }
79
- 100% {
80
- transform: translateY(9px) rotate(180deg);
81
- opacity: 0;
82
- }
83
- }
84
-
85
- .tipjar-coin-animation {
86
- animation: tipjar-coin-drop 2s ease-in-out infinite;
87
- transform-origin: center;
88
- }
89
178
  CSS
90
179
  end
91
180
  end
92
-
181
+
93
182
  def position_inline_styles(position)
94
183
  case position
95
184
  when :bottom_right
@@ -104,18 +193,18 @@ module TipjarHelper
104
193
  "position: fixed; bottom: 1rem; right: 1rem;"
105
194
  end
106
195
  end
107
-
196
+
108
197
  def tipjar_link(text = nil, options = {})
109
198
  config = RailsTipjar.config
110
199
  text ||= config.button_text
111
200
  amount = options[:amount] || :medium
112
-
201
+
113
202
  link_url = if amount.is_a?(Symbol)
114
203
  config.payment_links[amount]
115
204
  else
116
205
  config.payment_links[:custom] || config.payment_links[:medium]
117
206
  end
118
-
207
+
119
208
  link_options = {
120
209
  target: "_blank",
121
210
  rel: "noopener",
@@ -125,19 +214,19 @@ module TipjarHelper
125
214
  tipjar_analytics: config.analytics_enabled
126
215
  }
127
216
  }
128
-
217
+
129
218
  link_to text, link_url, link_options.merge(options.except(:amount, :class))
130
219
  end
131
-
220
+
132
221
  def tipjar_modal(options = {})
133
222
  config = RailsTipjar.config
134
-
223
+
135
224
  render partial: "tipjar/modal", locals: {
136
225
  config: config,
137
226
  options: options
138
227
  }
139
228
  end
140
-
229
+
141
230
  def config_position_classes(position)
142
231
  case position
143
232
  when :bottom_right
@@ -152,4 +241,144 @@ module TipjarHelper
152
241
  "fixed bottom-4 right-4"
153
242
  end
154
243
  end
155
- end
244
+
245
+ def icon_svg(icon)
246
+ case icon
247
+ when :coffee
248
+ '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
249
+ <defs>
250
+ <style>
251
+ @keyframes steam1 {
252
+ 0% { opacity: 0; transform: translateY(0) translateX(0); }
253
+ 20% { opacity: 0.8; }
254
+ 50% { opacity: 0.6; transform: translateY(-3px) translateX(1px); }
255
+ 80% { opacity: 0.3; }
256
+ 100% { opacity: 0; transform: translateY(-8px) translateX(-1px); }
257
+ }
258
+ @keyframes steam2 {
259
+ 0% { opacity: 0; transform: translateY(0) translateX(0); }
260
+ 15% { opacity: 0.7; }
261
+ 45% { opacity: 0.5; transform: translateY(-4px) translateX(-1px); }
262
+ 75% { opacity: 0.2; }
263
+ 100% { opacity: 0; transform: translateY(-9px) translateX(1px); }
264
+ }
265
+ @keyframes steam3 {
266
+ 0% { opacity: 0; transform: translateY(0) translateX(0); }
267
+ 25% { opacity: 0.6; }
268
+ 55% { opacity: 0.4; transform: translateY(-3px) translateX(0.5px); }
269
+ 85% { opacity: 0.1; }
270
+ 100% { opacity: 0; transform: translateY(-7px) translateX(-0.5px); }
271
+ }
272
+ .steam1 { animation: steam1 3s ease-out infinite; }
273
+ .steam2 { animation: steam2 3s ease-out infinite 0.5s; }
274
+ .steam3 { animation: steam3 3s ease-out infinite 1s; }
275
+ </style>
276
+ </defs>
277
+ <!-- Steam wisps with wavy paths -->
278
+ <path class="steam1" d="M8 9c0.5-0.5 0.5-1 0-1.5s-0.5-1 0-1.5s0.5-1 0-1.5" stroke-linecap="round" fill="none" opacity="0"/>
279
+ <path class="steam2" d="M12 8.5c-0.3-0.4 -0.3-0.8 0-1.2s0.3-0.8 0-1.2s-0.3-0.8 0-1.2" stroke-linecap="round" fill="none" opacity="0"/>
280
+ <path class="steam3" d="M15.5 9c0.4-0.3 0.4-0.7 0-1s-0.4-0.7 0-1s0.4-0.7 0-1" stroke-linecap="round" fill="none" opacity="0"/>
281
+ <!-- Coffee cup body -->
282
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 10h12v7a3 3 0 01-3 3H9a3 3 0 01-3-3v-7z"/>
283
+ <!-- Cup handle -->
284
+ <path stroke-linecap="round" stroke-linejoin="round" d="M18 10h1.5a2.5 2.5 0 010 5H18"/>
285
+ <!-- Saucer -->
286
+ <path stroke-linecap="round" stroke-linejoin="round" d="M4 20h16"/>
287
+ <!-- Coffee surface detail -->
288
+ <path stroke-linecap="round" stroke-linejoin="round" d="M8 12h8" opacity="0.3"/>
289
+ </svg>'
290
+ when :heart
291
+ '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
292
+ <path stroke-linecap="round" stroke-linejoin="round" d="M21 8.5c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 4 3 6.015 3 8.5c0 7.5 9 11.5 9 11.5s9-4 9-11.5z" />
293
+ </svg>'
294
+ when :star
295
+ '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
296
+ <path stroke-linecap="round" stroke-linejoin="round" d="M11.48 3.499a.562.562 0 011.04 0l2.125 5.111a.563.563 0 00.475.345l5.518.442c.499.04.701.663.321.988l-4.204 3.602a.563.563 0 00-.182.557l1.285 5.385a.562.562 0 01-.84.61l-4.725-2.885a.563.563 0 00-.586 0L6.982 20.54a.562.562 0 01-.84-.61l1.285-5.386a.562.562 0 00-.182-.557l-4.204-3.602a.563.563 0 01.321-.988l5.518-.442a.563.563 0 00.475-.345L11.48 3.5z" />
297
+ </svg>'
298
+ when :dollar
299
+ '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
300
+ <path stroke-linecap="round" stroke-linejoin="round" d="M12 6v12m-3-2.818l.879.659c1.171.879 3.07.879 4.242 0 1.172-.879 1.172-2.303 0-3.182C13.536 12.219 12.768 12 12 12c-.725 0-1.45-.22-2.003-.659-1.106-.879-1.106-2.303 0-3.182s2.9-.879 4.006 0l.415.33M21 12a9 9 0 11-18 0 9 9 0 0118 0z" />
301
+ </svg>'
302
+ when :jar
303
+ '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
304
+ <defs>
305
+ <style>
306
+ @keyframes tipjar-coin-drop-new {
307
+ 0% {
308
+ transform: translateY(0px);
309
+ opacity: 0;
310
+ }
311
+ 10% {
312
+ opacity: 1;
313
+ transform: translateY(2px);
314
+ }
315
+ 30% {
316
+ transform: translateY(6px);
317
+ }
318
+ 50% {
319
+ transform: translateY(10px);
320
+ opacity: 1;
321
+ }
322
+ 70% {
323
+ transform: translateY(15.5px);
324
+ opacity: 1;
325
+ }
326
+ 75% {
327
+ transform: translateY(16.3px);
328
+ opacity: 1;
329
+ }
330
+ 80% {
331
+ transform: translateY(15.8px);
332
+ opacity: 0.8;
333
+ }
334
+ 100% {
335
+ transform: translateY(16.3px);
336
+ opacity: 0;
337
+ }
338
+ }
339
+ .tipjar-coin-drop-new {
340
+ animation: tipjar-coin-drop-new 2.5s ease-in-out infinite;
341
+ }
342
+ </style>
343
+ </defs>
344
+ <!-- Jar lid -->
345
+ <rect x="9.5" y="3" width="5" height="1" rx="0.25" stroke-linecap="round" stroke-linejoin="round"/>
346
+ <!-- Jar threads/neck -->
347
+ <path stroke-linecap="round" stroke-linejoin="round" d="M10 4v0.8M14 4v0.8"/>
348
+ <!-- Jar body - much fatter with short taper -->
349
+ <path stroke-linecap="round" stroke-linejoin="round" d="M10 4.8h4M9 5.5c-1 0 -2 0.5 -2 1v12.5a2 2 0 002 2h6a2 2 0 002-2V6.5c0-0.5 -1-1 -2-1"/>
350
+ <!-- Coins at bottom - pile -->
351
+ <ellipse cx="10.5" cy="18.5" rx="1.5" ry="0.5" stroke-linecap="round"/>
352
+ <ellipse cx="13.5" cy="18.3" rx="1.5" ry="0.5" stroke-linecap="round"/>
353
+ <ellipse cx="12" cy="17.8" rx="1.5" ry="0.5" stroke-linecap="round"/>
354
+ <ellipse cx="11" cy="19" rx="1.3" ry="0.4" stroke-linecap="round"/>
355
+ <ellipse cx="13" cy="19" rx="1.3" ry="0.4" stroke-linecap="round"/>
356
+ <!-- Falling coin - starts above jar outside -->
357
+ <g class="tipjar-coin-drop-new">
358
+ <ellipse cx="12" cy="1.5" rx="1.5" ry="0.5" stroke-linecap="round"/>
359
+ </g>
360
+ </svg>'
361
+ else
362
+ # Allow custom SVG string
363
+ icon.to_s
364
+ end
365
+ end
366
+
367
+ def darken_color(hex_color)
368
+ # Remove # if present
369
+ hex = hex_color.gsub('#', '')
370
+
371
+ # Convert to RGB
372
+ r = hex[0..1].to_i(16)
373
+ g = hex[2..3].to_i(16)
374
+ b = hex[4..5].to_i(16)
375
+
376
+ # Darken by 20%
377
+ r = (r * 0.8).to_i
378
+ g = (g * 0.8).to_i
379
+ b = (b * 0.8).to_i
380
+
381
+ # Convert back to hex
382
+ "#%02x%02x%02x" % [r, g, b]
383
+ end
384
+ end
@@ -2,15 +2,15 @@
2
2
  data-tipjar-target="button"
3
3
  data-action="click->tipjar#openModal"
4
4
  class="tipjar-button <%= config_position_classes(position) %> <%= custom_class %>"
5
- style="z-index: <%= z_index %>;"
6
- aria-label="<%= config.button_text %>"
5
+ style="z-index: <%= z_index %>; background-color: <%= color %>; color: <%= text_color %>; <%= pulse ? 'animation: tipjar-pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;' : '' %>"
6
+ aria-label="<%= button_text %>"
7
7
  >
8
8
  <span class="tipjar-button-icon">
9
- <%= raw config.icon_svg %>
9
+ <%= raw icon_svg(icon) %>
10
10
  </span>
11
11
  <span class="tipjar-button-text">
12
- <%= config.button_text %>
12
+ <%= button_text %>
13
13
  </span>
14
14
  </button>
15
15
 
16
- <%= render "tipjar/modal", config: config %>
16
+ <%= render "tipjar/modal", locals: local_assigns.except(:position, :custom_class, :z_index, :icon, :button_text, :color, :text_color, :pulse) %>
@@ -63,10 +63,10 @@
63
63
  Continue to Payment
64
64
  </button>
65
65
 
66
- <!-- Powered by Stripe -->
66
+ <!-- Footer -->
67
67
  <div class="tipjar-modal-footer mt-4 text-center">
68
68
  <p class="text-xs text-gray-500">
69
- Secure payment powered by Stripe
69
+ Secure payment processing
70
70
  </p>
71
71
  </div>
72
72
  </div>
@@ -5,10 +5,12 @@ module Tipjar
5
5
  class InstallGenerator < Rails::Generators::Base
6
6
  source_root File.expand_path("templates", __dir__)
7
7
 
8
- desc "Creates a RailsTipjar initializer and copies necessary files"
8
+ desc "Creates an optional RailsTipjar initializer for backwards compatibility"
9
9
 
10
10
  def copy_initializer
11
- template "tipjar.rb", "config/initializers/tipjar.rb"
11
+ if yes?("Do you want to create an initializer file for backwards compatibility? (not recommended - use view helper options instead) [y/N]")
12
+ template "tipjar.rb", "config/initializers/tipjar.rb"
13
+ end
12
14
  end
13
15
 
14
16
  def add_stimulus_controller
@@ -68,23 +70,32 @@ module Tipjar
68
70
  say "RailsTipjar has been successfully installed!", :green
69
71
  say "\n"
70
72
  say "Next steps:", :yellow
71
- say "1. Create Stripe Payment Links at https://dashboard.stripe.com/payment-links", :yellow
72
- say "2. Configure your payment links in config/initializers/tipjar.rb", :yellow
73
- say "3. Add <%= tipjar_button %> to your layout file", :yellow
73
+ say "1. Create payment links with your preferred provider (Stripe, PayPal, Ko-fi, etc.)", :yellow
74
+ say "2. Add the tip jar to your views using the helper", :yellow
74
75
  say "\n"
75
- say "Example configuration:", :cyan
76
- say <<~CONFIG
77
- RailsTipjar.configure do |config|
78
- config.payment_links = {
79
- small: "https://buy.stripe.com/your-link-1",
80
- medium: "https://buy.stripe.com/your-link-2",
81
- large: "https://buy.stripe.com/your-link-3"
82
- }
83
- config.position = :bottom_right
84
- config.icon = :coffee
85
- config.theme = :light
86
- end
87
- CONFIG
76
+ say "Example usage in your views:", :cyan
77
+ say <<~USAGE
78
+ <!-- Simple button with direct link -->
79
+ <%= tip_jar payment_link: 'https://your.payment.link' %>
80
+
81
+ <!-- With custom options -->
82
+ <%= tip_jar payment_link: 'https://your.payment.link',
83
+ message: 'Support This Project',
84
+ icon: :heart,
85
+ color: '#8b5cf6',
86
+ pulse: true %>
87
+
88
+ <!-- Modal with multiple payment amounts -->
89
+ <%= tip_jar payment_links: {
90
+ small: 'https://link-5',
91
+ medium: 'https://link-10',
92
+ large: 'https://link-25'
93
+ },
94
+ modal_title: 'Support my work',
95
+ position: :bottom_left %>
96
+ USAGE
97
+ say "\n"
98
+ say "For more options, see the README: https://github.com/justinpaulson/rails_tipjar", :cyan
88
99
  say "\n"
89
100
  end
90
101
  end
@@ -1,16 +1,18 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  RailsTipjar.configure do |config|
4
- # Configure your Stripe Payment Links
5
- # Create payment links at: https://dashboard.stripe.com/payment-links
4
+ # Configure your payment links
5
+ # Works with any payment provider: Stripe, PayPal, Ko-fi, Buy Me a Coffee, etc.
6
6
  #
7
- # You can create different links for different amounts, or use a single
8
- # link that allows custom amounts
7
+ # Simple mode - just one payment link:
8
+ # config.payment_link = "https://your-payment-provider.com/your-link"
9
+ #
10
+ # Modal mode - multiple payment links for different amounts:
9
11
  config.payment_links = {
10
- small: "https://buy.stripe.com/test_REPLACE_WITH_YOUR_LINK",
11
- medium: "https://buy.stripe.com/test_REPLACE_WITH_YOUR_LINK",
12
- large: "https://buy.stripe.com/test_REPLACE_WITH_YOUR_LINK",
13
- custom: "https://buy.stripe.com/test_REPLACE_WITH_YOUR_LINK"
12
+ small: "https://REPLACE_WITH_YOUR_PAYMENT_LINK",
13
+ medium: "https://REPLACE_WITH_YOUR_PAYMENT_LINK",
14
+ large: "https://REPLACE_WITH_YOUR_PAYMENT_LINK",
15
+ custom: "https://REPLACE_WITH_YOUR_PAYMENT_LINK"
14
16
  }
15
17
 
16
18
  # Position of the tip jar button
@@ -50,4 +52,13 @@ RailsTipjar.configure do |config|
50
52
  # Tracks: button clicks, modal opens, amount selections, and successful tips
51
53
  # Works with Google Analytics 4 and Plausible out of the box
52
54
  config.analytics_enabled = false
55
+
56
+ # Customize button colors (hex values)
57
+ # Default: blue background (#3b82f6) with white text (#ffffff)
58
+ # config.color = "#3b82f6"
59
+ # config.text_color = "#ffffff"
60
+
61
+ # Enable pulsing animation on the button
62
+ # Default: false (no animation)
63
+ # config.pulse = true
53
64
  end
@@ -3,7 +3,7 @@ module RailsTipjar
3
3
  attr_accessor :payment_links, :position, :icon, :theme, :button_text,
4
4
  :modal_title, :modal_description, :custom_amounts,
5
5
  :button_class, :z_index, :analytics_enabled, :payment_link,
6
- :use_modal
6
+ :use_modal, :color, :text_color, :pulse
7
7
 
8
8
  def initialize
9
9
  # Simple mode - just one link
@@ -27,6 +27,9 @@ module RailsTipjar
27
27
  @button_class = ""
28
28
  @z_index = 1000
29
29
  @analytics_enabled = false
30
+ @color = "#3b82f6" # Default blue color
31
+ @text_color = "#ffffff" # Default white text color
32
+ @pulse = false # Default no pulsing animation
30
33
  end
31
34
 
32
35
  def position_classes
@@ -48,7 +51,46 @@ module RailsTipjar
48
51
  case @icon
49
52
  when :coffee
50
53
  '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
51
- <path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25v7.5c0 2.485 2.099 4.5 4.688 4.5 1.935 0 3.597-1.126 4.313-2.733.715 1.607 2.377 2.733 4.313 2.733 2.588 0 4.687-2.015 4.687-4.5v-7.5z" />
54
+ <defs>
55
+ <style>
56
+ @keyframes steam1 {
57
+ 0% { opacity: 0; transform: translateY(0) translateX(0); }
58
+ 20% { opacity: 0.8; }
59
+ 50% { opacity: 0.6; transform: translateY(-3px) translateX(1px); }
60
+ 80% { opacity: 0.3; }
61
+ 100% { opacity: 0; transform: translateY(-8px) translateX(-1px); }
62
+ }
63
+ @keyframes steam2 {
64
+ 0% { opacity: 0; transform: translateY(0) translateX(0); }
65
+ 15% { opacity: 0.7; }
66
+ 45% { opacity: 0.5; transform: translateY(-4px) translateX(-1px); }
67
+ 75% { opacity: 0.2; }
68
+ 100% { opacity: 0; transform: translateY(-9px) translateX(1px); }
69
+ }
70
+ @keyframes steam3 {
71
+ 0% { opacity: 0; transform: translateY(0) translateX(0); }
72
+ 25% { opacity: 0.6; }
73
+ 55% { opacity: 0.4; transform: translateY(-3px) translateX(0.5px); }
74
+ 85% { opacity: 0.1; }
75
+ 100% { opacity: 0; transform: translateY(-7px) translateX(-0.5px); }
76
+ }
77
+ .steam1 { animation: steam1 3s ease-out infinite; }
78
+ .steam2 { animation: steam2 3s ease-out infinite 0.5s; }
79
+ .steam3 { animation: steam3 3s ease-out infinite 1s; }
80
+ </style>
81
+ </defs>
82
+ <!-- Steam wisps with wavy paths -->
83
+ <path class="steam1" d="M8 9c0.5-0.5 0.5-1 0-1.5s-0.5-1 0-1.5s0.5-1 0-1.5" stroke-linecap="round" fill="none" opacity="0"/>
84
+ <path class="steam2" d="M12 8.5c-0.3-0.4 -0.3-0.8 0-1.2s0.3-0.8 0-1.2s-0.3-0.8 0-1.2" stroke-linecap="round" fill="none" opacity="0"/>
85
+ <path class="steam3" d="M15.5 9c0.4-0.3 0.4-0.7 0-1s-0.4-0.7 0-1s0.4-0.7 0-1" stroke-linecap="round" fill="none" opacity="0"/>
86
+ <!-- Coffee cup body -->
87
+ <path stroke-linecap="round" stroke-linejoin="round" d="M6 10h12v7a3 3 0 01-3 3H9a3 3 0 01-3-3v-7z"/>
88
+ <!-- Cup handle -->
89
+ <path stroke-linecap="round" stroke-linejoin="round" d="M18 10h1.5a2.5 2.5 0 010 5H18"/>
90
+ <!-- Saucer -->
91
+ <path stroke-linecap="round" stroke-linejoin="round" d="M4 20h16"/>
92
+ <!-- Coffee surface detail -->
93
+ <path stroke-linecap="round" stroke-linejoin="round" d="M8 12h8" opacity="0.3"/>
52
94
  </svg>'
53
95
  when :heart
54
96
  '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
@@ -64,20 +106,61 @@ module RailsTipjar
64
106
  </svg>'
65
107
  when :jar
66
108
  '<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="1.5" stroke="currentColor" class="w-6 h-6">
109
+ <defs>
110
+ <style>
111
+ @keyframes tipjar-coin-drop-new {
112
+ 0% {
113
+ transform: translateY(0px);
114
+ opacity: 0;
115
+ }
116
+ 10% {
117
+ opacity: 1;
118
+ transform: translateY(2px);
119
+ }
120
+ 30% {
121
+ transform: translateY(6px);
122
+ }
123
+ 50% {
124
+ transform: translateY(10px);
125
+ opacity: 1;
126
+ }
127
+ 70% {
128
+ transform: translateY(15.5px);
129
+ opacity: 1;
130
+ }
131
+ 75% {
132
+ transform: translateY(16.3px);
133
+ opacity: 1;
134
+ }
135
+ 80% {
136
+ transform: translateY(15.8px);
137
+ opacity: 0.8;
138
+ }
139
+ 100% {
140
+ transform: translateY(16.3px);
141
+ opacity: 0;
142
+ }
143
+ }
144
+ .tipjar-coin-drop-new {
145
+ animation: tipjar-coin-drop-new 2.5s ease-in-out infinite;
146
+ }
147
+ </style>
148
+ </defs>
67
149
  <!-- Jar lid -->
68
- <rect x="8.5" y="3" width="7" height="1.5" rx="0.25" stroke-linecap="round" stroke-linejoin="round"/>
150
+ <rect x="9.5" y="3" width="5" height="1" rx="0.25" stroke-linecap="round" stroke-linejoin="round"/>
69
151
  <!-- Jar threads/neck -->
70
- <path stroke-linecap="round" stroke-linejoin="round" d="M9 4.5v1.5M15 4.5v1.5"/>
71
- <!-- Jar body with taper -->
72
- <path stroke-linecap="round" stroke-linejoin="round" d="M9 6c0 0 -1 0.5 -1 1.5v11.5a2 2 0 002 2h4a2 2 0 002-2V7.5c0-1 -1-1.5 -1-1.5"/>
73
- <!-- Coins at bottom -->
152
+ <path stroke-linecap="round" stroke-linejoin="round" d="M10 4v0.8M14 4v0.8"/>
153
+ <!-- Jar body - much fatter with short taper -->
154
+ <path stroke-linecap="round" stroke-linejoin="round" d="M10 4.8h4M9 5.5c-1 0 -2 0.5 -2 1v12.5a2 2 0 002 2h6a2 2 0 002-2V6.5c0-0.5 -1-1 -2-1"/>
155
+ <!-- Coins at bottom - pile -->
74
156
  <ellipse cx="10.5" cy="18.5" rx="1.5" ry="0.5" stroke-linecap="round"/>
75
- <ellipse cx="14" cy="18" rx="1.5" ry="0.5" stroke-linecap="round"/>
76
- <ellipse cx="12" cy="17.5" rx="1.5" ry="0.5" stroke-linecap="round"/>
77
- <!-- Falling coin -->
78
- <g class="tipjar-coin-animation">
79
- <ellipse cx="12" cy="10" rx="1.5" ry="0.5" stroke-linecap="round"/>
80
- <path stroke-linecap="round" d="M12 10v1.5"/>
157
+ <ellipse cx="13.5" cy="18.3" rx="1.5" ry="0.5" stroke-linecap="round"/>
158
+ <ellipse cx="12" cy="17.8" rx="1.5" ry="0.5" stroke-linecap="round"/>
159
+ <ellipse cx="11" cy="19" rx="1.3" ry="0.4" stroke-linecap="round"/>
160
+ <ellipse cx="13" cy="19" rx="1.3" ry="0.4" stroke-linecap="round"/>
161
+ <!-- Falling coin - starts above jar outside -->
162
+ <g class="tipjar-coin-drop-new">
163
+ <ellipse cx="12" cy="1.5" rx="1.5" ry="0.5" stroke-linecap="round"/>
81
164
  </g>
82
165
  </svg>'
83
166
  else
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module RailsTipjar
4
- VERSION = "0.2.2"
4
+ VERSION = "1.0.0"
5
5
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rails_tipjar
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Justin Paulson
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-09-11 00:00:00.000000000 Z
11
+ date: 2025-09-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -38,8 +38,8 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '1.0'
41
- description: Easily add a customizable tip jar to any Rails application using Stripe
42
- Payment Links
41
+ description: Easily add a customizable tip jar to any Rails application. Works with
42
+ any payment provider.
43
43
  email:
44
44
  - justinapaulson@gmail.com
45
45
  executables: []