katalyst-kpop 3.0.0.beta.1 → 3.0.0.beta.3
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 +128 -127
- data/app/assets/builds/katalyst/kpop.js +128 -127
- data/app/assets/builds/katalyst/kpop.min.js +1 -1
- data/app/assets/builds/katalyst/kpop.min.js.map +1 -1
- data/app/components/kpop/frame_component.rb +1 -0
- data/app/javascript/kpop/application.js +2 -4
- data/app/javascript/kpop/controllers/frame_controller.js +39 -40
- data/app/javascript/kpop/modals/content_modal.js +1 -1
- data/app/javascript/kpop/turbo_actions.js +33 -0
- data/lib/katalyst/kpop/matchers/redirect_matcher.rb +1 -1
- data/lib/katalyst/kpop/matchers.rb +4 -4
- data/lib/katalyst/kpop/turbo.rb +13 -19
- data/lib/katalyst/kpop/version.rb +1 -1
- metadata +4 -6
- data/app/javascript/kpop/controllers/close_controller.js +0 -9
- data/app/javascript/kpop/controllers/redirect_controller.js +0 -22
- data/lib/katalyst/kpop/matchers/redirect_finder.rb +0 -16
- /data/app/javascript/kpop/{modals → utils}/stream_renderer.js +0 -0
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: e8895a41210047c88dc97b8e835bb7fa9ee830adca410ddb8d4234c6a5b34ce0
|
4
|
+
data.tar.gz: 1dcab15d941c4bb08a89381662daf0bbb72bc2c07d166019dc23d01901c3c522
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 5ad7d38939dd502cc3b1a558cc9f1d1afdbf32645c53adcd11d5f7b59bf45e5d88a6bec569cdc5112b331e274a5e986c44ad31404695c03d0389559a5d378b0a
|
7
|
+
data.tar.gz: a69d4f6299d6e21f5115078e4c75bb98946bffd7cddcd13d17e8d3d77a0e41839e314b3c85a1a2bceed4c4842496cc71786d150bed8ce022e8258061a1b5aacc
|
@@ -1,14 +1,6 @@
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
2
2
|
import { Turbo } from '@hotwired/turbo-rails';
|
3
3
|
|
4
|
-
class Kpop__CloseController extends Controller {
|
5
|
-
static outlets = ["kpop--frame"];
|
6
|
-
|
7
|
-
kpopFrameOutletConnected(frame) {
|
8
|
-
frame.dismiss();
|
9
|
-
}
|
10
|
-
}
|
11
|
-
|
12
4
|
class Modal {
|
13
5
|
constructor(id) {
|
14
6
|
this.id = id;
|
@@ -95,7 +87,7 @@ class ContentModal extends Modal {
|
|
95
87
|
|
96
88
|
return this.pop("turbo:load", () => {
|
97
89
|
this.debug("turbo-visit", this.fallbackLocationValue);
|
98
|
-
Turbo.visit(this.fallbackLocationValue
|
90
|
+
Turbo.visit(this.fallbackLocationValue);
|
99
91
|
});
|
100
92
|
|
101
93
|
// no specific close action required, this is turbo's responsibility
|
@@ -155,79 +147,6 @@ class FrameModal extends Modal {
|
|
155
147
|
}
|
156
148
|
}
|
157
149
|
|
158
|
-
class StreamModal extends Modal {
|
159
|
-
constructor(id, action) {
|
160
|
-
super(id);
|
161
|
-
|
162
|
-
this.action = action;
|
163
|
-
}
|
164
|
-
|
165
|
-
async open() {
|
166
|
-
await super.open();
|
167
|
-
|
168
|
-
window.history.pushState({ kpop: true, id: this.id }, "", window.location);
|
169
|
-
}
|
170
|
-
|
171
|
-
async dismiss() {
|
172
|
-
await super.dismiss();
|
173
|
-
|
174
|
-
if (this.isCurrentLocation) {
|
175
|
-
await this.pop("popstate", () => window.history.back());
|
176
|
-
}
|
177
|
-
|
178
|
-
this.frameElement.innerHTML = "";
|
179
|
-
}
|
180
|
-
|
181
|
-
beforeVisit(frame, e) {
|
182
|
-
super.beforeVisit(frame, e);
|
183
|
-
|
184
|
-
e.preventDefault();
|
185
|
-
|
186
|
-
frame.dismiss({ animate: false }).then(() => {
|
187
|
-
Turbo.visit(e.detail.url);
|
188
|
-
|
189
|
-
this.debug("before-visit-end");
|
190
|
-
});
|
191
|
-
}
|
192
|
-
|
193
|
-
popstate(frame, e) {
|
194
|
-
super.popstate(frame, e);
|
195
|
-
|
196
|
-
frame.dismiss({ animate: true, reason: "popstate" });
|
197
|
-
}
|
198
|
-
|
199
|
-
get isCurrentLocation() {
|
200
|
-
return window.history.state?.kpop && window.history.state?.id === this.id;
|
201
|
-
}
|
202
|
-
}
|
203
|
-
|
204
|
-
class StreamRenderer {
|
205
|
-
constructor(frame, action) {
|
206
|
-
this.frame = frame;
|
207
|
-
this.action = action;
|
208
|
-
}
|
209
|
-
|
210
|
-
render() {
|
211
|
-
this.frame.src = "";
|
212
|
-
this.frame.innerHTML = "";
|
213
|
-
this.frame.append(this.action.templateContent);
|
214
|
-
}
|
215
|
-
}
|
216
|
-
|
217
|
-
Turbo.StreamActions.kpop_open = function () {
|
218
|
-
const frame = () => {
|
219
|
-
return this.targetElements[0];
|
220
|
-
};
|
221
|
-
const animate = !frame?.kpop?.openValue;
|
222
|
-
|
223
|
-
frame()
|
224
|
-
.kpop.dismiss({ animate, reason: "before-turbo-stream" })
|
225
|
-
.then(() => {
|
226
|
-
new StreamRenderer(frame(), this).render();
|
227
|
-
frame().kpop.open(new StreamModal(this.target, this), { animate });
|
228
|
-
});
|
229
|
-
};
|
230
|
-
|
231
150
|
class Kpop__FrameController extends Controller {
|
232
151
|
static outlets = ["scrim"];
|
233
152
|
static targets = ["modal"];
|
@@ -269,7 +188,11 @@ class Kpop__FrameController extends Controller {
|
|
269
188
|
|
270
189
|
this.scrimConnected = true;
|
271
190
|
|
272
|
-
if (this.openValue)
|
191
|
+
if (this.openValue) {
|
192
|
+
scrim.show({ animate: false });
|
193
|
+
} else {
|
194
|
+
scrim.hide({ animate: false });
|
195
|
+
}
|
273
196
|
}
|
274
197
|
|
275
198
|
openValueChanged(open) {
|
@@ -281,14 +204,13 @@ class Kpop__FrameController extends Controller {
|
|
281
204
|
async open(modal, { animate = true } = {}) {
|
282
205
|
if (this.isOpen) {
|
283
206
|
this.debug("skip open as already open");
|
207
|
+
this.modal ||= modal;
|
284
208
|
return false;
|
285
209
|
}
|
286
210
|
|
287
|
-
this.opening ||= this.#
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
return this.opening;
|
211
|
+
return (this.opening ||= this.#nextFrame(() =>
|
212
|
+
this.#open(modal, { animate })
|
213
|
+
));
|
292
214
|
}
|
293
215
|
|
294
216
|
async dismiss({ animate = true, reason = "" } = {}) {
|
@@ -297,15 +219,9 @@ class Kpop__FrameController extends Controller {
|
|
297
219
|
return false;
|
298
220
|
}
|
299
221
|
|
300
|
-
this.dismissing ||= this.#
|
301
|
-
|
302
|
-
|
303
|
-
return this.#nextAnimationFrame(resolve);
|
304
|
-
});
|
305
|
-
});
|
306
|
-
});
|
307
|
-
|
308
|
-
return this.dismissing;
|
222
|
+
return (this.dismissing ||= this.#nextFrame(() =>
|
223
|
+
this.#dismiss({ animate, reason })
|
224
|
+
));
|
309
225
|
}
|
310
226
|
|
311
227
|
// EVENTS
|
@@ -320,10 +236,27 @@ class Kpop__FrameController extends Controller {
|
|
320
236
|
event.preventDefault();
|
321
237
|
|
322
238
|
this.dismiss({ animate: true, reason: "before-frame-render" }).then(() => {
|
239
|
+
this.debug("resume-frame-render", event.detail.newFrame.baseURI);
|
323
240
|
event.detail.resume();
|
324
241
|
});
|
325
242
|
}
|
326
243
|
|
244
|
+
beforeStreamRender(event) {
|
245
|
+
this.debug("before-stream-render", event.detail);
|
246
|
+
|
247
|
+
const resume = event.detail.render;
|
248
|
+
|
249
|
+
// Defer rendering until dismiss is complete.
|
250
|
+
// Dismiss may change history so we need to wait for it to complete to avoid
|
251
|
+
// losing DOM changes on restoration visits.
|
252
|
+
event.detail.render = (stream) => {
|
253
|
+
(this.dismissing || Promise.resolve()).then(() => {
|
254
|
+
this.debug("stream-render", stream);
|
255
|
+
resume(stream);
|
256
|
+
});
|
257
|
+
};
|
258
|
+
}
|
259
|
+
|
327
260
|
beforeVisit(e) {
|
328
261
|
this.debug("before-visit", e.detail.url);
|
329
262
|
|
@@ -356,8 +289,8 @@ class Kpop__FrameController extends Controller {
|
|
356
289
|
this.modal = modal;
|
357
290
|
this.openValue = true;
|
358
291
|
|
359
|
-
modal.open({ animate });
|
360
|
-
scrim?.show({ animate });
|
292
|
+
await modal.open({ animate });
|
293
|
+
await scrim?.show({ animate });
|
361
294
|
|
362
295
|
delete this.opening;
|
363
296
|
|
@@ -367,6 +300,12 @@ class Kpop__FrameController extends Controller {
|
|
367
300
|
async #dismiss({ animate = true, reason = "" } = {}) {
|
368
301
|
this.debug("dismiss-start", { animate, reason });
|
369
302
|
|
303
|
+
// if this element is detached then we've experienced a turbo navigation
|
304
|
+
if (!this.element.isConnected) {
|
305
|
+
this.debug("skip dismiss, element detached");
|
306
|
+
return;
|
307
|
+
}
|
308
|
+
|
370
309
|
if (!this.modal) {
|
371
310
|
console.warn("modal missing on dismiss");
|
372
311
|
}
|
@@ -381,12 +320,8 @@ class Kpop__FrameController extends Controller {
|
|
381
320
|
this.debug("dismiss-end");
|
382
321
|
}
|
383
322
|
|
384
|
-
#
|
385
|
-
return new Promise((
|
386
|
-
window.requestAnimationFrame(() => {
|
387
|
-
resolve(callback());
|
388
|
-
});
|
389
|
-
});
|
323
|
+
async #nextFrame(callback) {
|
324
|
+
return new Promise(window.requestAnimationFrame).then(callback);
|
390
325
|
}
|
391
326
|
|
392
327
|
debug(event, ...args) {
|
@@ -419,26 +354,6 @@ class Kpop__ModalController extends Controller {
|
|
419
354
|
}
|
420
355
|
}
|
421
356
|
|
422
|
-
class Kpop__RedirectController extends Controller {
|
423
|
-
static outlets = ["kpop--frame"];
|
424
|
-
static values = {
|
425
|
-
path: String,
|
426
|
-
target: String,
|
427
|
-
};
|
428
|
-
|
429
|
-
kpopFrameOutletConnected(frame) {
|
430
|
-
if (this.targetValue === frame.element.id) {
|
431
|
-
frame.dismiss().then(() => {
|
432
|
-
document.getElementById(this.targetValue).src = this.pathValue;
|
433
|
-
});
|
434
|
-
} else {
|
435
|
-
Turbo.visit(this.pathValue, { action: "replace" });
|
436
|
-
}
|
437
|
-
|
438
|
-
this.element.remove();
|
439
|
-
}
|
440
|
-
}
|
441
|
-
|
442
357
|
/**
|
443
358
|
* Scrim controller wraps an element that creates a whole page layer.
|
444
359
|
* It is intended to be used behind a modal or nav drawer.
|
@@ -588,11 +503,97 @@ function resetStyle(element, property, previousValue) {
|
|
588
503
|
}
|
589
504
|
}
|
590
505
|
|
506
|
+
class StreamModal extends Modal {
|
507
|
+
constructor(id, action) {
|
508
|
+
super(id);
|
509
|
+
|
510
|
+
this.action = action;
|
511
|
+
}
|
512
|
+
|
513
|
+
async open() {
|
514
|
+
await super.open();
|
515
|
+
|
516
|
+
window.history.pushState({ kpop: true, id: this.id }, "", window.location);
|
517
|
+
}
|
518
|
+
|
519
|
+
async dismiss() {
|
520
|
+
await super.dismiss();
|
521
|
+
|
522
|
+
if (this.isCurrentLocation) {
|
523
|
+
await this.pop("popstate", () => window.history.back());
|
524
|
+
}
|
525
|
+
|
526
|
+
this.frameElement.innerHTML = "";
|
527
|
+
}
|
528
|
+
|
529
|
+
beforeVisit(frame, e) {
|
530
|
+
super.beforeVisit(frame, e);
|
531
|
+
|
532
|
+
e.preventDefault();
|
533
|
+
|
534
|
+
frame.dismiss({ animate: false }).then(() => {
|
535
|
+
Turbo.visit(e.detail.url);
|
536
|
+
|
537
|
+
this.debug("before-visit-end");
|
538
|
+
});
|
539
|
+
}
|
540
|
+
|
541
|
+
popstate(frame, e) {
|
542
|
+
super.popstate(frame, e);
|
543
|
+
|
544
|
+
frame.dismiss({ animate: true, reason: "popstate" });
|
545
|
+
}
|
546
|
+
|
547
|
+
get isCurrentLocation() {
|
548
|
+
return window.history.state?.kpop && window.history.state?.id === this.id;
|
549
|
+
}
|
550
|
+
}
|
551
|
+
|
552
|
+
class StreamRenderer {
|
553
|
+
constructor(frame, action) {
|
554
|
+
this.frame = frame;
|
555
|
+
this.action = action;
|
556
|
+
}
|
557
|
+
|
558
|
+
render() {
|
559
|
+
this.frame.src = "";
|
560
|
+
this.frame.innerHTML = "";
|
561
|
+
this.frame.append(this.action.templateContent);
|
562
|
+
}
|
563
|
+
}
|
564
|
+
|
565
|
+
function kpop(action) {
|
566
|
+
return action.targetElements[0]?.kpop;
|
567
|
+
}
|
568
|
+
|
569
|
+
Turbo.StreamActions.kpop_open = function () {
|
570
|
+
const animate = !kpop(this).openValue;
|
571
|
+
|
572
|
+
kpop(this)
|
573
|
+
?.dismiss({ animate, reason: "before-turbo-stream" })
|
574
|
+
.then(() => {
|
575
|
+
new StreamRenderer(this.targetElements[0], this).render();
|
576
|
+
kpop(this)?.open(new StreamModal(this.target, this), { animate });
|
577
|
+
});
|
578
|
+
};
|
579
|
+
|
580
|
+
Turbo.StreamActions.kpop_dismiss = function () {
|
581
|
+
kpop(this)?.dismiss({ reason: "turbo_stream.kpop.dismiss" });
|
582
|
+
};
|
583
|
+
|
584
|
+
Turbo.StreamActions.kpop_redirect_to = function () {
|
585
|
+
if (this.dataset.turboFrame === this.target) {
|
586
|
+
this.targetElements[0].src = this.getAttribute("href");
|
587
|
+
} else {
|
588
|
+
Turbo.visit(this.getAttribute("href"), {
|
589
|
+
action: this.dataset.turboAction,
|
590
|
+
});
|
591
|
+
}
|
592
|
+
};
|
593
|
+
|
591
594
|
const Definitions = [
|
592
|
-
{ identifier: "kpop--close", controllerConstructor: Kpop__CloseController },
|
593
595
|
{ identifier: "kpop--frame", controllerConstructor: Kpop__FrameController },
|
594
596
|
{ identifier: "kpop--modal", controllerConstructor: Kpop__ModalController },
|
595
|
-
{ identifier: "kpop--redirect", controllerConstructor: Kpop__RedirectController },
|
596
597
|
{ identifier: "scrim", controllerConstructor: ScrimController },
|
597
598
|
];
|
598
599
|
|
@@ -1,14 +1,6 @@
|
|
1
1
|
import { Controller } from '@hotwired/stimulus';
|
2
2
|
import { Turbo } from '@hotwired/turbo-rails';
|
3
3
|
|
4
|
-
class Kpop__CloseController extends Controller {
|
5
|
-
static outlets = ["kpop--frame"];
|
6
|
-
|
7
|
-
kpopFrameOutletConnected(frame) {
|
8
|
-
frame.dismiss();
|
9
|
-
}
|
10
|
-
}
|
11
|
-
|
12
4
|
class Modal {
|
13
5
|
constructor(id) {
|
14
6
|
this.id = id;
|
@@ -95,7 +87,7 @@ class ContentModal extends Modal {
|
|
95
87
|
|
96
88
|
return this.pop("turbo:load", () => {
|
97
89
|
this.debug("turbo-visit", this.fallbackLocationValue);
|
98
|
-
Turbo.visit(this.fallbackLocationValue
|
90
|
+
Turbo.visit(this.fallbackLocationValue);
|
99
91
|
});
|
100
92
|
|
101
93
|
// no specific close action required, this is turbo's responsibility
|
@@ -155,79 +147,6 @@ class FrameModal extends Modal {
|
|
155
147
|
}
|
156
148
|
}
|
157
149
|
|
158
|
-
class StreamModal extends Modal {
|
159
|
-
constructor(id, action) {
|
160
|
-
super(id);
|
161
|
-
|
162
|
-
this.action = action;
|
163
|
-
}
|
164
|
-
|
165
|
-
async open() {
|
166
|
-
await super.open();
|
167
|
-
|
168
|
-
window.history.pushState({ kpop: true, id: this.id }, "", window.location);
|
169
|
-
}
|
170
|
-
|
171
|
-
async dismiss() {
|
172
|
-
await super.dismiss();
|
173
|
-
|
174
|
-
if (this.isCurrentLocation) {
|
175
|
-
await this.pop("popstate", () => window.history.back());
|
176
|
-
}
|
177
|
-
|
178
|
-
this.frameElement.innerHTML = "";
|
179
|
-
}
|
180
|
-
|
181
|
-
beforeVisit(frame, e) {
|
182
|
-
super.beforeVisit(frame, e);
|
183
|
-
|
184
|
-
e.preventDefault();
|
185
|
-
|
186
|
-
frame.dismiss({ animate: false }).then(() => {
|
187
|
-
Turbo.visit(e.detail.url);
|
188
|
-
|
189
|
-
this.debug("before-visit-end");
|
190
|
-
});
|
191
|
-
}
|
192
|
-
|
193
|
-
popstate(frame, e) {
|
194
|
-
super.popstate(frame, e);
|
195
|
-
|
196
|
-
frame.dismiss({ animate: true, reason: "popstate" });
|
197
|
-
}
|
198
|
-
|
199
|
-
get isCurrentLocation() {
|
200
|
-
return window.history.state?.kpop && window.history.state?.id === this.id;
|
201
|
-
}
|
202
|
-
}
|
203
|
-
|
204
|
-
class StreamRenderer {
|
205
|
-
constructor(frame, action) {
|
206
|
-
this.frame = frame;
|
207
|
-
this.action = action;
|
208
|
-
}
|
209
|
-
|
210
|
-
render() {
|
211
|
-
this.frame.src = "";
|
212
|
-
this.frame.innerHTML = "";
|
213
|
-
this.frame.append(this.action.templateContent);
|
214
|
-
}
|
215
|
-
}
|
216
|
-
|
217
|
-
Turbo.StreamActions.kpop_open = function () {
|
218
|
-
const frame = () => {
|
219
|
-
return this.targetElements[0];
|
220
|
-
};
|
221
|
-
const animate = !frame?.kpop?.openValue;
|
222
|
-
|
223
|
-
frame()
|
224
|
-
.kpop.dismiss({ animate, reason: "before-turbo-stream" })
|
225
|
-
.then(() => {
|
226
|
-
new StreamRenderer(frame(), this).render();
|
227
|
-
frame().kpop.open(new StreamModal(this.target, this), { animate });
|
228
|
-
});
|
229
|
-
};
|
230
|
-
|
231
150
|
class Kpop__FrameController extends Controller {
|
232
151
|
static outlets = ["scrim"];
|
233
152
|
static targets = ["modal"];
|
@@ -269,7 +188,11 @@ class Kpop__FrameController extends Controller {
|
|
269
188
|
|
270
189
|
this.scrimConnected = true;
|
271
190
|
|
272
|
-
if (this.openValue)
|
191
|
+
if (this.openValue) {
|
192
|
+
scrim.show({ animate: false });
|
193
|
+
} else {
|
194
|
+
scrim.hide({ animate: false });
|
195
|
+
}
|
273
196
|
}
|
274
197
|
|
275
198
|
openValueChanged(open) {
|
@@ -281,14 +204,13 @@ class Kpop__FrameController extends Controller {
|
|
281
204
|
async open(modal, { animate = true } = {}) {
|
282
205
|
if (this.isOpen) {
|
283
206
|
this.debug("skip open as already open");
|
207
|
+
this.modal ||= modal;
|
284
208
|
return false;
|
285
209
|
}
|
286
210
|
|
287
|
-
this.opening ||= this.#
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
return this.opening;
|
211
|
+
return (this.opening ||= this.#nextFrame(() =>
|
212
|
+
this.#open(modal, { animate })
|
213
|
+
));
|
292
214
|
}
|
293
215
|
|
294
216
|
async dismiss({ animate = true, reason = "" } = {}) {
|
@@ -297,15 +219,9 @@ class Kpop__FrameController extends Controller {
|
|
297
219
|
return false;
|
298
220
|
}
|
299
221
|
|
300
|
-
this.dismissing ||= this.#
|
301
|
-
|
302
|
-
|
303
|
-
return this.#nextAnimationFrame(resolve);
|
304
|
-
});
|
305
|
-
});
|
306
|
-
});
|
307
|
-
|
308
|
-
return this.dismissing;
|
222
|
+
return (this.dismissing ||= this.#nextFrame(() =>
|
223
|
+
this.#dismiss({ animate, reason })
|
224
|
+
));
|
309
225
|
}
|
310
226
|
|
311
227
|
// EVENTS
|
@@ -320,10 +236,27 @@ class Kpop__FrameController extends Controller {
|
|
320
236
|
event.preventDefault();
|
321
237
|
|
322
238
|
this.dismiss({ animate: true, reason: "before-frame-render" }).then(() => {
|
239
|
+
this.debug("resume-frame-render", event.detail.newFrame.baseURI);
|
323
240
|
event.detail.resume();
|
324
241
|
});
|
325
242
|
}
|
326
243
|
|
244
|
+
beforeStreamRender(event) {
|
245
|
+
this.debug("before-stream-render", event.detail);
|
246
|
+
|
247
|
+
const resume = event.detail.render;
|
248
|
+
|
249
|
+
// Defer rendering until dismiss is complete.
|
250
|
+
// Dismiss may change history so we need to wait for it to complete to avoid
|
251
|
+
// losing DOM changes on restoration visits.
|
252
|
+
event.detail.render = (stream) => {
|
253
|
+
(this.dismissing || Promise.resolve()).then(() => {
|
254
|
+
this.debug("stream-render", stream);
|
255
|
+
resume(stream);
|
256
|
+
});
|
257
|
+
};
|
258
|
+
}
|
259
|
+
|
327
260
|
beforeVisit(e) {
|
328
261
|
this.debug("before-visit", e.detail.url);
|
329
262
|
|
@@ -356,8 +289,8 @@ class Kpop__FrameController extends Controller {
|
|
356
289
|
this.modal = modal;
|
357
290
|
this.openValue = true;
|
358
291
|
|
359
|
-
modal.open({ animate });
|
360
|
-
scrim?.show({ animate });
|
292
|
+
await modal.open({ animate });
|
293
|
+
await scrim?.show({ animate });
|
361
294
|
|
362
295
|
delete this.opening;
|
363
296
|
|
@@ -367,6 +300,12 @@ class Kpop__FrameController extends Controller {
|
|
367
300
|
async #dismiss({ animate = true, reason = "" } = {}) {
|
368
301
|
this.debug("dismiss-start", { animate, reason });
|
369
302
|
|
303
|
+
// if this element is detached then we've experienced a turbo navigation
|
304
|
+
if (!this.element.isConnected) {
|
305
|
+
this.debug("skip dismiss, element detached");
|
306
|
+
return;
|
307
|
+
}
|
308
|
+
|
370
309
|
if (!this.modal) {
|
371
310
|
console.warn("modal missing on dismiss");
|
372
311
|
}
|
@@ -381,12 +320,8 @@ class Kpop__FrameController extends Controller {
|
|
381
320
|
this.debug("dismiss-end");
|
382
321
|
}
|
383
322
|
|
384
|
-
#
|
385
|
-
return new Promise((
|
386
|
-
window.requestAnimationFrame(() => {
|
387
|
-
resolve(callback());
|
388
|
-
});
|
389
|
-
});
|
323
|
+
async #nextFrame(callback) {
|
324
|
+
return new Promise(window.requestAnimationFrame).then(callback);
|
390
325
|
}
|
391
326
|
|
392
327
|
debug(event, ...args) {
|
@@ -419,26 +354,6 @@ class Kpop__ModalController extends Controller {
|
|
419
354
|
}
|
420
355
|
}
|
421
356
|
|
422
|
-
class Kpop__RedirectController extends Controller {
|
423
|
-
static outlets = ["kpop--frame"];
|
424
|
-
static values = {
|
425
|
-
path: String,
|
426
|
-
target: String,
|
427
|
-
};
|
428
|
-
|
429
|
-
kpopFrameOutletConnected(frame) {
|
430
|
-
if (this.targetValue === frame.element.id) {
|
431
|
-
frame.dismiss().then(() => {
|
432
|
-
document.getElementById(this.targetValue).src = this.pathValue;
|
433
|
-
});
|
434
|
-
} else {
|
435
|
-
Turbo.visit(this.pathValue, { action: "replace" });
|
436
|
-
}
|
437
|
-
|
438
|
-
this.element.remove();
|
439
|
-
}
|
440
|
-
}
|
441
|
-
|
442
357
|
/**
|
443
358
|
* Scrim controller wraps an element that creates a whole page layer.
|
444
359
|
* It is intended to be used behind a modal or nav drawer.
|
@@ -588,11 +503,97 @@ function resetStyle(element, property, previousValue) {
|
|
588
503
|
}
|
589
504
|
}
|
590
505
|
|
506
|
+
class StreamModal extends Modal {
|
507
|
+
constructor(id, action) {
|
508
|
+
super(id);
|
509
|
+
|
510
|
+
this.action = action;
|
511
|
+
}
|
512
|
+
|
513
|
+
async open() {
|
514
|
+
await super.open();
|
515
|
+
|
516
|
+
window.history.pushState({ kpop: true, id: this.id }, "", window.location);
|
517
|
+
}
|
518
|
+
|
519
|
+
async dismiss() {
|
520
|
+
await super.dismiss();
|
521
|
+
|
522
|
+
if (this.isCurrentLocation) {
|
523
|
+
await this.pop("popstate", () => window.history.back());
|
524
|
+
}
|
525
|
+
|
526
|
+
this.frameElement.innerHTML = "";
|
527
|
+
}
|
528
|
+
|
529
|
+
beforeVisit(frame, e) {
|
530
|
+
super.beforeVisit(frame, e);
|
531
|
+
|
532
|
+
e.preventDefault();
|
533
|
+
|
534
|
+
frame.dismiss({ animate: false }).then(() => {
|
535
|
+
Turbo.visit(e.detail.url);
|
536
|
+
|
537
|
+
this.debug("before-visit-end");
|
538
|
+
});
|
539
|
+
}
|
540
|
+
|
541
|
+
popstate(frame, e) {
|
542
|
+
super.popstate(frame, e);
|
543
|
+
|
544
|
+
frame.dismiss({ animate: true, reason: "popstate" });
|
545
|
+
}
|
546
|
+
|
547
|
+
get isCurrentLocation() {
|
548
|
+
return window.history.state?.kpop && window.history.state?.id === this.id;
|
549
|
+
}
|
550
|
+
}
|
551
|
+
|
552
|
+
class StreamRenderer {
|
553
|
+
constructor(frame, action) {
|
554
|
+
this.frame = frame;
|
555
|
+
this.action = action;
|
556
|
+
}
|
557
|
+
|
558
|
+
render() {
|
559
|
+
this.frame.src = "";
|
560
|
+
this.frame.innerHTML = "";
|
561
|
+
this.frame.append(this.action.templateContent);
|
562
|
+
}
|
563
|
+
}
|
564
|
+
|
565
|
+
function kpop(action) {
|
566
|
+
return action.targetElements[0]?.kpop;
|
567
|
+
}
|
568
|
+
|
569
|
+
Turbo.StreamActions.kpop_open = function () {
|
570
|
+
const animate = !kpop(this).openValue;
|
571
|
+
|
572
|
+
kpop(this)
|
573
|
+
?.dismiss({ animate, reason: "before-turbo-stream" })
|
574
|
+
.then(() => {
|
575
|
+
new StreamRenderer(this.targetElements[0], this).render();
|
576
|
+
kpop(this)?.open(new StreamModal(this.target, this), { animate });
|
577
|
+
});
|
578
|
+
};
|
579
|
+
|
580
|
+
Turbo.StreamActions.kpop_dismiss = function () {
|
581
|
+
kpop(this)?.dismiss({ reason: "turbo_stream.kpop.dismiss" });
|
582
|
+
};
|
583
|
+
|
584
|
+
Turbo.StreamActions.kpop_redirect_to = function () {
|
585
|
+
if (this.dataset.turboFrame === this.target) {
|
586
|
+
this.targetElements[0].src = this.getAttribute("href");
|
587
|
+
} else {
|
588
|
+
Turbo.visit(this.getAttribute("href"), {
|
589
|
+
action: this.dataset.turboAction,
|
590
|
+
});
|
591
|
+
}
|
592
|
+
};
|
593
|
+
|
591
594
|
const Definitions = [
|
592
|
-
{ identifier: "kpop--close", controllerConstructor: Kpop__CloseController },
|
593
595
|
{ identifier: "kpop--frame", controllerConstructor: Kpop__FrameController },
|
594
596
|
{ identifier: "kpop--modal", controllerConstructor: Kpop__ModalController },
|
595
|
-
{ identifier: "kpop--redirect", controllerConstructor: Kpop__RedirectController },
|
596
597
|
{ identifier: "scrim", controllerConstructor: ScrimController },
|
597
598
|
];
|
598
599
|
|
@@ -1,2 +1,2 @@
|
|
1
|
-
import{Controller as e}from"@hotwired/stimulus";import{Turbo as t}from"@hotwired/turbo-rails";class
|
1
|
+
import{Controller as e}from"@hotwired/stimulus";import{Turbo as t}from"@hotwired/turbo-rails";class s{constructor(e){this.id=e}async open(){this.debug("open")}async dismiss(){this.debug("dismiss")}beforeVisit(e,t){this.debug("before-visit",t.detail.url)}popstate(e,t){this.debug("popstate",t.state)}async pop(e,t){this.debug("pop");const s=new Promise((t=>{window.addEventListener(e,(()=>{t()}),{once:!0})}));return t(),s}get frameElement(){return document.getElementById(this.id)}get modalElement(){return this.frameElement?.querySelector("[data-controller*='kpop--modal']")}get currentLocationValue(){return this.modalElement?.dataset["kpop-ModalCurrentLocationValue"]||"/"}get fallbackLocationValue(){return this.modalElement?.dataset["kpop-ModalFallbackLocationValue"]||"/"}get isCurrentLocation(){return window.history.state?.turbo&&t.session.location.href===this.src}debug(e,...t){}}class i extends s{constructor(e,t=null){super(e),t&&(this.src=t)}async dismiss(){if(await super.dismiss(),this.visitStarted)this.debug("skipping dismiss, visit started");else{if(this.isCurrentLocation)return this.pop("turbo:load",(()=>{this.debug("turbo-visit",this.fallbackLocationValue),t.visit(this.fallbackLocationValue)}));this.debug("skipping dismiss, not current location")}}beforeVisit(e,t){super.beforeVisit(e,t),this.visitStarted=!0,e.scrimOutlet.hide({animate:!1})}get src(){return new URL(this.currentLocationValue.toString(),document.baseURI).toString()}}class n extends s{constructor(e,t){super(e),this.src=t}async dismiss(){await super.dismiss(),this.isCurrentLocation||this.debug("skipping dismiss, not current location"),await this.pop("turbo:load",(()=>window.history.back()))}beforeVisit(e,s){super.beforeVisit(e,s),s.preventDefault(),e.dismiss({animate:!1}).then((()=>{t.visit(s.detail.url),this.debug("before-visit-end")}))}popstate(e,t){super.popstate(e,t),e.scrimOutlet.hide({animate:!1})}}class a extends e{static outlets=["scrim"];static targets=["modal"];static values={open:Boolean};connect(){if(this.debug("connect",this.element.src),this.element.kpop=this,this.element.src&&this.element.complete)this.debug("new frame modal",this.element.src),this.open(new n(this.element.id,this.element.src),{animate:!1});else{this.element.querySelector("[data-controller*='kpop--modal']")&&(this.debug("new content modal",window.location.pathname),this.open(new i(this.element.id),{animate:!1}))}}disconnect(){this.debug("disconnect"),delete this.element.kpop,delete this.modal}scrimOutletConnected(e){this.debug("scrim-connected"),this.scrimConnected=!0,this.openValue?e.show({animate:!1}):e.hide({animate:!1})}openValueChanged(e){this.debug("open-changed",e),this.element.parentElement.style.display=e?"flex":"none"}async open(e,{animate:t=!0}={}){return this.isOpen?(this.debug("skip open as already open"),this.modal||=e,!1):this.opening||=this.#e((()=>this.#t(e,{animate:t})))}async dismiss({animate:e=!0,reason:t=""}={}){return this.isOpen?this.dismissing||=this.#e((()=>this.#s({animate:e,reason:t}))):(this.debug("skip dismiss as already closed"),!1)}popstate(e){this.modal?.popstate(this,e)}beforeFrameRender(e){this.debug("before-frame-render",e.detail.newFrame.baseURI),e.preventDefault(),this.dismiss({animate:!0,reason:"before-frame-render"}).then((()=>{this.debug("resume-frame-render",e.detail.newFrame.baseURI),e.detail.resume()}))}beforeStreamRender(e){this.debug("before-stream-render",e.detail);const t=e.detail.render;e.detail.render=e=>{(this.dismissing||Promise.resolve()).then((()=>{this.debug("stream-render",e),t(e)}))}}beforeVisit(e){this.debug("before-visit",e.detail.url),e.detail.url!==this.element.src&&this.isOpen&&this.modal.beforeVisit(this,e)}frameLoad(e){return this.debug("frame-load"),this.open(new n(this.element.id,this.element.src),{animate:!0})}get isOpen(){return this.openValue&&!this.dismissing}async#t(e,{animate:t=!0}={}){this.debug("open-start",{animate:t});const s=this.scrimConnected&&this.scrimOutlet;this.modal=e,this.openValue=!0,await e.open({animate:t}),await(s?.show({animate:t})),delete this.opening,this.debug("open-end")}async#s({animate:e=!0,reason:t=""}={}){this.debug("dismiss-start",{animate:e,reason:t}),this.element.isConnected?(this.modal||console.warn("modal missing on dismiss"),await this.scrimOutlet.hide({animate:e}),await(this.modal?.dismiss()),this.openValue=!1,this.modal=null,delete this.dismissing,this.debug("dismiss-end")):this.debug("skip dismiss, element detached")}async#e(e){return new Promise(window.requestAnimationFrame).then(e)}debug(e,...t){}}class o extends e{static values={open:Boolean,captive:Boolean,zIndex:Number};connect(){this.defaultZIndexValue=this.zIndexValue,this.defaultCaptiveValue=this.captiveValue,this.element.scrim=this}disconnect(){delete this.element.scrim}async show({captive:e=this.defaultCaptiveValue,zIndex:t=this.defaultZIndexValue,top:s=window.scrollY,animate:i=!0}={}){this.openValue&&await this.hide({animate:i}),this.openValue=!0,this.dispatch("show",{bubbles:!0}),this.#i(e,t,s),i&&(this.element.dataset.showAnimating="",await new Promise((e=>{this.element.addEventListener("animationend",(()=>e()),{once:!0})})),delete this.element.dataset.showAnimating)}async hide({animate:e=!0}={}){this.openValue&&!this.element.dataset.hideAnimating&&(this.dispatch("hide",{bubbles:!0}),e&&(this.element.dataset.hideAnimating="",await new Promise((e=>{this.element.addEventListener("animationend",(()=>e()),{once:!0})})),delete this.element.dataset.hideAnimating),this.#n(),this.openValue=!1)}dismiss(e){this.captiveValue||this.dispatch("dismiss",{bubbles:!0})}escape(e){"Escape"!==e.key||this.captiveValue||e.defaultPrevented||this.dispatch("dismiss",{bubbles:!0})}#i(e,t,s){this.captiveValue=e,this.zIndexValue=t,this.scrollY=s,this.previousPosition=document.body.style.position,this.previousTop=document.body.style.top,this.element.style.zIndex=this.zIndexValue,document.body.style.top=`-${s}px`,document.body.style.position="fixed"}#n(){this.captiveValue=this.defaultCaptiveValue,this.zIndexValue=this.defaultZIndexValue,r(this.element,"z-index",null),r(document.body,"position",null),r(document.body,"top",null),window.scrollTo({left:0,top:this.scrollY,behavior:"instant"}),delete this.scrollY,delete this.previousPosition,delete this.previousTop}}function r(e,t,s){s?e.style.setProperty(t,s):e.style.removeProperty(t)}class d extends s{constructor(e,t){super(e),this.action=t}async open(){await super.open(),window.history.pushState({kpop:!0,id:this.id},"",window.location)}async dismiss(){await super.dismiss(),this.isCurrentLocation&&await this.pop("popstate",(()=>window.history.back())),this.frameElement.innerHTML=""}beforeVisit(e,s){super.beforeVisit(e,s),s.preventDefault(),e.dismiss({animate:!1}).then((()=>{t.visit(s.detail.url),this.debug("before-visit-end")}))}popstate(e,t){super.popstate(e,t),e.dismiss({animate:!0,reason:"popstate"})}get isCurrentLocation(){return window.history.state?.kpop&&window.history.state?.id===this.id}}class l{constructor(e,t){this.frame=e,this.action=t}render(){this.frame.src="",this.frame.innerHTML="",this.frame.append(this.action.templateContent)}}function h(e){return e.targetElements[0]?.kpop}t.StreamActions.kpop_open=function(){const e=!h(this).openValue;h(this)?.dismiss({animate:e,reason:"before-turbo-stream"}).then((()=>{new l(this.targetElements[0],this).render(),h(this)?.open(new d(this.target,this),{animate:e})}))},t.StreamActions.kpop_dismiss=function(){h(this)?.dismiss({reason:"turbo_stream.kpop.dismiss"})},t.StreamActions.kpop_redirect_to=function(){this.dataset.turboFrame===this.target?this.targetElements[0].src=this.getAttribute("href"):t.visit(this.getAttribute("href"),{action:this.dataset.turboAction})};const m=[{identifier:"kpop--frame",controllerConstructor:a},{identifier:"kpop--modal",controllerConstructor:class extends e{static values={fallback_location:String,layout:String};connect(){this.debug("connect"),this.layoutValue&&document.querySelector("#kpop").classList.toggle(this.layoutValue,!0)}disconnect(){this.debug("disconnect"),this.layoutValue&&document.querySelector("#kpop").classList.toggle(this.layoutValue,!1)}debug(e,...t){}}},{identifier:"scrim",controllerConstructor:o}];export{m as default};
|
2
2
|
//# sourceMappingURL=kpop.min.js.map
|
@@ -1 +1 @@
|
|
1
|
-
{"version":3,"file":"kpop.min.js","sources":["../../../javascript/kpop/modals/modal.js","../../../javascript/kpop/modals/content_modal.js","../../../javascript/kpop/modals/frame_modal.js","../../../javascript/kpop/modals/stream_modal.js","../../../javascript/kpop/modals/stream_renderer.js","../../../javascript/kpop/controllers/frame_controller.js","../../../javascript/kpop/controllers/scrim_controller.js","../../../javascript/kpop/application.js","../../../javascript/kpop/controllers/close_controller.js","../../../javascript/kpop/controllers/modal_controller.js","../../../javascript/kpop/controllers/redirect_controller.js"],"sourcesContent":["import { Turbo } from \"@hotwired/turbo-rails\";\n\nimport DEBUG from \"../debug\";\n\nexport class Modal {\n constructor(id) {\n this.id = id;\n }\n\n async open() {\n this.debug(\"open\");\n }\n\n async dismiss() {\n this.debug(`dismiss`);\n }\n\n beforeVisit(frame, e) {\n this.debug(`before-visit`, e.detail.url);\n }\n\n popstate(frame, e) {\n this.debug(`popstate`, e.state);\n }\n\n async pop(event, callback) {\n this.debug(`pop`);\n\n const promise = new Promise((resolve) => {\n window.addEventListener(\n event,\n () => {\n resolve();\n },\n { once: true }\n );\n });\n\n callback();\n\n return promise;\n }\n\n get frameElement() {\n return document.getElementById(this.id);\n }\n\n get modalElement() {\n return this.frameElement?.querySelector(\"[data-controller*='kpop--modal']\");\n }\n\n get currentLocationValue() {\n return this.modalElement?.dataset[\"kpop-ModalCurrentLocationValue\"] || \"/\";\n }\n\n get fallbackLocationValue() {\n return this.modalElement?.dataset[\"kpop-ModalFallbackLocationValue\"] || \"/\";\n }\n\n get isCurrentLocation() {\n return (\n window.history.state?.turbo && Turbo.session.location.href === this.src\n );\n }\n\n debug(event, ...args) {\n if (DEBUG) console.debug(`${this.constructor.name}:${event}`, ...args);\n }\n}\n","import { Turbo } from \"@hotwired/turbo-rails\";\n\nimport { Modal } from \"./modal\";\n\nexport class ContentModal extends Modal {\n constructor(id, src = null) {\n super(id);\n\n if (src) this.src = src;\n }\n\n async dismiss() {\n await super.dismiss();\n\n if (this.visitStarted) {\n this.debug(\"skipping dismiss, visit started\");\n return;\n }\n if (!this.isCurrentLocation) {\n this.debug(\"skipping dismiss, not current location\");\n return;\n }\n\n return this.pop(\"turbo:load\", () => {\n this.debug(\"turbo-visit\", this.fallbackLocationValue);\n Turbo.visit(this.fallbackLocationValue, { action: \"replace\" });\n });\n\n // no specific close action required, this is turbo's responsibility\n }\n\n beforeVisit(frame, e) {\n super.beforeVisit(frame, e);\n\n this.visitStarted = true;\n\n frame.scrimOutlet.hide({ animate: false });\n }\n\n get src() {\n return new URL(\n this.currentLocationValue.toString(),\n document.baseURI\n ).toString();\n }\n}\n","import { Turbo } from \"@hotwired/turbo-rails\";\n\nimport { Modal } from \"./modal\";\n\nexport class FrameModal extends Modal {\n constructor(id, src) {\n super(id);\n this.src = src;\n }\n\n async dismiss() {\n await super.dismiss();\n\n if (!this.isCurrentLocation) {\n this.debug(\"skipping dismiss, not current location\");\n }\n\n await this.pop(\"turbo:load\", () => window.history.back());\n\n // no specific close action required, this is turbo's responsibility\n }\n\n beforeVisit(frame, e) {\n super.beforeVisit(frame, e);\n\n e.preventDefault();\n\n frame.dismiss({ animate: false }).then(() => {\n Turbo.visit(e.detail.url);\n\n this.debug(\"before-visit-end\");\n });\n }\n\n popstate(frame, e) {\n super.popstate(frame, e);\n\n // Turbo will restore modal state, but we need to reset the scrim\n frame.scrimOutlet.hide({ animate: false });\n }\n}\n","import { Turbo } from \"@hotwired/turbo-rails\";\n\nimport { Modal } from \"./modal\";\n\nexport class StreamModal extends Modal {\n constructor(id, action) {\n super(id);\n\n this.action = action;\n }\n\n async open() {\n await super.open();\n\n window.history.pushState({ kpop: true, id: this.id }, \"\", window.location);\n }\n\n async dismiss() {\n await super.dismiss();\n\n if (this.isCurrentLocation) {\n await this.pop(\"popstate\", () => window.history.back());\n }\n\n this.frameElement.innerHTML = \"\";\n }\n\n beforeVisit(frame, e) {\n super.beforeVisit(frame, e);\n\n e.preventDefault();\n\n frame.dismiss({ animate: false }).then(() => {\n Turbo.visit(e.detail.url);\n\n this.debug(\"before-visit-end\");\n });\n }\n\n popstate(frame, e) {\n super.popstate(frame, e);\n\n frame.dismiss({ animate: true, reason: \"popstate\" });\n }\n\n get isCurrentLocation() {\n return window.history.state?.kpop && window.history.state?.id === this.id;\n }\n}\n","import DEBUG from \"../debug\";\n\nexport class StreamRenderer {\n constructor(frame, action) {\n this.frame = frame;\n this.action = action;\n }\n\n render() {\n if (DEBUG) console.debug(\"stream-renderer:render\");\n this.frame.src = \"\";\n this.frame.innerHTML = \"\";\n this.frame.append(this.action.templateContent);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { Turbo } from \"@hotwired/turbo-rails\";\n\nimport DEBUG from \"../debug\";\nimport { ContentModal } from \"../modals/content_modal\";\nimport { FrameModal } from \"../modals/frame_modal\";\nimport { StreamModal } from \"../modals/stream_modal\";\nimport { StreamRenderer } from \"../modals/stream_renderer\";\n\nTurbo.StreamActions.kpop_open = function () {\n const frame = () => {\n return this.targetElements[0];\n };\n const animate = !frame?.kpop?.openValue;\n\n frame()\n .kpop.dismiss({ animate, reason: \"before-turbo-stream\" })\n .then(() => {\n new StreamRenderer(frame(), this).render();\n frame().kpop.open(new StreamModal(this.target, this), { animate });\n });\n};\n\nexport default class Kpop__FrameController extends Controller {\n static outlets = [\"scrim\"];\n static targets = [\"modal\"];\n static values = {\n open: Boolean,\n };\n\n connect() {\n this.debug(\"connect\", this.element.src);\n\n this.element.kpop = this;\n\n // restoration visit\n if (this.element.src && this.element.complete) {\n this.debug(\"new frame modal\", this.element.src);\n this.open(new FrameModal(this.element.id, this.element.src), {\n animate: false,\n });\n } else {\n const element = this.element.querySelector(\n \"[data-controller*='kpop--modal']\"\n );\n if (element) {\n this.debug(\"new content modal\", window.location.pathname);\n this.open(new ContentModal(this.element.id), { animate: false });\n }\n }\n }\n\n disconnect() {\n this.debug(\"disconnect\");\n\n delete this.element.kpop;\n delete this.modal;\n }\n\n scrimOutletConnected(scrim) {\n this.debug(\"scrim-connected\");\n\n this.scrimConnected = true;\n\n if (this.openValue) scrim.show({ animate: false });\n }\n\n openValueChanged(open) {\n this.debug(\"open-changed\", open);\n\n this.element.parentElement.style.display = open ? \"flex\" : \"none\";\n }\n\n async open(modal, { animate = true } = {}) {\n if (this.isOpen) {\n this.debug(\"skip open as already open\");\n return false;\n }\n\n this.opening ||= this.#nextAnimationFrame(() => {\n return this.#open(modal, { animate });\n });\n\n return this.opening;\n }\n\n async dismiss({ animate = true, reason = \"\" } = {}) {\n if (!this.isOpen) {\n this.debug(\"skip dismiss as already closed\");\n return false;\n }\n\n this.dismissing ||= this.#nextAnimationFrame(() => {\n return new Promise((resolve) => {\n this.#dismiss({ animate, reason }).then(() => {\n return this.#nextAnimationFrame(resolve);\n });\n });\n });\n\n return this.dismissing;\n }\n\n // EVENTS\n\n popstate(event) {\n this.modal?.popstate(this, event);\n }\n\n beforeFrameRender(event) {\n this.debug(\"before-frame-render\", event.detail.newFrame.baseURI);\n\n event.preventDefault();\n\n this.dismiss({ animate: true, reason: \"before-frame-render\" }).then(() => {\n event.detail.resume();\n });\n }\n\n beforeVisit(e) {\n this.debug(\"before-visit\", e.detail.url);\n\n // ignore visits to the current frame, these fire when the frame navigates\n if (e.detail.url === this.element.src) return;\n\n // ignore unless we're open\n if (!this.isOpen) return;\n\n this.modal.beforeVisit(this, e);\n }\n\n frameLoad(event) {\n this.debug(\"frame-load\");\n\n return this.open(new FrameModal(this.element.id, this.element.src), {\n animate: true,\n });\n }\n\n get isOpen() {\n return this.openValue && !this.dismissing;\n }\n\n async #open(modal, { animate = true } = {}) {\n this.debug(\"open-start\", { animate });\n\n const scrim = this.scrimConnected && this.scrimOutlet;\n\n this.modal = modal;\n this.openValue = true;\n\n modal.open({ animate });\n scrim?.show({ animate });\n\n delete this.opening;\n\n this.debug(\"open-end\");\n }\n\n async #dismiss({ animate = true, reason = \"\" } = {}) {\n this.debug(\"dismiss-start\", { animate, reason });\n\n if (!this.modal) {\n console.warn(\"modal missing on dismiss\");\n if (DEBUG) debugger;\n }\n\n await this.scrimOutlet.hide({ animate });\n await this.modal?.dismiss();\n\n this.openValue = false;\n this.modal = null;\n delete this.dismissing;\n\n this.debug(\"dismiss-end\");\n }\n\n #nextAnimationFrame(callback) {\n return new Promise((resolve) => {\n window.requestAnimationFrame(() => {\n resolve(callback());\n });\n });\n }\n\n debug(event, ...args) {\n if (DEBUG) console.debug(`FrameController:${event}`, ...args);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nimport DEBUG from \"../debug\";\n\n/**\n * Scrim controller wraps an element that creates a whole page layer.\n * It is intended to be used behind a modal or nav drawer.\n *\n * If the Scrim element receives a click event, it automatically triggers \"scrim:hide\".\n *\n * You can show and hide the scrim programmatically by calling show/hide on the controller, e.g. using an outlet.\n *\n * If you need to respond to the scrim showing or hiding you should subscribe to \"scrim:show\" and \"scrim:hide\".\n */\nexport default class ScrimController extends Controller {\n static values = {\n open: Boolean,\n captive: Boolean,\n zIndex: Number,\n };\n\n connect() {\n if (DEBUG) console.debug(\"scrim:connect\");\n\n this.defaultZIndexValue = this.zIndexValue;\n this.defaultCaptiveValue = this.captiveValue;\n\n this.element.scrim = this;\n }\n\n disconnect() {\n if (DEBUG) console.debug(\"scrim:disconnect\");\n\n delete this.element.scrim;\n }\n\n async show({\n captive = this.defaultCaptiveValue,\n zIndex = this.defaultZIndexValue,\n top = window.scrollY,\n animate = true,\n } = {}) {\n if (DEBUG) console.debug(\"scrim:before-show\");\n\n // hide the scrim before opening the new one if it's already open\n if (this.openValue) {\n await this.hide({ animate });\n }\n\n // update internal state\n this.openValue = true;\n\n // notify listeners of pending request\n this.dispatch(\"show\", { bubbles: true });\n\n if (DEBUG) console.debug(\"scrim:show-start\");\n\n // update state, perform style updates\n this.#show(captive, zIndex, top);\n\n if (animate) {\n // animate opening\n // this will trigger an animationEnd event via CSS that completes the open\n this.element.dataset.showAnimating = \"\";\n\n await new Promise((resolve) => {\n this.element.addEventListener(\"animationend\", () => resolve(), {\n once: true,\n });\n });\n\n delete this.element.dataset.showAnimating;\n }\n\n if (DEBUG) console.debug(\"scrim:show-end\");\n }\n\n async hide({ animate = true } = {}) {\n if (!this.openValue || this.element.dataset.hideAnimating) return;\n\n if (DEBUG) console.debug(\"scrim:before-hide\");\n\n // notify listeners of pending request\n this.dispatch(\"hide\", { bubbles: true });\n\n if (DEBUG) console.debug(\"scrim:hide-start\");\n\n if (animate) {\n // set animation state\n // this will trigger an animationEnd event via CSS that completes the hide\n this.element.dataset.hideAnimating = \"\";\n\n await new Promise((resolve) => {\n this.element.addEventListener(\"animationend\", () => resolve(), {\n once: true,\n });\n });\n\n delete this.element.dataset.hideAnimating;\n }\n\n this.#hide();\n\n this.openValue = false;\n\n if (DEBUG) console.debug(\"scrim:hide-end\");\n }\n\n dismiss(event) {\n if (DEBUG) console.debug(\"scrim:dismiss\");\n\n if (!this.captiveValue) this.dispatch(\"dismiss\", { bubbles: true });\n }\n\n escape(event) {\n if (\n event.key === \"Escape\" &&\n !this.captiveValue &&\n !event.defaultPrevented\n ) {\n this.dispatch(\"dismiss\", { bubbles: true });\n }\n }\n\n /**\n * Clips body to viewport size and sets the z-index\n */\n #show(captive, zIndex, top) {\n this.captiveValue = captive;\n this.zIndexValue = zIndex;\n this.scrollY = top;\n\n this.previousPosition = document.body.style.position;\n this.previousTop = document.body.style.top;\n\n this.element.style.zIndex = this.zIndexValue;\n document.body.style.top = `-${top}px`;\n document.body.style.position = \"fixed\";\n }\n\n /**\n * Unclips body from viewport size and unsets the z-index\n */\n #hide() {\n this.captiveValue = this.defaultCaptiveValue;\n this.zIndexValue = this.defaultZIndexValue;\n\n resetStyle(this.element, \"z-index\", null);\n resetStyle(document.body, \"position\", null);\n resetStyle(document.body, \"top\", null);\n\n window.scrollTo({ left: 0, top: this.scrollY, behavior: \"instant\" });\n\n delete this.scrollY;\n delete this.previousPosition;\n delete this.previousTop;\n }\n}\n\nfunction resetStyle(element, property, previousValue) {\n if (previousValue) {\n element.style.setProperty(property, previousValue);\n } else {\n element.style.removeProperty(property);\n }\n}\n","import CloseController from \"../kpop/controllers/close_controller\";\nimport FrameController from \"../kpop/controllers/frame_controller\";\nimport ModalController from \"../kpop/controllers/modal_controller\";\nimport RedirectController from \"../kpop/controllers/redirect_controller\";\nimport ScrimController from \"../kpop/controllers/scrim_controller\";\n\nconst Definitions = [\n { identifier: \"kpop--close\", controllerConstructor: CloseController },\n { identifier: \"kpop--frame\", controllerConstructor: FrameController },\n { identifier: \"kpop--modal\", controllerConstructor: ModalController },\n { identifier: \"kpop--redirect\", controllerConstructor: RedirectController },\n { identifier: \"scrim\", controllerConstructor: ScrimController },\n];\n\nexport { Definitions as default };\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class Kpop__CloseController extends Controller {\n static outlets = [\"kpop--frame\"];\n\n kpopFrameOutletConnected(frame) {\n frame.dismiss();\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nimport DEBUG from \"../debug\";\n\nexport default class Kpop__ModalController extends Controller {\n static values = {\n fallback_location: String,\n layout: String,\n };\n\n connect() {\n this.debug(\"connect\");\n\n if (this.layoutValue) {\n document.querySelector(\"#kpop\").classList.toggle(this.layoutValue, true);\n }\n }\n\n disconnect() {\n this.debug(\"disconnect\");\n\n if (this.layoutValue) {\n document.querySelector(\"#kpop\").classList.toggle(this.layoutValue, false);\n }\n }\n\n debug(event, ...args) {\n if (DEBUG) console.debug(`ModalController:${event}`, ...args);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\nimport { Turbo } from \"@hotwired/turbo-rails\";\n\nexport default class Kpop__RedirectController extends Controller {\n static outlets = [\"kpop--frame\"];\n static values = {\n path: String,\n target: String,\n };\n\n kpopFrameOutletConnected(frame) {\n if (this.targetValue === frame.element.id) {\n frame.dismiss().then(() => {\n document.getElementById(this.targetValue).src = this.pathValue;\n });\n } else {\n Turbo.visit(this.pathValue, { action: \"replace\" });\n }\n\n this.element.remove();\n }\n}\n"],"names":["Modal","constructor","id","this","open","debug","dismiss","beforeVisit","frame","e","detail","url","popstate","state","pop","event","callback","promise","Promise","resolve","window","addEventListener","once","frameElement","document","getElementById","modalElement","querySelector","currentLocationValue","dataset","fallbackLocationValue","isCurrentLocation","history","turbo","Turbo","session","location","href","src","args","ContentModal","super","visitStarted","visit","action","scrimOutlet","hide","animate","URL","toString","baseURI","FrameModal","back","preventDefault","then","StreamModal","pushState","kpop","innerHTML","reason","StreamRenderer","render","append","templateContent","StreamActions","kpop_open","targetElements","openValue","target","Kpop__FrameController","Controller","static","Boolean","connect","element","complete","pathname","disconnect","modal","scrimOutletConnected","scrim","scrimConnected","show","openValueChanged","parentElement","style","display","isOpen","opening","nextAnimationFrame","dismissing","beforeFrameRender","newFrame","resume","frameLoad","console","warn","requestAnimationFrame","ScrimController","captive","zIndex","Number","defaultZIndexValue","zIndexValue","defaultCaptiveValue","captiveValue","top","scrollY","dispatch","bubbles","showAnimating","hideAnimating","escape","key","defaultPrevented","previousPosition","body","position","previousTop","resetStyle","scrollTo","left","behavior","property","previousValue","setProperty","removeProperty","Definitions","identifier","controllerConstructor","kpopFrameOutletConnected","FrameController","fallback_location","String","layout","layoutValue","classList","toggle","path","targetValue","pathValue","remove"],"mappings":"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"}
|
1
|
+
{"version":3,"file":"kpop.min.js","sources":["../../../javascript/kpop/modals/modal.js","../../../javascript/kpop/modals/content_modal.js","../../../javascript/kpop/modals/frame_modal.js","../../../javascript/kpop/controllers/frame_controller.js","../../../javascript/kpop/controllers/scrim_controller.js","../../../javascript/kpop/modals/stream_modal.js","../../../javascript/kpop/utils/stream_renderer.js","../../../javascript/kpop/turbo_actions.js","../../../javascript/kpop/application.js","../../../javascript/kpop/controllers/modal_controller.js"],"sourcesContent":["import { Turbo } from \"@hotwired/turbo-rails\";\n\nimport DEBUG from \"../debug\";\n\nexport class Modal {\n constructor(id) {\n this.id = id;\n }\n\n async open() {\n this.debug(\"open\");\n }\n\n async dismiss() {\n this.debug(`dismiss`);\n }\n\n beforeVisit(frame, e) {\n this.debug(`before-visit`, e.detail.url);\n }\n\n popstate(frame, e) {\n this.debug(`popstate`, e.state);\n }\n\n async pop(event, callback) {\n this.debug(`pop`);\n\n const promise = new Promise((resolve) => {\n window.addEventListener(\n event,\n () => {\n resolve();\n },\n { once: true }\n );\n });\n\n callback();\n\n return promise;\n }\n\n get frameElement() {\n return document.getElementById(this.id);\n }\n\n get modalElement() {\n return this.frameElement?.querySelector(\"[data-controller*='kpop--modal']\");\n }\n\n get currentLocationValue() {\n return this.modalElement?.dataset[\"kpop-ModalCurrentLocationValue\"] || \"/\";\n }\n\n get fallbackLocationValue() {\n return this.modalElement?.dataset[\"kpop-ModalFallbackLocationValue\"] || \"/\";\n }\n\n get isCurrentLocation() {\n return (\n window.history.state?.turbo && Turbo.session.location.href === this.src\n );\n }\n\n debug(event, ...args) {\n if (DEBUG) console.debug(`${this.constructor.name}:${event}`, ...args);\n }\n}\n","import { Turbo } from \"@hotwired/turbo-rails\";\n\nimport { Modal } from \"./modal\";\n\nexport class ContentModal extends Modal {\n constructor(id, src = null) {\n super(id);\n\n if (src) this.src = src;\n }\n\n async dismiss() {\n await super.dismiss();\n\n if (this.visitStarted) {\n this.debug(\"skipping dismiss, visit started\");\n return;\n }\n if (!this.isCurrentLocation) {\n this.debug(\"skipping dismiss, not current location\");\n return;\n }\n\n return this.pop(\"turbo:load\", () => {\n this.debug(\"turbo-visit\", this.fallbackLocationValue);\n Turbo.visit(this.fallbackLocationValue);\n });\n\n // no specific close action required, this is turbo's responsibility\n }\n\n beforeVisit(frame, e) {\n super.beforeVisit(frame, e);\n\n this.visitStarted = true;\n\n frame.scrimOutlet.hide({ animate: false });\n }\n\n get src() {\n return new URL(\n this.currentLocationValue.toString(),\n document.baseURI\n ).toString();\n }\n}\n","import { Turbo } from \"@hotwired/turbo-rails\";\n\nimport { Modal } from \"./modal\";\n\nexport class FrameModal extends Modal {\n constructor(id, src) {\n super(id);\n this.src = src;\n }\n\n async dismiss() {\n await super.dismiss();\n\n if (!this.isCurrentLocation) {\n this.debug(\"skipping dismiss, not current location\");\n }\n\n await this.pop(\"turbo:load\", () => window.history.back());\n\n // no specific close action required, this is turbo's responsibility\n }\n\n beforeVisit(frame, e) {\n super.beforeVisit(frame, e);\n\n e.preventDefault();\n\n frame.dismiss({ animate: false }).then(() => {\n Turbo.visit(e.detail.url);\n\n this.debug(\"before-visit-end\");\n });\n }\n\n popstate(frame, e) {\n super.popstate(frame, e);\n\n // Turbo will restore modal state, but we need to reset the scrim\n frame.scrimOutlet.hide({ animate: false });\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nimport DEBUG from \"../debug\";\nimport { ContentModal } from \"../modals/content_modal\";\nimport { FrameModal } from \"../modals/frame_modal\";\n\nexport default class Kpop__FrameController extends Controller {\n static outlets = [\"scrim\"];\n static targets = [\"modal\"];\n static values = {\n open: Boolean,\n };\n\n connect() {\n this.debug(\"connect\", this.element.src);\n\n this.element.kpop = this;\n\n // restoration visit\n if (this.element.src && this.element.complete) {\n this.debug(\"new frame modal\", this.element.src);\n this.open(new FrameModal(this.element.id, this.element.src), {\n animate: false,\n });\n } else {\n const element = this.element.querySelector(\n \"[data-controller*='kpop--modal']\"\n );\n if (element) {\n this.debug(\"new content modal\", window.location.pathname);\n this.open(new ContentModal(this.element.id), { animate: false });\n }\n }\n }\n\n disconnect() {\n this.debug(\"disconnect\");\n\n delete this.element.kpop;\n delete this.modal;\n }\n\n scrimOutletConnected(scrim) {\n this.debug(\"scrim-connected\");\n\n this.scrimConnected = true;\n\n if (this.openValue) {\n scrim.show({ animate: false });\n } else {\n scrim.hide({ animate: false });\n }\n }\n\n openValueChanged(open) {\n this.debug(\"open-changed\", open);\n\n this.element.parentElement.style.display = open ? \"flex\" : \"none\";\n }\n\n async open(modal, { animate = true } = {}) {\n if (this.isOpen) {\n this.debug(\"skip open as already open\");\n this.modal ||= modal;\n return false;\n }\n\n return (this.opening ||= this.#nextFrame(() =>\n this.#open(modal, { animate })\n ));\n }\n\n async dismiss({ animate = true, reason = \"\" } = {}) {\n if (!this.isOpen) {\n this.debug(\"skip dismiss as already closed\");\n return false;\n }\n\n return (this.dismissing ||= this.#nextFrame(() =>\n this.#dismiss({ animate, reason })\n ));\n }\n\n // EVENTS\n\n popstate(event) {\n this.modal?.popstate(this, event);\n }\n\n beforeFrameRender(event) {\n this.debug(\"before-frame-render\", event.detail.newFrame.baseURI);\n\n event.preventDefault();\n\n this.dismiss({ animate: true, reason: \"before-frame-render\" }).then(() => {\n this.debug(\"resume-frame-render\", event.detail.newFrame.baseURI);\n event.detail.resume();\n });\n }\n\n beforeStreamRender(event) {\n this.debug(\"before-stream-render\", event.detail);\n\n const resume = event.detail.render;\n\n // Defer rendering until dismiss is complete.\n // Dismiss may change history so we need to wait for it to complete to avoid\n // losing DOM changes on restoration visits.\n event.detail.render = (stream) => {\n (this.dismissing || Promise.resolve()).then(() => {\n this.debug(\"stream-render\", stream);\n resume(stream);\n });\n };\n }\n\n beforeVisit(e) {\n this.debug(\"before-visit\", e.detail.url);\n\n // ignore visits to the current frame, these fire when the frame navigates\n if (e.detail.url === this.element.src) return;\n\n // ignore unless we're open\n if (!this.isOpen) return;\n\n this.modal.beforeVisit(this, e);\n }\n\n frameLoad(event) {\n this.debug(\"frame-load\");\n\n return this.open(new FrameModal(this.element.id, this.element.src), {\n animate: true,\n });\n }\n\n get isOpen() {\n return this.openValue && !this.dismissing;\n }\n\n async #open(modal, { animate = true } = {}) {\n this.debug(\"open-start\", { animate });\n\n const scrim = this.scrimConnected && this.scrimOutlet;\n\n this.modal = modal;\n this.openValue = true;\n\n await modal.open({ animate });\n await scrim?.show({ animate });\n\n delete this.opening;\n\n this.debug(\"open-end\");\n }\n\n async #dismiss({ animate = true, reason = \"\" } = {}) {\n this.debug(\"dismiss-start\", { animate, reason });\n\n // if this element is detached then we've experienced a turbo navigation\n if (!this.element.isConnected) {\n this.debug(\"skip dismiss, element detached\");\n return;\n }\n\n if (!this.modal) {\n console.warn(\"modal missing on dismiss\");\n if (DEBUG) debugger;\n }\n\n await this.scrimOutlet.hide({ animate });\n await this.modal?.dismiss();\n\n this.openValue = false;\n this.modal = null;\n delete this.dismissing;\n\n this.debug(\"dismiss-end\");\n }\n\n async #nextFrame(callback) {\n return new Promise(window.requestAnimationFrame).then(callback);\n }\n\n debug(event, ...args) {\n if (DEBUG) console.debug(`FrameController:${event}`, ...args);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nimport DEBUG from \"../debug\";\n\n/**\n * Scrim controller wraps an element that creates a whole page layer.\n * It is intended to be used behind a modal or nav drawer.\n *\n * If the Scrim element receives a click event, it automatically triggers \"scrim:hide\".\n *\n * You can show and hide the scrim programmatically by calling show/hide on the controller, e.g. using an outlet.\n *\n * If you need to respond to the scrim showing or hiding you should subscribe to \"scrim:show\" and \"scrim:hide\".\n */\nexport default class ScrimController extends Controller {\n static values = {\n open: Boolean,\n captive: Boolean,\n zIndex: Number,\n };\n\n connect() {\n if (DEBUG) console.debug(\"scrim:connect\");\n\n this.defaultZIndexValue = this.zIndexValue;\n this.defaultCaptiveValue = this.captiveValue;\n\n this.element.scrim = this;\n }\n\n disconnect() {\n if (DEBUG) console.debug(\"scrim:disconnect\");\n\n delete this.element.scrim;\n }\n\n async show({\n captive = this.defaultCaptiveValue,\n zIndex = this.defaultZIndexValue,\n top = window.scrollY,\n animate = true,\n } = {}) {\n if (DEBUG) console.debug(\"scrim:before-show\");\n\n // hide the scrim before opening the new one if it's already open\n if (this.openValue) {\n await this.hide({ animate });\n }\n\n // update internal state\n this.openValue = true;\n\n // notify listeners of pending request\n this.dispatch(\"show\", { bubbles: true });\n\n if (DEBUG) console.debug(\"scrim:show-start\");\n\n // update state, perform style updates\n this.#show(captive, zIndex, top);\n\n if (animate) {\n // animate opening\n // this will trigger an animationEnd event via CSS that completes the open\n this.element.dataset.showAnimating = \"\";\n\n await new Promise((resolve) => {\n this.element.addEventListener(\"animationend\", () => resolve(), {\n once: true,\n });\n });\n\n delete this.element.dataset.showAnimating;\n }\n\n if (DEBUG) console.debug(\"scrim:show-end\");\n }\n\n async hide({ animate = true } = {}) {\n if (!this.openValue || this.element.dataset.hideAnimating) return;\n\n if (DEBUG) console.debug(\"scrim:before-hide\");\n\n // notify listeners of pending request\n this.dispatch(\"hide\", { bubbles: true });\n\n if (DEBUG) console.debug(\"scrim:hide-start\");\n\n if (animate) {\n // set animation state\n // this will trigger an animationEnd event via CSS that completes the hide\n this.element.dataset.hideAnimating = \"\";\n\n await new Promise((resolve) => {\n this.element.addEventListener(\"animationend\", () => resolve(), {\n once: true,\n });\n });\n\n delete this.element.dataset.hideAnimating;\n }\n\n this.#hide();\n\n this.openValue = false;\n\n if (DEBUG) console.debug(\"scrim:hide-end\");\n }\n\n dismiss(event) {\n if (DEBUG) console.debug(\"scrim:dismiss\");\n\n if (!this.captiveValue) this.dispatch(\"dismiss\", { bubbles: true });\n }\n\n escape(event) {\n if (\n event.key === \"Escape\" &&\n !this.captiveValue &&\n !event.defaultPrevented\n ) {\n this.dispatch(\"dismiss\", { bubbles: true });\n }\n }\n\n /**\n * Clips body to viewport size and sets the z-index\n */\n #show(captive, zIndex, top) {\n this.captiveValue = captive;\n this.zIndexValue = zIndex;\n this.scrollY = top;\n\n this.previousPosition = document.body.style.position;\n this.previousTop = document.body.style.top;\n\n this.element.style.zIndex = this.zIndexValue;\n document.body.style.top = `-${top}px`;\n document.body.style.position = \"fixed\";\n }\n\n /**\n * Unclips body from viewport size and unsets the z-index\n */\n #hide() {\n this.captiveValue = this.defaultCaptiveValue;\n this.zIndexValue = this.defaultZIndexValue;\n\n resetStyle(this.element, \"z-index\", null);\n resetStyle(document.body, \"position\", null);\n resetStyle(document.body, \"top\", null);\n\n window.scrollTo({ left: 0, top: this.scrollY, behavior: \"instant\" });\n\n delete this.scrollY;\n delete this.previousPosition;\n delete this.previousTop;\n }\n}\n\nfunction resetStyle(element, property, previousValue) {\n if (previousValue) {\n element.style.setProperty(property, previousValue);\n } else {\n element.style.removeProperty(property);\n }\n}\n","import { Turbo } from \"@hotwired/turbo-rails\";\n\nimport { Modal } from \"./modal\";\n\nexport class StreamModal extends Modal {\n constructor(id, action) {\n super(id);\n\n this.action = action;\n }\n\n async open() {\n await super.open();\n\n window.history.pushState({ kpop: true, id: this.id }, \"\", window.location);\n }\n\n async dismiss() {\n await super.dismiss();\n\n if (this.isCurrentLocation) {\n await this.pop(\"popstate\", () => window.history.back());\n }\n\n this.frameElement.innerHTML = \"\";\n }\n\n beforeVisit(frame, e) {\n super.beforeVisit(frame, e);\n\n e.preventDefault();\n\n frame.dismiss({ animate: false }).then(() => {\n Turbo.visit(e.detail.url);\n\n this.debug(\"before-visit-end\");\n });\n }\n\n popstate(frame, e) {\n super.popstate(frame, e);\n\n frame.dismiss({ animate: true, reason: \"popstate\" });\n }\n\n get isCurrentLocation() {\n return window.history.state?.kpop && window.history.state?.id === this.id;\n }\n}\n","import DEBUG from \"../debug\";\n\nexport class StreamRenderer {\n constructor(frame, action) {\n this.frame = frame;\n this.action = action;\n }\n\n render() {\n if (DEBUG) console.debug(\"stream-renderer:render\");\n this.frame.src = \"\";\n this.frame.innerHTML = \"\";\n this.frame.append(this.action.templateContent);\n }\n}\n","import { Turbo } from \"@hotwired/turbo-rails\";\n\nimport { StreamModal } from \"./modals/stream_modal\";\nimport { StreamRenderer } from \"./utils/stream_renderer\";\n\nfunction kpop(action) {\n return action.targetElements[0]?.kpop;\n}\n\nTurbo.StreamActions.kpop_open = function () {\n const animate = !kpop(this).openValue;\n\n kpop(this)\n ?.dismiss({ animate, reason: \"before-turbo-stream\" })\n .then(() => {\n new StreamRenderer(this.targetElements[0], this).render();\n kpop(this)?.open(new StreamModal(this.target, this), { animate });\n });\n};\n\nTurbo.StreamActions.kpop_dismiss = function () {\n kpop(this)?.dismiss({ reason: \"turbo_stream.kpop.dismiss\" });\n};\n\nTurbo.StreamActions.kpop_redirect_to = function () {\n if (this.dataset.turboFrame === this.target) {\n this.targetElements[0].src = this.getAttribute(\"href\");\n } else {\n Turbo.visit(this.getAttribute(\"href\"), {\n action: this.dataset.turboAction,\n });\n }\n};\n","import FrameController from \"../kpop/controllers/frame_controller\";\nimport ModalController from \"../kpop/controllers/modal_controller\";\nimport ScrimController from \"../kpop/controllers/scrim_controller\";\n\nimport \"./turbo_actions\";\n\nconst Definitions = [\n { identifier: \"kpop--frame\", controllerConstructor: FrameController },\n { identifier: \"kpop--modal\", controllerConstructor: ModalController },\n { identifier: \"scrim\", controllerConstructor: ScrimController },\n];\n\nexport { Definitions as default };\n","import { Controller } from \"@hotwired/stimulus\";\n\nimport DEBUG from \"../debug\";\n\nexport default class Kpop__ModalController extends Controller {\n static values = {\n fallback_location: String,\n layout: String,\n };\n\n connect() {\n this.debug(\"connect\");\n\n if (this.layoutValue) {\n document.querySelector(\"#kpop\").classList.toggle(this.layoutValue, true);\n }\n }\n\n disconnect() {\n this.debug(\"disconnect\");\n\n if (this.layoutValue) {\n document.querySelector(\"#kpop\").classList.toggle(this.layoutValue, false);\n }\n }\n\n debug(event, ...args) {\n if (DEBUG) console.debug(`ModalController:${event}`, ...args);\n }\n}\n"],"names":["Modal","constructor","id","this","open","debug","dismiss","beforeVisit","frame","e","detail","url","popstate","state","pop","event","callback","promise","Promise","resolve","window","addEventListener","once","frameElement","document","getElementById","modalElement","querySelector","currentLocationValue","dataset","fallbackLocationValue","isCurrentLocation","history","turbo","Turbo","session","location","href","src","args","ContentModal","super","visitStarted","visit","scrimOutlet","hide","animate","URL","toString","baseURI","FrameModal","back","preventDefault","then","Kpop__FrameController","Controller","static","Boolean","connect","element","kpop","complete","pathname","disconnect","modal","scrimOutletConnected","scrim","scrimConnected","openValue","show","openValueChanged","parentElement","style","display","isOpen","opening","nextFrame","reason","dismissing","beforeFrameRender","newFrame","resume","beforeStreamRender","render","stream","frameLoad","isConnected","console","warn","requestAnimationFrame","ScrimController","captive","zIndex","Number","defaultZIndexValue","zIndexValue","defaultCaptiveValue","captiveValue","top","scrollY","dispatch","bubbles","showAnimating","hideAnimating","escape","key","defaultPrevented","previousPosition","body","position","previousTop","resetStyle","scrollTo","left","behavior","property","previousValue","setProperty","removeProperty","StreamModal","action","pushState","innerHTML","StreamRenderer","append","templateContent","targetElements","StreamActions","kpop_open","target","kpop_dismiss","kpop_redirect_to","turboFrame","getAttribute","turboAction","Definitions","identifier","controllerConstructor","FrameController","fallback_location","String","layout","layoutValue","classList","toggle"],"mappings":"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"}
|
@@ -12,6 +12,7 @@ module Kpop
|
|
12
12
|
scrim:dismiss@window->kpop--frame#dismiss
|
13
13
|
scrim:hide@window->kpop--frame#dismiss
|
14
14
|
turbo:before-frame-render->kpop--frame#beforeFrameRender
|
15
|
+
turbo:before-stream-render@window->kpop--frame#beforeStreamRender
|
15
16
|
turbo:before-visit@window->kpop--frame#beforeVisit
|
16
17
|
turbo:frame-load->kpop--frame#frameLoad
|
17
18
|
].freeze
|
@@ -1,14 +1,12 @@
|
|
1
|
-
import CloseController from "../kpop/controllers/close_controller";
|
2
1
|
import FrameController from "../kpop/controllers/frame_controller";
|
3
2
|
import ModalController from "../kpop/controllers/modal_controller";
|
4
|
-
import RedirectController from "../kpop/controllers/redirect_controller";
|
5
3
|
import ScrimController from "../kpop/controllers/scrim_controller";
|
6
4
|
|
5
|
+
import "./turbo_actions";
|
6
|
+
|
7
7
|
const Definitions = [
|
8
|
-
{ identifier: "kpop--close", controllerConstructor: CloseController },
|
9
8
|
{ identifier: "kpop--frame", controllerConstructor: FrameController },
|
10
9
|
{ identifier: "kpop--modal", controllerConstructor: ModalController },
|
11
|
-
{ identifier: "kpop--redirect", controllerConstructor: RedirectController },
|
12
10
|
{ identifier: "scrim", controllerConstructor: ScrimController },
|
13
11
|
];
|
14
12
|
|
@@ -1,25 +1,8 @@
|
|
1
1
|
import { Controller } from "@hotwired/stimulus";
|
2
|
-
import { Turbo } from "@hotwired/turbo-rails";
|
3
2
|
|
4
3
|
import DEBUG from "../debug";
|
5
4
|
import { ContentModal } from "../modals/content_modal";
|
6
5
|
import { FrameModal } from "../modals/frame_modal";
|
7
|
-
import { StreamModal } from "../modals/stream_modal";
|
8
|
-
import { StreamRenderer } from "../modals/stream_renderer";
|
9
|
-
|
10
|
-
Turbo.StreamActions.kpop_open = function () {
|
11
|
-
const frame = () => {
|
12
|
-
return this.targetElements[0];
|
13
|
-
};
|
14
|
-
const animate = !frame?.kpop?.openValue;
|
15
|
-
|
16
|
-
frame()
|
17
|
-
.kpop.dismiss({ animate, reason: "before-turbo-stream" })
|
18
|
-
.then(() => {
|
19
|
-
new StreamRenderer(frame(), this).render();
|
20
|
-
frame().kpop.open(new StreamModal(this.target, this), { animate });
|
21
|
-
});
|
22
|
-
};
|
23
6
|
|
24
7
|
export default class Kpop__FrameController extends Controller {
|
25
8
|
static outlets = ["scrim"];
|
@@ -62,7 +45,11 @@ export default class Kpop__FrameController extends Controller {
|
|
62
45
|
|
63
46
|
this.scrimConnected = true;
|
64
47
|
|
65
|
-
if (this.openValue)
|
48
|
+
if (this.openValue) {
|
49
|
+
scrim.show({ animate: false });
|
50
|
+
} else {
|
51
|
+
scrim.hide({ animate: false });
|
52
|
+
}
|
66
53
|
}
|
67
54
|
|
68
55
|
openValueChanged(open) {
|
@@ -74,14 +61,13 @@ export default class Kpop__FrameController extends Controller {
|
|
74
61
|
async open(modal, { animate = true } = {}) {
|
75
62
|
if (this.isOpen) {
|
76
63
|
this.debug("skip open as already open");
|
64
|
+
this.modal ||= modal;
|
77
65
|
return false;
|
78
66
|
}
|
79
67
|
|
80
|
-
this.opening ||= this.#
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
return this.opening;
|
68
|
+
return (this.opening ||= this.#nextFrame(() =>
|
69
|
+
this.#open(modal, { animate })
|
70
|
+
));
|
85
71
|
}
|
86
72
|
|
87
73
|
async dismiss({ animate = true, reason = "" } = {}) {
|
@@ -90,15 +76,9 @@ export default class Kpop__FrameController extends Controller {
|
|
90
76
|
return false;
|
91
77
|
}
|
92
78
|
|
93
|
-
this.dismissing ||= this.#
|
94
|
-
|
95
|
-
|
96
|
-
return this.#nextAnimationFrame(resolve);
|
97
|
-
});
|
98
|
-
});
|
99
|
-
});
|
100
|
-
|
101
|
-
return this.dismissing;
|
79
|
+
return (this.dismissing ||= this.#nextFrame(() =>
|
80
|
+
this.#dismiss({ animate, reason })
|
81
|
+
));
|
102
82
|
}
|
103
83
|
|
104
84
|
// EVENTS
|
@@ -113,10 +93,27 @@ export default class Kpop__FrameController extends Controller {
|
|
113
93
|
event.preventDefault();
|
114
94
|
|
115
95
|
this.dismiss({ animate: true, reason: "before-frame-render" }).then(() => {
|
96
|
+
this.debug("resume-frame-render", event.detail.newFrame.baseURI);
|
116
97
|
event.detail.resume();
|
117
98
|
});
|
118
99
|
}
|
119
100
|
|
101
|
+
beforeStreamRender(event) {
|
102
|
+
this.debug("before-stream-render", event.detail);
|
103
|
+
|
104
|
+
const resume = event.detail.render;
|
105
|
+
|
106
|
+
// Defer rendering until dismiss is complete.
|
107
|
+
// Dismiss may change history so we need to wait for it to complete to avoid
|
108
|
+
// losing DOM changes on restoration visits.
|
109
|
+
event.detail.render = (stream) => {
|
110
|
+
(this.dismissing || Promise.resolve()).then(() => {
|
111
|
+
this.debug("stream-render", stream);
|
112
|
+
resume(stream);
|
113
|
+
});
|
114
|
+
};
|
115
|
+
}
|
116
|
+
|
120
117
|
beforeVisit(e) {
|
121
118
|
this.debug("before-visit", e.detail.url);
|
122
119
|
|
@@ -149,8 +146,8 @@ export default class Kpop__FrameController extends Controller {
|
|
149
146
|
this.modal = modal;
|
150
147
|
this.openValue = true;
|
151
148
|
|
152
|
-
modal.open({ animate });
|
153
|
-
scrim?.show({ animate });
|
149
|
+
await modal.open({ animate });
|
150
|
+
await scrim?.show({ animate });
|
154
151
|
|
155
152
|
delete this.opening;
|
156
153
|
|
@@ -160,6 +157,12 @@ export default class Kpop__FrameController extends Controller {
|
|
160
157
|
async #dismiss({ animate = true, reason = "" } = {}) {
|
161
158
|
this.debug("dismiss-start", { animate, reason });
|
162
159
|
|
160
|
+
// if this element is detached then we've experienced a turbo navigation
|
161
|
+
if (!this.element.isConnected) {
|
162
|
+
this.debug("skip dismiss, element detached");
|
163
|
+
return;
|
164
|
+
}
|
165
|
+
|
163
166
|
if (!this.modal) {
|
164
167
|
console.warn("modal missing on dismiss");
|
165
168
|
if (DEBUG) debugger;
|
@@ -175,12 +178,8 @@ export default class Kpop__FrameController extends Controller {
|
|
175
178
|
this.debug("dismiss-end");
|
176
179
|
}
|
177
180
|
|
178
|
-
#
|
179
|
-
return new Promise((
|
180
|
-
window.requestAnimationFrame(() => {
|
181
|
-
resolve(callback());
|
182
|
-
});
|
183
|
-
});
|
181
|
+
async #nextFrame(callback) {
|
182
|
+
return new Promise(window.requestAnimationFrame).then(callback);
|
184
183
|
}
|
185
184
|
|
186
185
|
debug(event, ...args) {
|
@@ -23,7 +23,7 @@ export class ContentModal extends Modal {
|
|
23
23
|
|
24
24
|
return this.pop("turbo:load", () => {
|
25
25
|
this.debug("turbo-visit", this.fallbackLocationValue);
|
26
|
-
Turbo.visit(this.fallbackLocationValue
|
26
|
+
Turbo.visit(this.fallbackLocationValue);
|
27
27
|
});
|
28
28
|
|
29
29
|
// no specific close action required, this is turbo's responsibility
|
@@ -0,0 +1,33 @@
|
|
1
|
+
import { Turbo } from "@hotwired/turbo-rails";
|
2
|
+
|
3
|
+
import { StreamModal } from "./modals/stream_modal";
|
4
|
+
import { StreamRenderer } from "./utils/stream_renderer";
|
5
|
+
|
6
|
+
function kpop(action) {
|
7
|
+
return action.targetElements[0]?.kpop;
|
8
|
+
}
|
9
|
+
|
10
|
+
Turbo.StreamActions.kpop_open = function () {
|
11
|
+
const animate = !kpop(this).openValue;
|
12
|
+
|
13
|
+
kpop(this)
|
14
|
+
?.dismiss({ animate, reason: "before-turbo-stream" })
|
15
|
+
.then(() => {
|
16
|
+
new StreamRenderer(this.targetElements[0], this).render();
|
17
|
+
kpop(this)?.open(new StreamModal(this.target, this), { animate });
|
18
|
+
});
|
19
|
+
};
|
20
|
+
|
21
|
+
Turbo.StreamActions.kpop_dismiss = function () {
|
22
|
+
kpop(this)?.dismiss({ reason: "turbo_stream.kpop.dismiss" });
|
23
|
+
};
|
24
|
+
|
25
|
+
Turbo.StreamActions.kpop_redirect_to = function () {
|
26
|
+
if (this.dataset.turboFrame === this.target) {
|
27
|
+
this.targetElements[0].src = this.getAttribute("href");
|
28
|
+
} else {
|
29
|
+
Turbo.visit(this.getAttribute("href"), {
|
30
|
+
action: this.dataset.turboAction,
|
31
|
+
});
|
32
|
+
}
|
33
|
+
};
|
@@ -6,7 +6,6 @@ require "katalyst/kpop/matchers/capybara_parser"
|
|
6
6
|
require "katalyst/kpop/matchers/chained_matcher"
|
7
7
|
require "katalyst/kpop/matchers/frame_matcher"
|
8
8
|
require "katalyst/kpop/matchers/modal_matcher"
|
9
|
-
require "katalyst/kpop/matchers/redirect_finder"
|
10
9
|
require "katalyst/kpop/matchers/redirect_matcher"
|
11
10
|
require "katalyst/kpop/matchers/response_matcher"
|
12
11
|
require "katalyst/kpop/matchers/stream_matcher"
|
@@ -22,7 +21,9 @@ module Katalyst
|
|
22
21
|
# @example
|
23
22
|
# expect(response).to kpop_dismiss
|
24
23
|
def kpop_dismiss(id: "kpop")
|
25
|
-
ChainedMatcher.new(ResponseMatcher,
|
24
|
+
ChainedMatcher.new(ResponseMatcher,
|
25
|
+
CapybaraParser,
|
26
|
+
StreamMatcher.new(id:, action: "kpop_dismiss"))
|
26
27
|
end
|
27
28
|
|
28
29
|
# @api public
|
@@ -36,8 +37,7 @@ module Katalyst
|
|
36
37
|
|
37
38
|
ChainedMatcher.new(ResponseMatcher,
|
38
39
|
CapybaraParser,
|
39
|
-
StreamMatcher.new(id:, action: "
|
40
|
-
RedirectFinder,
|
40
|
+
StreamMatcher.new(id:, action: "kpop_redirect_to"),
|
41
41
|
RedirectMatcher.new(target))
|
42
42
|
end
|
43
43
|
|
data/lib/katalyst/kpop/turbo.rb
CHANGED
@@ -6,7 +6,7 @@ module Katalyst
|
|
6
6
|
module Kpop
|
7
7
|
module Turbo
|
8
8
|
class TagBuilder
|
9
|
-
delegate :action, :
|
9
|
+
delegate :action, :turbo_stream_action_tag, to: :@builder
|
10
10
|
|
11
11
|
def initialize(builder)
|
12
12
|
@builder = builder
|
@@ -24,31 +24,25 @@ module Katalyst
|
|
24
24
|
# <% end %>
|
25
25
|
# <% end %>
|
26
26
|
def open(content = nil, id: "kpop", **, &)
|
27
|
-
|
27
|
+
action(:kpop_open, id, content, **, &)
|
28
28
|
end
|
29
29
|
|
30
30
|
# Render a turbo stream action that will dismiss any open kpop modal.
|
31
31
|
def dismiss(id: "kpop")
|
32
|
-
|
33
|
-
tag.div("", data: {
|
34
|
-
controller: "kpop--close",
|
35
|
-
kpop__close_kpop__frame_outlet: "##{id}",
|
36
|
-
turbo_temporary: "",
|
37
|
-
})
|
38
|
-
end
|
32
|
+
turbo_stream_action_tag(:kpop_dismiss, target: id)
|
39
33
|
end
|
40
34
|
|
41
35
|
# Renders a kpop redirect controller response that will escape the frame and navigate to the given URL.
|
42
|
-
def redirect_to(
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
36
|
+
def redirect_to(href, id: "kpop", action: "replace", target: nil)
|
37
|
+
turbo_stream_action_tag(
|
38
|
+
:kpop_redirect_to,
|
39
|
+
target: id,
|
40
|
+
href:,
|
41
|
+
data: {
|
42
|
+
turbo_action: action,
|
43
|
+
turbo_frame: target,
|
44
|
+
},
|
45
|
+
)
|
52
46
|
end
|
53
47
|
end
|
54
48
|
end
|
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.beta.
|
4
|
+
version: 3.0.0.beta.3
|
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-10-
|
11
|
+
date: 2023-10-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: html-attributes-utils
|
@@ -82,17 +82,16 @@ files:
|
|
82
82
|
- app/components/scrim_component.rb
|
83
83
|
- app/helpers/kpop_helper.rb
|
84
84
|
- app/javascript/kpop/application.js
|
85
|
-
- app/javascript/kpop/controllers/close_controller.js
|
86
85
|
- app/javascript/kpop/controllers/frame_controller.js
|
87
86
|
- app/javascript/kpop/controllers/modal_controller.js
|
88
|
-
- app/javascript/kpop/controllers/redirect_controller.js
|
89
87
|
- app/javascript/kpop/controllers/scrim_controller.js
|
90
88
|
- app/javascript/kpop/debug.js
|
91
89
|
- app/javascript/kpop/modals/content_modal.js
|
92
90
|
- app/javascript/kpop/modals/frame_modal.js
|
93
91
|
- app/javascript/kpop/modals/modal.js
|
94
92
|
- app/javascript/kpop/modals/stream_modal.js
|
95
|
-
- app/javascript/kpop/
|
93
|
+
- app/javascript/kpop/turbo_actions.js
|
94
|
+
- app/javascript/kpop/utils/stream_renderer.js
|
96
95
|
- app/views/layouts/kpop.html.erb
|
97
96
|
- config/importmap.rb
|
98
97
|
- config/routes.rb
|
@@ -105,7 +104,6 @@ files:
|
|
105
104
|
- lib/katalyst/kpop/matchers/chained_matcher.rb
|
106
105
|
- lib/katalyst/kpop/matchers/frame_matcher.rb
|
107
106
|
- lib/katalyst/kpop/matchers/modal_matcher.rb
|
108
|
-
- lib/katalyst/kpop/matchers/redirect_finder.rb
|
109
107
|
- lib/katalyst/kpop/matchers/redirect_matcher.rb
|
110
108
|
- lib/katalyst/kpop/matchers/response_matcher.rb
|
111
109
|
- lib/katalyst/kpop/matchers/stream_matcher.rb
|
@@ -1,22 +0,0 @@
|
|
1
|
-
import { Controller } from "@hotwired/stimulus";
|
2
|
-
import { Turbo } from "@hotwired/turbo-rails";
|
3
|
-
|
4
|
-
export default class Kpop__RedirectController extends Controller {
|
5
|
-
static outlets = ["kpop--frame"];
|
6
|
-
static values = {
|
7
|
-
path: String,
|
8
|
-
target: String,
|
9
|
-
};
|
10
|
-
|
11
|
-
kpopFrameOutletConnected(frame) {
|
12
|
-
if (this.targetValue === frame.element.id) {
|
13
|
-
frame.dismiss().then(() => {
|
14
|
-
document.getElementById(this.targetValue).src = this.pathValue;
|
15
|
-
});
|
16
|
-
} else {
|
17
|
-
Turbo.visit(this.pathValue, { action: "replace" });
|
18
|
-
}
|
19
|
-
|
20
|
-
this.element.remove();
|
21
|
-
}
|
22
|
-
}
|
@@ -1,16 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require "katalyst/kpop/matchers/capybara_matcher"
|
4
|
-
|
5
|
-
module Katalyst
|
6
|
-
module Kpop
|
7
|
-
module Matchers
|
8
|
-
# @api private
|
9
|
-
class RedirectFinder < CapybaraMatcher
|
10
|
-
def initialize
|
11
|
-
super("[data-controller='kpop--redirect']")
|
12
|
-
end
|
13
|
-
end
|
14
|
-
end
|
15
|
-
end
|
16
|
-
end
|
File without changes
|