katalyst-kpop 3.0.0.beta.7 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/builds/katalyst/kpop.esm.js +200 -63
- data/app/assets/builds/katalyst/kpop.js +200 -63
- data/app/assets/builds/katalyst/kpop.min.js +1 -1
- data/app/assets/builds/katalyst/kpop.min.js.map +1 -1
- data/app/assets/stylesheets/katalyst/kpop/_frame.scss +0 -1
- data/app/assets/stylesheets/katalyst/kpop/_modal.scss +53 -46
- data/app/assets/stylesheets/katalyst/kpop/_variables.scss +1 -2
- data/app/components/kpop/modal/footer_component.rb +21 -0
- data/app/components/kpop/modal/header_component.rb +21 -0
- data/app/components/kpop/modal_component.html.erb +1 -3
- data/app/components/kpop/modal_component.rb +16 -4
- data/app/javascript/kpop/controllers/frame_controller.js +68 -50
- data/app/javascript/kpop/modals/content_modal.js +22 -5
- data/app/javascript/kpop/modals/frame_modal.js +77 -7
- data/app/javascript/kpop/modals/modal.js +9 -1
- data/app/javascript/kpop/modals/stream_modal.js +25 -0
- data/lib/katalyst/kpop/version.rb +1 -1
- metadata +6 -4
@@ -10,69 +10,76 @@
|
|
10
10
|
grid-template-rows: auto auto 1fr auto;
|
11
11
|
|
12
12
|
background-color: white;
|
13
|
-
border: 1px solid black;
|
14
13
|
border-radius: $border-radius;
|
15
14
|
overflow: hidden;
|
16
15
|
max-height: var(--max-height);
|
17
|
-
|
16
|
+
box-shadow: rgb(0 0 0 / 25%) 0 1px 2px, rgb(0 0 0 / 31%) 0 0 5px;
|
18
17
|
|
19
|
-
.kpop-title-bar {
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
18
|
+
.kpop-title-bar {
|
19
|
+
grid-area: title-bar;
|
20
|
+
display: grid;
|
21
|
+
grid-template-areas: "close title empty";
|
22
|
+
grid-template-columns: 3.5rem auto 3.5rem;
|
23
|
+
border-bottom: 1px solid $keyline-color;
|
24
|
+
min-height: 3.5rem;
|
25
|
+
align-items: center;
|
26
|
+
}
|
24
27
|
|
25
|
-
.kpop-
|
28
|
+
.kpop-header {
|
29
|
+
grid-area: header;
|
30
|
+
}
|
31
|
+
|
32
|
+
.kpop-content {
|
33
|
+
grid-area: content;
|
34
|
+
display: flex;
|
35
|
+
flex-direction: column;
|
36
|
+
overflow: auto;
|
37
|
+
}
|
38
|
+
|
39
|
+
.kpop-footer {
|
40
|
+
grid-area: footer;
|
41
|
+
border-top: 1px solid $keyline-color;
|
26
42
|
padding: $default-padding;
|
27
|
-
|
43
|
+
}
|
44
|
+
|
45
|
+
.kpop-title {
|
46
|
+
grid-area: title;
|
47
|
+
font-weight: bold;
|
48
|
+
text-align: center;
|
49
|
+
white-space: nowrap;
|
50
|
+
overflow: hidden;
|
51
|
+
text-overflow: ellipsis;
|
52
|
+
line-height: 3.5rem;
|
28
53
|
}
|
29
54
|
|
30
55
|
.kpop-close {
|
56
|
+
grid-area: close;
|
57
|
+
text-align: center;
|
31
58
|
background: none;
|
32
59
|
border: none;
|
33
|
-
color: $title-text-color;
|
34
|
-
cursor: pointer;
|
35
60
|
display: block;
|
36
61
|
font-size: 2rem;
|
37
|
-
font-weight:
|
38
|
-
padding: 0 0.75rem;
|
62
|
+
font-weight: 300;
|
39
63
|
text-decoration: none;
|
64
|
+
line-height: 3.5rem;
|
40
65
|
}
|
41
|
-
}
|
42
|
-
|
43
|
-
.kpop-header {
|
44
|
-
grid-area: header;
|
45
|
-
}
|
46
|
-
|
47
|
-
.kpop-content {
|
48
|
-
grid-area: content;
|
49
|
-
display: flex;
|
50
|
-
flex-direction: column;
|
51
|
-
overflow: auto;
|
52
|
-
}
|
53
66
|
|
54
|
-
.
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
padding: $default-padding;
|
59
|
-
}
|
60
|
-
|
61
|
-
.kpop-buttons {
|
62
|
-
display: flex;
|
63
|
-
gap: 0.5rem;
|
64
|
-
justify-content: space-between;
|
65
|
-
}
|
66
|
-
|
67
|
-
.kpop-modal.iframe {
|
68
|
-
.kpop-content {
|
69
|
-
overflow: unset;
|
67
|
+
.button-set {
|
68
|
+
display: flex;
|
69
|
+
gap: 0.5rem;
|
70
|
+
justify-content: flex-end;
|
70
71
|
}
|
71
72
|
|
72
|
-
iframe {
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
73
|
+
&.iframe {
|
74
|
+
.kpop-content {
|
75
|
+
overflow: unset;
|
76
|
+
}
|
77
|
+
|
78
|
+
iframe {
|
79
|
+
height: var(--max-height);
|
80
|
+
width: var(--max-width);
|
81
|
+
flex-grow: 1;
|
82
|
+
overflow: scroll;
|
83
|
+
}
|
77
84
|
}
|
78
85
|
}
|
@@ -17,9 +17,8 @@ $mobile-height: 400px !default;
|
|
17
17
|
|
18
18
|
// modal variables
|
19
19
|
$border-radius: 0.5rem !default;
|
20
|
-
$title-background-color: #344055 !default;
|
21
|
-
$title-text-color: white !default;
|
22
20
|
$default-padding: 1rem 1.5rem !default;
|
21
|
+
$keyline-color: #e0e0e0 !default;
|
23
22
|
|
24
23
|
// scrim variables
|
25
24
|
$scrim-background: rgba(0, 0, 0, 0.6) !default;
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kpop
|
4
|
+
module Modal
|
5
|
+
class FooterComponent < ViewComponent::Base
|
6
|
+
include HasHtmlAttributes
|
7
|
+
|
8
|
+
def call
|
9
|
+
tag.div(content, **html_attributes)
|
10
|
+
end
|
11
|
+
|
12
|
+
def inspect
|
13
|
+
"#<#{self.class.name}>"
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_html_attributes
|
17
|
+
{ class: "kpop-footer" }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Kpop
|
4
|
+
module Modal
|
5
|
+
class HeaderComponent < ViewComponent::Base
|
6
|
+
include HasHtmlAttributes
|
7
|
+
|
8
|
+
def call
|
9
|
+
tag.div(content, **html_attributes)
|
10
|
+
end
|
11
|
+
|
12
|
+
def inspect
|
13
|
+
"#<#{self.class.name}>"
|
14
|
+
end
|
15
|
+
|
16
|
+
def default_html_attributes
|
17
|
+
{ class: "kpop-header" }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -5,11 +5,15 @@ module Kpop
|
|
5
5
|
include HasHtmlAttributes
|
6
6
|
|
7
7
|
renders_one :title, "Kpop::Modal::TitleComponent"
|
8
|
-
renders_one :header
|
9
|
-
renders_one :footer
|
8
|
+
renders_one :header, "Kpop::Modal::HeaderComponent"
|
9
|
+
renders_one :footer, "Kpop::Modal::FooterComponent"
|
10
10
|
|
11
|
-
|
12
|
-
|
11
|
+
define_html_attribute_methods :content_attributes
|
12
|
+
|
13
|
+
def initialize(title:, fallback_location: nil, layout: nil, captive: false, **html_attributes)
|
14
|
+
self.content_attributes = html_attributes.delete(:content) if html_attributes.key?(:content)
|
15
|
+
|
16
|
+
super(**html_attributes)
|
13
17
|
|
14
18
|
@fallback_location = fallback_location
|
15
19
|
@layout = layout
|
@@ -18,6 +22,10 @@ module Kpop
|
|
18
22
|
with_title(title:, captive:) if title.present?
|
19
23
|
end
|
20
24
|
|
25
|
+
def with_footer_buttons(**, &)
|
26
|
+
with_footer(class: "button-set", **, &)
|
27
|
+
end
|
28
|
+
|
21
29
|
def inspect
|
22
30
|
"#<#{self.class.name} title: #{title.inspect}>"
|
23
31
|
end
|
@@ -35,5 +43,9 @@ module Kpop
|
|
35
43
|
},
|
36
44
|
}
|
37
45
|
end
|
46
|
+
|
47
|
+
def default_content_attributes
|
48
|
+
{ class: "kpop-content" }
|
49
|
+
end
|
38
50
|
end
|
39
51
|
end
|
@@ -15,22 +15,19 @@ export default class Kpop__FrameController extends Controller {
|
|
15
15
|
this.debug("connect", this.element.src);
|
16
16
|
|
17
17
|
this.element.kpop = this;
|
18
|
-
installNavigationInterception(this.element, this.element.delegate);
|
19
18
|
|
20
|
-
//
|
19
|
+
// allow our code to intercept frame navigation requests before dom changes
|
20
|
+
installNavigationInterception(this);
|
21
|
+
|
21
22
|
if (this.element.src && this.element.complete) {
|
22
23
|
this.debug("new frame modal", this.element.src);
|
23
|
-
|
24
|
-
|
25
|
-
|
24
|
+
FrameModal.connect(this, this.element);
|
25
|
+
} else if (this.modalElements.length > 0) {
|
26
|
+
this.debug("new content modal", window.location.pathname);
|
27
|
+
ContentModal.connect(this, this.element);
|
26
28
|
} else {
|
27
|
-
|
28
|
-
|
29
|
-
);
|
30
|
-
if (element) {
|
31
|
-
this.debug("new content modal", window.location.pathname);
|
32
|
-
this.open(new ContentModal(this.element.id), { animate: false });
|
33
|
-
}
|
29
|
+
this.debug("no modal");
|
30
|
+
this.clear();
|
34
31
|
}
|
35
32
|
}
|
36
33
|
|
@@ -66,6 +63,8 @@ export default class Kpop__FrameController extends Controller {
|
|
66
63
|
return false;
|
67
64
|
}
|
68
65
|
|
66
|
+
await this.dismissing;
|
67
|
+
|
69
68
|
return (this.opening ||= this.#nextFrame(() =>
|
70
69
|
this.#open(modal, { animate })
|
71
70
|
));
|
@@ -77,46 +76,45 @@ export default class Kpop__FrameController extends Controller {
|
|
77
76
|
return false;
|
78
77
|
}
|
79
78
|
|
79
|
+
await this.opening;
|
80
|
+
|
80
81
|
return (this.dismissing ||= this.#nextFrame(() =>
|
81
82
|
this.#dismiss({ animate, reason })
|
82
83
|
));
|
83
84
|
}
|
84
85
|
|
85
|
-
|
86
|
+
async clear() {
|
87
|
+
// clear the src from the frame (if any)
|
88
|
+
this.element.src = "";
|
86
89
|
|
87
|
-
|
88
|
-
this.
|
89
|
-
}
|
90
|
+
// remove any open modal(s)
|
91
|
+
this.modalElements.forEach((element) => element.remove());
|
90
92
|
|
91
|
-
|
92
|
-
this.
|
93
|
+
// mark the modal as hidden (will hide scrim on connect)
|
94
|
+
this.openValue = false;
|
93
95
|
|
94
|
-
//
|
95
|
-
|
96
|
-
|
97
|
-
if (this.element.hasAttribute("busy")) {
|
98
|
-
this.debug("clearing src to cancel turbo request");
|
99
|
-
this.element.src = "";
|
96
|
+
// close the scrim, if connected
|
97
|
+
if (this.scrimConnected) {
|
98
|
+
return this.scrimOutlet.hide({ animate: false });
|
100
99
|
}
|
101
100
|
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
}
|
101
|
+
// unset modal
|
102
|
+
this.modal = null;
|
103
|
+
}
|
106
104
|
|
107
|
-
|
108
|
-
console.warn("kpop: frame src doesn't match window", this.element.src, window.location.href, location);
|
109
|
-
// clear src so that turbo doesn't cache the frame in a loading state
|
110
|
-
this.element.delegate.ignoringChangesToAttribute("src", (() => {
|
111
|
-
this.element.src = "";
|
112
|
-
this.element.delegate.complete = false;
|
113
|
-
}));
|
114
|
-
}
|
105
|
+
// EVENTS
|
115
106
|
|
116
|
-
|
117
|
-
|
107
|
+
popstate(event) {
|
108
|
+
this.modal?.popstate(this, event);
|
118
109
|
}
|
119
110
|
|
111
|
+
/**
|
112
|
+
* Incoming frame render, dismiss the current modal (if any) first.
|
113
|
+
*
|
114
|
+
* We're starting the actual visit
|
115
|
+
*
|
116
|
+
* @param event turbo:before-render
|
117
|
+
*/
|
120
118
|
beforeFrameRender(event) {
|
121
119
|
this.debug("before-frame-render", event.detail.newFrame.baseURI);
|
122
120
|
|
@@ -159,15 +157,25 @@ export default class Kpop__FrameController extends Controller {
|
|
159
157
|
frameLoad(event) {
|
160
158
|
this.debug("frame-load");
|
161
159
|
|
162
|
-
|
163
|
-
|
164
|
-
|
160
|
+
const modal = new FrameModal(this.element.id, this.element.src);
|
161
|
+
|
162
|
+
window.addEventListener(
|
163
|
+
"turbo:visit",
|
164
|
+
(e) => {
|
165
|
+
this.open(modal, { animate: true });
|
166
|
+
},
|
167
|
+
{ once: true }
|
168
|
+
);
|
165
169
|
}
|
166
170
|
|
167
171
|
get isOpen() {
|
168
172
|
return this.openValue && !this.dismissing;
|
169
173
|
}
|
170
174
|
|
175
|
+
get modalElements() {
|
176
|
+
return this.element.querySelectorAll("[data-controller*='kpop--modal']");
|
177
|
+
}
|
178
|
+
|
171
179
|
async #open(modal, { animate = true } = {}) {
|
172
180
|
this.debug("open-start", { animate });
|
173
181
|
|
@@ -226,14 +234,24 @@ export default class Kpop__FrameController extends Controller {
|
|
226
234
|
*
|
227
235
|
* See Turbo issue: https://github.com/hotwired/turbo/issues/1055
|
228
236
|
*
|
229
|
-
* @param
|
237
|
+
* @param controller FrameController
|
230
238
|
*/
|
231
|
-
function installNavigationInterception(
|
232
|
-
|
233
|
-
controller.
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
+
function installNavigationInterception(controller) {
|
240
|
+
const TurboFrameController =
|
241
|
+
controller.element.delegate.constructor.prototype;
|
242
|
+
|
243
|
+
if (TurboFrameController._navigateFrame) return;
|
244
|
+
|
245
|
+
TurboFrameController._navigateFrame = TurboFrameController.navigateFrame;
|
246
|
+
TurboFrameController.navigateFrame = function (element, url, submitter) {
|
247
|
+
const frame = this.findFrameElement(element, submitter);
|
248
|
+
|
249
|
+
if (frame.kpop) {
|
250
|
+
FrameModal.visit(url, frame.kpop, frame, () => {
|
251
|
+
TurboFrameController._navigateFrame.call(this, element, url, submitter);
|
252
|
+
});
|
253
|
+
} else {
|
254
|
+
TurboFrameController._navigateFrame.call(this, element, url, submitter);
|
255
|
+
}
|
256
|
+
};
|
239
257
|
}
|
@@ -3,13 +3,31 @@ import { Turbo } from "@hotwired/turbo-rails";
|
|
3
3
|
import { Modal } from "./modal";
|
4
4
|
|
5
5
|
export class ContentModal extends Modal {
|
6
|
+
static connect(frame, element) {
|
7
|
+
frame.open(new ContentModal(element.id), { animate: false });
|
8
|
+
}
|
9
|
+
|
6
10
|
constructor(id, src = null) {
|
7
11
|
super(id);
|
8
12
|
|
9
13
|
if (src) this.src = src;
|
10
14
|
}
|
11
15
|
|
16
|
+
/**
|
17
|
+
* When the modal is dismissed we can't rely on a back navigation to close the
|
18
|
+
* modal as the user may have navigated to a different location. Instead we
|
19
|
+
* remove the content from the dom and replace the current history state with
|
20
|
+
* the fallback location, if set.
|
21
|
+
*
|
22
|
+
* If there is no fallback location, we may be showing a stream modal that was
|
23
|
+
* injected and cached by turbo. In this case, we clear the frame element and
|
24
|
+
* do not change history.
|
25
|
+
*
|
26
|
+
* @returns {Promise<void>}
|
27
|
+
*/
|
12
28
|
async dismiss() {
|
29
|
+
const fallbackLocation = this.fallbackLocationValue;
|
30
|
+
|
13
31
|
await super.dismiss();
|
14
32
|
|
15
33
|
if (this.visitStarted) {
|
@@ -21,12 +39,11 @@ export class ContentModal extends Modal {
|
|
21
39
|
return;
|
22
40
|
}
|
23
41
|
|
24
|
-
|
25
|
-
this.debug("turbo-visit", this.fallbackLocationValue);
|
26
|
-
Turbo.visit(this.fallbackLocationValue);
|
27
|
-
});
|
42
|
+
this.frameElement.innerHTML = "";
|
28
43
|
|
29
|
-
|
44
|
+
if (fallbackLocation) {
|
45
|
+
window.history.replaceState(window.history.state, "", fallbackLocation);
|
46
|
+
}
|
30
47
|
}
|
31
48
|
|
32
49
|
beforeVisit(frame, e) {
|
@@ -3,11 +3,81 @@ import { Turbo } from "@hotwired/turbo-rails";
|
|
3
3
|
import { Modal } from "./modal";
|
4
4
|
|
5
5
|
export class FrameModal extends Modal {
|
6
|
+
/**
|
7
|
+
* When the FrameController detects a frame element on connect, it runs this
|
8
|
+
* method to santity check the frame src and restore the modal state.
|
9
|
+
*
|
10
|
+
* @param frame FrameController
|
11
|
+
* @param element TurboFrame element
|
12
|
+
*/
|
13
|
+
static connect(frame, element) {
|
14
|
+
const modal = new FrameModal(element.id, element.src);
|
15
|
+
|
16
|
+
// state reconciliation for turbo restore of invalid frames
|
17
|
+
if (modal.isCurrentLocation) {
|
18
|
+
// restoration visit
|
19
|
+
this.debug("restore", element.src);
|
20
|
+
return frame.open(modal, { animate: false });
|
21
|
+
} else {
|
22
|
+
console.warn(
|
23
|
+
"kpop: restored frame src doesn't match window href",
|
24
|
+
modal.src,
|
25
|
+
window.location.href
|
26
|
+
);
|
27
|
+
return frame.clear();
|
28
|
+
}
|
29
|
+
}
|
30
|
+
|
31
|
+
/**
|
32
|
+
* When a user clicks a kpop link, turbo intercepts the click and calls
|
33
|
+
* navigateFrame on the turbo frame controller before setting the TurboFrame
|
34
|
+
* element's src attribute. KPOP intercepts this call and calls this method
|
35
|
+
* first so we cancel problematic navigations that might cache invalid states.
|
36
|
+
*
|
37
|
+
* @param location URL requested by turbo
|
38
|
+
* @param frame FrameController
|
39
|
+
* @param element TurboFrame element
|
40
|
+
* @param resolve continuation chain
|
41
|
+
*/
|
42
|
+
static visit(location, frame, element, resolve) {
|
43
|
+
// Ensure that turbo doesn't cache the frame in a loading state by cancelling
|
44
|
+
// the current request (if any) by clearing the src.
|
45
|
+
// Known issue: this won't work if the frame was previously rendering a useful src.
|
46
|
+
if (element.hasAttribute("busy")) {
|
47
|
+
this.debug("clearing src to cancel turbo request");
|
48
|
+
element.src = "";
|
49
|
+
}
|
50
|
+
|
51
|
+
if (element.src === location) {
|
52
|
+
this.debug("skipping navigate as already on location");
|
53
|
+
return;
|
54
|
+
}
|
55
|
+
|
56
|
+
if (element.src && element.src !== window.location.href) {
|
57
|
+
console.warn(
|
58
|
+
"kpop: frame src doesn't match window",
|
59
|
+
element.src,
|
60
|
+
window.location.href,
|
61
|
+
location
|
62
|
+
);
|
63
|
+
frame.clear();
|
64
|
+
}
|
65
|
+
|
66
|
+
this.debug("navigate to", location);
|
67
|
+
resolve();
|
68
|
+
}
|
69
|
+
|
6
70
|
constructor(id, src) {
|
7
71
|
super(id);
|
8
72
|
this.src = src;
|
9
73
|
}
|
10
74
|
|
75
|
+
/**
|
76
|
+
* FrameModals are closed by running pop state and awaiting the turbo:load
|
77
|
+
* event that follows on history restoration.
|
78
|
+
*
|
79
|
+
* @returns {Promise<void>}
|
80
|
+
*/
|
11
81
|
async dismiss() {
|
12
82
|
await super.dismiss();
|
13
83
|
|
@@ -20,6 +90,13 @@ export class FrameModal extends Modal {
|
|
20
90
|
// no specific close action required, this is turbo's responsibility
|
21
91
|
}
|
22
92
|
|
93
|
+
/**
|
94
|
+
* When user navigates from inside a Frame modal, dismiss the modal first so
|
95
|
+
* that the modal does not appear in the history stack.
|
96
|
+
*
|
97
|
+
* @param frame FrameController
|
98
|
+
* @param e Turbo navigation event
|
99
|
+
*/
|
23
100
|
beforeVisit(frame, e) {
|
24
101
|
super.beforeVisit(frame, e);
|
25
102
|
|
@@ -31,11 +108,4 @@ export class FrameModal extends Modal {
|
|
31
108
|
this.debug("before-visit-end");
|
32
109
|
});
|
33
110
|
}
|
34
|
-
|
35
|
-
popstate(frame, e) {
|
36
|
-
super.popstate(frame, e);
|
37
|
-
|
38
|
-
// Turbo will restore modal state, but we need to reset the scrim
|
39
|
-
frame.scrimOutlet.hide({ animate: false });
|
40
|
-
}
|
41
111
|
}
|
@@ -45,6 +45,10 @@ export class Modal {
|
|
45
45
|
return document.getElementById(this.id);
|
46
46
|
}
|
47
47
|
|
48
|
+
get controller() {
|
49
|
+
return this.frameElement?.kpop;
|
50
|
+
}
|
51
|
+
|
48
52
|
get modalElement() {
|
49
53
|
return this.frameElement?.querySelector("[data-controller*='kpop--modal']");
|
50
54
|
}
|
@@ -54,7 +58,7 @@ export class Modal {
|
|
54
58
|
}
|
55
59
|
|
56
60
|
get fallbackLocationValue() {
|
57
|
-
return this.modalElement?.dataset["kpop-ModalFallbackLocationValue"]
|
61
|
+
return this.modalElement?.dataset["kpop-ModalFallbackLocationValue"];
|
58
62
|
}
|
59
63
|
|
60
64
|
get isCurrentLocation() {
|
@@ -63,6 +67,10 @@ export class Modal {
|
|
63
67
|
);
|
64
68
|
}
|
65
69
|
|
70
|
+
static debug(event, ...args) {
|
71
|
+
if (DEBUG) console.debug(`${this.name}:${event}`, ...args);
|
72
|
+
}
|
73
|
+
|
66
74
|
debug(event, ...args) {
|
67
75
|
if (DEBUG) console.debug(`${this.constructor.name}:${event}`, ...args);
|
68
76
|
}
|
@@ -9,12 +9,24 @@ export class StreamModal extends Modal {
|
|
9
9
|
this.action = action;
|
10
10
|
}
|
11
11
|
|
12
|
+
/**
|
13
|
+
* When the modal opens, push a state event for the current location so that
|
14
|
+
* the user can dismiss the modal by navigating back.
|
15
|
+
*
|
16
|
+
* @returns {Promise<void>}
|
17
|
+
*/
|
12
18
|
async open() {
|
13
19
|
await super.open();
|
14
20
|
|
15
21
|
window.history.pushState({ kpop: true, id: this.id }, "", window.location);
|
16
22
|
}
|
17
23
|
|
24
|
+
/**
|
25
|
+
* On dismiss, pop the state event that was pushed when the modal opened,
|
26
|
+
* then clear any modals from the turbo frame element.
|
27
|
+
*
|
28
|
+
* @returns {Promise<void>}
|
29
|
+
*/
|
18
30
|
async dismiss() {
|
19
31
|
await super.dismiss();
|
20
32
|
|
@@ -25,6 +37,13 @@ export class StreamModal extends Modal {
|
|
25
37
|
this.frameElement.innerHTML = "";
|
26
38
|
}
|
27
39
|
|
40
|
+
/**
|
41
|
+
* On navigation from inside the modal, dismiss the modal first so that the
|
42
|
+
* modal does not appear in the history stack.
|
43
|
+
*
|
44
|
+
* @param frame TurboFrame element
|
45
|
+
* @param e Turbo navigation event
|
46
|
+
*/
|
28
47
|
beforeVisit(frame, e) {
|
29
48
|
super.beforeVisit(frame, e);
|
30
49
|
|
@@ -37,6 +56,12 @@ export class StreamModal extends Modal {
|
|
37
56
|
});
|
38
57
|
}
|
39
58
|
|
59
|
+
/**
|
60
|
+
* If the user pops state, dismiss the modal.
|
61
|
+
*
|
62
|
+
* @param frame FrameController
|
63
|
+
* @param e history event
|
64
|
+
*/
|
40
65
|
popstate(frame, e) {
|
41
66
|
super.popstate(frame, e);
|
42
67
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: katalyst-kpop
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 3.0.0
|
4
|
+
version: 3.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Katalyst Interactive
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2023-11-
|
11
|
+
date: 2023-11-02 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: html-attributes-utils
|
@@ -75,6 +75,8 @@ files:
|
|
75
75
|
- app/components/concerns/kpop/has_html_attributes.rb
|
76
76
|
- app/components/kpop/frame_component.html.erb
|
77
77
|
- app/components/kpop/frame_component.rb
|
78
|
+
- app/components/kpop/modal/footer_component.rb
|
79
|
+
- app/components/kpop/modal/header_component.rb
|
78
80
|
- app/components/kpop/modal/title_component.html.erb
|
79
81
|
- app/components/kpop/modal/title_component.rb
|
80
82
|
- app/components/kpop/modal_component.html.erb
|
@@ -127,9 +129,9 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
127
129
|
version: '3.2'
|
128
130
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
129
131
|
requirements:
|
130
|
-
- - "
|
132
|
+
- - ">="
|
131
133
|
- !ruby/object:Gem::Version
|
132
|
-
version:
|
134
|
+
version: '0'
|
133
135
|
requirements: []
|
134
136
|
rubygems_version: 3.4.20
|
135
137
|
signing_key:
|