rails-clipboard-helper 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/CHANGELOG.md +30 -0
- data/LICENSE.txt +22 -0
- data/README.md +247 -0
- data/Rakefile +12 -0
- data/app/assets/javascripts/rails_clipboard_helper.js +67 -0
- data/config/importmap.rb +4 -0
- data/lib/rails/clipboard/helper/engine.rb +22 -0
- data/lib/rails/clipboard/helper/railtie.rb +33 -0
- data/lib/rails/clipboard/helper/version.rb +9 -0
- data/lib/rails/clipboard/helper/view_helpers.rb +157 -0
- data/lib/rails/clipboard/helper.rb +13 -0
- data/sig/rails/clipboard/helper.rbs +8 -0
- metadata +96 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 984e6c3a3d1e5ba1b5920f7314eb40852eb315215c7f890839b58f47b86231cd
|
|
4
|
+
data.tar.gz: 649943b92152bfe760834a0308ec8d627d8b1c86ece18ec1a0e7cf1509e7c73d
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0e0a75285ebea6b01edcf0d1121d1eec6afee7be4cd3b1493989429326357db8706a2b4c894aea434bda48bbaf1b0d0efc34d07e5eacdc031d41ccaa97385b09
|
|
7
|
+
data.tar.gz: 2bca1927f5d7522169762ffef27134917ab5bcb9619734d9b67fb63a907c8cba8fb70077ebe9271adeaab5125002aa13bc51bfc52828b8053126a7c820ec4337
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [0.1.0] - 2025-11-09
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
- Initial release
|
|
12
|
+
- Implemented `clipboard_copy` helper method with SVG icon support
|
|
13
|
+
- SVG copy icon (default) that changes to checkmark on successful copy
|
|
14
|
+
- Icon-only mode (default), icon+text mode, and text-only mode
|
|
15
|
+
- Configurable icon position (left or right of text)
|
|
16
|
+
- Implemented `clipboard_javascript_tag` helper method (for backward compatibility)
|
|
17
|
+
- Clipboard copy functionality with automatic JavaScript loading
|
|
18
|
+
- Rails Engine for automatic asset integration
|
|
19
|
+
- Support for Sprockets, Importmap, esbuild, and webpack
|
|
20
|
+
- Turbo/Hotwire and Turbolinks support
|
|
21
|
+
- Customizable button text and CSS classes
|
|
22
|
+
- Content show/hide option
|
|
23
|
+
- Visual feedback on successful copy
|
|
24
|
+
- HTML character escaping
|
|
25
|
+
- Support for Rails 6.0 and above
|
|
26
|
+
- Comprehensive RSpec tests
|
|
27
|
+
- Documentation
|
|
28
|
+
|
|
29
|
+
[0.1.0]: https://github.com/dhq_boiler/rails-clipboard-helper/releases/tag/v0.1.0
|
|
30
|
+
|
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 dhq_boiler
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
22
|
+
|
data/README.md
ADDED
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
# Rails::Clipboard::Helper
|
|
2
|
+
|
|
3
|
+
A Rails view helper gem that displays a copy button alongside text content, allowing users to easily copy the content to their clipboard.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Easy text copying to clipboard
|
|
8
|
+
- Customizable styles
|
|
9
|
+
- Uses modern Clipboard API
|
|
10
|
+
- Visual feedback on successful copy
|
|
11
|
+
|
|
12
|
+
## Installation
|
|
13
|
+
|
|
14
|
+
Add this line to your application's Gemfile:
|
|
15
|
+
|
|
16
|
+
```ruby
|
|
17
|
+
gem 'rails-clipboard-helper'
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
And then execute:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
bundle install
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
### JavaScript Setup
|
|
29
|
+
|
|
30
|
+
The gem uses Rails Engine to automatically make JavaScript available. You need to include it in your asset pipeline:
|
|
31
|
+
|
|
32
|
+
**For Sprockets (Asset Pipeline):**
|
|
33
|
+
|
|
34
|
+
Add to `app/assets/javascripts/application.js`:
|
|
35
|
+
```javascript
|
|
36
|
+
//= require rails_clipboard_helper
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
**For Importmap (Rails 7+):**
|
|
40
|
+
|
|
41
|
+
Add to `config/importmap.rb`:
|
|
42
|
+
```ruby
|
|
43
|
+
pin "rails_clipboard_helper"
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
Then the JavaScript will be automatically loaded.
|
|
47
|
+
|
|
48
|
+
**For esbuild/webpack:**
|
|
49
|
+
|
|
50
|
+
The JavaScript file is located at `app/assets/javascripts/rails_clipboard_helper.js` in the gem.
|
|
51
|
+
You may need to configure your build tool to include it.
|
|
52
|
+
|
|
53
|
+
**Manual setup (alternative):**
|
|
54
|
+
|
|
55
|
+
If you prefer not to use the asset pipeline, add to your layout:
|
|
56
|
+
```erb
|
|
57
|
+
<%= clipboard_javascript_tag %>
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
**Features:**
|
|
61
|
+
- Automatic initialization on page load
|
|
62
|
+
- Turbo/Hotwire support (turbo:load, turbo:render)
|
|
63
|
+
- Turbolinks support (turbolinks:load)
|
|
64
|
+
- Prevents duplicate event listeners
|
|
65
|
+
|
|
66
|
+
### Basic Usage
|
|
67
|
+
|
|
68
|
+
Use the `clipboard_copy` helper in your views:
|
|
69
|
+
|
|
70
|
+
```erb
|
|
71
|
+
<%= clipboard_copy("Text you want to copy") %>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
By default, this displays an SVG copy icon. When clicked, it changes to a checkmark icon.
|
|
75
|
+
|
|
76
|
+
### Usage with Options
|
|
77
|
+
|
|
78
|
+
**Icon only (default):**
|
|
79
|
+
```erb
|
|
80
|
+
<%= clipboard_copy("Text to copy") %>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Icon with text:**
|
|
84
|
+
```erb
|
|
85
|
+
<%= clipboard_copy("Text to copy", button_text: "Copy") %>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Text only (no icon):**
|
|
89
|
+
```erb
|
|
90
|
+
<%= clipboard_copy("Text to copy", button_text: "Copy", show_icon: false) %>
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
**Icon on the right:**
|
|
94
|
+
```erb
|
|
95
|
+
<%= clipboard_copy("Text to copy", button_text: "Copy", icon_position: "right") %>
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
**More options:**
|
|
99
|
+
|
|
100
|
+
```erb
|
|
101
|
+
<%= clipboard_copy(
|
|
102
|
+
"Text you want to copy",
|
|
103
|
+
button_text: "Copy",
|
|
104
|
+
copied_text: "Copied!",
|
|
105
|
+
container_class: "my-clipboard-container",
|
|
106
|
+
content_class: "my-content",
|
|
107
|
+
button_class: "my-button"
|
|
108
|
+
) %>
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
### Hide Content
|
|
112
|
+
|
|
113
|
+
To display only the button and hide the content to be copied:
|
|
114
|
+
|
|
115
|
+
```erb
|
|
116
|
+
<%= clipboard_copy(
|
|
117
|
+
"Secret token: abc123xyz",
|
|
118
|
+
button_text: "Copy Token",
|
|
119
|
+
show_content: false
|
|
120
|
+
) %>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### Available Options
|
|
124
|
+
|
|
125
|
+
| Option | Default Value | Description |
|
|
126
|
+
|--------|---------------|-------------|
|
|
127
|
+
| `button_text` | `nil` | Text to display on button (if nil, shows icon only) |
|
|
128
|
+
| `copied_text` | `"Copied!"` | Text to display after successful copy |
|
|
129
|
+
| `show_icon` | `true` | Whether to show SVG icon |
|
|
130
|
+
| `icon_position` | `"left"` | Icon position: "left" or "right" (when button_text is provided) |
|
|
131
|
+
| `container_class` | `"clipboard-container"` | CSS class for container element |
|
|
132
|
+
| `content_class` | `"clipboard-content"` | CSS class for content element |
|
|
133
|
+
| `button_class` | `"clipboard-button"` | CSS class for button element |
|
|
134
|
+
| `show_content` | `true` | Whether to display content |
|
|
135
|
+
|
|
136
|
+
### Custom Styles
|
|
137
|
+
|
|
138
|
+
You can customize the styles with your own CSS:
|
|
139
|
+
|
|
140
|
+
```css
|
|
141
|
+
.clipboard-container {
|
|
142
|
+
display: flex;
|
|
143
|
+
align-items: center;
|
|
144
|
+
gap: 10px;
|
|
145
|
+
padding: 10px;
|
|
146
|
+
background: #f5f5f5;
|
|
147
|
+
border-radius: 8px;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
.clipboard-button {
|
|
151
|
+
padding: 8px 16px;
|
|
152
|
+
background: #007bff;
|
|
153
|
+
color: white;
|
|
154
|
+
border: none;
|
|
155
|
+
border-radius: 4px;
|
|
156
|
+
cursor: pointer;
|
|
157
|
+
transition: background 0.3s;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
.clipboard-button:hover {
|
|
161
|
+
background: #0056b3;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.clipboard-content {
|
|
165
|
+
font-family: monospace;
|
|
166
|
+
padding: 5px 10px;
|
|
167
|
+
background: white;
|
|
168
|
+
border: 1px solid #ddd;
|
|
169
|
+
border-radius: 4px;
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Examples
|
|
174
|
+
|
|
175
|
+
### Display and Copy API Key (Icon Only)
|
|
176
|
+
|
|
177
|
+
```erb
|
|
178
|
+
<div class="api-key-section">
|
|
179
|
+
<h3>Your API Key</h3>
|
|
180
|
+
<%= clipboard_copy(@user.api_key) %>
|
|
181
|
+
</div>
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### Copy Code Snippet (Icon with Text)
|
|
185
|
+
|
|
186
|
+
```erb
|
|
187
|
+
<% code_snippet = 'gem "rails-clipboard-helper"' %>
|
|
188
|
+
<%= clipboard_copy(
|
|
189
|
+
code_snippet,
|
|
190
|
+
button_text: "Copy Code",
|
|
191
|
+
container_class: "code-copy-container",
|
|
192
|
+
content_class: "code-snippet"
|
|
193
|
+
) %>
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### Hide Content (Icon Only Button)
|
|
197
|
+
|
|
198
|
+
```erb
|
|
199
|
+
<%= clipboard_copy(
|
|
200
|
+
"secret_token_abc123",
|
|
201
|
+
show_content: false
|
|
202
|
+
) %>
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
## Browser Support
|
|
206
|
+
|
|
207
|
+
This gem uses the modern Clipboard API, which is supported by:
|
|
208
|
+
|
|
209
|
+
- Chrome 43+
|
|
210
|
+
- Firefox 41+
|
|
211
|
+
- Safari 13.1+
|
|
212
|
+
- Edge 12+
|
|
213
|
+
|
|
214
|
+
Requires HTTPS or localhost for operation.
|
|
215
|
+
|
|
216
|
+
## Development
|
|
217
|
+
|
|
218
|
+
After checking out the repo:
|
|
219
|
+
|
|
220
|
+
```bash
|
|
221
|
+
bin/setup # Install dependencies
|
|
222
|
+
rake spec # Run tests
|
|
223
|
+
bin/console # Start interactive prompt
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
To install this gem onto your local machine:
|
|
227
|
+
|
|
228
|
+
```bash
|
|
229
|
+
bundle exec rake install
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
To release a new version:
|
|
233
|
+
|
|
234
|
+
1. Update the version number in `version.rb`
|
|
235
|
+
2. Run `bundle exec rake release`
|
|
236
|
+
|
|
237
|
+
## Contributing
|
|
238
|
+
|
|
239
|
+
Bug reports and pull requests are welcome at https://github.com/dhq_boiler/rails-clipboard-helper
|
|
240
|
+
|
|
241
|
+
## License
|
|
242
|
+
|
|
243
|
+
This gem is available as open source software.
|
|
244
|
+
|
|
245
|
+
## Author
|
|
246
|
+
|
|
247
|
+
dhq_boiler (dhq_boiler@live.jp)
|
data/Rakefile
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
// Rails Clipboard Helper - Automatic clipboard functionality
|
|
2
|
+
(function() {
|
|
3
|
+
'use strict';
|
|
4
|
+
|
|
5
|
+
function initClipboardButtons() {
|
|
6
|
+
document.querySelectorAll('[data-clipboard-target]').forEach(function(button) {
|
|
7
|
+
// Skip if already initialized
|
|
8
|
+
if (button.dataset.clipboardInitialized) return;
|
|
9
|
+
button.dataset.clipboardInitialized = 'true';
|
|
10
|
+
|
|
11
|
+
button.addEventListener('click', function() {
|
|
12
|
+
const targetId = this.getAttribute('data-clipboard-target');
|
|
13
|
+
const targetElement = document.getElementById(targetId);
|
|
14
|
+
|
|
15
|
+
// Support both old (text) and new (HTML) data attributes
|
|
16
|
+
const originalContent = this.getAttribute('data-original-html') || this.getAttribute('data-original-text');
|
|
17
|
+
const copiedContent = this.getAttribute('data-copied-html') || this.getAttribute('data-copied-text');
|
|
18
|
+
|
|
19
|
+
let textToCopy;
|
|
20
|
+
if (targetElement.tagName === 'INPUT' || targetElement.tagName === 'TEXTAREA') {
|
|
21
|
+
textToCopy = targetElement.value;
|
|
22
|
+
} else {
|
|
23
|
+
textToCopy = targetElement.textContent;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
navigator.clipboard.writeText(textToCopy).then(() => {
|
|
27
|
+
// Use innerHTML for HTML content (SVG icons), textContent for plain text
|
|
28
|
+
if (this.getAttribute('data-original-html')) {
|
|
29
|
+
this.innerHTML = copiedContent;
|
|
30
|
+
} else {
|
|
31
|
+
this.textContent = copiedContent;
|
|
32
|
+
}
|
|
33
|
+
this.style.background = '#d4edda';
|
|
34
|
+
|
|
35
|
+
setTimeout(() => {
|
|
36
|
+
if (this.getAttribute('data-original-html')) {
|
|
37
|
+
this.innerHTML = originalContent;
|
|
38
|
+
} else {
|
|
39
|
+
this.textContent = originalContent;
|
|
40
|
+
}
|
|
41
|
+
this.style.background = '#f8f9fa';
|
|
42
|
+
}, 2000);
|
|
43
|
+
}).catch(err => {
|
|
44
|
+
console.error('Failed to copy to clipboard:', err);
|
|
45
|
+
alert('Failed to copy');
|
|
46
|
+
});
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Initialize on DOMContentLoaded
|
|
52
|
+
if (document.readyState === 'loading') {
|
|
53
|
+
document.addEventListener('DOMContentLoaded', initClipboardButtons);
|
|
54
|
+
} else {
|
|
55
|
+
initClipboardButtons();
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// Re-initialize on Turbo events (for Turbo/Hotwire support)
|
|
59
|
+
if (typeof Turbo !== 'undefined') {
|
|
60
|
+
document.addEventListener('turbo:load', initClipboardButtons);
|
|
61
|
+
document.addEventListener('turbo:render', initClipboardButtons);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// Re-initialize on turbolinks events (for older Turbolinks support)
|
|
65
|
+
document.addEventListener('turbolinks:load', initClipboardButtons);
|
|
66
|
+
})();
|
|
67
|
+
|
data/config/importmap.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rails
|
|
4
|
+
module Clipboard
|
|
5
|
+
module Helper
|
|
6
|
+
class Engine < ::Rails::Engine
|
|
7
|
+
isolate_namespace Rails::Clipboard::Helper
|
|
8
|
+
|
|
9
|
+
initializer "rails_clipboard_helper.assets" do |app|
|
|
10
|
+
app.config.assets.precompile += %w[rails_clipboard_helper.js] if app.config.respond_to?(:assets)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
initializer "rails_clipboard_helper.view_helpers" do
|
|
14
|
+
ActiveSupport.on_load(:action_view) do
|
|
15
|
+
include Rails::Clipboard::Helper::ViewHelpers
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Rails
|
|
4
|
+
module Clipboard
|
|
5
|
+
module Helper
|
|
6
|
+
class Railtie < ::Rails::Railtie
|
|
7
|
+
initializer "rails_clipboard_helper.view_helpers" do
|
|
8
|
+
ActiveSupport.on_load(:action_view) do
|
|
9
|
+
include Rails::Clipboard::Helper::ViewHelpers
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
initializer "rails_clipboard_helper.assets" do |app|
|
|
14
|
+
if app.config.respond_to?(:assets)
|
|
15
|
+
app.config.assets.precompile += %w[rails_clipboard_helper.js]
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Add paths for asset pipeline
|
|
20
|
+
config.before_initialize do |app|
|
|
21
|
+
if app.config.respond_to?(:assets)
|
|
22
|
+
app.config.assets.paths << root.join("app", "assets", "javascripts")
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def self.root
|
|
27
|
+
@root ||= Pathname.new(File.expand_path("../../../..", __dir__))
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "securerandom"
|
|
4
|
+
require "erb"
|
|
5
|
+
require "active_support/core_ext/string/output_safety"
|
|
6
|
+
|
|
7
|
+
module Rails
|
|
8
|
+
module Clipboard
|
|
9
|
+
module Helper
|
|
10
|
+
module ViewHelpers
|
|
11
|
+
# Helper method to display text content with a copy button
|
|
12
|
+
#
|
|
13
|
+
# @param content [String] Content to copy
|
|
14
|
+
# @param options [Hash] Options
|
|
15
|
+
# @option options [String] :button_text Button text (default: nil - shows icon only)
|
|
16
|
+
# @option options [String] :copied_text Text after copying (default: "Copied!")
|
|
17
|
+
# @option options [String] :container_class CSS class for container
|
|
18
|
+
# @option options [String] :content_class CSS class for content
|
|
19
|
+
# @option options [String] :button_class CSS class for button
|
|
20
|
+
# @option options [Boolean] :show_content Whether to display content (default: true)
|
|
21
|
+
# @option options [Boolean] :show_icon Whether to show SVG icon (default: true)
|
|
22
|
+
# @option options [String] :icon_position Icon position relative to text: 'left' or 'right' (default: 'left')
|
|
23
|
+
#
|
|
24
|
+
# @return [String] HTML string
|
|
25
|
+
def clipboard_copy(content, options = {})
|
|
26
|
+
button_text = options.fetch(:button_text, nil)
|
|
27
|
+
copied_text = options.fetch(:copied_text, "Copied!")
|
|
28
|
+
container_class = options.fetch(:container_class, "clipboard-container")
|
|
29
|
+
content_class = options.fetch(:content_class, "clipboard-content")
|
|
30
|
+
button_class = options.fetch(:button_class, "clipboard-button")
|
|
31
|
+
show_content = options.fetch(:show_content, true)
|
|
32
|
+
show_icon = options.fetch(:show_icon, true)
|
|
33
|
+
icon_position = options.fetch(:icon_position, "left")
|
|
34
|
+
|
|
35
|
+
unique_id = "clipboard-#{SecureRandom.hex(8)}"
|
|
36
|
+
|
|
37
|
+
# SVG copy icon
|
|
38
|
+
copy_icon = <<~SVG.strip
|
|
39
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-copy" viewBox="0 0 16 16" style="vertical-align: middle;">
|
|
40
|
+
<path fill-rule="evenodd" d="M4 2a2 2 0 0 1 2-2h8a2 2 0 0 1 2 2v8a2 2 0 0 1-2 2H6a2 2 0 0 1-2-2zm2-1a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1V2a1 1 0 0 0-1-1zM2 5a1 1 0 0 0-1 1v8a1 1 0 0 0 1 1h8a1 1 0 0 0 1-1v-1h1v1a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2h1v1z"/>
|
|
41
|
+
</svg>
|
|
42
|
+
SVG
|
|
43
|
+
|
|
44
|
+
# Check icon for copied state
|
|
45
|
+
check_icon = <<~SVG.strip
|
|
46
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-check" viewBox="0 0 16 16" style="vertical-align: middle;">
|
|
47
|
+
<path d="M10.97 4.97a.75.75 0 0 1 1.07 1.05l-3.99 4.99a.75.75 0 0 1-1.08.02L4.324 8.384a.75.75 0 1 1 1.06-1.06l2.094 2.093 3.473-4.425a.267.267 0 0 1 .02-.022z"/>
|
|
48
|
+
</svg>
|
|
49
|
+
SVG
|
|
50
|
+
|
|
51
|
+
# Build button content
|
|
52
|
+
if show_icon && button_text
|
|
53
|
+
# Icon + text
|
|
54
|
+
if icon_position == "right"
|
|
55
|
+
button_content = "#{ERB::Util.html_escape(button_text)} #{copy_icon}"
|
|
56
|
+
copied_content = "#{ERB::Util.html_escape(copied_text)} #{check_icon}"
|
|
57
|
+
else
|
|
58
|
+
button_content = "#{copy_icon} #{ERB::Util.html_escape(button_text)}"
|
|
59
|
+
copied_content = "#{check_icon} #{ERB::Util.html_escape(copied_text)}"
|
|
60
|
+
end
|
|
61
|
+
elsif show_icon
|
|
62
|
+
# Icon only
|
|
63
|
+
button_content = copy_icon
|
|
64
|
+
copied_content = check_icon
|
|
65
|
+
else
|
|
66
|
+
# Text only
|
|
67
|
+
button_content = ERB::Util.html_escape(button_text || "Copy")
|
|
68
|
+
copied_content = ERB::Util.html_escape(copied_text)
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
html = <<~HTML
|
|
72
|
+
<div class="#{container_class}" style="display: flex; align-items: center; gap: 10px;">
|
|
73
|
+
<button
|
|
74
|
+
type="button"
|
|
75
|
+
class="#{button_class}"
|
|
76
|
+
data-clipboard-target="#{unique_id}"
|
|
77
|
+
data-original-html="#{ERB::Util.html_escape(button_content)}"
|
|
78
|
+
data-copied-html="#{ERB::Util.html_escape(copied_content)}"
|
|
79
|
+
style="padding: 5px 10px; cursor: pointer; border: 1px solid #ccc; border-radius: 4px; background: #f8f9fa; display: inline-flex; align-items: center; gap: 4px;"
|
|
80
|
+
>
|
|
81
|
+
#{button_content}
|
|
82
|
+
</button>
|
|
83
|
+
#{show_content ? %Q(<span class="#{content_class}" id="#{unique_id}">#{ERB::Util.html_escape(content)}</span>) : %Q(<input type="hidden" id="#{unique_id}" value="#{ERB::Util.html_escape(content)}">)}
|
|
84
|
+
</div>
|
|
85
|
+
HTML
|
|
86
|
+
|
|
87
|
+
html.html_safe
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Helper method that includes JavaScript (optional - JavaScript is auto-loaded)
|
|
91
|
+
# This method is kept for backward compatibility
|
|
92
|
+
#
|
|
93
|
+
# Note: JavaScript is automatically loaded via asset pipeline.
|
|
94
|
+
# You only need to call this method if you're not using the asset pipeline.
|
|
95
|
+
def clipboard_javascript_tag
|
|
96
|
+
javascript_tag do
|
|
97
|
+
<<~JAVASCRIPT
|
|
98
|
+
(function() {
|
|
99
|
+
function initClipboardButtons() {
|
|
100
|
+
document.querySelectorAll('[data-clipboard-target]').forEach(function(button) {
|
|
101
|
+
if (button.dataset.clipboardInitialized) return;
|
|
102
|
+
button.dataset.clipboardInitialized = 'true';
|
|
103
|
+
|
|
104
|
+
button.addEventListener('click', function() {
|
|
105
|
+
const targetId = this.getAttribute('data-clipboard-target');
|
|
106
|
+
const targetElement = document.getElementById(targetId);
|
|
107
|
+
|
|
108
|
+
// Support both old (text) and new (HTML) data attributes
|
|
109
|
+
const originalContent = this.getAttribute('data-original-html') || this.getAttribute('data-original-text');
|
|
110
|
+
const copiedContent = this.getAttribute('data-copied-html') || this.getAttribute('data-copied-text');
|
|
111
|
+
|
|
112
|
+
let textToCopy;
|
|
113
|
+
if (targetElement.tagName === 'INPUT' || targetElement.tagName === 'TEXTAREA') {
|
|
114
|
+
textToCopy = targetElement.value;
|
|
115
|
+
} else {
|
|
116
|
+
textToCopy = targetElement.textContent;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
navigator.clipboard.writeText(textToCopy).then(() => {
|
|
120
|
+
// Use innerHTML for HTML content (SVG icons), textContent for plain text
|
|
121
|
+
if (this.getAttribute('data-original-html')) {
|
|
122
|
+
this.innerHTML = copiedContent;
|
|
123
|
+
} else {
|
|
124
|
+
this.textContent = copiedContent;
|
|
125
|
+
}
|
|
126
|
+
this.style.background = '#d4edda';
|
|
127
|
+
|
|
128
|
+
setTimeout(() => {
|
|
129
|
+
if (this.getAttribute('data-original-html')) {
|
|
130
|
+
this.innerHTML = originalContent;
|
|
131
|
+
} else {
|
|
132
|
+
this.textContent = originalContent;
|
|
133
|
+
}
|
|
134
|
+
this.style.background = '#f8f9fa';
|
|
135
|
+
}, 2000);
|
|
136
|
+
}).catch(err => {
|
|
137
|
+
console.error('Failed to copy to clipboard:', err);
|
|
138
|
+
alert('Failed to copy');
|
|
139
|
+
});
|
|
140
|
+
});
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
if (document.readyState === 'loading') {
|
|
145
|
+
document.addEventListener('DOMContentLoaded', initClipboardButtons);
|
|
146
|
+
} else {
|
|
147
|
+
initClipboardButtons();
|
|
148
|
+
}
|
|
149
|
+
})();
|
|
150
|
+
JAVASCRIPT
|
|
151
|
+
end
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
end
|
|
155
|
+
end
|
|
156
|
+
end
|
|
157
|
+
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "helper/version"
|
|
4
|
+
require_relative "helper/view_helpers"
|
|
5
|
+
require_relative "helper/engine" if defined?(Rails::Engine)
|
|
6
|
+
|
|
7
|
+
module Rails
|
|
8
|
+
module Clipboard
|
|
9
|
+
module Helper
|
|
10
|
+
class Error < StandardError; end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: rails-clipboard-helper
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.1.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- dhq_boiler
|
|
8
|
+
bindir: exe
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies:
|
|
12
|
+
- !ruby/object:Gem::Dependency
|
|
13
|
+
name: railties
|
|
14
|
+
requirement: !ruby/object:Gem::Requirement
|
|
15
|
+
requirements:
|
|
16
|
+
- - ">="
|
|
17
|
+
- !ruby/object:Gem::Version
|
|
18
|
+
version: '6.0'
|
|
19
|
+
- - "<"
|
|
20
|
+
- !ruby/object:Gem::Version
|
|
21
|
+
version: '9.0'
|
|
22
|
+
type: :runtime
|
|
23
|
+
prerelease: false
|
|
24
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
25
|
+
requirements:
|
|
26
|
+
- - ">="
|
|
27
|
+
- !ruby/object:Gem::Version
|
|
28
|
+
version: '6.0'
|
|
29
|
+
- - "<"
|
|
30
|
+
- !ruby/object:Gem::Version
|
|
31
|
+
version: '9.0'
|
|
32
|
+
- !ruby/object:Gem::Dependency
|
|
33
|
+
name: actionview
|
|
34
|
+
requirement: !ruby/object:Gem::Requirement
|
|
35
|
+
requirements:
|
|
36
|
+
- - ">="
|
|
37
|
+
- !ruby/object:Gem::Version
|
|
38
|
+
version: '6.0'
|
|
39
|
+
- - "<"
|
|
40
|
+
- !ruby/object:Gem::Version
|
|
41
|
+
version: '9.0'
|
|
42
|
+
type: :runtime
|
|
43
|
+
prerelease: false
|
|
44
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
45
|
+
requirements:
|
|
46
|
+
- - ">="
|
|
47
|
+
- !ruby/object:Gem::Version
|
|
48
|
+
version: '6.0'
|
|
49
|
+
- - "<"
|
|
50
|
+
- !ruby/object:Gem::Version
|
|
51
|
+
version: '9.0'
|
|
52
|
+
description: A Rails view helper gem that displays text content alongside a copy button,
|
|
53
|
+
allowing users to easily copy the content to their clipboard.
|
|
54
|
+
email:
|
|
55
|
+
- dhq_boiler@live.jp
|
|
56
|
+
executables: []
|
|
57
|
+
extensions: []
|
|
58
|
+
extra_rdoc_files: []
|
|
59
|
+
files:
|
|
60
|
+
- CHANGELOG.md
|
|
61
|
+
- LICENSE.txt
|
|
62
|
+
- README.md
|
|
63
|
+
- Rakefile
|
|
64
|
+
- app/assets/javascripts/rails_clipboard_helper.js
|
|
65
|
+
- config/importmap.rb
|
|
66
|
+
- lib/rails/clipboard/helper.rb
|
|
67
|
+
- lib/rails/clipboard/helper/engine.rb
|
|
68
|
+
- lib/rails/clipboard/helper/railtie.rb
|
|
69
|
+
- lib/rails/clipboard/helper/version.rb
|
|
70
|
+
- lib/rails/clipboard/helper/view_helpers.rb
|
|
71
|
+
- sig/rails/clipboard/helper.rbs
|
|
72
|
+
homepage: https://github.com/dhq_boiler/rails-clipboard-helper
|
|
73
|
+
licenses:
|
|
74
|
+
- MIT
|
|
75
|
+
metadata:
|
|
76
|
+
homepage_uri: https://github.com/dhq_boiler/rails-clipboard-helper
|
|
77
|
+
changelog_uri: https://github.com/dhq_boiler/rails-clipboard-helper/blob/main/CHANGELOG.md
|
|
78
|
+
bug_tracker_uri: https://github.com/dhq_boiler/rails-clipboard-helper/issues
|
|
79
|
+
rdoc_options: []
|
|
80
|
+
require_paths:
|
|
81
|
+
- lib
|
|
82
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
83
|
+
requirements:
|
|
84
|
+
- - ">="
|
|
85
|
+
- !ruby/object:Gem::Version
|
|
86
|
+
version: 3.2.0
|
|
87
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
88
|
+
requirements:
|
|
89
|
+
- - ">="
|
|
90
|
+
- !ruby/object:Gem::Version
|
|
91
|
+
version: '0'
|
|
92
|
+
requirements: []
|
|
93
|
+
rubygems_version: 3.6.9
|
|
94
|
+
specification_version: 4
|
|
95
|
+
summary: Rails helper for displaying text with a copy-to-clipboard button
|
|
96
|
+
test_files: []
|