katalyst-kpop 3.4.0 → 4.0.0.beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +92 -74
  3. data/app/assets/builds/katalyst/kpop.esm.js +463 -457
  4. data/app/assets/builds/katalyst/kpop.js +463 -457
  5. data/app/assets/builds/katalyst/kpop.min.js +1 -1
  6. data/app/assets/builds/katalyst/kpop.min.js.map +1 -1
  7. data/app/assets/stylesheets/katalyst/kpop.css +69 -0
  8. data/app/components/kpop/frame_component.html.erb +3 -14
  9. data/app/components/kpop/frame_component.rb +15 -11
  10. data/app/components/kpop/modal_component.html.erb +7 -6
  11. data/app/components/kpop/modal_component.rb +9 -32
  12. data/app/controllers/concerns/katalyst/kpop/frame_request.rb +67 -8
  13. data/app/javascript/kpop/application.js +68 -7
  14. data/app/javascript/kpop/controllers/frame_controller.js +96 -66
  15. data/app/javascript/kpop/modals/content_modal.js +2 -58
  16. data/app/javascript/kpop/modals/frame_modal.js +19 -76
  17. data/app/javascript/kpop/modals/modal.js +96 -49
  18. data/app/javascript/kpop/modals/stream_modal.js +11 -62
  19. data/app/javascript/kpop/utils/debug.js +22 -0
  20. data/app/javascript/kpop/utils/link_observer.js +151 -0
  21. data/app/javascript/kpop/utils/ruleset.js +43 -0
  22. data/app/javascript/kpop/utils/stream_actions.js +21 -0
  23. data/app/views/layouts/kpop/frame.html.erb +3 -1
  24. data/app/views/layouts/kpop/stream.html.erb +3 -0
  25. data/lib/katalyst/kpop/engine.rb +1 -8
  26. data/lib/katalyst/kpop/matchers/modal_matcher.rb +1 -1
  27. data/lib/katalyst/kpop/matchers/src_matcher.rb +33 -0
  28. data/lib/katalyst/kpop/matchers.rb +11 -40
  29. metadata +8 -19
  30. data/app/assets/stylesheets/katalyst/kpop/_frame.scss +0 -90
  31. data/app/assets/stylesheets/katalyst/kpop/_modal.scss +0 -88
  32. data/app/assets/stylesheets/katalyst/kpop/_scrim.scss +0 -46
  33. data/app/assets/stylesheets/katalyst/kpop/_side_panel.scss +0 -64
  34. data/app/assets/stylesheets/katalyst/kpop/_variables.scss +0 -24
  35. data/app/assets/stylesheets/katalyst/kpop.scss +0 -6
  36. data/app/components/kpop/modal/footer_component.rb +0 -21
  37. data/app/components/kpop/modal/header_component.rb +0 -21
  38. data/app/components/kpop/modal/title_component.html.erb +0 -6
  39. data/app/components/kpop/modal/title_component.rb +0 -28
  40. data/app/components/scrim_component.rb +0 -32
  41. data/app/helpers/kpop_helper.rb +0 -32
  42. data/app/javascript/kpop/controllers/modal_controller.js +0 -30
  43. data/app/javascript/kpop/controllers/scrim_controller.js +0 -159
  44. data/app/javascript/kpop/debug.js +0 -3
  45. data/app/javascript/kpop/turbo_actions.js +0 -46
  46. data/app/javascript/kpop/utils/stream_renderer.js +0 -15
  47. data/lib/katalyst/kpop/turbo.rb +0 -49
@@ -1,2 +1,2 @@
1
- import{Controller as e}from"@hotwired/stimulus";import{Turbo as t}from"@hotwired/turbo-rails";class i{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 i=new Promise(t=>{window.addEventListener(e,()=>{t()},{once:!0})});return t(),i}get frameElement(){return document.getElementById(this.id)}get controller(){return this.frameElement?.kpop}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}static debug(e,...t){}debug(e,...t){}}class s extends i{static connect(e,t){e.open(new s(t.id),{animate:!1})}constructor(e,t=null){super(e),t&&(this.src=t)}async dismiss(){const e=this.fallbackLocationValue;await super.dismiss(),this.visitStarted?this.debug("skipping dismiss, visit started"):this.isCurrentLocation?(this.frameElement.innerHTML="",e&&window.history.replaceState(window.history.state,"",e)):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 i{static connect(e,t){const i=new n(t.id,t.src);return i.isCurrentLocation?(this.debug("restore",t.src),e.open(i,{animate:!1})):(console.warn("kpop: restored frame src doesn't match window href",i.src,window.location.href),e.clear())}static visit(e,t,i,s){i.hasAttribute("busy")&&(this.debug("clearing src to cancel turbo request"),i.src=""),i.src!==e?(i.src&&i.src!==window.location.href&&(console.warn("kpop: frame src doesn't match window",i.src,window.location.href,e),t.clear()),this.debug("navigate to",e),s()):this.debug("skipping navigate as already on location")}constructor(e,t){super(e),this.src=t}async dismiss(){await super.dismiss(),this.isCurrentLocation?await this.pop("turbo:load",()=>window.history.back()):this.debug("skipping dismiss, not current location")}beforeVisit(e,i){super.beforeVisit(e,i),i.preventDefault(),e.dismiss({animate:!1}).then(()=>{t.visit(i.detail.url),this.debug("before-visit-end")})}}class a extends e{static outlets=["scrim"];static targets=["modal"];static values={open:Boolean};connect(){this.debug("connect",this.element.src),this.element.kpop=this,function(e){const t=e.element.delegate.constructor.prototype;if(t._linkClickIntercepted)return;t._linkClickIntercepted=t.linkClickIntercepted,t.linkClickIntercepted=function(e,i){const s=e?.getAttribute("data-turbo-frame")||this.element.getAttribute("target");let a=document.getElementById(s);a instanceof Turbo.FrameElement||(a=this.element),a.kpop?(a.kpop.debug("navigate-frame %s => %s",a.src,i),n.visit(i,a.kpop,a,()=>{t._linkClickIntercepted.call(this,e,i)})):t._linkClickIntercepted.call(this,e,i)}}(this),this.element.src&&this.element.complete?(this.debug("new frame modal",this.element.src),n.connect(this,this.element)):this.modalElements.length>0?(this.debug("new content modal",window.location.pathname),s.connect(this,this.element)):(this.debug("no modal"),this.clear())}disconnect(){this.debug("disconnect",this.element.src),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):(await this.dismissing,this.opening||=this.#e(()=>this.#t(e,{animate:t})))}async dismiss({animate:e=!0,reason:t=""}={}){return this.isOpen?(await this.opening,this.dismissing||=this.#e(()=>this.#i({animate:e,reason:t}))):(this.debug("skip dismiss as already closed"),!1)}async clear(){if(this.element.src="",this.modalElements.forEach(e=>e.remove()),this.openValue=!1,this.scrimConnected)return this.scrimOutlet.hide({animate:!1});this.modal=null}popstate(e){this.modal?.popstate(this,e)}beforeFrameRender(e){this.debug("before-frame-render",e.detail.newFrame.baseURI),e.detail.newFrame.id===this.element.id&&(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=async e=>{await this.dismissing,this.debug("stream-render",e),await 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){this.debug("frame-load");const t=new n(this.element.id,this.element.src);window.addEventListener("turbo:visit",e=>{this.open(t,{animate:!0})},{once:!0})}get isOpen(){return this.openValue&&!this.dismissing}get modalElements(){return this.element.querySelectorAll("[data-controller*='kpop--modal']")}async#t(e,{animate:t=!0}={}){this.debug("open-start",{animate:t});const i=this.scrimConnected&&this.scrimOutlet;this.modal=e,this.openValue=!0,await e.open({animate:t}),await(i?.show({animate:t})),delete this.opening,this.debug("open-end"),Turbo.session.view.forceReloaded&&console.error("Turbo-Frame response is incompatible with current page")}async#i({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:i=window.scrollY,animate:s=!0}={}){this.openValue&&await this.hide({animate:s}),this.openValue=!0,this.dispatch("show",{bubbles:!0}),this.#s(e,t,i),s&&(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})}#s(e,t,i){this.captiveValue=e,this.zIndexValue=t,this.scrollY=i,this.element.style.zIndex=this.zIndexValue,document.body.style.top=`-${i}px`,document.body.style.position="fixed",document.body.style.paddingRight=`-${this.scrollPadding}px`,document.body.scrollHeight>window.innerHeight&&(document.body.style.overflowY="scroll")}#n(){this.captiveValue=this.defaultCaptiveValue,this.zIndexValue=this.defaultZIndexValue,this.element.style.removeProperty("z-index"),document.body.style.removeProperty("position"),document.body.style.removeProperty("top"),document.body.style.removeProperty("overflow-y"),window.scrollTo({left:0,top:this.scrollY,behavior:"instant"}),delete this.scrollY}}class r extends i{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,i){super.beforeVisit(e,i),i.preventDefault(),e.dismiss({animate:!1}).then(()=>{t.visit(i.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 d{constructor(e,t){this.frame=e,this.action=t}render(){this.frame.src="",this.frame.innerHTML="",this.frame.append(this.action.templateContent)}}function l(e){return e.targetElements[0]?.kpop}t.StreamActions.kpop_open=function(){const e=!l(this).openValue;l(this)?.dismiss({animate:e,reason:"before-turbo-stream"}).then(()=>{new d(this.targetElements[0],this).render(),l(this)?.open(new r(this.target,this),{animate:e})})},t.StreamActions.kpop_dismiss=function(){l(this)?.dismiss({reason:"turbo_stream.kpop.dismiss"})},t.StreamActions.kpop_redirect_to=function(){if(this.dataset.turboFrame===this.target){const e=document.createElement("A");e.setAttribute("data-turbo-action","replace"),this.targetElements[0].delegate.linkClickIntercepted(e,this.getAttribute("href"))}else t.visit(this.getAttribute("href"),{action:this.dataset.turboAction})};const c=[{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{c as default};
1
+ import{Controller as e}from"@hotwired/stimulus";import{Turbo as t}from"@hotwired/turbo-rails";let s=!1;const i=function(e){return s?console.debug.bind(console,"[%s] %s",e):n},n=()=>{};Object.defineProperty(i,"enabled",{get:function(){return s},set:function(e){s=e}});class r{constructor(e,t,s=null){this.frame=e,this.element=t,this.uri=new URL(s||t.dataset.src,window.location.origin)}connect(){this.element.addEventListener("cancel",this.cancel),this.element.addEventListener("close",this.close),this.element.addEventListener("mousedown",this.scrim)}disconnect(){this.element.removeEventListener("cancel",this.cancel),this.element.removeEventListener("close",this.close),this.element.removeEventListener("mousedown",this.scrim)}get src(){return this.uri.pathname+this.uri.search+this.uri.hash}cancel=e=>{this.debug("event:cancel",e),e.preventDefault(),this.frame.dismiss({animate:!0,reason:"dialog:cancel"})};close=e=>{this.debug("event:close",e),this.frame.clear({reason:"dialog:close"})};scrim=e=>{"DIALOG"===e.target.tagName&&(this.debug("event:scrim",e),this.frame.dismiss({animate:!0,reason:"dialog:scrim"}))};async open({animate:e=!0}={}){this.debug("open-start",e),await a(this.element,e,()=>this.element.showModal()),this.debug("open-end")}async dismiss({animate:e=!0}={}){this.debug("dismiss-start",e),await a(this.element,e,()=>this.element.removeAttribute("open")),this.debug("dismiss-end"),this.element.close()}beforeVisit(e,t){this.debug("before-visit",t.detail.url),this.frame.clear()}static get debug(){return i(this.name)}get debug(){return i(this.constructor.name)}}function a(e,t,s){if(!t)return s();const i=function(e,t="0.2s"){const s=getComputedStyle(e).getPropertyValue("--animation-duration")||t,i=parseFloat(s);return s.endsWith("ms")?i:1e3*i}(e);return new Promise(n=>{const r=()=>{e.removeEventListener("animationend",r,{once:!0}),clearTimeout(a),e.toggleAttribute("animate",!1),n()};e.addEventListener("animationend",r,{once:!0});const a=setTimeout(r,i);e.toggleAttribute("animate",t),s()})}class o extends r{static connect(e,t){e.open(new o(e,t),{animate:!1})}}class c extends r{static connect(e,t,s){return this.debug("restore",s),e.open(new c(e,t,s),{animate:!1})}static load(e,t,s){return this.debug("load",s),e.open(new c(e,t,s),{animate:!0})}static visit(e,t,s,i){s.hasAttribute("busy")&&(this.debug("clearing src to cancel turbo request"),s.src=""),this.debug("navigate to",e),i()}}class d extends e{static values={open:Boolean};connect(){this.debug("connect",this.element.src),this.element.kpop=this,function(e){const t=e.element.delegate.constructor.prototype;if(t._linkClickIntercepted)return;t._linkClickIntercepted=t.linkClickIntercepted,t.linkClickIntercepted=function(e,s){const i=e?.getAttribute("data-turbo-frame")||this.element.getAttribute("target");let n=document.getElementById(i);n instanceof Turbo.FrameElement||(n=this.element),n.kpop?(n.kpop.debug("navigate-frame %s => %s",n.src,s),c.visit(s,n.kpop,n,()=>{t._linkClickIntercepted.call(this,e,s)})):t._linkClickIntercepted.call(this,e,s)}}(this);const e=this.element.querySelector("dialog");this.element.src&&e?(this.debug("new frame modal",this.element.src),c.connect(this,e,this.element.src).then(()=>{})):e?(this.debug("new content modal",e),o.connect(this,e)):(this.debug("no modal"),this.clear())}disconnect(){this.debug("disconnect",this.element.src),delete this.element.kpop,delete this.modal}openValueChanged(e){this.debug("open-changed",e)}async open(e,{animate:t=!0}={}){return this.isOpen?(this.debug("skip open as already open"),this.modal||=e,!1):(await this.dismissing,this.opening||=Promise.resolve().then(()=>(e.connect(),this.#e(e,{animate:t}))))}async dismiss({animate:e=!0,reason:t=""}={}){return this.debug("event:dismiss",t),this.isOpen?(await this.opening,this.dismissing||=this.#t({animate:e,reason:t})):(this.debug("skip dismiss as already closed"),!1)}clear({reason:e=""}={}){this.debug("event:clear",e),this.element.src="",this.element.innerHTML="",this.openValue=!1,this.modal&&this.modal.disconnect(),delete this.modal,delete this.dismissing}beforeFrameRender(e){this.debug("before-frame-render",e.detail.newFrame.baseURI),e.detail.newFrame.id===this.element.id&&(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=async e=>{await this.dismissing,this.debug("stream-render",e),await t(e)}}beforeVisit(e){if(this.debug("before-visit",e.detail.url),e.detail.url===this.element.src)return;if("/resume_historical_location"===new URL(e.detail.url.toString(),document.baseURI).pathname)return e.preventDefault(),this.dismiss();this.isOpen&&this.modal.beforeVisit(this,e)}frameLoad(e){this.debug("frame-load"),c.load(this,e.target.firstElementChild,e.target.src).then(()=>{})}beforeFetchRequest(){const e=document.activeElement;e===document.body?delete this.lastFetchFocusRef:this.lastFetchFocusRef=new WeakRef(e)}get isOpen(){return this.openValue&&!this.dismissing}async#e(e,{animate:t=!0}={}){var s;return this.debug("open-start",{animate:t}),this.previousFocusRef=document.activeElement===document.body?this.lastFetchFocusRef:new WeakRef(document.activeElement),this.debug("capture focus",this.previousFocusRef?.deref()),this.modal=e,this.openValue=!0,this.element.delegate.sourceURL=this.modal.src,await e.open({animate:t}),delete this.opening,this.debug("open-end"),(s=this.modal?.element,s?s.querySelector("[autofocus]")??s.querySelector("button:not([disabled])"):null)?.focus(),Turbo.session.view.forceReloaded&&console.error("Turbo-Frame response is incompatible with current page"),!0}async#t({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.modal?.dismiss({animate:e})),this.clear(),this.previousFocusRef?.deref()?.focus(),this.debug("restore focus",this.previousFocusRef?.deref()),delete this.previousFocusRef,this.debug("dismiss-end")):this.debug("skip dismiss, element detached")}async#s(e){return new Promise(window.requestAnimationFrame).then(e)}get debug(){return i("FrameController")}}class u{started=!1;constructor(e,t){this.delegate=e,this.eventTarget=t}start(){this.started||("loading"===this.eventTarget.readyState?this.eventTarget.addEventListener("DOMContentLoaded",this.#i,{once:!0}):this.#i())}stop(){this.started&&(this.eventTarget.removeEventListener("mouseenter",this.#n,{capture:!0,passive:!0}),this.eventTarget.removeEventListener("turbo:before-prefetch",this.#n,{capture:!0,passive:!0}),this.eventTarget.removeEventListener("focusin",this.#n,{capture:!0,passive:!0}),this.eventTarget.removeEventListener("mouseleave",this.#r,{capture:!0,passive:!0}),this.eventTarget.removeEventListener("focusout",this.#r,{capture:!0,passive:!0}),this.started=!1)}#i=()=>{this.started||(this.started=!0,this.eventTarget.addEventListener("mouseenter",this.#n,{capture:!0,passive:!0}),this.eventTarget.addEventListener("focusin",this.#n,{capture:!0,passive:!0}),this.eventTarget.addEventListener("turbo:before-prefetch",this.#n,{capture:!0,passive:!0}),this.eventTarget.addEventListener("mouseleave",this.#r,{capture:!0,passive:!0}),this.eventTarget.addEventListener("focusout",this.#r,{capture:!0,passive:!0}))};#n=e=>{const t=e.target;if(t.matches&&t.matches("a[href]:not([target^=_]):not([download]):not([data-turbo-frame]")&&this.#a(t)){const e=t,s=function(e){return new URL(e.getAttribute("href").toString(),document.baseURI)}(e);this.delegate.isModalLink(e,s)&&(e.dataset.turboFrame="kpop")}};#r=e=>{const t=e.target;t.matches&&t.matches("a[href][data-turbo-frame='kpop']")&&delete t.dataset.turboFrame};#a(e){return!!e.getAttribute("href")&&(!h(e)&&(!l(e)&&(!m(e)&&!p(e))))}}const h=e=>e.origin!==document.location.origin||!["http:","https:"].includes(e.protocol)||e.hasAttribute("target"),l=e=>e.pathname+e.search===document.location.pathname+document.location.search||e.href.startsWith("#"),m=e=>"false"===e.getAttribute("data-turbo"),p=e=>{const t=e.getAttribute("data-turbo-method");return!(!t||"get"===t.toLowerCase())||(!!g(e)||(!!e.hasAttribute("data-turbo-confirm")||!!e.hasAttribute("data-turbo-stream")))},g=e=>e.hasAttribute("data-remote")||e.hasAttribute("data-behavior")||e.hasAttribute("data-confirm")||e.hasAttribute("data-method");class b{constructor(e=[]){this.rules=[],e.forEach(e=>{this.#o(e)})}properties(e){return this.rules.reduce((t,s)=>s(e,t),{})}#o({patterns:e,properties:t}){e.forEach(e=>{this.rules.push(function(e,t){return(s,i)=>e.test(s.pathname)?{...i,...t}:i}(new RegExp(e),t))})}}class f extends r{static async open(e,t){const s=!e.isOpen;await e.dismiss({animate:s,reason:"turbo-stream.kpop_open"}),e.element.append(t.templateContent);const i=e.element.querySelector("dialog"),n=i.dataset.src;await e.open(new f(e,i,n),{animate:s})}}class v{start(){t.StreamActions.kpop_open=w}stop(){delete t.StreamActions.kpop_open}}function w(){const e=this.targetElements[0]?.kpop;e&&f.open(e,this).then(()=>{})}const L=[{identifier:"kpop--frame",controllerConstructor:d}];class k{static configure(e={}){return this.instance||=new this(e),i.enabled=this.instance.debug,this.instance}constructor({rules:e=[],debug:t=!1}={}){this.config={rules:e,debug:t},this.ruleset=new b(e),this.linkObserver=new u(this,document),this.streamActions=new v}start(){return this.streamActions.start(),this.linkObserver.start(),window.addEventListener("turbo:before-fetch-request",R),this.debug&&(document.addEventListener("focusin",E),document.addEventListener("focusout",A)),this}stop(){window.removeEventListener("turbo:before-fetch-request",R),document.removeEventListener("focusin",E),document.removeEventListener("focusout",A),this.streamActions.stop(),this.linkObserver.stop()}isModalLink(e,t){return"modal"===this.ruleset.properties(t).context}get debug(){return Boolean(this.config.debug)}}const E=e=>i("Application")("focus",e.target),A=e=>i("Application")("blur",e.target),R=e=>{const t=e.detail.fetchOptions.headers;t.Accept?.includes("text/vnd.turbo-stream.html")&&(t["Kpop-Available"]="true")};export{L as controllers,k 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/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 controller() {\n return this.frameElement?.kpop;\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 static debug(event, ...args) {\n if (DEBUG) console.debug(`${this.name}:${event}`, ...args);\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 static connect(frame, element) {\n frame.open(new ContentModal(element.id), { animate: false });\n }\n\n constructor(id, src = null) {\n super(id);\n\n if (src) this.src = src;\n }\n\n /**\n * When the modal is dismissed we can't rely on a back navigation to close the\n * modal as the user may have navigated to a different location. Instead we\n * remove the content from the dom and replace the current history state with\n * the fallback location, if set.\n *\n * If there is no fallback location, we may be showing a stream modal that was\n * injected and cached by turbo. In this case, we clear the frame element and\n * do not change history.\n *\n * @returns {Promise<void>}\n */\n async dismiss() {\n const fallbackLocation = this.fallbackLocationValue;\n\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 this.frameElement.innerHTML = \"\";\n\n if (fallbackLocation) {\n window.history.replaceState(window.history.state, \"\", fallbackLocation);\n }\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 /**\n * When the FrameController detects a frame element on connect, it runs this\n * method to sanity check the frame src and restore the modal state.\n *\n * @param frame FrameController\n * @param element TurboFrame element\n */\n static connect(frame, element) {\n const modal = new FrameModal(element.id, element.src);\n\n // state reconciliation for turbo restore of invalid frames\n if (modal.isCurrentLocation) {\n // restoration visit\n this.debug(\"restore\", element.src);\n return frame.open(modal, { animate: false });\n } else {\n console.warn(\n \"kpop: restored frame src doesn't match window href\",\n modal.src,\n window.location.href,\n );\n return frame.clear();\n }\n }\n\n /**\n * When a user clicks a kpop link, turbo intercepts the click and calls\n * #navigateFrame on the turbo frame controller before setting the TurboFrame\n * element's src attribute. KPOP intercepts this call and calls this method\n * first so we cancel problematic navigations that might cache invalid states.\n *\n * @param location URL requested by turbo\n * @param frame FrameController\n * @param element TurboFrame element\n * @param resolve continuation chain\n */\n static visit(location, frame, element, resolve) {\n // Ensure that turbo doesn't cache the frame in a loading state by cancelling\n // the current request (if any) by clearing the src.\n // Known issue: this won't work if the frame was previously rendering a useful src.\n if (element.hasAttribute(\"busy\")) {\n this.debug(\"clearing src to cancel turbo request\");\n element.src = \"\";\n }\n\n if (element.src === location) {\n this.debug(\"skipping navigate as already on location\");\n return;\n }\n\n if (element.src && element.src !== window.location.href) {\n console.warn(\n \"kpop: frame src doesn't match window\",\n element.src,\n window.location.href,\n location,\n );\n frame.clear();\n }\n\n this.debug(\"navigate to\", location);\n resolve();\n }\n\n constructor(id, src) {\n super(id);\n this.src = src;\n }\n\n /**\n * FrameModals are closed by running pop state and awaiting the turbo:load\n * event that follows on history restoration.\n *\n * @returns {Promise<void>}\n */\n async dismiss() {\n await super.dismiss();\n\n if (!this.isCurrentLocation) {\n this.debug(\"skipping dismiss, not current location\");\n } else {\n await this.pop(\"turbo:load\", () => window.history.back());\n }\n\n // no specific close action required, this is turbo's responsibility\n }\n\n /**\n * When user navigates from inside a Frame modal, dismiss the modal first so\n * that the modal does not appear in the history stack.\n *\n * @param frame FrameController\n * @param e Turbo navigation event\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","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 // allow our code to intercept frame navigation requests before dom changes\n installNavigationInterception(this);\n\n if (this.element.src && this.element.complete) {\n this.debug(\"new frame modal\", this.element.src);\n FrameModal.connect(this, this.element);\n } else if (this.modalElements.length > 0) {\n this.debug(\"new content modal\", window.location.pathname);\n ContentModal.connect(this, this.element);\n } else {\n this.debug(\"no modal\");\n this.clear();\n }\n }\n\n disconnect() {\n this.debug(\"disconnect\", this.element.src);\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 await this.dismissing;\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 await this.opening;\n\n return (this.dismissing ||= this.#nextFrame(() =>\n this.#dismiss({ animate, reason }),\n ));\n }\n\n async clear() {\n // clear the src from the frame (if any)\n this.element.src = \"\";\n\n // remove any open modal(s)\n this.modalElements.forEach((element) => element.remove());\n\n // mark the modal as hidden (will hide scrim on connect)\n this.openValue = false;\n\n // close the scrim, if connected\n if (this.scrimConnected) {\n return this.scrimOutlet.hide({ animate: false });\n }\n\n // unset modal\n this.modal = null;\n }\n\n // EVENTS\n\n popstate(event) {\n this.modal?.popstate(this, event);\n }\n\n /**\n * Incoming frame render, dismiss the current modal (if any) first.\n *\n * We're starting the actual visit\n *\n * @param event turbo:before-render\n */\n beforeFrameRender(event) {\n this.debug(\"before-frame-render\", event.detail.newFrame.baseURI);\n\n if (event.detail.newFrame.id !== this.element.id) return;\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 = async (stream) => {\n await this.dismissing;\n\n this.debug(\"stream-render\", stream);\n\n await resume(stream);\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 const modal = new FrameModal(this.element.id, this.element.src);\n\n window.addEventListener(\n \"turbo:visit\",\n (e) => {\n this.open(modal, { animate: true });\n },\n { once: true },\n );\n }\n\n get isOpen() {\n return this.openValue && !this.dismissing;\n }\n\n get modalElements() {\n return this.element.querySelectorAll(\"[data-controller*='kpop--modal']\");\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 // Detect https://github.com/hotwired/turbo-rails/issues/580\n if (Turbo.session.view.forceReloaded) {\n console.error(\"Turbo-Frame response is incompatible with current page\");\n }\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\n/**\n * Monkey patch for Turbo#FrameController.\n *\n * Intercept calls to linkClickIntercepted(element, location) and ensures\n * that src is cleared if the frame is busy so that we don't restore an\n * in-progress src on restoration visits.\n *\n * See Turbo issue: https://github.com/hotwired/turbo/issues/1055\n *\n * @param controller FrameController\n */\nfunction installNavigationInterception(controller) {\n const TurboFrameController =\n controller.element.delegate.constructor.prototype;\n\n if (TurboFrameController._linkClickIntercepted) return;\n\n TurboFrameController._linkClickIntercepted =\n TurboFrameController.linkClickIntercepted;\n TurboFrameController.linkClickIntercepted = function (element, location) {\n // #findFrameElement\n const id =\n element?.getAttribute(\"data-turbo-frame\") ||\n this.element.getAttribute(\"target\");\n let frame = document.getElementById(id);\n if (!(frame instanceof Turbo.FrameElement)) {\n frame = this.element;\n }\n\n if (frame.kpop) {\n frame.kpop.debug(\"navigate-frame %s => %s\", frame.src, location);\n FrameModal.visit(location, frame.kpop, frame, () => {\n TurboFrameController._linkClickIntercepted.call(\n this,\n element,\n location,\n );\n });\n } else {\n TurboFrameController._linkClickIntercepted.call(this, element, location);\n }\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.element.style.zIndex = this.zIndexValue;\n document.body.style.top = `-${top}px`;\n document.body.style.position = \"fixed\";\n document.body.style.paddingRight = `-${this.scrollPadding}px`;\n\n if (document.body.scrollHeight > window.innerHeight) {\n document.body.style.overflowY = \"scroll\";\n }\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 this.element.style.removeProperty(\"z-index\");\n document.body.style.removeProperty(\"position\");\n document.body.style.removeProperty(\"top\");\n document.body.style.removeProperty(\"overflow-y\");\n\n window.scrollTo({ left: 0, top: this.scrollY, behavior: \"instant\" });\n\n delete this.scrollY;\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 /**\n * When the modal opens, push a state event for the current location so that\n * the user can dismiss the modal by navigating back.\n *\n * @returns {Promise<void>}\n */\n async open() {\n await super.open();\n\n window.history.pushState({ kpop: true, id: this.id }, \"\", window.location);\n }\n\n /**\n * On dismiss, pop the state event that was pushed when the modal opened,\n * then clear any modals from the turbo frame element.\n *\n * @returns {Promise<void>}\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 /**\n * On navigation from inside the modal, dismiss the modal first so that the\n * modal does not appear in the history stack.\n *\n * @param frame TurboFrame element\n * @param e Turbo navigation event\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 /**\n * If the user pops state, dismiss the modal.\n *\n * @param frame FrameController\n * @param e history event\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 DEBUG from \"./debug\";\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 if (DEBUG)\n console.debug(\n `kpop: redirecting ${this.target} to ${this.getAttribute(\"href\")}`,\n );\n const a = document.createElement(\"A\");\n a.setAttribute(\"data-turbo-action\", \"replace\");\n this.targetElements[0].delegate.linkClickIntercepted(\n a,\n this.getAttribute(\"href\"),\n );\n } else {\n if (DEBUG)\n console.debug(`kpop: redirecting to ${this.getAttribute(\"href\")}`);\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","controller","kpop","modalElement","querySelector","currentLocationValue","dataset","fallbackLocationValue","isCurrentLocation","history","turbo","Turbo","session","location","href","src","args","ContentModal","connect","element","animate","super","fallbackLocation","visitStarted","innerHTML","replaceState","scrimOutlet","hide","URL","toString","baseURI","FrameModal","modal","console","warn","clear","visit","hasAttribute","back","preventDefault","then","Kpop__FrameController","Controller","static","Boolean","TurboFrameController","delegate","prototype","_linkClickIntercepted","linkClickIntercepted","getAttribute","FrameElement","call","installNavigationInterception","complete","modalElements","length","pathname","disconnect","scrimOutletConnected","scrim","scrimConnected","openValue","show","openValueChanged","parentElement","style","display","isOpen","dismissing","opening","nextFrame","reason","forEach","remove","beforeFrameRender","newFrame","resume","beforeStreamRender","render","async","stream","frameLoad","querySelectorAll","view","forceReloaded","error","isConnected","requestAnimationFrame","ScrimController","captive","zIndex","Number","defaultZIndexValue","zIndexValue","defaultCaptiveValue","captiveValue","top","scrollY","dispatch","bubbles","showAnimating","hideAnimating","escape","key","defaultPrevented","body","position","paddingRight","scrollPadding","scrollHeight","innerHeight","overflowY","removeProperty","scrollTo","left","behavior","StreamModal","action","pushState","StreamRenderer","append","templateContent","targetElements","StreamActions","kpop_open","target","kpop_dismiss","kpop_redirect_to","turboFrame","a","createElement","setAttribute","turboAction","Definitions","identifier","controllerConstructor","FrameController","fallback_location","String","layout","layoutValue","classList","toggle"],"mappings":"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"}
1
+ {"version":3,"file":"kpop.min.js","sources":["../../../javascript/kpop/utils/debug.js","../../../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/utils/link_observer.js","../../../javascript/kpop/utils/ruleset.js","../../../javascript/kpop/modals/stream_modal.js","../../../javascript/kpop/utils/stream_actions.js","../../../javascript/kpop/application.js"],"sourcesContent":["let enabled = false;\n\nconst debug = function (receiver) {\n if (enabled) {\n return console.debug.bind(console, \"[%s] %s\", receiver);\n } else {\n return noop;\n }\n};\n\nconst noop = () => {};\n\nObject.defineProperty(debug, \"enabled\", {\n get: function () {\n return enabled;\n },\n set: function (debug) {\n enabled = debug;\n },\n});\n\nexport default debug;\n","import debug from \"../utils/debug\";\n\nexport class Modal {\n constructor(frame, dialog, src = null) {\n this.frame = frame;\n this.element = dialog;\n this.uri = new URL(src || dialog.dataset.src, window.location.origin);\n }\n\n connect() {\n this.element.addEventListener(\"cancel\", this.cancel);\n this.element.addEventListener(\"close\", this.close);\n this.element.addEventListener(\"mousedown\", this.scrim);\n }\n\n disconnect() {\n this.element.removeEventListener(\"cancel\", this.cancel);\n this.element.removeEventListener(\"close\", this.close);\n this.element.removeEventListener(\"mousedown\", this.scrim);\n }\n\n get src() {\n return this.uri.pathname + this.uri.search + this.uri.hash;\n }\n\n cancel = (e) => {\n this.debug(\"event:cancel\", e);\n\n e.preventDefault();\n\n this.frame.dismiss({ animate: true, reason: \"dialog:cancel\" });\n };\n\n close = (e) => {\n this.debug(\"event:close\", e);\n\n this.frame.clear({ reason: \"dialog:close\" });\n };\n\n scrim = (e) => {\n if (e.target.tagName === \"DIALOG\") {\n this.debug(\"event:scrim\", e);\n\n this.frame.dismiss({ animate: true, reason: \"dialog:scrim\" });\n }\n };\n\n async open({ animate = true } = {}) {\n this.debug(\"open-start\", animate);\n\n await animation(this.element, animate, () => this.element.showModal());\n\n this.debug(\"open-end\");\n }\n\n /**\n * Modals are closed by animating out the modal then removing the modal\n * element from the wrapping frame.\n *\n * @returns {Promise<void>}\n */\n async dismiss({ animate = true } = {}) {\n this.debug(\"dismiss-start\", animate);\n\n await animation(this.element, animate, () =>\n this.element.removeAttribute(\"open\"),\n );\n\n this.debug(\"dismiss-end\");\n\n this.element.close();\n }\n\n /**\n * When user navigates from inside a modal, dismiss the modal first so\n * that the modal does not appear in the history stack.\n *\n * @param frame FrameController\n * @param e Turbo navigation event\n */\n beforeVisit(frame, e) {\n this.debug(`before-visit`, e.detail.url);\n\n this.frame.clear();\n }\n\n static get debug() {\n return debug(this.name);\n }\n\n get debug() {\n return debug(this.constructor.name);\n }\n}\n\nfunction animation(el, animate, trigger) {\n if (!animate) return trigger();\n\n const duration = animationDuration(el);\n\n return new Promise((resolve) => {\n const resolver = () => {\n el.removeEventListener(\"animationend\", resolver, { once: true });\n clearTimeout(timeout);\n el.toggleAttribute(\"animate\", false);\n resolve();\n };\n\n el.addEventListener(\"animationend\", resolver, { once: true });\n const timeout = setTimeout(resolver, duration);\n\n el.toggleAttribute(\"animate\", animate);\n trigger();\n });\n}\n\nfunction animationDuration(el, defaultValue = \"0.2s\") {\n const value =\n getComputedStyle(el).getPropertyValue(\"--animation-duration\") ||\n defaultValue;\n const num = parseFloat(value);\n if (value.endsWith(\"ms\")) return num;\n return num * 1000;\n}\n","import { Modal } from \"./modal\";\n\nexport class ContentModal extends Modal {\n static connect(frame, dialog) {\n frame.open(new ContentModal(frame, dialog), { animate: false });\n }\n}\n","import { Modal } from \"./modal\";\n\nexport class FrameModal extends Modal {\n /**\n * When the FrameController detects a frame element on connect, it runs this\n * method to sanity check the frame src and restore the modal state.\n *\n * @param {Kpop__FrameController} frame\n * @param {HTMLDialogElement} dialog\n * @param {String} src\n */\n static connect(frame, dialog, src) {\n // restoration visit\n this.debug(\"restore\", src);\n return frame.open(new FrameModal(frame, dialog, src), { animate: false });\n }\n\n /**\n * When the FrameController detects a frame load event, it runs this\n * method to open the modal.\n *\n * @param {Kpop__FrameController} frame\n * @param {HTMLDialogElement} dialog\n * @param {String} src\n */\n static load(frame, dialog, src) {\n this.debug(\"load\", src);\n return frame.open(new FrameModal(frame, dialog, src), { animate: true });\n }\n\n /**\n * When a user clicks a kpop link, turbo intercepts the click and calls\n * #navigateFrame on the turbo frame controller before setting the TurboFrame\n * element's src attribute. KPOP intercepts this call and calls this method\n * first so we cancel problematic navigations that might cache invalid states.\n *\n * @param location URL requested by turbo\n * @param frame FrameController\n * @param element TurboFrame element\n * @param resolve continuation chain\n */\n static visit(location, frame, element, resolve) {\n // Ensure that turbo doesn't cache the frame in a loading state by cancelling\n // the current request (if any) by clearing the src.\n // Known issue: this won't work if the frame was previously rendering a useful src.\n if (element.hasAttribute(\"busy\")) {\n this.debug(\"clearing src to cancel turbo request\");\n element.src = \"\";\n }\n\n this.debug(\"navigate to\", location);\n resolve();\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nimport { ContentModal } from \"../modals/content_modal\";\nimport { FrameModal } from \"../modals/frame_modal\";\n\nimport debug from \"../utils/debug\";\n\nexport default class Kpop__FrameController extends Controller {\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 // allow our code to intercept frame navigation requests before dom changes\n installNavigationInterception(this);\n\n const dialog = this.element.querySelector(\"dialog\");\n\n if (this.element.src && dialog) {\n this.debug(\"new frame modal\", this.element.src);\n FrameModal.connect(this, dialog, this.element.src).then(() => {});\n } else if (dialog) {\n this.debug(\"new content modal\", dialog);\n ContentModal.connect(this, dialog);\n } else {\n this.debug(\"no modal\");\n this.clear();\n }\n }\n\n disconnect() {\n this.debug(\"disconnect\", this.element.src);\n\n delete this.element.kpop;\n delete this.modal;\n }\n\n openValueChanged(open) {\n this.debug(\"open-changed\", open);\n }\n\n /**\n * Animate an attached modal into the foreground. Returns a promise that\n * resolves when the animation is complete.\n *\n * @param modal\n * @param animate\n * @returns {Promise<Boolean>}\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 await this.dismissing;\n\n return (this.opening ||= Promise.resolve().then(() => {\n modal.connect();\n return this.#open(modal, { animate });\n }));\n }\n\n /**\n * Cause a modal to hide. Returns a promise that will resolve when the\n * animation (if requested) is finished.\n *\n * If the modal is already animating out, returns the existing promise instead.\n *\n * @param {Boolean} animate\n * @param {String} reason\n * @returns {Promise}\n */\n async dismiss({ animate = true, reason = \"\" } = {}) {\n this.debug(\"event:dismiss\", reason);\n\n if (!this.isOpen) {\n this.debug(\"skip dismiss as already closed\");\n return false;\n }\n\n await this.opening;\n\n return (this.dismissing ||= this.#dismiss({ animate, reason }));\n }\n\n /**\n * Clean up after a modal is finished dismissing.\n */\n clear({ reason = \"\" } = {}) {\n this.debug(\"event:clear\", reason);\n\n // clear the src from the frame (if any)\n this.element.src = \"\";\n this.element.innerHTML = \"\";\n\n // mark the modal as hidden\n this.openValue = false;\n\n // unset modal\n if (this.modal) this.modal.disconnect();\n delete this.modal;\n delete this.dismissing;\n }\n\n // EVENTS\n\n /**\n * Incoming frame render, dismiss the current modal (if any) first.\n *\n * We're starting the actual visit\n *\n * @param event turbo:before-render\n */\n beforeFrameRender(event) {\n this.debug(\"before-frame-render\", event.detail.newFrame.baseURI);\n\n if (event.detail.newFrame.id !== this.element.id) return;\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 = async (stream) => {\n await this.dismissing;\n\n this.debug(\"stream-render\", stream);\n\n await resume(stream);\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 const url = new URL(e.detail.url.toString(), document.baseURI);\n if (url.pathname === \"/resume_historical_location\") {\n e.preventDefault();\n return this.dismiss();\n }\n\n // ignore unless we're open\n if (!this.isOpen) return;\n\n this.modal.beforeVisit(this, e);\n }\n\n frameLoad(e) {\n this.debug(\"frame-load\");\n\n FrameModal.load(this, e.target.firstElementChild, e.target.src).then(\n () => {},\n );\n }\n\n /**\n * Outgoing fetch request. Capture the initiator so we can return focus if it causes a modal to show.\n */\n beforeFetchRequest() {\n const focusElement = document.activeElement;\n\n if (focusElement === document.body) {\n delete this.lastFetchFocusRef;\n } else {\n this.lastFetchFocusRef = new WeakRef(focusElement);\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 this.previousFocusRef =\n document.activeElement === document.body\n ? this.lastFetchFocusRef\n : new WeakRef(document.activeElement);\n this.debug(\"capture focus\", this.previousFocusRef?.deref());\n\n this.modal = modal;\n this.openValue = true;\n\n // Set turbo-frame[src] without causing a load event\n this.element.delegate.sourceURL = this.modal.src;\n\n await modal.open({ animate });\n\n delete this.opening;\n\n this.debug(\"open-end\");\n\n autofocus(this.modal?.element)?.focus();\n\n // Detect https://github.com/hotwired/turbo-rails/issues/580\n if (Turbo.session.view.forceReloaded) {\n console.error(\"Turbo-Frame response is incompatible with current page\");\n }\n\n return true;\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 }\n\n await this.modal?.dismiss({ animate });\n\n this.clear();\n\n this.previousFocusRef?.deref()?.focus();\n this.debug(\"restore focus\", this.previousFocusRef?.deref());\n delete this.previousFocusRef;\n\n this.debug(\"dismiss-end\");\n }\n\n async #nextFrame(callback) {\n return new Promise(window.requestAnimationFrame).then(callback);\n }\n\n get debug() {\n return debug(\"FrameController\");\n }\n}\n\n/**\n * Monkey patch for Turbo#FrameController.\n *\n * Intercept calls to linkClickIntercepted(element, location) and ensures\n * that src is cleared if the frame is busy so that we don't restore an\n * in-progress src on restoration visits.\n *\n * See Turbo issue: https://github.com/hotwired/turbo/issues/1055\n *\n * @param controller FrameController\n */\nfunction installNavigationInterception(controller) {\n const TurboFrameController =\n controller.element.delegate.constructor.prototype;\n\n if (TurboFrameController._linkClickIntercepted) return;\n\n TurboFrameController._linkClickIntercepted =\n TurboFrameController.linkClickIntercepted;\n TurboFrameController.linkClickIntercepted = function (element, location) {\n // #findFrameElement\n const id =\n element?.getAttribute(\"data-turbo-frame\") ||\n this.element.getAttribute(\"target\");\n let frame = document.getElementById(id);\n if (!(frame instanceof Turbo.FrameElement)) {\n frame = this.element;\n }\n\n if (frame.kpop) {\n frame.kpop.debug(\"navigate-frame %s => %s\", frame.src, location);\n FrameModal.visit(location, frame.kpop, frame, () => {\n TurboFrameController._linkClickIntercepted.call(\n this,\n element,\n location,\n );\n });\n } else {\n TurboFrameController._linkClickIntercepted.call(this, element, location);\n }\n };\n}\n\nfunction autofocus(container) {\n if (!container) return null;\n\n return (\n container.querySelector(\"[autofocus]\") ??\n container.querySelector(\"button:not([disabled])\")\n );\n}\n","/**\n * Based on Turbo's LinkObserver, checks links on mouse-over and focus to see\n * whether they should open in modals. If they should, then sets the\n * data-turbo-frame attribute so it will be prefetched and opened in the context\n * of the kpop turbo frame.\n */\nexport default class LinkObserver {\n started = false;\n\n constructor(delegate, eventTarget) {\n this.delegate = delegate;\n this.eventTarget = eventTarget;\n }\n\n start() {\n if (this.started) return;\n if (this.eventTarget.readyState === \"loading\") {\n this.eventTarget.addEventListener(\"DOMContentLoaded\", this.#enable, {\n once: true,\n });\n } else {\n this.#enable();\n }\n }\n\n stop() {\n if (!this.started) return;\n this.eventTarget.removeEventListener(\"mouseenter\", this.#addKpopLink, {\n capture: true,\n passive: true,\n });\n this.eventTarget.removeEventListener(\n \"turbo:before-prefetch\",\n this.#addKpopLink,\n {\n capture: true,\n passive: true,\n },\n );\n this.eventTarget.removeEventListener(\"focusin\", this.#addKpopLink, {\n capture: true,\n passive: true,\n });\n this.eventTarget.removeEventListener(\"mouseleave\", this.#removeKpopLink, {\n capture: true,\n passive: true,\n });\n this.eventTarget.removeEventListener(\"focusout\", this.#removeKpopLink, {\n capture: true,\n passive: true,\n });\n this.started = false;\n }\n\n #enable = () => {\n if (this.started) return;\n this.started = true;\n this.eventTarget.addEventListener(\"mouseenter\", this.#addKpopLink, {\n capture: true,\n passive: true,\n });\n this.eventTarget.addEventListener(\"focusin\", this.#addKpopLink, {\n capture: true,\n passive: true,\n });\n this.eventTarget.addEventListener(\n \"turbo:before-prefetch\",\n this.#addKpopLink,\n {\n capture: true,\n passive: true,\n },\n );\n this.eventTarget.addEventListener(\"mouseleave\", this.#removeKpopLink, {\n capture: true,\n passive: true,\n });\n this.eventTarget.addEventListener(\"focusout\", this.#removeKpopLink, {\n capture: true,\n passive: true,\n });\n };\n\n #addKpopLink = (event) => {\n const target = event.target;\n const isLink =\n target.matches &&\n target.matches(\n \"a[href]:not([target^=_]):not([download]):not([data-turbo-frame]\",\n );\n if (isLink && this.#isPrefetchable(target)) {\n const link = target;\n const location = getLocationForLink(link);\n if (this.delegate.isModalLink(link, location)) {\n link.dataset.turboFrame = \"kpop\";\n }\n }\n };\n\n #removeKpopLink = (event) => {\n const target = event.target;\n const isLink =\n target.matches && target.matches(\"a[href][data-turbo-frame='kpop']\");\n if (isLink) {\n delete target.dataset.turboFrame;\n }\n };\n\n #isPrefetchable(link) {\n const href = link.getAttribute(\"href\");\n if (!href) return false;\n if (unfetchableLink(link)) return false;\n if (linkToTheSamePage(link)) return false;\n if (linkOptsOut(link)) return false;\n if (nonSafeLink(link)) return false;\n return true;\n }\n}\n\nfunction getLocationForLink(link) {\n return new URL(link.getAttribute(\"href\").toString(), document.baseURI);\n}\n\nconst unfetchableLink = (link) =>\n link.origin !== document.location.origin ||\n ![\"http:\", \"https:\"].includes(link.protocol) ||\n link.hasAttribute(\"target\");\n\nconst linkToTheSamePage = (link) =>\n link.pathname + link.search ===\n document.location.pathname + document.location.search ||\n link.href.startsWith(\"#\");\n\nconst linkOptsOut = (link) => {\n return link.getAttribute(\"data-turbo\") === \"false\";\n};\n\nconst nonSafeLink = (link) => {\n const turboMethod = link.getAttribute(\"data-turbo-method\");\n if (turboMethod && turboMethod.toLowerCase() !== \"get\") return true;\n if (isUJS(link)) return true;\n if (link.hasAttribute(\"data-turbo-confirm\")) return true;\n if (link.hasAttribute(\"data-turbo-stream\")) return true;\n return false;\n};\n\nconst isUJS = (link) =>\n link.hasAttribute(\"data-remote\") ||\n link.hasAttribute(\"data-behavior\") ||\n link.hasAttribute(\"data-confirm\") ||\n link.hasAttribute(\"data-method\");\n","/**\n * Similar to Hotwire's PathConfiguration.json, this class compiles a list of\n * rules to check link hrefs against so that we can identify links that\n * should open in a KPOP modal.\n *\n * Unlike Hotwire Native, we can't intercept 303s in the browser before they\n * load. Browser sandbox prevents us from inspecting the location of redirect\n * requests so we can only intercept links that match modals directly.\n *\n * For posts and redirects, we need server support (flash modals, streams).\n */\nexport default class Ruleset {\n constructor(rules = []) {\n this.rules = [];\n\n rules.forEach((rule) => {\n this.#compileRule(rule);\n });\n }\n\n /**\n * Returns properties for the given URL\n *\n * @param {URL} location\n * @returns {} properties\n */\n properties(location) {\n return this.rules.reduce((c, f) => f(location, c), {});\n }\n\n #compileRule({ patterns, properties }) {\n patterns.forEach((pattern) => {\n this.rules.push(locationMatcher(new RegExp(pattern), properties));\n });\n }\n}\n\nfunction locationMatcher(re, properties) {\n return (location, accumulator) =>\n re.test(location.pathname)\n ? { ...accumulator, ...properties }\n : accumulator;\n}\n","import { Modal } from \"./modal\";\n\nexport class StreamModal extends Modal {\n /**\n * When a turbo-stream[action=kpop_open] element is rendered, it runs this\n * method to load the modal template as a StreamModal.\n *\n * @param {Kpop__FrameController} frame\n * @param {Turbo.StreamElement} action\n */\n static async open(frame, action) {\n const animate = !frame.isOpen;\n\n await frame.dismiss({ animate, reason: \"turbo-stream.kpop_open\" });\n\n frame.element.append(action.templateContent);\n\n const dialog = frame.element.querySelector(\"dialog\");\n const src = dialog.dataset.src;\n\n await frame.open(new StreamModal(frame, dialog, src), { animate });\n }\n}\n","import { Turbo } from \"@hotwired/turbo-rails\";\n\nimport { StreamModal } from \"../modals/stream_modal\";\n\nexport default class StreamActions {\n start() {\n Turbo.StreamActions.kpop_open = openStreamModal;\n }\n\n stop() {\n delete Turbo.StreamActions.kpop_open;\n }\n}\n\nfunction openStreamModal() {\n const frame = this.targetElements[0]?.kpop;\n\n if (frame) {\n StreamModal.open(frame, this).then(() => {});\n }\n}\n","import FrameController from \"../kpop/controllers/frame_controller\";\n\nimport debug from \"./utils/debug\";\nimport LinkObserver from \"./utils/link_observer\";\nimport Ruleset from \"./utils/ruleset\";\nimport StreamActions from \"./utils/stream_actions\";\n\nexport const controllers = [\n { identifier: \"kpop--frame\", controllerConstructor: FrameController },\n];\n\nexport default class Application {\n static configure(config = {}) {\n this.instance ||= new this(config);\n debug.enabled = this.instance.debug;\n return this.instance;\n }\n\n constructor({ rules = [], debug = false } = {}) {\n this.config = { rules, debug };\n this.ruleset = new Ruleset(rules);\n this.linkObserver = new LinkObserver(this, document);\n this.streamActions = new StreamActions();\n }\n\n start() {\n this.streamActions.start();\n this.linkObserver.start();\n\n window.addEventListener(\n \"turbo:before-fetch-request\",\n addKpopToRequestHeaders,\n );\n\n if (this.debug) {\n document.addEventListener(\"focusin\", debugFocusIn);\n document.addEventListener(\"focusout\", debugFocusOut);\n }\n\n return this;\n }\n\n stop() {\n window.removeEventListener(\n \"turbo:before-fetch-request\",\n addKpopToRequestHeaders,\n );\n document.removeEventListener(\"focusin\", debugFocusIn);\n document.removeEventListener(\"focusout\", debugFocusOut);\n\n this.streamActions.stop();\n this.linkObserver.stop();\n }\n\n isModalLink(link, location) {\n const properties = this.ruleset.properties(location);\n return properties.context === \"modal\";\n }\n\n get debug() {\n return Boolean(this.config.debug);\n }\n}\n\nconst debugFocusIn = (e) => debug(\"Application\")(\"focus\", e.target);\nconst debugFocusOut = (e) => debug(\"Application\")(\"blur\", e.target);\n\nconst addKpopToRequestHeaders = (e) => {\n const headers = e.detail.fetchOptions.headers;\n\n if (headers[\"Accept\"]?.includes(\"text/vnd.turbo-stream.html\")) {\n headers[\"Kpop-Available\"] = \"true\";\n }\n};\n"],"names":["enabled","debug","receiver","console","bind","noop","Object","defineProperty","get","set","Modal","constructor","frame","dialog","src","this","element","uri","URL","dataset","window","location","origin","connect","addEventListener","cancel","close","scrim","disconnect","removeEventListener","pathname","search","hash","e","preventDefault","dismiss","animate","reason","clear","target","tagName","open","animation","showModal","removeAttribute","beforeVisit","detail","url","name","el","trigger","duration","defaultValue","value","getComputedStyle","getPropertyValue","num","parseFloat","endsWith","animationDuration","Promise","resolve","resolver","once","clearTimeout","timeout","toggleAttribute","setTimeout","ContentModal","FrameModal","load","visit","hasAttribute","Kpop__FrameController","Controller","static","Boolean","kpop","controller","TurboFrameController","delegate","prototype","_linkClickIntercepted","linkClickIntercepted","id","getAttribute","document","getElementById","Turbo","FrameElement","call","installNavigationInterception","querySelector","then","modal","openValueChanged","isOpen","dismissing","opening","innerHTML","openValue","beforeFrameRender","event","newFrame","baseURI","resume","beforeStreamRender","render","async","stream","toString","frameLoad","firstElementChild","beforeFetchRequest","focusElement","activeElement","body","lastFetchFocusRef","WeakRef","container","previousFocusRef","deref","sourceURL","focus","session","view","forceReloaded","error","isConnected","warn","nextFrame","callback","requestAnimationFrame","LinkObserver","started","eventTarget","start","readyState","enable","stop","addKpopLink","capture","passive","removeKpopLink","matches","isPrefetchable","link","getLocationForLink","isModalLink","turboFrame","unfetchableLink","linkToTheSamePage","linkOptsOut","nonSafeLink","includes","protocol","href","startsWith","turboMethod","toLowerCase","isUJS","Ruleset","rules","forEach","rule","compileRule","properties","reduce","c","f","patterns","pattern","push","re","accumulator","test","locationMatcher","RegExp","StreamModal","action","append","templateContent","StreamActions","kpop_open","openStreamModal","targetElements","controllers","identifier","controllerConstructor","FrameController","Application","configure","config","instance","ruleset","linkObserver","streamActions","addKpopToRequestHeaders","debugFocusIn","debugFocusOut","context","headers","fetchOptions"],"mappings":"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"}
@@ -0,0 +1,69 @@
1
+ .kpop {
2
+ --opening-animation: kpop--animate--slide-in-up;
3
+ --closing-animation: kpop--animate--slide-out-down;
4
+
5
+ ::backdrop {
6
+ background: var(--dialog-background, rgba(0, 0, 0, 0.6));
7
+ backdrop-filter: blur(1px);
8
+
9
+ --opening-animation: kpop--animate--fade-in;
10
+ --closing-animation: kpop--animate--fade-out;
11
+ }
12
+
13
+ dialog[animate],
14
+ [animate]::backdrop {
15
+ transition:
16
+ display var(--animation-duration, 0.2s) allow-discrete,
17
+ overlay var(--animation-duration, 0.2s) allow-discrete;
18
+ }
19
+
20
+ dialog[animate],
21
+ [animate]::backdrop {
22
+ animation: var(--closing-animation) var(--animation-duration, 0.2s) forwards;
23
+ }
24
+
25
+ dialog[animate][open],
26
+ [animate][open]::backdrop {
27
+ animation: var(--opening-animation) var(--animation-duration, 0.2s) forwards;
28
+ }
29
+ }
30
+
31
+ @keyframes kpop--animate--slide-in-up {
32
+ from {
33
+ transform: translateY(10%);
34
+ opacity: 0;
35
+ }
36
+ to {
37
+ transform: translateY(0%);
38
+ opacity: 1;
39
+ }
40
+ }
41
+
42
+ @keyframes kpop--animate--slide-out-down {
43
+ from {
44
+ transform: translateY(0%);
45
+ opacity: 1;
46
+ }
47
+ to {
48
+ transform: translateY(10%);
49
+ opacity: 0;
50
+ }
51
+ }
52
+
53
+ @keyframes kpop--animate--fade-in {
54
+ from {
55
+ opacity: 0;
56
+ }
57
+ to {
58
+ opacity: 1;
59
+ }
60
+ }
61
+
62
+ @keyframes kpop--animate--fade-out {
63
+ from {
64
+ opacity: 1;
65
+ }
66
+ to {
67
+ opacity: 0;
68
+ }
69
+ }
@@ -1,14 +1,3 @@
1
- <div class="kpop--container">
2
- <%= turbo_frame_tag(id, **html_attributes) do %>
3
- <%= content %>
4
- <% if flash[:kpop] %>
5
- <%= tag.div("", data: {
6
- controller: "kpop--redirect",
7
- kpop__redirect_kpop__frame_outlet: "##{id}",
8
- kpop__redirect_path_value: flash[:kpop],
9
- kpop__redirect_target_value: id,
10
- turbo_temporary: "",
11
- }) %>
12
- <% end %>
13
- <% end %>
14
- </div>
1
+ <%= turbo_frame_tag(id, **html_attributes) do %>
2
+ <%= content %>
3
+ <% end %>
@@ -8,20 +8,25 @@ module Kpop
8
8
  attr_reader :id
9
9
 
10
10
  ACTIONS = %w[
11
- popstate@window->kpop--frame#popstate
12
- scrim:dismiss@window->kpop--frame#dismiss
13
- scrim:hide@window->kpop--frame#dismiss
11
+ turbo:before-fetch-request@window->kpop--frame#beforeFetchRequest
14
12
  turbo:before-frame-render->kpop--frame#beforeFrameRender
15
13
  turbo:before-stream-render@window->kpop--frame#beforeStreamRender
16
14
  turbo:before-visit@window->kpop--frame#beforeVisit
17
15
  turbo:frame-load->kpop--frame#frameLoad
18
16
  ].freeze
19
17
 
20
- def initialize(id: "kpop", scrim: "#scrim", **)
18
+ def initialize(id: "kpop", **)
21
19
  super(**)
22
20
 
23
- @id = id
24
- @scrim = scrim
21
+ @id = id
22
+ end
23
+
24
+ def modal_flash?
25
+ request.get? && flash.key?(:modal_location)
26
+ end
27
+
28
+ def modal_location
29
+ flash[:modal_location] if modal_flash?
25
30
  end
26
31
 
27
32
  def inspect
@@ -32,13 +37,12 @@ module Kpop
32
37
 
33
38
  def default_html_attributes
34
39
  {
35
- class: "kpop--frame",
40
+ class: "kpop",
36
41
  data: {
37
- controller: "kpop--frame",
38
- action: ACTIONS.join(" "),
39
- "kpop--frame-scrim-outlet": @scrim,
40
- turbo_action: "advance",
42
+ controller: "kpop--frame",
43
+ action: ACTIONS.join(" "),
41
44
  },
45
+ src: modal_location,
42
46
  target: "_top",
43
47
  }
44
48
  end
@@ -1,6 +1,7 @@
1
- <%= tag.div(**html_attributes) do %>
2
- <%= title if title? %>
3
- <%= header if header? %>
4
- <%= tag.div(content, **content_attributes) %>
5
- <%= footer if footer? %>
6
- <% end %>
1
+ <dialog aria-label="<%= title %>" class="<%= modal_class %>" closedby="closerequest" data-src="<%= path %>">
2
+ <header>
3
+ <h2 id="kpop-title"><%= title %></h2>
4
+ <button aria-label="close" data-action="click->kpop--frame#dismiss:prevent">×</button>
5
+ </header>
6
+ <%= content %>
7
+ </dialog>
@@ -2,28 +2,17 @@
2
2
 
3
3
  module Kpop
4
4
  class ModalComponent < ViewComponent::Base
5
- include Katalyst::HtmlAttributes
5
+ attr_reader :title
6
6
 
7
- renders_one :title, "Kpop::Modal::TitleComponent"
8
- renders_one :header, "Kpop::Modal::HeaderComponent"
9
- renders_one :footer, "Kpop::Modal::FooterComponent"
7
+ def initialize(title:, modal_class:)
8
+ super()
10
9
 
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)
17
-
18
- @fallback_location = fallback_location
19
- @layout = layout
20
-
21
- # Generate a title bar. This can be overridden by calling title_bar again.
22
- with_title(title:, captive:) if title.present?
10
+ @title = title
11
+ @modal_class = modal_class
23
12
  end
24
13
 
25
- def with_footer_buttons(**, &)
26
- with_footer(class: "button-set", **, &)
14
+ def modal_class
15
+ @modal_class.to_s.dasherize
27
16
  end
28
17
 
29
18
  def inspect
@@ -32,20 +21,8 @@ module Kpop
32
21
 
33
22
  private
34
23
 
35
- def default_html_attributes
36
- {
37
- class: "kpop-modal",
38
- data: {
39
- controller: "kpop--modal",
40
- "kpop--modal-current-location-value": request.path,
41
- "kpop--modal-fallback-location-value": @fallback_location,
42
- "kpop--modal-layout-value": @layout&.to_s&.dasherize,
43
- },
44
- }
45
- end
46
-
47
- def default_content_attributes
48
- { class: "kpop-content" }
24
+ def path
25
+ request.fullpath
49
26
  end
50
27
  end
51
28
  end
@@ -2,8 +2,10 @@
2
2
 
3
3
  # Kpop Frame Requests use a different layout than Turbo Frame requests.
4
4
  #
5
- # The layout used is <tt>kpop/frame.html.erb</tt>. If there's a need to customize this layout, an application can
6
- # supply its own (such as <tt>app/views/layouts/kpop/frame.html.erb</tt>) which will be used instead.
5
+ # The layout used is <tt>kpop/frame.html.erb</tt>. If there's a need to
6
+ # customise this layout, an application can supply its own
7
+ # (<tt>app/views/layouts/kpop/frame.html.erb</tt>)
8
+ # which will be used instead.
7
9
  #
8
10
  # This module is automatically included in <tt>ActionController::Base</tt>.
9
11
  module Katalyst
@@ -12,36 +14,93 @@ module Katalyst
12
14
  extend ActiveSupport::Concern
13
15
 
14
16
  class_methods do
17
+ # Sets the expectation that these actions will be wrapped in a modal.
18
+ # Adds custom layouts, rendering, and redirect behaviours to make this
19
+ # happen.
20
+ #
21
+ # If a get request is received for one of these paths which does not
22
+ # already support modals (i.e. hotwire native, kpop frame requests)
23
+ # then the user will be redirected back, or to the provided fallback
24
+ # location with a flash set so that modal shows when that page renders.
25
+ #
26
+ # Add the routes to your kpop config in application.js so that the
27
+ # extra flash request is not required for normal visits.
28
+ #
15
29
  # Example:
16
- # require_kpop only: %i[new edit] { url_for(resource) }
17
- def require_kpop(**constraints, &fallback_location)
30
+ # expects_kpop(only: %i[new create]) { url_for(action: :index) }
31
+ def expects_kpop(**constraints, &fallback_location)
18
32
  define_method(:kpop_fallback_location, fallback_location) if fallback_location
19
33
 
20
- before_action :require_kpop, **constraints
34
+ before_action :expects_kpop, **constraints
21
35
  end
22
36
  end
23
37
 
24
38
  included do
39
+ add_flash_types :modal_location
40
+
25
41
  layout -> { turbo_frame_layout }
26
42
  end
27
43
 
28
- private
44
+ def kpop_available?
45
+ request.headers["Kpop-Available"] == "true"
46
+ end
29
47
 
30
48
  def kpop_frame_request?
31
49
  turbo_frame_request_id == "kpop"
32
50
  end
33
51
 
34
- def require_kpop
35
- redirect_back(fallback_location: kpop_fallback_location, status: :see_other) unless kpop_frame_request?
52
+ def kpop_stream_request?
53
+ request.headers["Kpop-Stream-Request"] == "true"
54
+ end
55
+
56
+ private
57
+
58
+ def expects_kpop
59
+ if kpop_available? && request.headers["Accept"].include?("text/vnd.turbo-stream.html")
60
+ request.headers["Kpop-Stream-Request"] = "true"
61
+ end
62
+
63
+ return if !request.get? || turbo_frame_request? || kpop_stream_request? || hotwire_native_app?
64
+
65
+ redirect_back_or_to(kpop_fallback_location, status: :see_other, modal_location: request.fullpath)
66
+ end
67
+
68
+ def render(...)
69
+ if kpop_stream_request?
70
+ response_body = super
71
+
72
+ if rendered_format == Mime[:html]
73
+ response_body = render_to_string(turbo_stream: turbo_stream.action(:kpop_open, "kpop", response_body))
74
+ self.content_type = Mime[:turbo_stream]
75
+ self.response_body = response_body
76
+ end
77
+
78
+ response_body
79
+ else
80
+ super
81
+ end
36
82
  end
37
83
 
38
84
  def turbo_frame_layout
39
85
  if kpop_frame_request?
40
86
  "kpop/frame"
87
+ elsif kpop_stream_request?
88
+ "kpop/stream"
41
89
  elsif turbo_frame_request?
42
90
  "turbo_rails/frame"
43
91
  end
44
92
  end
93
+
94
+ # Add support for closing kpop modals
95
+ #
96
+ # @overload Turbo::Native::Navigation#turbo_native_action_or_redirect
97
+ def turbo_native_action_or_redirect(url, action, redirect_type, options = {})
98
+ if kpop_stream_request?
99
+ redirect_to send("turbo_#{action}_historical_location_url", notice: options[:notice])
100
+ else
101
+ super
102
+ end
103
+ end
45
104
  end
46
105
  end
47
106
  end
@@ -1,13 +1,74 @@
1
1
  import FrameController from "../kpop/controllers/frame_controller";
2
- import ModalController from "../kpop/controllers/modal_controller";
3
- import ScrimController from "../kpop/controllers/scrim_controller";
4
2
 
5
- import "./turbo_actions";
3
+ import debug from "./utils/debug";
4
+ import LinkObserver from "./utils/link_observer";
5
+ import Ruleset from "./utils/ruleset";
6
+ import StreamActions from "./utils/stream_actions";
6
7
 
7
- const Definitions = [
8
+ export const controllers = [
8
9
  { identifier: "kpop--frame", controllerConstructor: FrameController },
9
- { identifier: "kpop--modal", controllerConstructor: ModalController },
10
- { identifier: "scrim", controllerConstructor: ScrimController },
11
10
  ];
12
11
 
13
- export { Definitions as default };
12
+ export default class Application {
13
+ static configure(config = {}) {
14
+ this.instance ||= new this(config);
15
+ debug.enabled = this.instance.debug;
16
+ return this.instance;
17
+ }
18
+
19
+ constructor({ rules = [], debug = false } = {}) {
20
+ this.config = { rules, debug };
21
+ this.ruleset = new Ruleset(rules);
22
+ this.linkObserver = new LinkObserver(this, document);
23
+ this.streamActions = new StreamActions();
24
+ }
25
+
26
+ start() {
27
+ this.streamActions.start();
28
+ this.linkObserver.start();
29
+
30
+ window.addEventListener(
31
+ "turbo:before-fetch-request",
32
+ addKpopToRequestHeaders,
33
+ );
34
+
35
+ if (this.debug) {
36
+ document.addEventListener("focusin", debugFocusIn);
37
+ document.addEventListener("focusout", debugFocusOut);
38
+ }
39
+
40
+ return this;
41
+ }
42
+
43
+ stop() {
44
+ window.removeEventListener(
45
+ "turbo:before-fetch-request",
46
+ addKpopToRequestHeaders,
47
+ );
48
+ document.removeEventListener("focusin", debugFocusIn);
49
+ document.removeEventListener("focusout", debugFocusOut);
50
+
51
+ this.streamActions.stop();
52
+ this.linkObserver.stop();
53
+ }
54
+
55
+ isModalLink(link, location) {
56
+ const properties = this.ruleset.properties(location);
57
+ return properties.context === "modal";
58
+ }
59
+
60
+ get debug() {
61
+ return Boolean(this.config.debug);
62
+ }
63
+ }
64
+
65
+ const debugFocusIn = (e) => debug("Application")("focus", e.target);
66
+ const debugFocusOut = (e) => debug("Application")("blur", e.target);
67
+
68
+ const addKpopToRequestHeaders = (e) => {
69
+ const headers = e.detail.fetchOptions.headers;
70
+
71
+ if (headers["Accept"]?.includes("text/vnd.turbo-stream.html")) {
72
+ headers["Kpop-Available"] = "true";
73
+ }
74
+ };