kiso 0.6.2.pre → 0.6.3.pre
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/app/javascript/controllers/kiso/button_loading_controller.js +120 -0
- data/app/javascript/controllers/kiso/dropdown_menu_controller.js +4 -1
- data/app/javascript/controllers/kiso/index.d.ts +1 -0
- data/app/javascript/controllers/kiso/index.js +3 -0
- data/app/views/kiso/components/_button.html.erb +6 -3
- data/lib/kiso/version.rb +1 -1
- metadata +2 -1
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: fc54430908619f2cb1581ca13089cfceb7b9c527f7050a0079cfced84ed088ea
|
|
4
|
+
data.tar.gz: ea34c1970ff36648de7571bac67bec65cf5a57083a4afb4afe933c89eb27d1b2
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: '02761138a1d470cec63228c7d3e5ffe2d9d93d271427814059cb6193450492051755bba4514f945c225a669276540b9a6bd223469ec6eb0b05b13ae361ef2601'
|
|
7
|
+
data.tar.gz: 2bc1f27fc05aa8da6aecf7e3b6eaae668e02afc45caa3636ebc55270738588dd4dc61102554e0cadfe33e99cba0a72a9e4e56decb3094b4646d5955a28e03ca3
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Automatic loading state for buttons inside Turbo forms.
|
|
5
|
+
*
|
|
6
|
+
* When the button's form submits via Turbo, the controller saves the button's
|
|
7
|
+
* original content, injects an animated spinner SVG, and disables the button.
|
|
8
|
+
* On `turbo:submit-end`, the original content and state are restored.
|
|
9
|
+
*
|
|
10
|
+
* Attach via `data-controller="kiso--button-loading"` on the `<button>` element.
|
|
11
|
+
* The Button partial adds this automatically when `loading_auto: true`.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* <form action="/save" method="post" data-turbo="true">
|
|
15
|
+
* <button type="submit"
|
|
16
|
+
* data-controller="kiso--button-loading"
|
|
17
|
+
* data-slot="button"
|
|
18
|
+
* class="...">
|
|
19
|
+
* Save
|
|
20
|
+
* </button>
|
|
21
|
+
* </form>
|
|
22
|
+
*
|
|
23
|
+
* @fires kiso--button-loading:loading - When the button enters loading state
|
|
24
|
+
* @fires kiso--button-loading:complete - When the button exits loading state
|
|
25
|
+
*/
|
|
26
|
+
export default class extends Controller {
|
|
27
|
+
connect() {
|
|
28
|
+
this._form = this.element.closest("form")
|
|
29
|
+
if (!this._form) return
|
|
30
|
+
|
|
31
|
+
this._handleSubmitStart = this._handleSubmitStart.bind(this)
|
|
32
|
+
this._handleSubmitEnd = this._handleSubmitEnd.bind(this)
|
|
33
|
+
|
|
34
|
+
this._form.addEventListener("turbo:submit-start", this._handleSubmitStart)
|
|
35
|
+
this._form.addEventListener("turbo:submit-end", this._handleSubmitEnd)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
disconnect() {
|
|
39
|
+
if (!this._form) return
|
|
40
|
+
|
|
41
|
+
this._form.removeEventListener("turbo:submit-start", this._handleSubmitStart)
|
|
42
|
+
this._form.removeEventListener("turbo:submit-end", this._handleSubmitEnd)
|
|
43
|
+
this._restore()
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Saves original button content and enters loading state.
|
|
48
|
+
*
|
|
49
|
+
* @param {CustomEvent} event - turbo:submit-start event
|
|
50
|
+
* @private
|
|
51
|
+
*/
|
|
52
|
+
_handleSubmitStart(event) {
|
|
53
|
+
// Only react if this button triggered the submission
|
|
54
|
+
if (event.detail?.formSubmission?.submitter !== this.element) return
|
|
55
|
+
|
|
56
|
+
this._savedNodes = Array.from(this.element.childNodes).map((n) => n.cloneNode(true))
|
|
57
|
+
this._wasDisabled = this.element.disabled
|
|
58
|
+
|
|
59
|
+
// Create spinner SVG via DOM APIs (safe, no innerHTML)
|
|
60
|
+
const spinner = document.createElementNS("http://www.w3.org/2000/svg", "svg")
|
|
61
|
+
spinner.setAttribute("viewBox", "0 0 24 24")
|
|
62
|
+
spinner.setAttribute("fill", "none")
|
|
63
|
+
spinner.setAttribute("stroke", "currentColor")
|
|
64
|
+
spinner.setAttribute("stroke-width", "2")
|
|
65
|
+
spinner.setAttribute("stroke-linecap", "round")
|
|
66
|
+
spinner.setAttribute("stroke-linejoin", "round")
|
|
67
|
+
spinner.setAttribute("class", "animate-spin shrink-0 pointer-events-none size-4")
|
|
68
|
+
|
|
69
|
+
const path = document.createElementNS("http://www.w3.org/2000/svg", "path")
|
|
70
|
+
path.setAttribute("d", "M21 12a9 9 0 1 1-6.219-8.56")
|
|
71
|
+
spinner.appendChild(path)
|
|
72
|
+
|
|
73
|
+
// Replace content: spinner + original text
|
|
74
|
+
const textContent = this.element.textContent?.trim()
|
|
75
|
+
while (this.element.firstChild) {
|
|
76
|
+
this.element.removeChild(this.element.firstChild)
|
|
77
|
+
}
|
|
78
|
+
this.element.appendChild(spinner)
|
|
79
|
+
if (textContent) {
|
|
80
|
+
this.element.appendChild(document.createTextNode(` ${textContent}`))
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
this.element.disabled = true
|
|
84
|
+
this.element.setAttribute("aria-busy", "true")
|
|
85
|
+
|
|
86
|
+
this.dispatch("loading")
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Restores original button content and state on turbo:submit-end.
|
|
91
|
+
*
|
|
92
|
+
* @private
|
|
93
|
+
*/
|
|
94
|
+
_handleSubmitEnd() {
|
|
95
|
+
this._restore()
|
|
96
|
+
this.dispatch("complete")
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Restores the button to its pre-loading state.
|
|
101
|
+
*
|
|
102
|
+
* @private
|
|
103
|
+
*/
|
|
104
|
+
_restore() {
|
|
105
|
+
if (!this._savedNodes) return
|
|
106
|
+
|
|
107
|
+
while (this.element.firstChild) {
|
|
108
|
+
this.element.removeChild(this.element.firstChild)
|
|
109
|
+
}
|
|
110
|
+
for (const node of this._savedNodes) {
|
|
111
|
+
this.element.appendChild(node)
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
this.element.disabled = this._wasDisabled
|
|
115
|
+
this.element.removeAttribute("aria-busy")
|
|
116
|
+
|
|
117
|
+
this._savedNodes = undefined
|
|
118
|
+
this._wasDisabled = undefined
|
|
119
|
+
}
|
|
120
|
+
}
|
|
@@ -158,7 +158,10 @@ export default class extends Controller {
|
|
|
158
158
|
if (item.dataset.disabled === "true") return
|
|
159
159
|
|
|
160
160
|
this.dispatch("select", { detail: { item } })
|
|
161
|
-
|
|
161
|
+
// Defer close to the next frame so other click handlers on the item
|
|
162
|
+
// (e.g. dialog triggers, custom actions) complete before the menu
|
|
163
|
+
// hides and removes elements from view. See #233.
|
|
164
|
+
requestAnimationFrame(() => this.close())
|
|
162
165
|
}
|
|
163
166
|
|
|
164
167
|
/**
|
|
@@ -6,6 +6,7 @@ declare const KisoUi: {
|
|
|
6
6
|
|
|
7
7
|
export default KisoUi
|
|
8
8
|
export const KisoAlertController: typeof Controller
|
|
9
|
+
export const KisoButtonLoadingController: typeof Controller
|
|
9
10
|
export const KisoComboboxController: typeof Controller
|
|
10
11
|
export const KisoCommandController: typeof Controller
|
|
11
12
|
export const KisoCommandDialogController: typeof Controller
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
*/
|
|
20
20
|
|
|
21
21
|
import KisoAlertController from "./alert_controller.js"
|
|
22
|
+
import KisoButtonLoadingController from "./button_loading_controller.js"
|
|
22
23
|
import KisoComboboxController from "./combobox_controller.js"
|
|
23
24
|
import KisoCommandController from "./command_controller.js"
|
|
24
25
|
import KisoCommandDialogController from "./command_dialog_controller.js"
|
|
@@ -43,6 +44,7 @@ const KisoUi = {
|
|
|
43
44
|
*/
|
|
44
45
|
start(application) {
|
|
45
46
|
application.register("kiso--alert", KisoAlertController)
|
|
47
|
+
application.register("kiso--button-loading", KisoButtonLoadingController)
|
|
46
48
|
application.register("kiso--combobox", KisoComboboxController)
|
|
47
49
|
application.register("kiso--command", KisoCommandController)
|
|
48
50
|
application.register("kiso--command-dialog", KisoCommandDialogController)
|
|
@@ -64,6 +66,7 @@ const KisoUi = {
|
|
|
64
66
|
export default KisoUi
|
|
65
67
|
export {
|
|
66
68
|
KisoAlertController,
|
|
69
|
+
KisoButtonLoadingController,
|
|
67
70
|
KisoComboboxController,
|
|
68
71
|
KisoCommandController,
|
|
69
72
|
KisoCommandDialogController,
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
<%# locals: (color: :primary, variant: :solid, size: :md, block: false,
|
|
2
2
|
type: :button, href: nil, method: nil, disabled: false,
|
|
3
|
-
loading: false, form: {}, css_classes: "", **component_options) %>
|
|
3
|
+
loading: false, loading_auto: false, form: {}, css_classes: "", **component_options) %>
|
|
4
4
|
<%# Polymorphic button that renders as <button>, <a>, or button_to depending on props.
|
|
5
5
|
With href: renders an anchor tag. With href: + method: (non-GET) renders a Rails
|
|
6
6
|
button_to form for safe non-GET navigation. Without href: renders a plain <button>.
|
|
7
|
-
With loading: true, prepends an animated spinner and disables the button.
|
|
7
|
+
With loading: true, prepends an animated spinner and disables the button.
|
|
8
|
+
With loading_auto: true, attaches kiso--button-loading Stimulus controller for
|
|
9
|
+
automatic loading state on Turbo form submissions. %>
|
|
8
10
|
<%
|
|
9
11
|
css = Kiso::Themes::Button.render(
|
|
10
12
|
color: color, variant: variant, size: size, block: block, class: css_classes)
|
|
11
|
-
data = kiso_prepare_options(component_options, slot: "button"
|
|
13
|
+
data = kiso_prepare_options(component_options, slot: "button",
|
|
14
|
+
**(loading_auto ? { controller: "kiso--button-loading" } : {}))
|
|
12
15
|
use_button_to = href.present? && method.present? && method.to_s != "get"
|
|
13
16
|
is_disabled = disabled || loading
|
|
14
17
|
component_options[:"aria-busy"] = true if loading
|
data/lib/kiso/version.rb
CHANGED
metadata
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: kiso
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.6.
|
|
4
|
+
version: 0.6.3.pre
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Steve Clarke
|
|
@@ -100,6 +100,7 @@ files:
|
|
|
100
100
|
- app/helpers/kiso/theme_helper.rb
|
|
101
101
|
- app/helpers/kiso/ui_context_helper.rb
|
|
102
102
|
- app/javascript/controllers/kiso/alert_controller.js
|
|
103
|
+
- app/javascript/controllers/kiso/button_loading_controller.js
|
|
103
104
|
- app/javascript/controllers/kiso/combobox_controller.js
|
|
104
105
|
- app/javascript/controllers/kiso/command_controller.js
|
|
105
106
|
- app/javascript/controllers/kiso/command_dialog_controller.js
|