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 +4 -4
- data/README.md +170 -102
- data/app/helpers/tipjar_helper.rb +308 -79
- data/app/views/tipjar/_button.html.erb +5 -5
- data/app/views/tipjar/_modal.html.erb +2 -2
- data/lib/generators/tipjar/install/install_generator.rb +29 -18
- data/lib/generators/tipjar/install/templates/tipjar.rb +19 -8
- data/lib/rails_tipjar/configuration.rb +96 -13
- data/lib/rails_tipjar/version.rb +1 -1
- metadata +4 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 7f5dec9fdb53e29e9d8cd8a8265faad3fbb11d2aa01e29ace84f630400379f2c
|
4
|
+
data.tar.gz: ce6ac3c9a93e85d5839da4dd757490ae4b0dab9026ad46acd4dfff7643db3b66
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
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
|
-
-
|
9
|
-
-
|
10
|
-
-
|
11
|
-
-
|
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
|
20
|
+
## Why Use Payment Links?
|
17
21
|
|
18
22
|
- **No payment code** in your Rails apps
|
19
|
-
- **PCI
|
20
|
-
- **
|
21
|
-
- **International support**
|
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
|
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
|
54
|
+
### 1. Create Payment Links
|
47
55
|
|
48
|
-
|
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
|
-
|
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
|
-
|
65
|
+
Create links for different amounts or use a single link that accepts custom amounts.
|
56
66
|
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
-
|
116
|
+
#### In Layouts
|
72
117
|
|
73
|
-
|
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
|
-
<%=
|
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
|
-
##
|
138
|
+
## Helper Options
|
93
139
|
|
94
|
-
|
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
|
-
|
142
|
+
### Common Options
|
141
143
|
|
142
|
-
|
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
|
-
|
145
|
-
<!-- Default button with all config options -->
|
146
|
-
<%= tipjar_button %>
|
158
|
+
### Modal-Specific Options
|
147
159
|
|
148
|
-
|
149
|
-
|
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
|
-
|
152
|
-
|
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
|
-
|
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
|
-
|
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.
|
232
|
-
3.
|
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
|
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
|
3
|
-
|
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
|
-
|
6
|
-
|
7
|
-
|
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
|
-
#
|
10
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
if config.payment_link
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
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
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
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="<%=
|
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
|
9
|
+
<%= raw icon_svg(icon) %>
|
10
10
|
</span>
|
11
11
|
<span class="tipjar-button-text">
|
12
|
-
<%=
|
12
|
+
<%= button_text %>
|
13
13
|
</span>
|
14
14
|
</button>
|
15
15
|
|
16
|
-
<%= render "tipjar/modal",
|
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
|
-
<!--
|
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
|
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
|
8
|
+
desc "Creates an optional RailsTipjar initializer for backwards compatibility"
|
9
9
|
|
10
10
|
def copy_initializer
|
11
|
-
|
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
|
72
|
-
say "2.
|
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
|
76
|
-
say <<~
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
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
|
5
|
-
#
|
4
|
+
# Configure your payment links
|
5
|
+
# Works with any payment provider: Stripe, PayPal, Ko-fi, Buy Me a Coffee, etc.
|
6
6
|
#
|
7
|
-
#
|
8
|
-
#
|
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://
|
11
|
-
medium: "https://
|
12
|
-
large: "https://
|
13
|
-
custom: "https://
|
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
|
-
<
|
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="
|
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="
|
71
|
-
<!-- Jar body with taper -->
|
72
|
-
<path stroke-linecap="round" stroke-linejoin="round" d="
|
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="
|
76
|
-
<ellipse cx="12" cy="17.
|
77
|
-
|
78
|
-
<
|
79
|
-
|
80
|
-
|
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
|
data/lib/rails_tipjar/version.rb
CHANGED
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.
|
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
|
+
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
|
42
|
-
|
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: []
|