katalyst-koi 5.9.0 → 5.10.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8582986afb1655b001fc9162effa0edbfa7c38af79c40aa83fc872d9a79320cc
4
- data.tar.gz: 21311cb4d39159cd52313c14b0b8855582a375cf15664590ee576a73a682958a
3
+ metadata.gz: 319724f0fb3d95cdb522b2255aff29edd723b82d30a719b772e47ffcae3e4c98
4
+ data.tar.gz: fd2421bb764b29caf26567b5b41df493fb77083c6bff490db7b4c01eff1f08ad
5
5
  SHA512:
6
- metadata.gz: c93bb5c51cf9a7ff0787138ec2c0560a6dbbff5a76b6216cf991b01f1bf24f93a0888521d7749c5fbd39216aa8359cfa86017b1f31fc294a46ba0d67d56b6468
7
- data.tar.gz: 4cbff5c09e4aec8b0bc355caf5e2f4dc31dd3f086c63a7795be867f4cecff9e71b94110f88415723d11f127bf8f0c72eae0c8ce8e68db87edf73a18d477a3136
6
+ metadata.gz: d558ecb83c1aaa9503a689c56af22c7000a0fb5ad9230f59ec8c8db02ed98e2f1883452893fbd556600cebee65e131913f63f728b8819dddd02d6b4f79b8474b
7
+ data.tar.gz: 2e1b7ad2aef1522792bf15f767b1500e6abfd8c718070c094b2fda2be93dce2ad0d5928f19214f401786f6848cdb482752e4c84c162799a8667c14a0b4b626ec
@@ -1,10 +1,10 @@
1
1
  import '@hotwired/turbo-rails';
2
- import govuk, { initAll } from '@katalyst/govuk-formbuilder';
3
2
  import '@rails/actiontext';
4
3
  import * as Lexxy from 'lexxy';
5
4
  import 'trix';
6
5
  import { Application, Controller } from '@hotwired/stimulus';
7
6
  import content from '@katalyst/content';
7
+ import GOVUK from '@katalyst/govuk-formbuilder';
8
8
  import navigation from '@katalyst/navigation';
9
9
  import tables from '@katalyst/tables';
10
10
 
@@ -470,7 +470,7 @@ class WebauthnRegistrationController extends Controller {
470
470
  }
471
471
 
472
472
  application$1.load(content);
473
- application$1.load(govuk);
473
+ GOVUK.start(application$1);
474
474
  application$1.load(navigation);
475
475
  application$1.load(tables);
476
476
 
@@ -539,19 +539,6 @@ class KoiToolbarElement extends HTMLElement {
539
539
 
540
540
  customElements.define("koi-toolbar", KoiToolbarElement);
541
541
 
542
- /** Initialize GOVUK */
543
- function initGOVUK() {
544
- document.body.classList.toggle("js-enabled", true);
545
- document.body.classList.toggle(
546
- "govuk-frontend-supported",
547
- "noModule" in HTMLScriptElement.prototype,
548
- );
549
- initAll();
550
- }
551
-
552
- window.addEventListener("turbo:load", initGOVUK);
553
- if (window.Turbo) initGOVUK();
554
-
555
542
  var application = { Lexxy, Trix };
556
543
 
557
544
  export { application as default };
@@ -1,10 +1,10 @@
1
1
  import '@hotwired/turbo-rails';
2
- import govuk, { initAll } from '@katalyst/govuk-formbuilder';
3
2
  import '@rails/actiontext';
4
3
  import * as Lexxy from 'lexxy';
5
4
  import 'trix';
6
5
  import { Application, Controller } from '@hotwired/stimulus';
7
6
  import content from '@katalyst/content';
7
+ import GOVUK from '@katalyst/govuk-formbuilder';
8
8
  import navigation from '@katalyst/navigation';
9
9
  import tables from '@katalyst/tables';
10
10
 
@@ -470,7 +470,7 @@ class WebauthnRegistrationController extends Controller {
470
470
  }
471
471
 
472
472
  application$1.load(content);
473
- application$1.load(govuk);
473
+ GOVUK.start(application$1);
474
474
  application$1.load(navigation);
475
475
  application$1.load(tables);
476
476
 
@@ -539,19 +539,6 @@ class KoiToolbarElement extends HTMLElement {
539
539
 
540
540
  customElements.define("koi-toolbar", KoiToolbarElement);
541
541
 
542
- /** Initialize GOVUK */
543
- function initGOVUK() {
544
- document.body.classList.toggle("js-enabled", true);
545
- document.body.classList.toggle(
546
- "govuk-frontend-supported",
547
- "noModule" in HTMLScriptElement.prototype,
548
- );
549
- initAll();
550
- }
551
-
552
- window.addEventListener("turbo:load", initGOVUK);
553
- if (window.Turbo) initGOVUK();
554
-
555
542
  var application = { Lexxy, Trix };
556
543
 
557
544
  export { application as default };
@@ -1,2 +1,2 @@
1
- import"@hotwired/turbo-rails";import e,{initAll as t}from"@katalyst/govuk-formbuilder";import"@rails/actiontext";import*as i from"lexxy";import"trix";import{Application as r,Controller as s}from"@hotwired/stimulus";import o from"@katalyst/content";import n from"@katalyst/navigation";import a from"@katalyst/tables";i.configure({global:{authenticatedUploads:!0}}),document.addEventListener("lexxy:initialize",({target:e})=>{e.querySelectorAll("lexxy-link-dropdown input[type=url]").forEach(e=>{e.type="text",e.pattern="(https?|mailto:|tel:|/|#).*?"})});const l=window.Trix;l.config.blockAttributes.heading4={tagName:"h4",terminal:!0,breakOnReturn:!0,group:!1},delete l.config.blockAttributes.heading1;const c=r.start();c.load(o),c.load(e),c.load(n),c.load(a);const u=[{identifier:"clipboard",controllerConstructor:class extends s{static targets=["source"];static classes=["supported"];connect(){"clipboard"in navigator&&this.element.classList.add(this.supportedClass)}copy(e){e.preventDefault(),navigator.clipboard.writeText(this.sourceTarget.value),this.element.classList.add("copied"),setTimeout(()=>{this.element.classList.remove("copied")},2e3)}}},{identifier:"flash",controllerConstructor:class extends s{close(e){e.target.closest("li").remove(),0===this.element.children.length&&this.element.remove()}}},{identifier:"keyboard",controllerConstructor:class extends s{static values={mapping:String,depth:{type:Number,default:2}};event(e){if(function(e){if(!(e instanceof HTMLElement))return!1;const t=e.nodeName.toLowerCase(),i=(e.getAttribute("type")||"").toLowerCase();return"select"===t||"textarea"===t||"trix-editor"===t||"input"===t&&"submit"!==i&&"reset"!==i&&"checkbox"!==i&&"radio"!==i&&"file"!==i||e.isContentEditable}(e.target)||this.#e(e))return;const t=this.describeEvent(e);this.buffer=[...this.buffer||[],t].slice(0-this.depthValue);const i=this.buffer.reduceRight((e,t)=>"string"==typeof e||void 0===e?e:e[t],this.mappings);if("string"!=typeof i)return;this.buffer=[],e.preventDefault();const r=new CustomEvent(i,{detail:{cause:e},bubbles:!0});e.target.dispatchEvent(r)}describeEvent(e){return[e.ctrlKey&&"C",e.metaKey&&"M",e.altKey&&"A",e.shiftKey&&"S",e.code].filter(e=>e).join("-")}get mappings(){const e=this.mappingValue.replaceAll(/\s+/g," ").split(" ").filter(e=>e.length>0),t={};return e.forEach(e=>this.#t(t,e)),Object.defineProperty(this,"mappings",{value:t,writable:!1}),t}#t(e,t){const[i,r]=t.split("->"),s=i.split("+"),o=s.shift();(e=s.reduceRight((e,t)=>e[t]||={},e))[o]=r}#e(e){switch(e.code){case"ControlLeft":case"ControlRight":case"MetaLeft":case"MetaRight":case"ShiftLeft":case"ShiftRight":case"AltLeft":case"AltRight":return!0;default:return!1}}}},{identifier:"modal",controllerConstructor:class extends s{static targets=["dialog"];connect(){this.element.addEventListener("turbo:submit-end",this.onSubmit)}disconnect(){this.element.removeEventListener("turbo:submit-end",this.onSubmit)}outside(e){"DIALOG"===e.target.tagName&&this.dismiss()}dismiss(){this.dialogTarget&&(this.dialogTarget.open||this.dialogTarget.close(),this.element.removeAttribute("src"),this.dialogTarget.remove())}dialogTargetConnected(e){e.showModal()}onSubmit=e=>{e.detail.success&&"closeDialog"in e.detail.formSubmission?.submitter?.dataset&&(this.dialogTarget.close(),this.element.removeAttribute("src"),this.dialogTarget.remove())}}},{identifier:"navigation",controllerConstructor:class extends s{static targets=["filter"];filter(){const e=this.filterTarget.value;this.clearFilter(e),e.length>0&&this.applyFilter(e)}go(){this.element.querySelector("li:not([hidden]) > a").click()}clear(){0===this.filterTarget.value.length&&this.filterTarget.blur()}applyFilter(e){this.links.filter(t=>!this.prefixSearch(e.toLowerCase(),t.innerText.toLowerCase())).forEach(e=>{e.toggleAttribute("hidden",!0)}),this.menus.filter(e=>!e.matches("li:has(li:not([hidden]) > a)")).forEach(e=>{e.toggleAttribute("hidden",!0)})}clearFilter(e){this.element.querySelectorAll("li").forEach(e=>{e.toggleAttribute("hidden",!1)})}prefixSearch(e,t){const i=t.length,r=e.length;if(r>i)return!1;if(r===i)return e===t;e:for(let s=0,o=0;s<r;s++){const r=e.charCodeAt(s);if(32!==r){for(;o<i;){if(t.charCodeAt(o++)===r)continue e;for(;o<i&&32!==t.charCodeAt(o++););}return!1}for(;o<i&&32!==t.charCodeAt(o++););}return!0}toggle(){this.element.open?this.close():this.open()}open(){this.element.open||this.element.showModal()}close(){this.element.open&&this.element.close()}click(e){e.target===this.element&&this.close()}onMorphAttribute=e=>{if(e.target===this.element&&"open"===e.detail.attributeName)e.preventDefault()};get links(){return Array.from(this.element.querySelectorAll("li:has(> a)"))}get menus(){return Array.from(this.element.querySelectorAll("li:has(> ul)"))}}},{identifier:"navigation-toggle",controllerConstructor:class extends s{trigger(){this.dispatch("toggle",{prefix:"navigation",bubbles:!0})}}},{identifier:"pagy-nav",controllerConstructor:class extends s{connect(){document.addEventListener("shortcut:page-prev",this.prevPage),document.addEventListener("shortcut:page-next",this.nextPage)}disconnect(){document.removeEventListener("shortcut:page-prev",this.prevPage),document.removeEventListener("shortcut:page-next",this.nextPage)}nextPage=()=>{this.element.querySelector("a:last-child").click()};prevPage=()=>{this.element.querySelector("a:first-child").click()}}},{identifier:"sluggable",controllerConstructor:class extends s{static targets=["source","slug"];static values={slug:String};sourceChanged(e){""===this.slugValue&&(this.slugTarget.value=this.sourceTarget.value.toLowerCase().replace(/'/g,"-").replace(/[^-\w\s]/g,"").replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,""))}slugChanged(e){this.slugValue=this.slugTarget.value}}},{identifier:"webauthn-authentication",controllerConstructor:class extends s{static targets=["response"];static values={options:Object};async authenticate(){const e=await navigator.credentials.get(this.options);this.responseTarget.value=JSON.stringify(e.toJSON()),this.element.requestSubmit()}get options(){return{publicKey:PublicKeyCredential.parseRequestOptionsFromJSON(this.optionsValue)}}}},{identifier:"webauthn-registration",controllerConstructor:class extends s{static targets=["response"];static values={options:Object};submit(e){this.responseTarget.value||(e.preventDefault(),this.createCredential().then(()=>{e.target.submit()}))}async createCredential(){const e=await navigator.credentials.create(this.options);this.responseTarget.value=JSON.stringify(e.toJSON())}get options(){return{publicKey:PublicKeyCredential.parseCreationOptionsFromJSON(this.optionsValue)}}}}];await import("controllers/hw_combobox_controller").then(({default:e})=>{u.push({identifier:"hw-combobox",controllerConstructor:e})}).catch(()=>null),c.load(u);class h extends HTMLElement{constructor(){super(),this.setAttribute("role","toolbar")}}function d(){document.body.classList.toggle("js-enabled",!0),document.body.classList.toggle("govuk-frontend-supported","noModule"in HTMLScriptElement.prototype),t()}customElements.define("koi-toolbar",h),window.addEventListener("turbo:load",d),window.Turbo&&d();var g={Lexxy:i,Trix:l};export{g as default};
1
+ import"@hotwired/turbo-rails";import"@rails/actiontext";import*as e from"lexxy";import"trix";import{Application as t,Controller as i}from"@hotwired/stimulus";import r from"@katalyst/content";import s from"@katalyst/govuk-formbuilder";import o from"@katalyst/navigation";import a from"@katalyst/tables";e.configure({global:{authenticatedUploads:!0}}),document.addEventListener("lexxy:initialize",({target:e})=>{e.querySelectorAll("lexxy-link-dropdown input[type=url]").forEach(e=>{e.type="text",e.pattern="(https?|mailto:|tel:|/|#).*?"})});const n=window.Trix;n.config.blockAttributes.heading4={tagName:"h4",terminal:!0,breakOnReturn:!0,group:!1},delete n.config.blockAttributes.heading1;const l=t.start();l.load(r),s.start(l),l.load(o),l.load(a);const c=[{identifier:"clipboard",controllerConstructor:class extends i{static targets=["source"];static classes=["supported"];connect(){"clipboard"in navigator&&this.element.classList.add(this.supportedClass)}copy(e){e.preventDefault(),navigator.clipboard.writeText(this.sourceTarget.value),this.element.classList.add("copied"),setTimeout(()=>{this.element.classList.remove("copied")},2e3)}}},{identifier:"flash",controllerConstructor:class extends i{close(e){e.target.closest("li").remove(),0===this.element.children.length&&this.element.remove()}}},{identifier:"keyboard",controllerConstructor:class extends i{static values={mapping:String,depth:{type:Number,default:2}};event(e){if(function(e){if(!(e instanceof HTMLElement))return!1;const t=e.nodeName.toLowerCase(),i=(e.getAttribute("type")||"").toLowerCase();return"select"===t||"textarea"===t||"trix-editor"===t||"input"===t&&"submit"!==i&&"reset"!==i&&"checkbox"!==i&&"radio"!==i&&"file"!==i||e.isContentEditable}(e.target)||this.#e(e))return;const t=this.describeEvent(e);this.buffer=[...this.buffer||[],t].slice(0-this.depthValue);const i=this.buffer.reduceRight((e,t)=>"string"==typeof e||void 0===e?e:e[t],this.mappings);if("string"!=typeof i)return;this.buffer=[],e.preventDefault();const r=new CustomEvent(i,{detail:{cause:e},bubbles:!0});e.target.dispatchEvent(r)}describeEvent(e){return[e.ctrlKey&&"C",e.metaKey&&"M",e.altKey&&"A",e.shiftKey&&"S",e.code].filter(e=>e).join("-")}get mappings(){const e=this.mappingValue.replaceAll(/\s+/g," ").split(" ").filter(e=>e.length>0),t={};return e.forEach(e=>this.#t(t,e)),Object.defineProperty(this,"mappings",{value:t,writable:!1}),t}#t(e,t){const[i,r]=t.split("->"),s=i.split("+"),o=s.shift();(e=s.reduceRight((e,t)=>e[t]||={},e))[o]=r}#e(e){switch(e.code){case"ControlLeft":case"ControlRight":case"MetaLeft":case"MetaRight":case"ShiftLeft":case"ShiftRight":case"AltLeft":case"AltRight":return!0;default:return!1}}}},{identifier:"modal",controllerConstructor:class extends i{static targets=["dialog"];connect(){this.element.addEventListener("turbo:submit-end",this.onSubmit)}disconnect(){this.element.removeEventListener("turbo:submit-end",this.onSubmit)}outside(e){"DIALOG"===e.target.tagName&&this.dismiss()}dismiss(){this.dialogTarget&&(this.dialogTarget.open||this.dialogTarget.close(),this.element.removeAttribute("src"),this.dialogTarget.remove())}dialogTargetConnected(e){e.showModal()}onSubmit=e=>{e.detail.success&&"closeDialog"in e.detail.formSubmission?.submitter?.dataset&&(this.dialogTarget.close(),this.element.removeAttribute("src"),this.dialogTarget.remove())}}},{identifier:"navigation",controllerConstructor:class extends i{static targets=["filter"];filter(){const e=this.filterTarget.value;this.clearFilter(e),e.length>0&&this.applyFilter(e)}go(){this.element.querySelector("li:not([hidden]) > a").click()}clear(){0===this.filterTarget.value.length&&this.filterTarget.blur()}applyFilter(e){this.links.filter(t=>!this.prefixSearch(e.toLowerCase(),t.innerText.toLowerCase())).forEach(e=>{e.toggleAttribute("hidden",!0)}),this.menus.filter(e=>!e.matches("li:has(li:not([hidden]) > a)")).forEach(e=>{e.toggleAttribute("hidden",!0)})}clearFilter(e){this.element.querySelectorAll("li").forEach(e=>{e.toggleAttribute("hidden",!1)})}prefixSearch(e,t){const i=t.length,r=e.length;if(r>i)return!1;if(r===i)return e===t;e:for(let s=0,o=0;s<r;s++){const r=e.charCodeAt(s);if(32!==r){for(;o<i;){if(t.charCodeAt(o++)===r)continue e;for(;o<i&&32!==t.charCodeAt(o++););}return!1}for(;o<i&&32!==t.charCodeAt(o++););}return!0}toggle(){this.element.open?this.close():this.open()}open(){this.element.open||this.element.showModal()}close(){this.element.open&&this.element.close()}click(e){e.target===this.element&&this.close()}onMorphAttribute=e=>{if(e.target===this.element&&"open"===e.detail.attributeName)e.preventDefault()};get links(){return Array.from(this.element.querySelectorAll("li:has(> a)"))}get menus(){return Array.from(this.element.querySelectorAll("li:has(> ul)"))}}},{identifier:"navigation-toggle",controllerConstructor:class extends i{trigger(){this.dispatch("toggle",{prefix:"navigation",bubbles:!0})}}},{identifier:"pagy-nav",controllerConstructor:class extends i{connect(){document.addEventListener("shortcut:page-prev",this.prevPage),document.addEventListener("shortcut:page-next",this.nextPage)}disconnect(){document.removeEventListener("shortcut:page-prev",this.prevPage),document.removeEventListener("shortcut:page-next",this.nextPage)}nextPage=()=>{this.element.querySelector("a:last-child").click()};prevPage=()=>{this.element.querySelector("a:first-child").click()}}},{identifier:"sluggable",controllerConstructor:class extends i{static targets=["source","slug"];static values={slug:String};sourceChanged(e){""===this.slugValue&&(this.slugTarget.value=this.sourceTarget.value.toLowerCase().replace(/'/g,"-").replace(/[^-\w\s]/g,"").replace(/[^a-z0-9]+/g,"-").replace(/(^-|-$)/g,""))}slugChanged(e){this.slugValue=this.slugTarget.value}}},{identifier:"webauthn-authentication",controllerConstructor:class extends i{static targets=["response"];static values={options:Object};async authenticate(){const e=await navigator.credentials.get(this.options);this.responseTarget.value=JSON.stringify(e.toJSON()),this.element.requestSubmit()}get options(){return{publicKey:PublicKeyCredential.parseRequestOptionsFromJSON(this.optionsValue)}}}},{identifier:"webauthn-registration",controllerConstructor:class extends i{static targets=["response"];static values={options:Object};submit(e){this.responseTarget.value||(e.preventDefault(),this.createCredential().then(()=>{e.target.submit()}))}async createCredential(){const e=await navigator.credentials.create(this.options);this.responseTarget.value=JSON.stringify(e.toJSON())}get options(){return{publicKey:PublicKeyCredential.parseCreationOptionsFromJSON(this.optionsValue)}}}}];await import("controllers/hw_combobox_controller").then(({default:e})=>{c.push({identifier:"hw-combobox",controllerConstructor:e})}).catch(()=>null),l.load(c);class u extends HTMLElement{constructor(){super(),this.setAttribute("role","toolbar")}}customElements.define("koi-toolbar",u);var h={Lexxy:e,Trix:n};export{h as default};
2
2
  //# sourceMappingURL=koi.min.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"koi.min.js","sources":["../../../javascript/koi/utils/lexxy.js","../../../javascript/koi/utils/trix.js","../../../javascript/koi/controllers/application.js","../../../javascript/koi/controllers/index.js","../../../javascript/koi/controllers/clipboard_controller.js","../../../javascript/koi/controllers/flash_controller.js","../../../javascript/koi/controllers/keyboard_controller.js","../../../javascript/koi/controllers/modal_controller.js","../../../javascript/koi/controllers/navigation_controller.js","../../../javascript/koi/controllers/navigation_toggle_controller.js","../../../javascript/koi/controllers/pagy_nav_controller.js","../../../javascript/koi/controllers/sluggable_controller.js","../../../javascript/koi/controllers/webauthn_authentication_controller.js","../../../javascript/koi/controllers/webauthn_registration_controller.js","../../../javascript/koi/elements/toolbar.js","../../../javascript/koi/application.js"],"sourcesContent":["import * as Lexxy from \"lexxy\";\n\nLexxy.configure({\n global: {\n authenticatedUploads: true,\n },\n});\n\n// The link toolbar's URL input is type=url, whose checkValidity() rejects\n// schemeless values like #anchor and /relative that lexxy's link node happily\n// preserves. Switch it to type=text with the same pattern Koi applies to the\n// trix link dialog (see Koi::Tags::TrixToolbar::TRIX_DIALOGS_TEMPLATE) so both\n// editors validate link input identically.\ndocument.addEventListener(\"lexxy:initialize\", ({ target }) => {\n target\n .querySelectorAll(\"lexxy-link-dropdown input[type=url]\")\n .forEach((input) => {\n input.type = \"text\";\n input.pattern = \"(https?|mailto:|tel:|/|#).*?\";\n });\n});\n\nexport default Lexxy;\n","import \"trix\";\n\n// Trix does not provide an export\nconst Trix = window.Trix;\n\n// Koi uses H4 for headings instead of H1\nTrix.config.blockAttributes[\"heading4\"] = {\n tagName: \"h4\",\n terminal: true,\n breakOnReturn: true,\n group: false,\n};\n\n// Remove H1 from trix list of acceptable tags\ndelete Trix.config.blockAttributes.heading1;\n\nexport default Trix;\n","import { Application } from \"@hotwired/stimulus\";\n\nconst application = Application.start();\n\nexport { application };\n","import { application } from \"./application\";\n\nimport content from \"@katalyst/content\";\napplication.load(content);\n\nimport govuk from \"@katalyst/govuk-formbuilder\";\napplication.load(govuk);\n\nimport navigation from \"@katalyst/navigation\";\napplication.load(navigation);\n\nimport tables from \"@katalyst/tables\";\napplication.load(tables);\n\nimport ClipboardController from \"./clipboard_controller\";\nimport FlashController from \"./flash_controller\";\nimport KeyboardController from \"./keyboard_controller\";\nimport ModalController from \"./modal_controller\";\nimport NavigationController from \"./navigation_controller\";\nimport NavigationToggleController from \"./navigation_toggle_controller\";\nimport PagyNavController from \"./pagy_nav_controller\";\nimport SluggableController from \"./sluggable_controller\";\nimport WebauthnAuthenticationController from \"./webauthn_authentication_controller\";\nimport WebauthnRegistrationController from \"./webauthn_registration_controller\";\n\nconst Definitions = [\n {\n identifier: \"clipboard\",\n controllerConstructor: ClipboardController,\n },\n {\n identifier: \"flash\",\n controllerConstructor: FlashController,\n },\n {\n identifier: \"keyboard\",\n controllerConstructor: KeyboardController,\n },\n {\n identifier: \"modal\",\n controllerConstructor: ModalController,\n },\n {\n identifier: \"navigation\",\n controllerConstructor: NavigationController,\n },\n {\n identifier: \"navigation-toggle\",\n controllerConstructor: NavigationToggleController,\n },\n {\n identifier: \"pagy-nav\",\n controllerConstructor: PagyNavController,\n },\n {\n identifier: \"sluggable\",\n controllerConstructor: SluggableController,\n },\n {\n identifier: \"webauthn-authentication\",\n controllerConstructor: WebauthnAuthenticationController,\n },\n {\n identifier: \"webauthn-registration\",\n controllerConstructor: WebauthnRegistrationController,\n },\n];\n\n// dynamically attempt to load hw_combobox_controller, this is an optional dependency\nawait import(\"controllers/hw_combobox_controller\")\n .then(({ default: HwComboboxController }) => {\n Definitions.push({\n identifier: \"hw-combobox\",\n controllerConstructor: HwComboboxController,\n });\n })\n .catch(() => null);\n\napplication.load(Definitions);\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class ClipboardController extends Controller {\n static targets = [\"source\"];\n\n static classes = [\"supported\"];\n\n connect() {\n if (\"clipboard\" in navigator) {\n this.element.classList.add(this.supportedClass);\n }\n }\n\n copy(event) {\n event.preventDefault();\n navigator.clipboard.writeText(this.sourceTarget.value);\n\n this.element.classList.add(\"copied\");\n setTimeout(() => {\n this.element.classList.remove(\"copied\");\n }, 2000);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class FlashController extends Controller {\n close(e) {\n e.target.closest(\"li\").remove();\n\n // remove the flash container if there are no more flashes\n if (this.element.children.length === 0) {\n this.element.remove();\n }\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nconst DEBUG = false;\n\nexport default class KeyboardController extends Controller {\n static values = {\n mapping: String,\n depth: { type: Number, default: 2 },\n };\n\n event(cause) {\n if (isFormField(cause.target) || this.#ignore(cause)) return;\n\n const key = this.describeEvent(cause);\n\n this.buffer = [...(this.buffer || []), key].slice(0 - this.depthValue);\n\n if (DEBUG) console.debug(\"[keyboard] buffer:\", ...this.buffer);\n\n // test whether the tail of the buffer matches any of the configured chords\n const action = this.buffer.reduceRight((mapping, key) => {\n if (typeof mapping === \"string\" || typeof mapping === \"undefined\") {\n return mapping;\n } else {\n return mapping[key];\n }\n }, this.mappings);\n\n // if we don't have a string we may have a miss or an incomplete chord\n if (typeof action !== \"string\") return;\n\n // clear the buffer and prevent the key from being consumed elsewhere\n this.buffer = [];\n cause.preventDefault();\n\n if (DEBUG) console.debug(\"[keyboard] event: %s\", action);\n\n // fire the configured event\n const event = new CustomEvent(action, {\n detail: { cause: cause },\n bubbles: true,\n });\n cause.target.dispatchEvent(event);\n }\n\n /**\n * @param event KeyboardEvent input event to describe\n * @return String description of keyboard event, e.g. 'C-KeyV' (CTRL+V)\n */\n describeEvent(event) {\n return [\n event.ctrlKey && \"C\",\n event.metaKey && \"M\",\n event.altKey && \"A\",\n event.shiftKey && \"S\",\n event.code,\n ]\n .filter((w) => w)\n .join(\"-\");\n }\n\n /**\n * Build a tree for efficiently looking up key chords, where the last key in the sequence\n * is the first key in tree.\n */\n get mappings() {\n const inputs = this.mappingValue\n .replaceAll(/\\s+/g, \" \")\n .split(\" \")\n .filter((f) => f.length > 0);\n const mappings = {};\n\n inputs.forEach((mapping) => this.#parse(mappings, mapping));\n\n // memoize the result\n Object.defineProperty(this, \"mappings\", {\n value: mappings,\n writable: false,\n });\n\n return mappings;\n }\n\n /**\n * Parse a key chord pattern and an event and store it in the inverted tree lookup structure.\n *\n * @param mappings inverted tree lookup for key chords\n * @param mapping input definition, e.g. \"C-KeyC+C-KeyV->paste\"\n */\n #parse(mappings, mapping) {\n const [pattern, event] = mapping.split(\"->\");\n const keys = pattern.split(\"+\");\n const first = keys.shift();\n\n mappings = keys.reduceRight(\n (mappings, key) => (mappings[key] ||= {}),\n mappings,\n );\n mappings[first] = event;\n }\n\n /**\n * Ignore modifier keys, as they will be captured in normal key presses.\n *\n * @param event KeyboardEvent\n * @returns {boolean} true if key event should be ignored\n */\n #ignore(event) {\n switch (event.code) {\n case \"ControlLeft\":\n case \"ControlRight\":\n case \"MetaLeft\":\n case \"MetaRight\":\n case \"ShiftLeft\":\n case \"ShiftRight\":\n case \"AltLeft\":\n case \"AltRight\":\n return true;\n default:\n return false;\n }\n }\n}\n\n/**\n * Detect input nodes where we should not listen for events.\n *\n * Credit: github.com\n */\nfunction isFormField(element) {\n if (!(element instanceof HTMLElement)) {\n return false;\n }\n\n const name = element.nodeName.toLowerCase();\n const type = (element.getAttribute(\"type\") || \"\").toLowerCase();\n return (\n name === \"select\" ||\n name === \"textarea\" ||\n name === \"trix-editor\" ||\n (name === \"input\" &&\n type !== \"submit\" &&\n type !== \"reset\" &&\n type !== \"checkbox\" &&\n type !== \"radio\" &&\n type !== \"file\") ||\n element.isContentEditable\n );\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class ModalController extends Controller {\n static targets = [\"dialog\"];\n\n connect() {\n this.element.addEventListener(\"turbo:submit-end\", this.onSubmit);\n }\n\n disconnect() {\n this.element.removeEventListener(\"turbo:submit-end\", this.onSubmit);\n }\n\n outside(e) {\n if (e.target.tagName === \"DIALOG\") this.dismiss();\n }\n\n dismiss() {\n if (!this.dialogTarget) return;\n if (!this.dialogTarget.open) this.dialogTarget.close();\n\n this.element.removeAttribute(\"src\");\n this.dialogTarget.remove();\n }\n\n dialogTargetConnected(dialog) {\n dialog.showModal();\n }\n\n onSubmit = (event) => {\n if (\n event.detail.success &&\n \"closeDialog\" in event.detail.formSubmission?.submitter?.dataset\n ) {\n this.dialogTarget.close();\n this.element.removeAttribute(\"src\");\n this.dialogTarget.remove();\n }\n };\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class NavigationController extends Controller {\n static targets = [\"filter\"];\n\n filter() {\n const filter = this.filterTarget.value;\n this.clearFilter(filter);\n\n if (filter.length > 0) {\n this.applyFilter(filter);\n }\n }\n\n go() {\n this.element.querySelector(\"li:not([hidden]) > a\").click();\n }\n\n clear() {\n if (this.filterTarget.value.length === 0) this.filterTarget.blur();\n }\n\n applyFilter(filter) {\n // hide items that don't match the search filter\n this.links\n .filter(\n (li) =>\n !this.prefixSearch(filter.toLowerCase(), li.innerText.toLowerCase()),\n )\n .forEach((li) => {\n li.toggleAttribute(\"hidden\", true);\n });\n\n this.menus\n .filter((li) => !li.matches(\"li:has(li:not([hidden]) > a)\"))\n .forEach((li) => {\n li.toggleAttribute(\"hidden\", true);\n });\n }\n\n clearFilter(filter) {\n this.element.querySelectorAll(\"li\").forEach((li) => {\n li.toggleAttribute(\"hidden\", false);\n });\n }\n\n prefixSearch(needle, haystack) {\n const haystackLength = haystack.length;\n const needleLength = needle.length;\n if (needleLength > haystackLength) {\n return false;\n }\n if (needleLength === haystackLength) {\n return needle === haystack;\n }\n outer: for (let i = 0, j = 0; i < needleLength; i++) {\n const needleChar = needle.charCodeAt(i);\n if (needleChar === 32) {\n // skip ahead to next space in the haystack\n while (j < haystackLength && haystack.charCodeAt(j++) !== 32) {}\n continue;\n }\n while (j < haystackLength) {\n if (haystack.charCodeAt(j++) === needleChar) continue outer;\n // skip ahead to the next space in the haystack\n while (j < haystackLength && haystack.charCodeAt(j++) !== 32) {}\n }\n return false;\n }\n return true;\n }\n\n toggle() {\n this.element.open ? this.close() : this.open();\n }\n\n open() {\n if (!this.element.open) this.element.showModal();\n }\n\n close() {\n if (this.element.open) this.element.close();\n }\n\n click(e) {\n if (e.target === this.element) this.close();\n }\n\n onMorphAttribute = (e) => {\n if (e.target !== this.element) return;\n\n switch (e.detail.attributeName) {\n case \"open\":\n e.preventDefault();\n }\n };\n\n get links() {\n return Array.from(this.element.querySelectorAll(\"li:has(> a)\"));\n }\n\n get menus() {\n return Array.from(this.element.querySelectorAll(\"li:has(> ul)\"));\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class NavigationToggleController extends Controller {\n trigger() {\n this.dispatch(\"toggle\", { prefix: \"navigation\", bubbles: true });\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class PagyNavController extends Controller {\n connect() {\n document.addEventListener(\"shortcut:page-prev\", this.prevPage);\n document.addEventListener(\"shortcut:page-next\", this.nextPage);\n }\n\n disconnect() {\n document.removeEventListener(\"shortcut:page-prev\", this.prevPage);\n document.removeEventListener(\"shortcut:page-next\", this.nextPage);\n }\n\n nextPage = () => {\n this.element.querySelector(\"a:last-child\").click();\n };\n\n prevPage = () => {\n this.element.querySelector(\"a:first-child\").click();\n };\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\n/**\n * Connect an input (e.g. title) to slug.\n */\nexport default class SluggableController extends Controller {\n static targets = [\"source\", \"slug\"];\n static values = {\n slug: String,\n };\n\n sourceChanged(e) {\n if (this.slugValue === \"\") {\n this.slugTarget.value = parameterize(this.sourceTarget.value);\n }\n }\n\n slugChanged(e) {\n this.slugValue = this.slugTarget.value;\n }\n}\n\nfunction parameterize(input) {\n return input\n .toLowerCase()\n .replace(/'/g, \"-\")\n .replace(/[^-\\w\\s]/g, \"\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/(^-|-$)/g, \"\");\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class WebauthnAuthenticationController extends Controller {\n static targets = [\"response\"];\n static values = {\n options: Object,\n };\n\n async authenticate() {\n const credential = await navigator.credentials.get(this.options);\n\n this.responseTarget.value = JSON.stringify(credential.toJSON());\n\n this.element.requestSubmit();\n }\n\n get options() {\n return {\n publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(\n this.optionsValue,\n ),\n };\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class WebauthnRegistrationController extends Controller {\n static targets = [\"response\"];\n static values = {\n options: Object,\n };\n\n submit(e) {\n if (this.responseTarget.value) return;\n\n e.preventDefault();\n this.createCredential().then(() => {\n e.target.submit();\n });\n }\n\n async createCredential() {\n const credential = await navigator.credentials.create(this.options);\n this.responseTarget.value = JSON.stringify(credential.toJSON());\n }\n\n get options() {\n return {\n publicKey: PublicKeyCredential.parseCreationOptionsFromJSON(\n this.optionsValue,\n ),\n };\n }\n}\n","class KoiToolbarElement extends HTMLElement {\n constructor() {\n super();\n\n this.setAttribute(\"role\", \"toolbar\");\n }\n}\n\ncustomElements.define(\"koi-toolbar\", KoiToolbarElement);\n","import \"@hotwired/turbo-rails\";\nimport { initAll } from \"@katalyst/govuk-formbuilder\";\nimport \"@rails/actiontext\";\nimport Lexxy from \"./utils/lexxy\";\nimport Trix from \"./utils/trix\";\n\nimport \"./controllers\";\nimport \"./elements\";\n\n/** Initialize GOVUK */\nfunction initGOVUK() {\n document.body.classList.toggle(\"js-enabled\", true);\n document.body.classList.toggle(\n \"govuk-frontend-supported\",\n \"noModule\" in HTMLScriptElement.prototype,\n );\n initAll();\n}\n\nwindow.addEventListener(\"turbo:load\", initGOVUK);\nif (window.Turbo) initGOVUK();\n\nexport default { Lexxy, Trix };\n"],"names":["Lexxy","configure","global","authenticatedUploads","document","addEventListener","target","querySelectorAll","forEach","input","type","pattern","Trix","window","config","blockAttributes","tagName","terminal","breakOnReturn","group","heading1","application","Application","start","load","content","govuk","navigation","tables","Definitions","identifier","controllerConstructor","Controller","static","connect","navigator","this","element","classList","add","supportedClass","copy","event","preventDefault","clipboard","writeText","sourceTarget","value","setTimeout","remove","close","e","closest","children","length","mapping","String","depth","Number","default","cause","HTMLElement","name","nodeName","toLowerCase","getAttribute","isContentEditable","isFormField","ignore","key","describeEvent","buffer","slice","depthValue","action","reduceRight","mappings","CustomEvent","detail","bubbles","dispatchEvent","ctrlKey","metaKey","altKey","shiftKey","code","filter","w","join","inputs","mappingValue","replaceAll","split","f","parse","Object","defineProperty","writable","keys","first","shift","onSubmit","disconnect","removeEventListener","outside","dismiss","dialogTarget","open","removeAttribute","dialogTargetConnected","dialog","showModal","success","formSubmission","submitter","dataset","filterTarget","clearFilter","applyFilter","go","querySelector","click","clear","blur","links","li","prefixSearch","innerText","toggleAttribute","menus","matches","needle","haystack","haystackLength","needleLength","outer","i","j","needleChar","charCodeAt","toggle","onMorphAttribute","attributeName","Array","from","trigger","dispatch","prefix","prevPage","nextPage","slug","sourceChanged","slugValue","slugTarget","replace","slugChanged","options","authenticate","credential","credentials","get","responseTarget","JSON","stringify","toJSON","requestSubmit","publicKey","PublicKeyCredential","parseRequestOptionsFromJSON","optionsValue","submit","createCredential","then","create","parseCreationOptionsFromJSON","import","HwComboboxController","push","catch","KoiToolbarElement","constructor","super","setAttribute","initGOVUK","body","HTMLScriptElement","prototype","initAll","customElements","define","Turbo"],"mappings":"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"}
1
+ {"version":3,"file":"koi.min.js","sources":["../../../javascript/koi/utils/lexxy.js","../../../javascript/koi/utils/trix.js","../../../javascript/koi/controllers/application.js","../../../javascript/koi/controllers/index.js","../../../javascript/koi/controllers/clipboard_controller.js","../../../javascript/koi/controllers/flash_controller.js","../../../javascript/koi/controllers/keyboard_controller.js","../../../javascript/koi/controllers/modal_controller.js","../../../javascript/koi/controllers/navigation_controller.js","../../../javascript/koi/controllers/navigation_toggle_controller.js","../../../javascript/koi/controllers/pagy_nav_controller.js","../../../javascript/koi/controllers/sluggable_controller.js","../../../javascript/koi/controllers/webauthn_authentication_controller.js","../../../javascript/koi/controllers/webauthn_registration_controller.js","../../../javascript/koi/elements/toolbar.js","../../../javascript/koi/application.js"],"sourcesContent":["import * as Lexxy from \"lexxy\";\n\nLexxy.configure({\n global: {\n authenticatedUploads: true,\n },\n});\n\n// The link toolbar's URL input is type=url, whose checkValidity() rejects\n// schemeless values like #anchor and /relative that lexxy's link node happily\n// preserves. Switch it to type=text with the same pattern Koi applies to the\n// trix link dialog (see Koi::Tags::TrixToolbar::TRIX_DIALOGS_TEMPLATE) so both\n// editors validate link input identically.\ndocument.addEventListener(\"lexxy:initialize\", ({ target }) => {\n target\n .querySelectorAll(\"lexxy-link-dropdown input[type=url]\")\n .forEach((input) => {\n input.type = \"text\";\n input.pattern = \"(https?|mailto:|tel:|/|#).*?\";\n });\n});\n\nexport default Lexxy;\n","import \"trix\";\n\n// Trix does not provide an export\nconst Trix = window.Trix;\n\n// Koi uses H4 for headings instead of H1\nTrix.config.blockAttributes[\"heading4\"] = {\n tagName: \"h4\",\n terminal: true,\n breakOnReturn: true,\n group: false,\n};\n\n// Remove H1 from trix list of acceptable tags\ndelete Trix.config.blockAttributes.heading1;\n\nexport default Trix;\n","import { Application } from \"@hotwired/stimulus\";\n\nconst application = Application.start();\n\nexport { application };\n","import { application } from \"./application\";\n\nimport content from \"@katalyst/content\";\napplication.load(content);\n\nimport GOVUK from \"@katalyst/govuk-formbuilder\";\nGOVUK.start(application);\n\nimport navigation from \"@katalyst/navigation\";\napplication.load(navigation);\n\nimport tables from \"@katalyst/tables\";\napplication.load(tables);\n\nimport ClipboardController from \"./clipboard_controller\";\nimport FlashController from \"./flash_controller\";\nimport KeyboardController from \"./keyboard_controller\";\nimport ModalController from \"./modal_controller\";\nimport NavigationController from \"./navigation_controller\";\nimport NavigationToggleController from \"./navigation_toggle_controller\";\nimport PagyNavController from \"./pagy_nav_controller\";\nimport SluggableController from \"./sluggable_controller\";\nimport WebauthnAuthenticationController from \"./webauthn_authentication_controller\";\nimport WebauthnRegistrationController from \"./webauthn_registration_controller\";\n\nconst Definitions = [\n {\n identifier: \"clipboard\",\n controllerConstructor: ClipboardController,\n },\n {\n identifier: \"flash\",\n controllerConstructor: FlashController,\n },\n {\n identifier: \"keyboard\",\n controllerConstructor: KeyboardController,\n },\n {\n identifier: \"modal\",\n controllerConstructor: ModalController,\n },\n {\n identifier: \"navigation\",\n controllerConstructor: NavigationController,\n },\n {\n identifier: \"navigation-toggle\",\n controllerConstructor: NavigationToggleController,\n },\n {\n identifier: \"pagy-nav\",\n controllerConstructor: PagyNavController,\n },\n {\n identifier: \"sluggable\",\n controllerConstructor: SluggableController,\n },\n {\n identifier: \"webauthn-authentication\",\n controllerConstructor: WebauthnAuthenticationController,\n },\n {\n identifier: \"webauthn-registration\",\n controllerConstructor: WebauthnRegistrationController,\n },\n];\n\n// dynamically attempt to load hw_combobox_controller, this is an optional dependency\nawait import(\"controllers/hw_combobox_controller\")\n .then(({ default: HwComboboxController }) => {\n Definitions.push({\n identifier: \"hw-combobox\",\n controllerConstructor: HwComboboxController,\n });\n })\n .catch(() => null);\n\napplication.load(Definitions);\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class ClipboardController extends Controller {\n static targets = [\"source\"];\n\n static classes = [\"supported\"];\n\n connect() {\n if (\"clipboard\" in navigator) {\n this.element.classList.add(this.supportedClass);\n }\n }\n\n copy(event) {\n event.preventDefault();\n navigator.clipboard.writeText(this.sourceTarget.value);\n\n this.element.classList.add(\"copied\");\n setTimeout(() => {\n this.element.classList.remove(\"copied\");\n }, 2000);\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class FlashController extends Controller {\n close(e) {\n e.target.closest(\"li\").remove();\n\n // remove the flash container if there are no more flashes\n if (this.element.children.length === 0) {\n this.element.remove();\n }\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nconst DEBUG = false;\n\nexport default class KeyboardController extends Controller {\n static values = {\n mapping: String,\n depth: { type: Number, default: 2 },\n };\n\n event(cause) {\n if (isFormField(cause.target) || this.#ignore(cause)) return;\n\n const key = this.describeEvent(cause);\n\n this.buffer = [...(this.buffer || []), key].slice(0 - this.depthValue);\n\n if (DEBUG) console.debug(\"[keyboard] buffer:\", ...this.buffer);\n\n // test whether the tail of the buffer matches any of the configured chords\n const action = this.buffer.reduceRight((mapping, key) => {\n if (typeof mapping === \"string\" || typeof mapping === \"undefined\") {\n return mapping;\n } else {\n return mapping[key];\n }\n }, this.mappings);\n\n // if we don't have a string we may have a miss or an incomplete chord\n if (typeof action !== \"string\") return;\n\n // clear the buffer and prevent the key from being consumed elsewhere\n this.buffer = [];\n cause.preventDefault();\n\n if (DEBUG) console.debug(\"[keyboard] event: %s\", action);\n\n // fire the configured event\n const event = new CustomEvent(action, {\n detail: { cause: cause },\n bubbles: true,\n });\n cause.target.dispatchEvent(event);\n }\n\n /**\n * @param event KeyboardEvent input event to describe\n * @return String description of keyboard event, e.g. 'C-KeyV' (CTRL+V)\n */\n describeEvent(event) {\n return [\n event.ctrlKey && \"C\",\n event.metaKey && \"M\",\n event.altKey && \"A\",\n event.shiftKey && \"S\",\n event.code,\n ]\n .filter((w) => w)\n .join(\"-\");\n }\n\n /**\n * Build a tree for efficiently looking up key chords, where the last key in the sequence\n * is the first key in tree.\n */\n get mappings() {\n const inputs = this.mappingValue\n .replaceAll(/\\s+/g, \" \")\n .split(\" \")\n .filter((f) => f.length > 0);\n const mappings = {};\n\n inputs.forEach((mapping) => this.#parse(mappings, mapping));\n\n // memoize the result\n Object.defineProperty(this, \"mappings\", {\n value: mappings,\n writable: false,\n });\n\n return mappings;\n }\n\n /**\n * Parse a key chord pattern and an event and store it in the inverted tree lookup structure.\n *\n * @param mappings inverted tree lookup for key chords\n * @param mapping input definition, e.g. \"C-KeyC+C-KeyV->paste\"\n */\n #parse(mappings, mapping) {\n const [pattern, event] = mapping.split(\"->\");\n const keys = pattern.split(\"+\");\n const first = keys.shift();\n\n mappings = keys.reduceRight(\n (mappings, key) => (mappings[key] ||= {}),\n mappings,\n );\n mappings[first] = event;\n }\n\n /**\n * Ignore modifier keys, as they will be captured in normal key presses.\n *\n * @param event KeyboardEvent\n * @returns {boolean} true if key event should be ignored\n */\n #ignore(event) {\n switch (event.code) {\n case \"ControlLeft\":\n case \"ControlRight\":\n case \"MetaLeft\":\n case \"MetaRight\":\n case \"ShiftLeft\":\n case \"ShiftRight\":\n case \"AltLeft\":\n case \"AltRight\":\n return true;\n default:\n return false;\n }\n }\n}\n\n/**\n * Detect input nodes where we should not listen for events.\n *\n * Credit: github.com\n */\nfunction isFormField(element) {\n if (!(element instanceof HTMLElement)) {\n return false;\n }\n\n const name = element.nodeName.toLowerCase();\n const type = (element.getAttribute(\"type\") || \"\").toLowerCase();\n return (\n name === \"select\" ||\n name === \"textarea\" ||\n name === \"trix-editor\" ||\n (name === \"input\" &&\n type !== \"submit\" &&\n type !== \"reset\" &&\n type !== \"checkbox\" &&\n type !== \"radio\" &&\n type !== \"file\") ||\n element.isContentEditable\n );\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class ModalController extends Controller {\n static targets = [\"dialog\"];\n\n connect() {\n this.element.addEventListener(\"turbo:submit-end\", this.onSubmit);\n }\n\n disconnect() {\n this.element.removeEventListener(\"turbo:submit-end\", this.onSubmit);\n }\n\n outside(e) {\n if (e.target.tagName === \"DIALOG\") this.dismiss();\n }\n\n dismiss() {\n if (!this.dialogTarget) return;\n if (!this.dialogTarget.open) this.dialogTarget.close();\n\n this.element.removeAttribute(\"src\");\n this.dialogTarget.remove();\n }\n\n dialogTargetConnected(dialog) {\n dialog.showModal();\n }\n\n onSubmit = (event) => {\n if (\n event.detail.success &&\n \"closeDialog\" in event.detail.formSubmission?.submitter?.dataset\n ) {\n this.dialogTarget.close();\n this.element.removeAttribute(\"src\");\n this.dialogTarget.remove();\n }\n };\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class NavigationController extends Controller {\n static targets = [\"filter\"];\n\n filter() {\n const filter = this.filterTarget.value;\n this.clearFilter(filter);\n\n if (filter.length > 0) {\n this.applyFilter(filter);\n }\n }\n\n go() {\n this.element.querySelector(\"li:not([hidden]) > a\").click();\n }\n\n clear() {\n if (this.filterTarget.value.length === 0) this.filterTarget.blur();\n }\n\n applyFilter(filter) {\n // hide items that don't match the search filter\n this.links\n .filter(\n (li) =>\n !this.prefixSearch(filter.toLowerCase(), li.innerText.toLowerCase()),\n )\n .forEach((li) => {\n li.toggleAttribute(\"hidden\", true);\n });\n\n this.menus\n .filter((li) => !li.matches(\"li:has(li:not([hidden]) > a)\"))\n .forEach((li) => {\n li.toggleAttribute(\"hidden\", true);\n });\n }\n\n clearFilter(filter) {\n this.element.querySelectorAll(\"li\").forEach((li) => {\n li.toggleAttribute(\"hidden\", false);\n });\n }\n\n prefixSearch(needle, haystack) {\n const haystackLength = haystack.length;\n const needleLength = needle.length;\n if (needleLength > haystackLength) {\n return false;\n }\n if (needleLength === haystackLength) {\n return needle === haystack;\n }\n outer: for (let i = 0, j = 0; i < needleLength; i++) {\n const needleChar = needle.charCodeAt(i);\n if (needleChar === 32) {\n // skip ahead to next space in the haystack\n while (j < haystackLength && haystack.charCodeAt(j++) !== 32) {}\n continue;\n }\n while (j < haystackLength) {\n if (haystack.charCodeAt(j++) === needleChar) continue outer;\n // skip ahead to the next space in the haystack\n while (j < haystackLength && haystack.charCodeAt(j++) !== 32) {}\n }\n return false;\n }\n return true;\n }\n\n toggle() {\n this.element.open ? this.close() : this.open();\n }\n\n open() {\n if (!this.element.open) this.element.showModal();\n }\n\n close() {\n if (this.element.open) this.element.close();\n }\n\n click(e) {\n if (e.target === this.element) this.close();\n }\n\n onMorphAttribute = (e) => {\n if (e.target !== this.element) return;\n\n switch (e.detail.attributeName) {\n case \"open\":\n e.preventDefault();\n }\n };\n\n get links() {\n return Array.from(this.element.querySelectorAll(\"li:has(> a)\"));\n }\n\n get menus() {\n return Array.from(this.element.querySelectorAll(\"li:has(> ul)\"));\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class NavigationToggleController extends Controller {\n trigger() {\n this.dispatch(\"toggle\", { prefix: \"navigation\", bubbles: true });\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class PagyNavController extends Controller {\n connect() {\n document.addEventListener(\"shortcut:page-prev\", this.prevPage);\n document.addEventListener(\"shortcut:page-next\", this.nextPage);\n }\n\n disconnect() {\n document.removeEventListener(\"shortcut:page-prev\", this.prevPage);\n document.removeEventListener(\"shortcut:page-next\", this.nextPage);\n }\n\n nextPage = () => {\n this.element.querySelector(\"a:last-child\").click();\n };\n\n prevPage = () => {\n this.element.querySelector(\"a:first-child\").click();\n };\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\n/**\n * Connect an input (e.g. title) to slug.\n */\nexport default class SluggableController extends Controller {\n static targets = [\"source\", \"slug\"];\n static values = {\n slug: String,\n };\n\n sourceChanged(e) {\n if (this.slugValue === \"\") {\n this.slugTarget.value = parameterize(this.sourceTarget.value);\n }\n }\n\n slugChanged(e) {\n this.slugValue = this.slugTarget.value;\n }\n}\n\nfunction parameterize(input) {\n return input\n .toLowerCase()\n .replace(/'/g, \"-\")\n .replace(/[^-\\w\\s]/g, \"\")\n .replace(/[^a-z0-9]+/g, \"-\")\n .replace(/(^-|-$)/g, \"\");\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class WebauthnAuthenticationController extends Controller {\n static targets = [\"response\"];\n static values = {\n options: Object,\n };\n\n async authenticate() {\n const credential = await navigator.credentials.get(this.options);\n\n this.responseTarget.value = JSON.stringify(credential.toJSON());\n\n this.element.requestSubmit();\n }\n\n get options() {\n return {\n publicKey: PublicKeyCredential.parseRequestOptionsFromJSON(\n this.optionsValue,\n ),\n };\n }\n}\n","import { Controller } from \"@hotwired/stimulus\";\n\nexport default class WebauthnRegistrationController extends Controller {\n static targets = [\"response\"];\n static values = {\n options: Object,\n };\n\n submit(e) {\n if (this.responseTarget.value) return;\n\n e.preventDefault();\n this.createCredential().then(() => {\n e.target.submit();\n });\n }\n\n async createCredential() {\n const credential = await navigator.credentials.create(this.options);\n this.responseTarget.value = JSON.stringify(credential.toJSON());\n }\n\n get options() {\n return {\n publicKey: PublicKeyCredential.parseCreationOptionsFromJSON(\n this.optionsValue,\n ),\n };\n }\n}\n","class KoiToolbarElement extends HTMLElement {\n constructor() {\n super();\n\n this.setAttribute(\"role\", \"toolbar\");\n }\n}\n\ncustomElements.define(\"koi-toolbar\", KoiToolbarElement);\n","import \"@hotwired/turbo-rails\";\nimport \"@rails/actiontext\";\nimport Lexxy from \"./utils/lexxy\";\nimport Trix from \"./utils/trix\";\n\nimport \"./controllers\";\nimport \"./elements\";\n\nexport default { Lexxy, Trix };\n"],"names":["Lexxy","configure","global","authenticatedUploads","document","addEventListener","target","querySelectorAll","forEach","input","type","pattern","Trix","window","config","blockAttributes","tagName","terminal","breakOnReturn","group","heading1","application","Application","start","load","content","GOVUK","navigation","tables","Definitions","identifier","controllerConstructor","Controller","static","connect","navigator","this","element","classList","add","supportedClass","copy","event","preventDefault","clipboard","writeText","sourceTarget","value","setTimeout","remove","close","e","closest","children","length","mapping","String","depth","Number","default","cause","HTMLElement","name","nodeName","toLowerCase","getAttribute","isContentEditable","isFormField","ignore","key","describeEvent","buffer","slice","depthValue","action","reduceRight","mappings","CustomEvent","detail","bubbles","dispatchEvent","ctrlKey","metaKey","altKey","shiftKey","code","filter","w","join","inputs","mappingValue","replaceAll","split","f","parse","Object","defineProperty","writable","keys","first","shift","onSubmit","disconnect","removeEventListener","outside","dismiss","dialogTarget","open","removeAttribute","dialogTargetConnected","dialog","showModal","success","formSubmission","submitter","dataset","filterTarget","clearFilter","applyFilter","go","querySelector","click","clear","blur","links","li","prefixSearch","innerText","toggleAttribute","menus","matches","needle","haystack","haystackLength","needleLength","outer","i","j","needleChar","charCodeAt","toggle","onMorphAttribute","attributeName","Array","from","trigger","dispatch","prefix","prevPage","nextPage","slug","sourceChanged","slugValue","slugTarget","replace","slugChanged","options","authenticate","credential","credentials","get","responseTarget","JSON","stringify","toJSON","requestSubmit","publicKey","PublicKeyCredential","parseRequestOptionsFromJSON","optionsValue","submit","createCredential","then","create","parseCreationOptionsFromJSON","import","HwComboboxController","push","catch","KoiToolbarElement","constructor","super","setAttribute","customElements","define"],"mappings":"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"}
@@ -1,4 +1,5 @@
1
- .button {
1
+ .button,
2
+ :where(.govuk-button) {
2
3
  /* Configuration */
3
4
  --button-padding: 0.5em 1em;
4
5
  --button-gap: 0.5em;
@@ -72,7 +73,8 @@
72
73
  --button-padding: 0.5em;
73
74
  }
74
75
 
75
- &[data-ghost-button] {
76
+ &[data-ghost-button],
77
+ &:where(.govuk-button--secondary) {
76
78
  --button-bg: transparent;
77
79
  --button-border-color: currentColor;
78
80
  --button-color: currentColor;
@@ -1,3 +1,5 @@
1
+ /* HTML File inputs */
2
+
1
3
  .govuk-file-upload {
2
4
  max-width: 100%;
3
5
  }
@@ -18,3 +20,260 @@
18
20
  opacity: 0.5;
19
21
  cursor: not-allowed;
20
22
  }
23
+
24
+ /* Drop zone (JS uploads) */
25
+
26
+ .govuk-file-upload-wrapper {
27
+ display: block;
28
+ position: relative;
29
+ z-index: 0;
30
+ border: var(--stroke-input);
31
+ border-radius: var(--input-radius);
32
+ padding: var(--space-xs-s);
33
+
34
+ .govuk-form-group--error & {
35
+ border-color: var(--govuk-error-color);
36
+ }
37
+
38
+ &:has(.govuk-file-upload-button:hover) {
39
+ border-color: var(--color-dark-glare);
40
+ }
41
+ }
42
+
43
+ .govuk-file-upload-wrapper--disabled {
44
+ /* the button disables pointer events, so the cursor lives on the wrapper */
45
+ cursor: not-allowed;
46
+ }
47
+
48
+ .govuk-file-upload-button {
49
+ --button-font-weight: 400;
50
+
51
+ width: 100%;
52
+ background: transparent;
53
+ border: none;
54
+ padding: 0;
55
+ cursor: pointer;
56
+ border-radius: var(--radius-s);
57
+
58
+ &:disabled {
59
+ pointer-events: none;
60
+ opacity: 0.5;
61
+ }
62
+
63
+ &:focus-visible {
64
+ outline: none;
65
+ }
66
+
67
+ &:focus-visible .govuk-button {
68
+ outline: var(--focus-outline);
69
+ outline-offset: 2px;
70
+ }
71
+
72
+ .govuk-attachment + & {
73
+ margin-block-start: var(--flow-space, var(--space-s));
74
+ }
75
+ }
76
+
77
+ .govuk-file-upload-wrapper:has(.govuk-file-upload-button--dragging) {
78
+ background-color: var(--color-tint);
79
+ }
80
+
81
+ .govuk-file-upload-button__status {
82
+ /* visually hidden */
83
+ border: 0;
84
+ clip: rect(0 0 0 0);
85
+ height: auto;
86
+ margin: 0;
87
+ overflow: hidden;
88
+ padding: 0;
89
+ position: absolute;
90
+ width: 1px;
91
+ white-space: nowrap;
92
+ }
93
+
94
+ .govuk-file-upload-button__pseudo-button-container {
95
+ display: flex;
96
+ flex-wrap: wrap;
97
+ align-items: baseline;
98
+ gap: var(--space-2xs);
99
+ }
100
+
101
+ .govuk-file-upload-button__pseudo-button {
102
+ flex-shrink: 0;
103
+ }
104
+
105
+ .govuk-file-upload-button__instruction {
106
+ margin: 0;
107
+ text-align: left;
108
+ }
109
+
110
+ /* Attachments */
111
+
112
+ .govuk-attachment {
113
+ display: grid;
114
+ grid-template-areas: "preview caption actions";
115
+ grid-template-columns: auto 1fr auto;
116
+ margin: 0 0 var(--flow-space) 0;
117
+ padding: var(--space-xs);
118
+ grid-gap: var(--space-s);
119
+ border: 1px solid var(--color-mid);
120
+ border-radius: var(--input-radius);
121
+
122
+ .preview {
123
+ grid-area: preview;
124
+ max-width: 4rem;
125
+ aspect-ratio: 1/1;
126
+ object-fit: cover;
127
+ border-radius: var(--radius-s);
128
+ }
129
+
130
+ .caption {
131
+ display: flex;
132
+ flex-direction: column;
133
+ grid-area: caption;
134
+ font-family: var(--font-base);
135
+ font-size: var(--size-step-0);
136
+ padding-block-start: unset;
137
+ }
138
+
139
+ .filename {
140
+ font-weight: var(--font-bold);
141
+ }
142
+
143
+ .size {
144
+ color: var(--govuk-hint-color);
145
+ }
146
+
147
+ .govuk-attachment-progress {
148
+ inline-size: auto;
149
+ max-inline-size: 16em;
150
+ margin-block-start: var(--space-xs);
151
+ block-size: var(--space-2xs);
152
+ appearance: none;
153
+ border: none;
154
+ border-radius: var(--radius-s);
155
+ overflow: hidden;
156
+
157
+ background-color: var(--color-mid);
158
+
159
+ &::-webkit-progress-bar {
160
+ background-color: var(--color-mid);
161
+ }
162
+
163
+ &::-webkit-progress-value {
164
+ background-color: var(--color-dark);
165
+ transition: width var(--transition-movement);
166
+ }
167
+
168
+ &::-moz-progress-bar {
169
+ background-color: var(--color-dark);
170
+ }
171
+ }
172
+
173
+ .actions {
174
+ grid-area: actions;
175
+ flex-direction: column;
176
+ align-items: stretch;
177
+ justify-content: center;
178
+ }
179
+
180
+ .actions button {
181
+ display: none; /* Buttons require JavaScript */
182
+ }
183
+
184
+ &[data-state] .status {
185
+ margin-block-start: auto;
186
+ }
187
+
188
+ &[data-state="upload-successful"] {
189
+ .status {
190
+ color: var(--color-success);
191
+ }
192
+ }
193
+
194
+ &[data-state="upload-failed"] {
195
+ border-color: var(--govuk-error-color);
196
+
197
+ .status {
198
+ color: var(--govuk-error-color);
199
+ }
200
+ }
201
+ }
202
+
203
+ /* With JavaScript running the button is the figure's only interactive
204
+ control; the select still carries the submitted value but leaves the
205
+ display, tab order, and accessibility tree. */
206
+ .govuk-frontend-supported .govuk-attachment {
207
+ .actions select {
208
+ display: none;
209
+ }
210
+
211
+ .actions button {
212
+ display: revert;
213
+ }
214
+ }
215
+
216
+ /*
217
+ * Hide duplicate inputs when multiple is not enabled, this allows reverting
218
+ * but the value from the last select will overwrite the others in save.
219
+ */
220
+ .govuk-file-upload-wrapper:has(input[type="file"]:not([multiple])) {
221
+ .govuk-attachment:has(+ .govuk-attachment) {
222
+ display: none;
223
+ }
224
+ }
225
+
226
+ /* Movement */
227
+
228
+ .govuk-attachment {
229
+ .status {
230
+ align-self: flex-start;
231
+ }
232
+
233
+ &[data-state="upload-successful"] {
234
+ .status {
235
+ animation: govuk-attachment-state-pop var(--transition-bounce);
236
+ }
237
+ }
238
+
239
+ &[data-state="upload-failed"] {
240
+ .status {
241
+ animation: govuk-attachment-state-pop var(--transition-bounce);
242
+ }
243
+ }
244
+ }
245
+
246
+ @keyframes govuk-attachment-state-pop {
247
+ 50% {
248
+ scale: 1.05;
249
+ }
250
+ }
251
+
252
+ .govuk-attachment {
253
+ &:has(.govuk-attachment-progress):has(.preview) {
254
+ &::before {
255
+ content: "";
256
+ position: absolute;
257
+ display: block;
258
+ margin: var(--space-xs);
259
+ width: calc(64px - 2 * var(--space-xs));
260
+ padding: 4px; /* spinner width */
261
+ aspect-ratio: 1;
262
+ border-radius: 50%;
263
+ background: var(--color-light);
264
+ --mask:
265
+ conic-gradient(#0000 10%, #000), linear-gradient(#000 0 0) content-box;
266
+ -webkit-mask: var(--mask);
267
+ mask: var(--mask);
268
+ -webkit-mask-composite: source-out;
269
+ mask-composite: subtract;
270
+ animation: govuk-attachment-upload-spinner 1s infinite linear;
271
+ }
272
+ }
273
+ }
274
+
275
+ @keyframes govuk-attachment-upload-spinner {
276
+ to {
277
+ transform: rotate(1turn);
278
+ }
279
+ }
@@ -14,8 +14,6 @@
14
14
  @import url("combobox.css");
15
15
  @import url("date-input.css");
16
16
  @import url("file-upload.css");
17
- @import url("image-field.css");
18
- @import url("document-field.css");
19
17
  @import url("lexxy.css");
20
18
  @import url("radios.css");
21
19
  @import url("password.css");
@@ -1,5 +1,4 @@
1
1
  import "@hotwired/turbo-rails";
2
- import { initAll } from "@katalyst/govuk-formbuilder";
3
2
  import "@rails/actiontext";
4
3
  import Lexxy from "./utils/lexxy";
5
4
  import Trix from "./utils/trix";
@@ -7,17 +6,4 @@ import Trix from "./utils/trix";
7
6
  import "./controllers";
8
7
  import "./elements";
9
8
 
10
- /** Initialize GOVUK */
11
- function initGOVUK() {
12
- document.body.classList.toggle("js-enabled", true);
13
- document.body.classList.toggle(
14
- "govuk-frontend-supported",
15
- "noModule" in HTMLScriptElement.prototype,
16
- );
17
- initAll();
18
- }
19
-
20
- window.addEventListener("turbo:load", initGOVUK);
21
- if (window.Turbo) initGOVUK();
22
-
23
9
  export default { Lexxy, Trix };
@@ -3,8 +3,8 @@ import { application } from "./application";
3
3
  import content from "@katalyst/content";
4
4
  application.load(content);
5
5
 
6
- import govuk from "@katalyst/govuk-formbuilder";
7
- application.load(govuk);
6
+ import GOVUK from "@katalyst/govuk-formbuilder";
7
+ GOVUK.start(application);
8
8
 
9
9
  import navigation from "@katalyst/navigation";
10
10
  application.load(navigation);
@@ -13,8 +13,5 @@
13
13
  <%= form.govuk_password_field :password, autofocus: true, autocomplete: "off" %>
14
14
  <%= hidden_field_tag(:redirect, params[:redirect]) %>
15
15
  <%= form.button("Next", type: :submit, class: "button") %>
16
-
17
- <%# init govuk js to provide the show/hide button %>
18
- <%= govuk_formbuilder_init %>
19
16
  <% end %>
20
17
  <% end %>
@@ -40,6 +40,12 @@ module Koi
40
40
  @attachment.present?
41
41
  end
42
42
 
43
+ # Introspected attachments are all built with type :attachment;
44
+ # whether the field submits an array follows the reflection's macro.
45
+ def attachments?
46
+ @attachment.present? && @attachment.macro == :has_many_attached
47
+ end
48
+
43
49
  def enum?
44
50
  @enum.present?
45
51
  end
@@ -70,8 +70,8 @@ module Koi
70
70
  def lexxy_rich_textarea(attribute_name, **attributes, &)
71
71
  attributes = {
72
72
  class: "lexxy-content",
73
- data: { direct_upload_url: @template.main_app.admin_direct_uploads_url },
74
- }.merge_html(**attributes)
73
+ data: { direct_upload_url: },
74
+ }.merge_html(attributes)
75
75
 
76
76
  super
77
77
  end
@@ -83,11 +83,21 @@ module Koi
83
83
  def trix_rich_textarea(attribute_name, **attributes, &)
84
84
  attributes = {
85
85
  class: "trix-content",
86
- data: { direct_upload_url: @template.main_app.admin_direct_uploads_url },
87
- }.merge_html(**attributes)
86
+ data: { direct_upload_url: },
87
+ }.merge_html(attributes)
88
88
 
89
89
  super
90
90
  end
91
+
92
+ private
93
+
94
+ def direct_upload_url
95
+ if @template.respond_to?(:admin_direct_uploads_url)
96
+ @template.admin_direct_uploads_url
97
+ elsif @template.respond_to?(:main_app) && @template.main_app.respond_to?(:admin_direct_uploads_url)
98
+ @template.main_app.admin_direct_uploads_url
99
+ end
100
+ end
91
101
  end
92
102
  end
93
103
  end