dynaspan 0.1.5 → 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 +5 -5
- data/CHANGELOG.md +105 -0
- data/LICENSE +1 -1
- data/README.md +244 -159
- data/app/assets/javascripts/dynaspan/dynaspan.js +271 -0
- data/app/assets/stylesheets/dynaspan/dynaspan.css +44 -0
- data/app/helpers/dynaspan/application_helper.rb +161 -48
- data/app/views/dynaspan/_dynaspan.html.erb +32 -0
- data/config/importmap.rb +3 -0
- data/lib/dynaspan/engine.rb +32 -0
- data/lib/dynaspan/version.rb +3 -1
- data/lib/dynaspan.rb +4 -28
- metadata +81 -28
- data/.gitignore +0 -19
- data/CODE_OF_CONDUCT.md +0 -3
- data/Gemfile +0 -4
- data/Rakefile +0 -13
- data/app/views/dynaspan/_dynaspan_text_area.html.erb +0 -24
- data/app/views/dynaspan/_dynaspan_text_field.html.erb +0 -24
- data/app/views/dynaspan/_dynaspan_text_select.html.erb +0 -26
- data/config/routes.rb +0 -2
- data/dynaspan.gemspec +0 -20
- data/lib/assets/javascripts/dynaspan/basepath.js.erb +0 -6
- data/lib/assets/javascripts/dynaspan-jquery.js +0 -2
- data/lib/dynaspan/tasks.rake +0 -13
- data/vendor/assets/javascripts/dynaspan/dynaspan.js +0 -56
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 047acad1b508d8c2fbdc2a9950051996829266886c98f4f81e0c32294aa7bc95
|
|
4
|
+
data.tar.gz: ea52a09c58dcc0f959f1ac6b6c947a7aafbb2b079e0223c8b3513eaf1a339a5d
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 31f57f891c11a653217bd72ef0fcd37227826ac53cf967ce69cb97a57c1d29af8515e217399795c2b0882c7e6dfeeb91ebfc863051c2d34b2bdeb91dccaa8d07
|
|
7
|
+
data.tar.gz: 83aa3a3b9be60f735e6d2e449844754b2b50a3de8a8b6e46980e88a042d0d447262320e386e14d4482645248eb5c382124eb0011858326ebabc4e4a812f90eea
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
Dynaspan is now a modern Rails engine for Rails 7.1 through 8.x. The helper API
|
|
6
|
+
(`dynaspan_text_field`, `dynaspan_text_area`, `dynaspan_select`) and its options
|
|
7
|
+
are unchanged.
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
|
|
11
|
+
- Dependency-free JavaScript. jQuery, rails-ujs and jquery_ujs are no longer
|
|
12
|
+
required. Updates are sent with `fetch` and include the CSRF token.
|
|
13
|
+
- Works with importmap-rails (`import "dynaspan"`, pinned automatically),
|
|
14
|
+
Propshaft and Sprockets.
|
|
15
|
+
- Keyboard support. The text can be reached with <kbd>Tab</kbd> and opened with
|
|
16
|
+
<kbd>Enter</kbd> or <kbd>Space</kbd>. <kbd>Enter</kbd> saves a text field,
|
|
17
|
+
<kbd>Ctrl</kbd>/<kbd>⌘</kbd>+<kbd>Enter</kbd> saves a text area and
|
|
18
|
+
<kbd>Esc</kbd> cancels the edit.
|
|
19
|
+
- DOM events: `dynaspan:open`, `dynaspan:close`, `dynaspan:update`
|
|
20
|
+
(cancelable), `dynaspan:success` and `dynaspan:error`.
|
|
21
|
+
- `ds-saving` and `ds-error` CSS classes on the Dynaspan block.
|
|
22
|
+
- Turbo Stream (`update.turbo_stream.erb`) and JavaScript (`update.js.erb`)
|
|
23
|
+
responses are applied to the page.
|
|
24
|
+
- An optional stylesheet: `dynaspan/dynaspan.css`.
|
|
25
|
+
- `window.Dynaspan.show(id)`, `.hide(id)` and `.cancel(id)` for scripting.
|
|
26
|
+
- A test suite (helper and browser tests) and CI across Rails 7.1–8.1.
|
|
27
|
+
|
|
28
|
+
### Changed
|
|
29
|
+
|
|
30
|
+
- Requires Ruby 3.1+ and Rails 7.1+.
|
|
31
|
+
- Helpers are available in every view automatically. You no longer need to
|
|
32
|
+
`include Dynaspan::ApplicationHelper` (keeping it does no harm).
|
|
33
|
+
- Inline `onclick`/`onblur`/`onfocus` handlers were replaced by delegated event
|
|
34
|
+
listeners, so Dynaspan works with a `script-src` Content Security Policy
|
|
35
|
+
that disallows inline scripts (`callback_on_update` and
|
|
36
|
+
`callback_with_values` still need `unsafe-eval`).
|
|
37
|
+
- A request is sent only when the value actually changed.
|
|
38
|
+
- The three partials were merged into `dynaspan/_dynaspan.html.erb`.
|
|
39
|
+
- Forms get `data-turbo="false"` and are no longer marked `remote: true`.
|
|
40
|
+
Dynaspan submits them itself.
|
|
41
|
+
- For a `has_many` nested record the parameters are now sent as
|
|
42
|
+
`parent[children_attributes][0][attribute]`, which also works for new
|
|
43
|
+
(unsaved) nested records.
|
|
44
|
+
- The `[edit]` element is rendered only when edit text is given.
|
|
45
|
+
|
|
46
|
+
### Fixed
|
|
47
|
+
|
|
48
|
+
- User input is displayed as text, never as HTML.
|
|
49
|
+
- `callback_with_values` works when the entered text contains quotes.
|
|
50
|
+
- Callback strings are HTML escaped in the data attributes.
|
|
51
|
+
- Reserved `html_options` (`:id`, `:onblur`, `:onfocus`) are really removed.
|
|
52
|
+
- Ids generated for namespaced models (`Admin::User`) are valid selectors.
|
|
53
|
+
- A blank nested value no longer displays the parent record's attribute of
|
|
54
|
+
the same name.
|
|
55
|
+
- `dynaspan_select` finds the label for hash, grouped and pre-rendered
|
|
56
|
+
(`options_for_select`) choices without building a regular expression from
|
|
57
|
+
user data.
|
|
58
|
+
- A nested record without `accepts_nested_attributes_for` raises a helpful
|
|
59
|
+
`ArgumentError` instead of silently editing the parent record.
|
|
60
|
+
- `helper` is no longer called on `ActionController::API` controllers.
|
|
61
|
+
|
|
62
|
+
### Removed
|
|
63
|
+
|
|
64
|
+
- Rails 3/4 specific code, the unused `basepath.js.erb`, `dynaspan-jquery.js`
|
|
65
|
+
and the `assets:precompile` non-digest rake task.
|
|
66
|
+
|
|
67
|
+
## 0.1.5 / 0.1.4
|
|
68
|
+
|
|
69
|
+
- `dynaspan_select` displays the option label rather than its value.
|
|
70
|
+
- Safeguard for enum values.
|
|
71
|
+
|
|
72
|
+
## 0.1.3
|
|
73
|
+
|
|
74
|
+
- `:unique_id` defaults to an id based on the record plus random characters.
|
|
75
|
+
- Added `:html_options`.
|
|
76
|
+
- Added `dynaspan_select` with `:choices`, `:options` and block support.
|
|
77
|
+
|
|
78
|
+
## 0.1.2
|
|
79
|
+
|
|
80
|
+
- Added `:unique_id` and `:form_for` options.
|
|
81
|
+
|
|
82
|
+
## 0.1.1
|
|
83
|
+
|
|
84
|
+
- Added `:callback_with_values`.
|
|
85
|
+
|
|
86
|
+
## 0.1.0
|
|
87
|
+
|
|
88
|
+
- `:hidden_fields` work for non-nested records.
|
|
89
|
+
|
|
90
|
+
## 0.0.9
|
|
91
|
+
|
|
92
|
+
- Added `:callback_on_update`.
|
|
93
|
+
|
|
94
|
+
## 0.0.8
|
|
95
|
+
|
|
96
|
+
- Options hash with `:hidden_fields` for nested records.
|
|
97
|
+
- The nested record id is only sent when it exists, allowing new nested records.
|
|
98
|
+
|
|
99
|
+
## 0.0.7
|
|
100
|
+
|
|
101
|
+
- `ds-dialog-open` class while the field is open.
|
|
102
|
+
|
|
103
|
+
## 0.0.6
|
|
104
|
+
|
|
105
|
+
- `ds-content-present` class when the field has content.
|
data/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
The MIT License (MIT)
|
|
2
2
|
|
|
3
|
-
Copyright (C) 2014-
|
|
3
|
+
Copyright (C) 2014-2026 by Daniel P. Clark
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
data/README.md
CHANGED
|
@@ -1,215 +1,300 @@
|
|
|
1
|
-
|
|
2
|
-
[](http://badge.fury.io/rb/dynaspan)
|
|
3
|
-
[](https://codeclimate.com/github/danielpclark/dynaspan)
|
|
4
|
-
#####[JSFiddle Demo](http://jsfiddle.net/680v09y8/)
|
|
1
|
+
# Dynaspan
|
|
5
2
|
|
|
6
|
-
|
|
3
|
+
[](https://rubygems.org/gems/dynaspan)
|
|
4
|
+
[](https://github.com/danielpclark/dynaspan/actions/workflows/ci.yml)
|
|
7
5
|
|
|
8
|
-
|
|
6
|
+
**Click-to-edit, in-place AJAX editing for Rails.**
|
|
9
7
|
|
|
10
|
-
|
|
8
|
+
Dynaspan shows a record's attribute as ordinary text on your page. Click the
|
|
9
|
+
text and it turns into a text field, text area or select. Click away (or press
|
|
10
|
+
<kbd>Enter</kbd>) and the change is sent to your controller's `update` action
|
|
11
|
+
over AJAX, and the field turns back into plain text.
|
|
11
12
|
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
- [ ] Next add `include Dynaspan::ApplicationHelper` inside your **ApplicationHelper** module
|
|
15
|
-
- [ ] Add `//= require dynaspan/dynaspan` to your **application.js** file
|
|
16
|
-
|
|
17
|
-
And it's installed!
|
|
18
|
-
|
|
19
|
-
###Usage
|
|
20
|
-
|
|
21
|
-
Simple example:
|
|
22
|
-
```ruby
|
|
23
|
-
dynaspan_text_field(user, :name)
|
|
13
|
+
```erb
|
|
14
|
+
<%= dynaspan_text_field(@user, :name) %>
|
|
24
15
|
```
|
|
25
|
-
And that's it. As long as you have a User object with a name field, this will update through
|
|
26
|
-
the UserController's update method. **user** is an User Object instance eg: `user = User.first`.
|
|
27
16
|
|
|
28
|
-
|
|
17
|
+

|
|
29
18
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
```
|
|
34
|
-
Polymorphic/Nested Example #2:
|
|
35
|
-
```ruby
|
|
36
|
-
dynaspan_text_field(profile, profile.websites, :url, '[edit]',
|
|
37
|
-
{
|
|
38
|
-
hidden_fields: {page_name: 'page2'},
|
|
39
|
-
callback_on_update: "alert('Awesome!');"
|
|
40
|
-
}
|
|
41
|
-
)
|
|
42
|
-
```
|
|
43
|
-
This will show the value of note in the comment object as plain text. It can be clicked on to instantly become a text field input. And once unselected the `@article` object will update with its nested attribute object `comment` and its new value in the `note` attribute.
|
|
19
|
+
| Before: plain text on the page | After a click: an input, ready to type |
|
|
20
|
+
| --- | --- |
|
|
21
|
+
|  |  |
|
|
44
22
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
```ruby
|
|
53
|
-
dynaspan_text_field(Object,SymField)
|
|
54
|
-
```
|
|
55
|
-
It is unconventional but the order remains the same despite the optional fields.
|
|
23
|
+
- Text fields, text areas and selects
|
|
24
|
+
- Nested attributes (`accepts_nested_attributes_for`), one level deep
|
|
25
|
+
- No jQuery, rails-ujs or Turbo required, but it works alongside all three
|
|
26
|
+
- Works with importmap-rails, Propshaft and Sprockets
|
|
27
|
+
- Keyboard accessible: <kbd>Tab</kbd> to the text, <kbd>Enter</kbd> to edit,
|
|
28
|
+
<kbd>Enter</kbd> to save, <kbd>Esc</kbd> to cancel
|
|
29
|
+
- DOM events and CSS state classes for styling and custom behaviour
|
|
56
30
|
|
|
57
|
-
|
|
31
|
+
## Requirements
|
|
58
32
|
|
|
59
|
-
|
|
60
|
-
|
|
33
|
+
- Ruby 3.1+
|
|
34
|
+
- Rails 7.1, 7.2, 8.0 or 8.1
|
|
61
35
|
|
|
62
|
-
|
|
36
|
+
## Installation
|
|
63
37
|
|
|
64
|
-
|
|
65
|
-
For example **dynaspan_text_field(author, author.stories, :title)**. This works as a nested attribute so it includes Polymorphic Objects.
|
|
38
|
+
Add the gem to your Gemfile and run `bundle install`:
|
|
66
39
|
|
|
67
|
-
|
|
68
|
-
|
|
40
|
+
```ruby
|
|
41
|
+
gem 'dynaspan'
|
|
42
|
+
```
|
|
69
43
|
|
|
70
|
-
|
|
44
|
+
Then load the JavaScript using whichever asset setup your app uses.
|
|
71
45
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
- **:callback_with_values** will allow you to put a JavaScript command you want called on update and include as many parameters as you'd like. It will dynamically append a last parameter which is a Hash of two values. The first is the CSS selector id of the Dynaspan block that just performed the action, the second value is the actual text that was entered. The keys in this Hash are **ds_selector** and **ds_input**
|
|
75
|
-
- **:unique_id** allows custom ID labelling. This is no longer recommended to be used as the in-built method is thorough in its uniqeness.
|
|
76
|
-
- **:form_for** allows adding or over-writing any form_for parameter (besides the object being written to). This takes a Hash of parameters just like you would give in a view for your form_for form. If you have a namespaced object to update use the **url:** option in the hash for the path to use in updating your object.
|
|
77
|
-
- **:html_options** add your own html options to the input field. Includes ability to add additional classes with `html_options: {class: "example"}`. **:id**, **:onfocus**, and **:onblur** are reserved.
|
|
78
|
-
- **:choices** used for **dynaspan_select** for the choices of the select box.
|
|
79
|
-
- **:options** used for **dynaspan_select** for the options of the select box; such as **:disabled**, **:prompt**, or **:include_blank**.
|
|
80
|
-
- **&block** used only with **dynaspan_select** for passing a block to Rails' form select method.
|
|
46
|
+
**importmap-rails** (the Rails 7+ default). The pin is added for you, so just
|
|
47
|
+
import it in `app/javascript/application.js`:
|
|
81
48
|
|
|
82
|
-
|
|
49
|
+
```js
|
|
50
|
+
import "dynaspan"
|
|
51
|
+
```
|
|
83
52
|
|
|
84
|
-
|
|
85
|
-
and the symbol for the field are all part of the main Object being updated. There is no expected AJAX reply. It's
|
|
86
|
-
a silent set it and forget it method. If you don't have your update method configured with a `.js` response then it
|
|
87
|
-
will successfully perform the update on the object, and then send a complaint about a response but no one will notice
|
|
88
|
-
(unless maybe you look at the server logs). In other words the client experience is only good, and the server
|
|
89
|
-
won't hiccup over it.
|
|
53
|
+
**Sprockets.** Add this to `app/assets/javascripts/application.js`:
|
|
90
54
|
|
|
91
|
-
|
|
55
|
+
```js
|
|
56
|
+
//= require dynaspan/dynaspan
|
|
57
|
+
```
|
|
92
58
|
|
|
93
|
-
**
|
|
59
|
+
**Propshaft, jsbundling-rails or anything else.** Add a script tag to your
|
|
60
|
+
layout:
|
|
94
61
|
|
|
95
|
-
|
|
62
|
+
```erb
|
|
63
|
+
<%= javascript_include_tag "dynaspan/dynaspan", defer: true %>
|
|
64
|
+
```
|
|
96
65
|
|
|
97
|
-
|
|
66
|
+
Optionally, add the default styles (hover highlight, full-width inputs, saving
|
|
67
|
+
and error states):
|
|
98
68
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
box. If you don't want it to drop you can style it with the proper CSS selector for content present. E.G.
|
|
103
|
-
`.ds-content-present > dyna-span-edit-text { margin-top:-18px; }` You can set the height to whatever your input
|
|
104
|
-
field height is to maintain the position of the edit text.
|
|
69
|
+
```erb
|
|
70
|
+
<%= stylesheet_link_tag "dynaspan/dynaspan" %>
|
|
71
|
+
```
|
|
105
72
|
|
|
106
|
-
|
|
107
|
-
is "ds-dialog-open". This is also to use in CSS styles. This feature was added since CSS doesn't support
|
|
108
|
-
calling parents with selectors. Example usage:
|
|
73
|
+
The view helpers are available in every view automatically.
|
|
109
74
|
|
|
110
|
-
|
|
111
|
-
.ds-content-present > .dyna-span-edit-text {
|
|
112
|
-
margin-top:-18px;
|
|
113
|
-
}
|
|
75
|
+
## Usage
|
|
114
76
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
77
|
+
```erb
|
|
78
|
+
<%= dynaspan_text_field(@user, :name) %>
|
|
79
|
+
<%= dynaspan_text_area(@user, :bio) %>
|
|
80
|
+
<%= dynaspan_select(@user, :role, choices: [["Administrator", "admin"], ["Editor", "editor"]]) %>
|
|
118
81
|
```
|
|
119
82
|
|
|
120
|
-
|
|
121
|
-
|
|
83
|
+
Each helper renders the current value as text, together with a hidden form for
|
|
84
|
+
the record. When the value changes, the form is submitted to the record's
|
|
85
|
+
`update` route (`PATCH /users/:id`), just as a normal `form_with(model: @user)`
|
|
86
|
+
form would be. Your controller only needs to permit the attribute:
|
|
122
87
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
88
|
+
```ruby
|
|
89
|
+
class UsersController < ApplicationController
|
|
90
|
+
def update
|
|
91
|
+
@user = User.find(params[:id])
|
|
92
|
+
|
|
93
|
+
respond_to do |format|
|
|
94
|
+
if @user.update(user_params)
|
|
95
|
+
format.html { redirect_to @user }
|
|
96
|
+
format.json { render json: @user }
|
|
97
|
+
else
|
|
98
|
+
format.html { render :edit, status: :unprocessable_entity }
|
|
99
|
+
format.json { render json: @user.errors, status: :unprocessable_entity }
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
private
|
|
105
|
+
|
|
106
|
+
def user_params
|
|
107
|
+
params.require(:user).permit(:name, :bio, :role)
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
```
|
|
126
111
|
|
|
127
|
-
|
|
112
|
+
The request asks for Turbo Stream, JavaScript, JSON or HTML, in that order, so
|
|
113
|
+
a standard scaffold controller works as it is. Turbo Stream
|
|
114
|
+
(`update.turbo_stream.erb`) and JavaScript (`update.js.erb`) responses are
|
|
115
|
+
applied to the page. A response with an error status (such as a failed
|
|
116
|
+
validation) adds the `ds-error` class to the field and fires `dynaspan:error`.
|
|
128
117
|
|
|
129
|
-
|
|
118
|
+
### Edit text
|
|
130
119
|
|
|
131
|
-
|
|
120
|
+
Blank values have nothing to click on. Pass some edit text as the argument
|
|
121
|
+
after the attribute to give people something to click:
|
|
132
122
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
- Added **&block** used only with **dynaspan_select** for passing a block to Rails' form select method.
|
|
123
|
+
```erb
|
|
124
|
+
<%= dynaspan_text_field(@user, :nickname, "[edit]") %>
|
|
125
|
+
```
|
|
137
126
|
|
|
127
|
+
### Nested records
|
|
138
128
|
|
|
129
|
+
Pass the nested record before the attribute to edit it through its parent. The
|
|
130
|
+
parent must have `accepts_nested_attributes_for` for the association:
|
|
139
131
|
|
|
140
|
-
|
|
132
|
+
```ruby
|
|
133
|
+
class Article < ApplicationRecord
|
|
134
|
+
has_many :comments
|
|
135
|
+
accepts_nested_attributes_for :comments
|
|
136
|
+
end
|
|
137
|
+
```
|
|
141
138
|
|
|
142
|
-
|
|
139
|
+
```erb
|
|
140
|
+
<%= dynaspan_text_field(@article, comment, :note, "[edit]") %>
|
|
141
|
+
```
|
|
143
142
|
|
|
144
|
-
|
|
145
|
-
|
|
143
|
+
This submits `article[comments_attributes][0][id]` and
|
|
144
|
+
`article[comments_attributes][0][note]` to `ArticlesController#update`.
|
|
145
|
+
Remember to permit `comments_attributes: [:id, :note]`.
|
|
146
146
|
|
|
147
|
-
|
|
147
|
+
### Arguments
|
|
148
148
|
|
|
149
|
-
Added a JavaScript callback that will **append** a Hash/Dictionary of the updated Dynaspan Object to the end of your
|
|
150
|
-
functions parameters. The method is named **callback_with_values**.
|
|
151
149
|
```ruby
|
|
152
|
-
{
|
|
153
|
-
|
|
154
|
-
}
|
|
150
|
+
dynaspan_text_field(record, nested_record = nil, :attribute, edit_text = nil, options = {})
|
|
151
|
+
dynaspan_text_area(record, nested_record = nil, :attribute, edit_text = nil, options = {})
|
|
152
|
+
dynaspan_select(record, nested_record = nil, :attribute, edit_text = nil, options = {}, &block)
|
|
155
153
|
```
|
|
156
|
-
This will be called everytime the Dynaspan field submits and it will **inject** the following result **as the last parameter**:
|
|
157
|
-
```ruby
|
|
158
|
-
{
|
|
159
|
-
ds_selector: "dyna_span_unique_label<#>",
|
|
160
|
-
ds_input: "the entered text from the input field"
|
|
161
|
-
}
|
|
162
|
-
```
|
|
163
|
-
####Version 0.1.0
|
|
164
154
|
|
|
165
|
-
|
|
155
|
+
1. **record**: the model instance whose `update` action is called.
|
|
156
|
+
2. **nested_record** (optional): a record from one of `record`'s nested
|
|
157
|
+
attribute associations.
|
|
158
|
+
3. **:attribute**: a Symbol naming the attribute to edit.
|
|
159
|
+
4. **edit_text** (optional): a String shown next to the value that can also be
|
|
160
|
+
clicked to start editing.
|
|
161
|
+
5. **options** (optional): a Hash, see below.
|
|
162
|
+
|
|
163
|
+
### Options
|
|
164
|
+
|
|
165
|
+
| Option | Description |
|
|
166
|
+
| --- | --- |
|
|
167
|
+
| `:choices` | `dynaspan_select` only. The choices for the select: an array, a hash, grouped choices or `options_for_select` output. |
|
|
168
|
+
| `:options` | `dynaspan_select` only. Options for Rails' `select`, such as `include_blank:` or `prompt:`. |
|
|
169
|
+
| `&block` | `dynaspan_select` only. Passed through to Rails' `select`. |
|
|
170
|
+
| `:html_options` | HTML attributes for the input, e.g. `{ class: "wide", rows: 4, placeholder: "Name" }`. Classes are added to Dynaspan's own. `:id`, `:onblur` and `:onfocus` are reserved. |
|
|
171
|
+
| `:hidden_fields` | A Hash of extra values to submit, rendered as hidden fields: `{ page_name: "profile" }`. |
|
|
172
|
+
| `:form_for` | Options for the underlying `form_for`, e.g. `{ url: admin_user_path(@user) }` for namespaced routes. |
|
|
173
|
+
| `:unique_id` | Sets the id suffix used for the elements. By default an id is built from the record, the attribute and some random characters. |
|
|
174
|
+
| `:callback_on_update` | A string of JavaScript run each time a change is sent, e.g. `"refreshTotals();"`. |
|
|
175
|
+
| `:callback_with_values` | A JavaScript function call as a string, e.g. `"saved();"`. A Hash is appended as its last argument: `{ ds_selector: "#dyna_span_block…", ds_input: "entered text" }`. |
|
|
176
|
+
|
|
177
|
+
For new code, prefer the [JavaScript events](#javascript-events) over the two
|
|
178
|
+
callback options. The callbacks are evaluated as strings, so they need
|
|
179
|
+
`unsafe-eval` if you use a Content Security Policy.
|
|
180
|
+
|
|
181
|
+
## Keyboard
|
|
182
|
+
|
|
183
|
+
| Key | Where | Action |
|
|
184
|
+
| --- | --- | --- |
|
|
185
|
+
| <kbd>Tab</kbd> | Page | Move focus to a Dynaspan value |
|
|
186
|
+
| <kbd>Enter</kbd> / <kbd>Space</kbd> | Value | Start editing |
|
|
187
|
+
| <kbd>Enter</kbd> | Text field | Save |
|
|
188
|
+
| <kbd>Ctrl</kbd>/<kbd>⌘</kbd> + <kbd>Enter</kbd> | Text area | Save |
|
|
189
|
+
| <kbd>Esc</kbd> | Field | Cancel and restore the previous value |
|
|
190
|
+
|
|
191
|
+
Clicking anywhere outside the field also saves it. Nothing is sent to the
|
|
192
|
+
server when the value did not change.
|
|
193
|
+
|
|
194
|
+
## JavaScript events
|
|
195
|
+
|
|
196
|
+
Events are dispatched on the Dynaspan block (`div.dyna-span`) and bubble up
|
|
197
|
+
to `document`:
|
|
198
|
+
|
|
199
|
+
| Event | When | `event.detail` |
|
|
200
|
+
| --- | --- | --- |
|
|
201
|
+
| `dynaspan:open` | The field is shown | `selector`, `value` |
|
|
202
|
+
| `dynaspan:close` | The field is hidden | `selector`, `value`, `changed` |
|
|
203
|
+
| `dynaspan:update` | A change is about to be sent. Call `preventDefault()` to skip the request. | `selector`, `input` (display text), `value`, `form` |
|
|
204
|
+
| `dynaspan:success` | The server responded successfully | same as `update`, plus `response` |
|
|
205
|
+
| `dynaspan:error` | The request failed or returned an error status | same as `update`, plus `error` and `response` |
|
|
206
|
+
|
|
207
|
+
```js
|
|
208
|
+
document.addEventListener("dynaspan:success", (event) => {
|
|
209
|
+
console.log("Saved", event.detail.value)
|
|
210
|
+
})
|
|
211
|
+
```
|
|
166
212
|
|
|
167
|
-
|
|
213
|
+
You can also control a field from JavaScript by its id (set it with the
|
|
214
|
+
`:unique_id` option):
|
|
168
215
|
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
callback_on_update: "someMethod('some-relative-instance-value');"
|
|
174
|
-
}
|
|
216
|
+
```js
|
|
217
|
+
Dynaspan.show("user-name") // open the field
|
|
218
|
+
Dynaspan.hide("user-name") // close it and save if changed
|
|
219
|
+
Dynaspan.cancel("user-name") // close it and discard the change
|
|
175
220
|
```
|
|
176
|
-
####Version 0.0.8
|
|
177
221
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
222
|
+
If jQuery is on the page, the 0.x API (`$().dynaspan.upShow(id)`,
|
|
223
|
+
`upHide(id)` and `upLast(id)`) still works.
|
|
224
|
+
|
|
225
|
+
## Styling
|
|
226
|
+
|
|
227
|
+
The markup looks like this:
|
|
228
|
+
|
|
229
|
+
```html
|
|
230
|
+
<div id="dyna_span_block{id}" class="dyna-span ds-content-present" data-dynaspan="{id}">
|
|
231
|
+
<div id="dyna_span_div{id}" class="dyna-span-form"><form>…the input…</form></div>
|
|
232
|
+
<span id="dyna_span_span{id}" class="dyna-span dyna-span-text">The value</span>
|
|
233
|
+
<div class="dyna-span-edit-text pull-right">[edit]</div>
|
|
234
|
+
</div>
|
|
184
235
|
```
|
|
185
|
-
You can add as many hidden fields to your Dynaspan objects as you'd like.
|
|
186
236
|
|
|
187
|
-
|
|
237
|
+
These classes are toggled on the outer `div.dyna-span`:
|
|
188
238
|
|
|
189
|
-
|
|
190
|
-
|
|
239
|
+
| Class | Present when |
|
|
240
|
+
| --- | --- |
|
|
241
|
+
| `ds-content-present` | The value isn't blank |
|
|
242
|
+
| `ds-dialog-open` | The input is showing |
|
|
243
|
+
| `ds-saving` | A request is in flight |
|
|
244
|
+
| `ds-error` | The last request failed |
|
|
191
245
|
|
|
192
|
-
|
|
246
|
+
The input always has the classes `dyna-span-input` and `form-control` (so it
|
|
247
|
+
fits in with Bootstrap), plus any you add with `html_options`. For example, to
|
|
248
|
+
keep the edit text beside a value instead of below it:
|
|
193
249
|
|
|
194
|
-
|
|
250
|
+
```css
|
|
251
|
+
.ds-content-present > .dyna-span-edit-text { margin-top: -18px; }
|
|
252
|
+
.ds-dialog-open > .dyna-span-edit-text { margin-top: -24px; }
|
|
253
|
+
```
|
|
195
254
|
|
|
196
|
-
|
|
255
|
+
To change the markup itself, copy
|
|
256
|
+
[`app/views/dynaspan/_dynaspan.html.erb`](app/views/dynaspan/_dynaspan.html.erb)
|
|
257
|
+
into your application at the same path.
|
|
258
|
+
|
|
259
|
+
## Upgrading from 0.x
|
|
260
|
+
|
|
261
|
+
- Rails 7.1+ and Ruby 3.1+ are required.
|
|
262
|
+
- jQuery and rails-ujs are no longer needed. Load `dynaspan/dynaspan` as shown in
|
|
263
|
+
[Installation](#installation). If you had `//= require dynaspan/dynaspan`
|
|
264
|
+
already, it keeps working.
|
|
265
|
+
- `include Dynaspan::ApplicationHelper` is no longer necessary.
|
|
266
|
+
- A request is sent only when the value changed, so `callback_on_update` and
|
|
267
|
+
`callback_with_values` only run for real changes.
|
|
268
|
+
- If you overrode `_dynaspan_text_field`, `_dynaspan_text_area` or
|
|
269
|
+
`_dynaspan_text_select`, move your changes to `_dynaspan.html.erb`.
|
|
270
|
+
- Passing a nested record whose association lacks
|
|
271
|
+
`accepts_nested_attributes_for` now raises an `ArgumentError`.
|
|
272
|
+
|
|
273
|
+
See the [CHANGELOG](CHANGELOG.md) for everything else.
|
|
274
|
+
|
|
275
|
+
## Development
|
|
276
|
+
|
|
277
|
+
```sh
|
|
278
|
+
bundle install
|
|
279
|
+
bundle exec rake test # helper and browser tests (needs Chrome/Chromium)
|
|
280
|
+
RAILS_VERSION=7.2 bundle update # test against another Rails version
|
|
281
|
+
bin/demo # the demo app at http://localhost:3000
|
|
282
|
+
```
|
|
197
283
|
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
in the Software without restriction, including without limitation the rights
|
|
201
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
202
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
203
|
-
furnished to do so, subject to the following conditions:
|
|
284
|
+
The browser tests use [Cuprite](https://github.com/rubycdp/cuprite). Set
|
|
285
|
+
`BROWSER_PATH` if Chrome isn't found automatically.
|
|
204
286
|
|
|
205
|
-
The
|
|
206
|
-
|
|
287
|
+
The demo GIF and screenshots above are recorded from `bin/demo` with
|
|
288
|
+
[Playwright](https://playwright.dev) and assembled with
|
|
289
|
+
[Pillow](https://python-pillow.org):
|
|
290
|
+
|
|
291
|
+
```sh
|
|
292
|
+
PORT=3999 bin/demo &
|
|
293
|
+
node script/demo/record.js # captures frames and the screenshots
|
|
294
|
+
python3 script/demo/build_gif.py # writes docs/images/dynaspan-demo.gif
|
|
295
|
+
```
|
|
207
296
|
|
|
208
|
-
|
|
209
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
210
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
211
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
212
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
213
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
214
|
-
THE SOFTWARE.
|
|
297
|
+
## License
|
|
215
298
|
|
|
299
|
+
The MIT License (MIT). Copyright (C) 2014-2026 by Daniel P. Clark. See
|
|
300
|
+
[LICENSE](LICENSE).
|