shoelace-rails 0.1.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.
Files changed (81) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/main.yml +85 -0
  3. data/.gitignore +20 -0
  4. data/Appraisals +25 -0
  5. data/CHANGELOG.md +3 -0
  6. data/CODE_OF_CONDUCT.md +84 -0
  7. data/Gemfile +11 -0
  8. data/LICENSE.txt +21 -0
  9. data/README.md +178 -0
  10. data/Rakefile +27 -0
  11. data/app/helpers/shoelace/form_helper.rb +451 -0
  12. data/bin/console +15 -0
  13. data/bin/setup +8 -0
  14. data/dist/.keep +0 -0
  15. data/dist/types/.keep +0 -0
  16. data/gemfiles/rails_50.gemfile +12 -0
  17. data/gemfiles/rails_51.gemfile +11 -0
  18. data/gemfiles/rails_52.gemfile +11 -0
  19. data/gemfiles/rails_60.gemfile +11 -0
  20. data/gemfiles/rails_61.gemfile +11 -0
  21. data/gemfiles/rails_70.gemfile +11 -0
  22. data/gemfiles/rails_edge.gemfile +14 -0
  23. data/lib/shoelace/engine.rb +8 -0
  24. data/lib/shoelace/rails/version.rb +7 -0
  25. data/lib/shoelace/rails.rb +10 -0
  26. data/lib/shoelace/testing.rb +40 -0
  27. data/package.json +50 -0
  28. data/rollup.config.js +49 -0
  29. data/shoelace-rails.gemspec +35 -0
  30. data/src/index.ts +2 -0
  31. data/src/turbo/index.ts +6 -0
  32. data/src/turbo/polyfills/formdata-event.js +27 -0
  33. data/src/turbo/sl-turbo-form.ts +110 -0
  34. data/src/turbolinks/features/confirm.ts +42 -0
  35. data/src/turbolinks/features/disable.ts +94 -0
  36. data/src/turbolinks/features/remote.ts +107 -0
  37. data/src/turbolinks/index.ts +6 -0
  38. data/src/turbolinks/selectors.ts +38 -0
  39. data/src/turbolinks/start.ts +38 -0
  40. data/src/turbolinks/turbolinks.ts +78 -0
  41. data/src/turbolinks/utils/ajax.ts +146 -0
  42. data/src/turbolinks/utils/csp.ts +20 -0
  43. data/src/turbolinks/utils/csrf.ts +33 -0
  44. data/src/turbolinks/utils/dom.ts +40 -0
  45. data/src/turbolinks/utils/event.ts +57 -0
  46. data/src/turbolinks/utils/form.ts +58 -0
  47. data/test/dummy_app/Gemfile +19 -0
  48. data/test/dummy_app/Rakefile +6 -0
  49. data/test/dummy_app/app/controllers/hotwire_forms_controller.rb +46 -0
  50. data/test/dummy_app/app/controllers/turbolinks_forms_controller.rb +37 -0
  51. data/test/dummy_app/app/models/user.rb +16 -0
  52. data/test/dummy_app/app/packs/entrypoints/hotwire.js +1 -0
  53. data/test/dummy_app/app/packs/entrypoints/turbolinks.js +5 -0
  54. data/test/dummy_app/app/views/hotwire_forms/form.html.erb +45 -0
  55. data/test/dummy_app/app/views/hotwire_forms/show.html.erb +5 -0
  56. data/test/dummy_app/app/views/layouts/application.html.erb +39 -0
  57. data/test/dummy_app/app/views/turbolinks_forms/form.html.erb +44 -0
  58. data/test/dummy_app/app/views/turbolinks_forms/show.html.erb +5 -0
  59. data/test/dummy_app/bin/rails +5 -0
  60. data/test/dummy_app/bin/webpack +18 -0
  61. data/test/dummy_app/bin/yarn +18 -0
  62. data/test/dummy_app/config/application.rb +16 -0
  63. data/test/dummy_app/config/boot.rb +4 -0
  64. data/test/dummy_app/config/environment.rb +2 -0
  65. data/test/dummy_app/config/environments/development.rb +10 -0
  66. data/test/dummy_app/config/environments/test.rb +18 -0
  67. data/test/dummy_app/config/routes.rb +4 -0
  68. data/test/dummy_app/config/webpack/development.js +5 -0
  69. data/test/dummy_app/config/webpack/production.js +1 -0
  70. data/test/dummy_app/config/webpack/test.js +5 -0
  71. data/test/dummy_app/config/webpacker.yml +33 -0
  72. data/test/dummy_app/config.ru +6 -0
  73. data/test/dummy_app/package.json +24 -0
  74. data/test/dummy_app/test/system/hotwire_form_test.rb +65 -0
  75. data/test/dummy_app/test/system/turbolinks_form_test.rb +39 -0
  76. data/test/dummy_app/test/test_helper.rb +68 -0
  77. data/test/helpers/form_helper_test.rb +397 -0
  78. data/test/test_helper.rb +18 -0
  79. data/tsconfig.json +19 -0
  80. data/yarn.lock +249 -0
  81. metadata +196 -0
data/package.json ADDED
@@ -0,0 +1,50 @@
1
+ {
2
+ "name": "@yuki24/shoelace-rails",
3
+ "version": "0.1.0-beta.5",
4
+ "author": "Yuki Nishijima <yuki24@hey.com>",
5
+ "license": "MIT",
6
+ "description": "Unobtrusive Javascript and Turbolinks support for shoelace.style",
7
+ "homepage": "https://github.com/yuki24/shoelace-rails",
8
+ "module": "dist/shoelace-rails.es2017-esm.js",
9
+ "main": "dist/shoelace-rails.es2017-umd.js",
10
+ "types": "dist/types/index.d.ts",
11
+ "files": [
12
+ "dist/*.js",
13
+ "dist/*.js.map",
14
+ "dist/types/**/*"
15
+ ],
16
+ "repository": {
17
+ "type": "git",
18
+ "url": "git+https://github.com/yuki24/shoelace-rails.git"
19
+ },
20
+ "bugs": {
21
+ "url": "https://github.com/yuki24/shoelace-rails"
22
+ },
23
+ "scripts": {
24
+ "build": "yarn types && rollup -c",
25
+ "clean": "rm -fr dist",
26
+ "prettier-project": "yarn run prettier-write 'src/**/*.{ts,tsx,js,jsx}'",
27
+ "prettier-write": "yarn run prettier --write",
28
+ "release": "yarn clean && yarn build && npm publish --access public",
29
+ "types": "tsc --noEmit false --emitDeclarationOnly true --outDir dist/types"
30
+ },
31
+ "devDependencies": {
32
+ "@hotwired/turbo": "^7.0.1",
33
+ "@rollup/plugin-node-resolve": "^13.0.4",
34
+ "@rollup/plugin-typescript": "^8.2.5",
35
+ "@shoelace-style/shoelace": "^2.0.0-beta.52",
36
+ "prettier": "^2.3.2",
37
+ "rollup": "^2.56.2",
38
+ "tslib": "^2.3.1",
39
+ "turbolinks": "5.x",
40
+ "typescript": "^4.3.5"
41
+ },
42
+ "peerDependencies": {
43
+ "@hotwired/turbo": "7.x",
44
+ "turbolinks": "5.x"
45
+ },
46
+ "prettier": {
47
+ "printWidth": 120,
48
+ "semi": false
49
+ }
50
+ }
data/rollup.config.js ADDED
@@ -0,0 +1,49 @@
1
+ import resolve from "@rollup/plugin-node-resolve"
2
+ import typescript from "@rollup/plugin-typescript"
3
+
4
+ export default [
5
+ {
6
+ external: ['turbolinks'],
7
+ input: "src/index.ts",
8
+ output: [
9
+ {
10
+ name: "Shoelace Rails",
11
+ file: "dist/shoelace-rails.es5-umd.js",
12
+ format: "umd",
13
+ sourcemap: true,
14
+ }
15
+ ],
16
+ plugins: [
17
+ resolve(),
18
+ typescript({ target: "es5", downlevelIteration: true })
19
+ ],
20
+ watch: {
21
+ include: "src/**"
22
+ }
23
+ },
24
+
25
+ {
26
+ external: ['turbolinks'],
27
+ input: "src/index.ts",
28
+ output: [
29
+ {
30
+ name: "Shoelace Rails",
31
+ file: "dist/shoelace-rails.es2017-umd.js",
32
+ format: "umd",
33
+ sourcemap: true,
34
+ },
35
+ {
36
+ file: "dist/shoelace-rails.es2017-esm.js",
37
+ format: "es",
38
+ sourcemap: true,
39
+ }
40
+ ],
41
+ plugins: [
42
+ resolve(),
43
+ typescript()
44
+ ],
45
+ watch: {
46
+ include: "src/**"
47
+ }
48
+ }
49
+ ]
@@ -0,0 +1,35 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/shoelace/rails/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "shoelace-rails"
7
+ spec.version = Shoelace::Rails::VERSION
8
+ spec.authors = ["Yuki Nishijima"]
9
+ spec.email = ["yuki24@hey.com"]
10
+
11
+ spec.summary = "Rails view helpers Shoelace.style, the design system."
12
+ spec.description = "The shoelace-rails gem adds useful view helper methods for using Shoalace Web Components."
13
+ spec.homepage = "https://github.com/yuki24/shoelace-rails"
14
+ spec.license = "MIT"
15
+ spec.required_ruby_version = ">= 2.5.0"
16
+
17
+ spec.metadata["homepage_uri"] = spec.homepage
18
+ spec.metadata["source_code_uri"] = "https://github.com/yuki24/shoelace-rails"
19
+ spec.metadata["changelog_uri"] = "https://github.com/yuki24/shoelace-rails/releases"
20
+
21
+ spec.files = Dir.chdir(File.expand_path(__dir__)) do
22
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{\A?:test/}) }
23
+ end
24
+
25
+ spec.bindir = "exe"
26
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
27
+ spec.require_paths = ["lib"]
28
+
29
+ spec.add_dependency "actionview", ">= 5.2.0"
30
+ spec.add_dependency "actionpack", ">= 5.2.0"
31
+
32
+ spec.add_development_dependency "appraisal"
33
+ spec.add_development_dependency "minitest", ">= 5.14.4"
34
+ spec.add_development_dependency "rails-dom-testing", ">= 2.0.3"
35
+ end
data/src/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export * from "./turbo"
2
+ export * from "./turbolinks"
@@ -0,0 +1,6 @@
1
+ import "./polyfills/formdata-event"
2
+ import { SlTurboFormElement } from "./sl-turbo-form"
3
+
4
+ export const startTurbo = () => {
5
+ customElements.define("sl-turbo-form", SlTurboFormElement)
6
+ }
@@ -0,0 +1,27 @@
1
+ (function(){
2
+ /*
3
+
4
+ Copyright (c) 2020 The Polymer Project Authors. All rights reserved. This
5
+ code may only be used under the BSD style license found at
6
+ http://polymer.github.io/LICENSE.txt The complete set of authors may be
7
+ found at http://polymer.github.io/AUTHORS.txt The complete set of
8
+ contributors may be found at http://polymer.github.io/CONTRIBUTORS.txt Code
9
+ distributed by Google as part of the polymer project is also subject to an
10
+ additional IP rights grant found at http://polymer.github.io/PATENTS.txt
11
+ */
12
+ 'use strict';function aa(a){var b=0;return function(){return b<a.length?{done:!1,value:a[b++]}:{done:!0}}}function n(a){var b="undefined"!=typeof Symbol&&Symbol.iterator&&a[Symbol.iterator];return b?b.call(a):{next:aa(a)}}function ba(a){for(var b,c=[];!(b=a.next()).done;)c.push(b.value);return c}var ca="function"==typeof Object.create?Object.create:function(a){function b(){}b.prototype=a;return new b};
13
+ function da(a){a=["object"==typeof globalThis&&globalThis,a,"object"==typeof window&&window,"object"==typeof self&&self,"object"==typeof global&&global];for(var b=0;b<a.length;++b){var c=a[b];if(c&&c.Math==Math)return c}throw Error("Cannot find global object");}var ea=da(this),q;
14
+ if("function"==typeof Object.setPrototypeOf)q=Object.setPrototypeOf;else{var r;a:{var ha={a:!0},ia={};try{ia.__proto__=ha;r=ia.a;break a}catch(a){}r=!1}q=r?function(a,b){a.__proto__=b;if(a.__proto__!==b)throw new TypeError(a+" is not extensible");return a}:null}var ja=q,ka=window.Document.prototype,la=ka.createElement,ma=ka.createEvent;var t=window.Event,u=t.prototype,na=u.initEvent,oa=null===u||void 0===u?void 0:u.stopImmediatePropagation,pa=null===u||void 0===u?void 0:u.stopPropagation,w=Object.getOwnPropertyDescriptor(u,"defaultPrevented"),x=Object.getOwnPropertyDescriptor(u,"target");Object.getOwnPropertyDescriptor(u,"type");var qa=window.document;var ra=null===x||void 0===x?void 0:x.get,y=void 0!==ra?function(a){return ra.call(a)}:function(a){return a.target},sa=null===w||void 0===w?void 0:w.get,ta=void 0!==sa?function(a){return sa.call(a)}:function(a){return a.defaultPrevented};function ua(a,b,c){Object.setPrototypeOf(a,Object.getPrototypeOf(b));for(var d=n(Object.keys(b)),e=d.next();!e.done;e=d.next())e=e.value,"prototype"!==e&&Object.defineProperty(a,e,Object.getOwnPropertyDescriptor(b,e));a.prototype=c;Object.defineProperty(c,"constructor",{writable:!0,configurable:!0,enumerable:!1,value:c.constructor})};var va=new WeakMap,wa=new WeakMap;function z(a,b){b=void 0===b?{}:b;try{var c=new t(a,b)}catch(e){c=ma.call(qa,"Event");var d=b.bubbles;b=b.cancelable;na.call(c,a,void 0===d?!1:d,void 0===b?!1:b)}Object.setPrototypeOf(c,Object.getPrototypeOf(this));return c}ua(z,t,u);
15
+ function xa(){z.prototype.constructor=z;Object.setPrototypeOf(z,Function.prototype);z.prototype.stopImmediatePropagation=function(){wa.set(this,!0);return oa.call(this)};z.prototype.stopPropagation=function(){va.set(this,!0);return pa.call(this)};window.Event=z};var A=window.EventTarget,B=null===A||void 0===A?void 0:A.prototype,ya=null===B||void 0===B?void 0:B.addEventListener,za=null===B||void 0===B?void 0:B.removeEventListener,Aa=null===B||void 0===B?void 0:B.dispatchEvent;var C=window.Node.prototype,E=C.addEventListener,Ba=C.appendChild,Ca=C.dispatchEvent,Da=C.getRootNode,Ea=C.insertBefore,Fa=C.removeChild,Ga=C.removeEventListener,F=Object.getOwnPropertyDescriptor(C,"parentNode");var G=window.Window.prototype,H=G.addEventListener,Ha=G.removeEventListener,Ia=G.dispatchEvent;var Ja=A?ya:function(a,b,c){if(this instanceof Node)return E.call(this,a,b,c);if(this instanceof Window)return H.call(this,a,b,c);throw new TypeError("Unsupported.");},Ka=A?za:function(a,b,c){if(this instanceof Node)return Ga.call(this,a,b,c);if(this instanceof Window)return Ha.call(this,a,b,c);throw new TypeError("Unsupported.");},Ma=A?Aa:function(a){if(this instanceof Node)return Ca.call(this,a);if(this instanceof Window)return Ia.call(this,a);throw new TypeError("Unsupported.");};var Na=null===F||void 0===F?void 0:F.get,I=void 0!==Na?function(a){return Na.call(a)}:function(a){return a.parentNode};function Oa(a){if(void 0!==Da)return Da.call(a,void 0);for(var b=I(a);null!==b;)a=b,b=I(b);return a};var J=window.HTMLInputElement.prototype,K=Object.getOwnPropertyDescriptor(J,"name"),L=Object.getOwnPropertyDescriptor(J,"type"),M=Object.getOwnPropertyDescriptor(J,"value");var N,Pa,Qa,Ra=null!==(N=(null!==L&&void 0!==L?L:{}).set)&&void 0!==N?N:function(a){this.type=a},Sa=null!==(Pa=(null!==K&&void 0!==K?K:{}).set)&&void 0!==Pa?Pa:function(a){this.name=a},Ta=null!==(Qa=(null!==M&&void 0!==M?M:{}).set)&&void 0!==Qa?Qa:function(a){this.value=a};var O=window.Element.prototype,P=O.getAttribute,Ua=O.hasAttribute,Va=O.removeAttribute,Wa=O.setAttribute;var Xa=Object.getOwnPropertyDescriptor(window.HTMLCollection.prototype,"length");var Ya,Za=null!==(Ya=(null!==Xa&&void 0!==Xa?Xa:{}).get)&&void 0!==Ya?Ya:function(){return this.length};var Q=window.HTMLFormElement.prototype,$a=Q.submit,ab=Object.getOwnPropertyDescriptor(Q,"elements");var bb=window.FormData,R=bb.prototype,cb=R.append,db=R.delete,eb=R.set;var fb=new WeakMap;function S(a,b){b=void 0===b?{}:b;a=z.call(this,a,b)||this;b=b.formData;if(!(b instanceof FormData))throw new TypeError("Failed to construct 'FormDataEvent': member formData is not of type FormData.");fb.set(a,b);return a}S.prototype=ca(z.prototype);S.prototype.constructor=S;if(ja)ja(S,z);else for(var T in z)if("prototype"!=T)if(Object.defineProperties){var gb=Object.getOwnPropertyDescriptor(z,T);gb&&Object.defineProperty(S,T,gb)}else S[T]=z[T];
16
+ ea.Object.defineProperties(S.prototype,{formData:{configurable:!0,enumerable:!0,get:function(){return fb.get(this)}}});var U=new WeakMap;function V(a){var b=new bb(a);Object.setPrototypeOf(b,Object.getPrototypeOf(this));U.set(b,[]);a instanceof HTMLFormElement&&Ma.call(a,new S("formdata",{bubbles:!0,formData:b}));return b}ua(V,bb,R);
17
+ function hb(){V.prototype.constructor=V;V.prototype.append=function(a,b){var c=U.get(this);if("string"!==typeof b)throw Error("Unsupported.");c.push({i:"append",name:a,value:b});return cb.call(this,a,b)};void 0!==db&&(V.prototype["delete"]=function(a){U.get(this).push({i:"delete",name:a});return db.call(this,a)});void 0!==eb&&(V.prototype.set=function(a,b){var c=U.get(this);if("string"!==typeof b)throw Error("Unsupported.");c.push({i:"set",name:a,value:b});return eb.call(this,a,b)});window.FormData=
18
+ V};function ib(a){function b(p){for(var h=ab.get.call(a),k=Za.call(h),m=0;m<k;m++){var v=h[m],fa;if(fa=P.call(v,"name")===p)fa=!Ua.call(v,"disabled");if(fa)return v}}function c(p){for(var h=ab.get.call(a),k=Za.call(h),m=0;m<k;m++){var v=h[m];P.call(v,"name")===p&&(f.has(v)||f.set(v,P.call(v,"disabled")),Wa.call(v,"disabled",""))}}function d(p,h,k){var m=la.call(qa,"input",void 0);Ra.call(m,"hidden");Sa.call(m,p);Ta.call(m,h);void 0!==k?(p=I(k),Ea.call(p,m,k)):Ba.call(a,m);g.push(m)}var e=new V(a),g=
19
+ [],f=new Map;e=n(U.get(e));for(var l=e.next();!l.done;l=e.next())switch(l=l.value,l.i){case "append":d(l.name,l.value);break;case "delete":c(l.name);break;case "set":var D=l;l=D.name;D=D.value;var La=b(l);void 0===La?d(l,D):(c(l),d(l,D,La));break;default:throw Error("UNREACHABLE");}setTimeout(function(){for(var p=n(g),h=p.next();!h.done;h=p.next()){h=h.value;var k=I(h);k&&Fa.call(k,h)}p=n(f);for(h=p.next();!h.done;h=p.next())k=n(h.value),h=k.next().value,k=k.next().value,P.call(h,"disabled")!==k&&
20
+ (null===k?Va.call(h,"disabled"):Wa.call(h,"disabled",k))})};function W(){this.h=[]}W.prototype.push=function(a){for(var b=a.g,c=a.capture,d=n(this.h),e=d.next();!e.done;e=d.next())if(e=e.value,b===e.g&&c===e.capture)return;this.h.push(a)};W.prototype.delete=function(a){var b=a.g;a=a.capture;for(var c=0;c<this.h.length;c++){var d=this.h[c];if(b===d.g&&a===d.capture){this.h.splice(c,1);break}}};
21
+ ea.Object.defineProperties(W.prototype,{length:{configurable:!0,enumerable:!0,get:function(){return this.h.length}},l:{configurable:!0,enumerable:!0,get:function(){for(var a=this.h,b=a.length-1;0<=b;b--){var c=a[b];if(c.capture)return c.g}}},j:{configurable:!0,enumerable:!0,get:function(){for(var a=this.h,b=a.length-1;0<=b;b--){var c=a[b];if(!c.capture)return c.g}}}});var X=new WeakMap;function jb(a,b,c){var d;b&&(X.has(a)||X.set(a,new W),c="boolean"===typeof c?c:null!==(d=null===c||void 0===c?void 0:c.capture)&&void 0!==d?d:!1,X.get(a).push({g:b,capture:c}))}function kb(a,b,c){var d;b&&(a=X.get(a),void 0!==a&&(c="boolean"===typeof c?c:null!==(d=null===c||void 0===c?void 0:c.capture)&&void 0!==d?d:!1,a.delete({g:b,capture:c})))};var Y=new WeakMap,lb=new WeakMap,Z=new WeakMap;function mb(a){if(!lb.has(a)){lb.set(a,!0);var b=y(a);if(b instanceof HTMLFormElement){b=Oa(b);var c=nb(function(){});Z.set(a,{target:b,g:c});Ja.call(b,"submit",c);jb(b,c)}}}
22
+ function nb(a){return function e(c,d){for(var g=[],f=1;f<arguments.length;++f)g[f-1]=arguments[f];g="function"===typeof a?a.call.apply(a,[this,c].concat(g instanceof Array?g:ba(n(g)))):a.handleEvent.apply(a,[c].concat(g instanceof Array?g:ba(n(g))));f=y(c)instanceof HTMLFormElement;if(wa.has(c)&&f)ob(c);else if(va.has(c)&&f){f=X.get(this);var l=f.j;e!==f.l&&e!==l||ob(c)}else Z.has(c)&&(f=Z.get(c),void 0!==f&&this===f.target&&(f=X.get(this).j,e===f&&ob(c)));return g}}
23
+ function ob(a){var b=Z.get(a);if(b){var c=b.target;b=b.g;Ka.call(c,"submit",b);kb(c,b);Z.delete(a)}ta(a)||ib(y(a))};var pb=new WeakMap;function qb(a,b){a.addEventListener=function(c,d,e){if("submit"===c&&null!==d){var g=nb(d);pb.set(d,g);d=g}g=b.call(this,c,d,e);if("formdata"===c){c=d;var f;c&&(e="boolean"===typeof e?e:null!==(f=null===e||void 0===e?void 0:e.capture)&&void 0!==f?f:!1,f=Y.get(this),void 0===f?(f=new W,f.push({g:c,capture:e}),Y.set(this,f),Ja.call(this,"submit",mb,!0)):f.push({g:c,capture:e}))}else"submit"===c&&null!==d&&jb(this,d,e);return g}}
24
+ function rb(a,b){a.removeEventListener=function(c,d,e){var g;"submit"===c&&null!==d&&(d=null!==(g=pb.get(d))&&void 0!==g?g:d);g=b.call(this,c,d,e);if("formdata"===c){c=d;var f;c&&(d=Y.get(this),void 0!==d&&(e="boolean"===typeof e?e:null!==(f=null===e||void 0===e?void 0:e.capture)&&void 0!==f?f:!1,d.delete({g:c,capture:e}),0===d.length&&(Y.delete(this),Ka.call(this,"submit",mb,!0))))}else"submit"===c&&null!==d&&kb(this,d,e);return g}};function sb(){Q.submit=function(){ib(this);return $a.call(this)}};void 0===window.FormDataEvent&&(xa(),B&&(qb(B,ya),rb(B,za)),E&&(qb(C,E),rb(C,Ga)),H&&(qb(G,H),rb(G,Ha)),hb(),Object.defineProperty(window,"FormDataEvent",{writable:!0,enumerable:!1,configurable:!0,value:S}),Q&&sb());
25
+ }).call(self);
26
+
27
+ //# sourceMappingURL=formdata-event.min.js.map
@@ -0,0 +1,110 @@
1
+ interface SlForm extends HTMLElement {
2
+ getFormData: () => FormData
3
+ }
4
+
5
+ interface SlButton extends HTMLElement {
6
+ submit: boolean
7
+ }
8
+
9
+ interface FormDataEvent extends Event {
10
+ formData: FormData
11
+ }
12
+
13
+ interface SubmitEvent extends CustomEvent {
14
+ submitter: Element
15
+ }
16
+
17
+ const submittersByForm: WeakMap<HTMLFormElement, HTMLElement> = new WeakMap()
18
+
19
+ const cloneAttributes = (target, source) =>
20
+ [...source.attributes]
21
+ .filter(({ nodeName }) => !["id", "class"].includes(nodeName))
22
+ .forEach(({ nodeName, nodeValue }) => target.setAttribute(nodeName, nodeValue))
23
+
24
+ const findSubmitterFromClickTarget = (target: EventTarget | null): HTMLElement => {
25
+ const element = target instanceof Element ? target : target instanceof Node ? target.parentElement : null
26
+
27
+ if (element?.tagName === "SL-BUTTON") {
28
+ const slButton = element as SlButton
29
+ return slButton.submit ? slButton : null
30
+ } else {
31
+ const candidate = element?.closest("input, button") as HTMLInputElement | HTMLFormElement
32
+ return candidate?.type === "submit" ? candidate : null
33
+ }
34
+ }
35
+
36
+ export class SlTurboFormElement extends HTMLElement {
37
+ private readonly form: HTMLFormElement
38
+ private called: boolean
39
+
40
+ static template = `
41
+ <sl-form>
42
+ <slot></slot>
43
+ </sl-form>
44
+ `
45
+
46
+ static get observedAttributes() {
47
+ return ["action", "method", "enctype", "accept-charset", "data"]
48
+ }
49
+
50
+ constructor() {
51
+ super()
52
+
53
+ // The <sl-form> component needs to be rendered within the shadow DOM so we can safely use the <slot>, which
54
+ // should contain shoelace form controls.
55
+ const shadowRoot = this.attachShadow({ mode: "open" })
56
+ shadowRoot.innerHTML = SlTurboFormElement.template
57
+
58
+ // The normal <form> element needs to be rendered within the light DOM so we can emit a custom 'submit' event
59
+ // with appropriate formdata, target, etc.
60
+ this.form = document.createElement("form")
61
+ this.form.style.display = "none"
62
+ cloneAttributes(this.form, this)
63
+ this.appendChild(this.form)
64
+ }
65
+
66
+ connectedCallback() {
67
+ this.addEventListener("click", this.clickCaptured, true)
68
+ this.addEventListener("sl-submit", this.handleSubmit)
69
+ this.addEventListener("formdata", this.handleFormData)
70
+ }
71
+
72
+ disconnectedCallback() {
73
+ this.removeEventListener("click", this.clickCaptured, true)
74
+ this.removeEventListener("sl-submit", this.handleSubmit)
75
+ this.removeEventListener("formdata", this.handleFormData)
76
+ }
77
+
78
+ handleFormData = (event: FormDataEvent) => {
79
+ const { formData, target } = event
80
+
81
+ if (this.form === target && !this.called) {
82
+ this.called = true
83
+ const slForm = this.shadowRoot.querySelector("sl-form") as SlForm
84
+
85
+ for (const [key, value] of slForm.getFormData().entries()) {
86
+ formData.append(key, value)
87
+ }
88
+ }
89
+ }
90
+
91
+ handleSubmit = (event: CustomEvent) => {
92
+ event.stopImmediatePropagation()
93
+ const submitter = submittersByForm.get(this.form)
94
+ const submitEvent = new CustomEvent("submit", { bubbles: true, cancelable: true }) as SubmitEvent
95
+ Object.defineProperty(submitEvent, "submitter", { get: () => submitter })
96
+
97
+ const cancelled = this.form.dispatchEvent(submitEvent)
98
+ if (cancelled) {
99
+ this.form.submit()
100
+ }
101
+ }
102
+
103
+ clickCaptured = (event: Event) => {
104
+ const submitter = findSubmitterFromClickTarget(event.target)
105
+
106
+ if (submitter) {
107
+ submittersByForm.set(this.form, submitter)
108
+ }
109
+ }
110
+ }
@@ -0,0 +1,42 @@
1
+ // This code was heavily inspired by the rails-ujs project.
2
+ // Copyright (c) 2007-2021 Rails Core team.
3
+ import { fire, stopEverything } from "../utils/event"
4
+
5
+ export const handleConfirm = (event) => {
6
+ const { target } = event
7
+
8
+ if (!allowAction(target)) {
9
+ return stopEverything(event)
10
+ }
11
+ }
12
+
13
+ // For 'data-confirm' attribute:
14
+ // - Fires `confirm` event
15
+ // - Shows the confirmation dialog
16
+ // - Fires the `confirm:complete` event
17
+ //
18
+ // Returns `true` if no function stops the chain and user chose yes `false` otherwise.
19
+ // Attaching a handler to the element's `confirm` event that returns a `falsy` value cancels the confirmation dialog.
20
+ // Attaching a handler to the element's `confirm:complete` event that returns a `falsy` value makes this function
21
+ // return false. The `confirm:complete` event is fired whether or not the user answered true or false to the dialog.
22
+ const allowAction = (element) => {
23
+ const message = element.getAttribute("data-confirm")
24
+
25
+ if (!message) {
26
+ return true
27
+ }
28
+
29
+ let callback = null,
30
+ answer = false
31
+ if (fire(element, "confirm")) {
32
+ try {
33
+ answer = confirm(message)
34
+ } catch (error) {
35
+ // no-op...
36
+ }
37
+
38
+ callback = fire(element, "confirm:complete", [answer])
39
+ }
40
+
41
+ return answer && callback
42
+ }
@@ -0,0 +1,94 @@
1
+ // This code was heavily inspired by the rails-ujs project.
2
+ // Copyright (c) 2007-2021 Rails Core team.
3
+ import { buttonDisableSelector, formDisableSelector, formEnableSelector, formSubmitSelector } from "../selectors"
4
+ import { getData, matches, setData } from "../utils/dom"
5
+ import { stopEverything } from "../utils/event"
6
+ import { formElements } from "../utils/form"
7
+
8
+ export const handleDisabledElement = (event) => {
9
+ const element: HTMLInputElement | HTMLFormElement = event.target
10
+
11
+ if (element.disabled) {
12
+ return stopEverything(event)
13
+ }
14
+ }
15
+
16
+ // Unified function to enable an element (link, button and form)
17
+ export const enableElement = function (e) {
18
+ let element
19
+
20
+ if (e instanceof Event) {
21
+ if (isXhrRedirect(e)) {
22
+ return
23
+ }
24
+ element = e.target
25
+ } else {
26
+ element = e
27
+ }
28
+
29
+ if (matches(element, buttonDisableSelector) || matches(element, formEnableSelector)) {
30
+ return enableFormElement(element)
31
+ } else if (matches(element, formSubmitSelector)) {
32
+ return enableFormElements(element)
33
+ }
34
+ }
35
+
36
+ // Unified function to disable an element (link, button and form)
37
+ export const disableElement = function (e) {
38
+ const element = e instanceof Event ? e.target : e
39
+
40
+ if (matches(element, buttonDisableSelector) || matches(element, formDisableSelector)) {
41
+ return disableFormElement(element)
42
+ } else if (matches(element, formSubmitSelector)) {
43
+ return disableFormElements(element)
44
+ }
45
+ }
46
+
47
+ // Disables form elements:
48
+ // - Caches element value in 'ujs:enable-with' data store
49
+ // - Replaces element text with value of 'data-disable-with' attribute
50
+ // - Sets disabled property to true
51
+ const disableFormElements = (form) => formElements(form, formDisableSelector).forEach(disableFormElement)
52
+
53
+ const disableFormElement = function (element) {
54
+ if (getData(element, "ujs:disabled")) {
55
+ return
56
+ }
57
+
58
+ const replacement = element.getAttribute("data-disable-with")
59
+ if (replacement != null) {
60
+ if (matches(element, "button")) {
61
+ setData(element, "ujs:enable-with", element.innerHTML)
62
+ element.innerHTML = replacement
63
+ } else {
64
+ setData(element, "ujs:enable-with", element.value)
65
+ element.value = replacement
66
+ }
67
+ }
68
+ element.disabled = true
69
+ return setData(element, "ujs:disabled", true)
70
+ }
71
+
72
+ // Re-enables disabled form elements:
73
+ // - Replaces element text with cached value from 'ujs:enable-with' data store (created in `disableFormElements`)
74
+ // - Sets disabled property to false
75
+ const enableFormElements = (form) => formElements(form, formEnableSelector).forEach(enableFormElement)
76
+
77
+ const enableFormElement = function (element) {
78
+ const originalText = getData(element, "ujs:enable-with")
79
+ if (originalText != null) {
80
+ if (matches(element, "button")) {
81
+ element.innerHTML = originalText
82
+ } else {
83
+ element.value = originalText
84
+ }
85
+ setData(element, "ujs:enable-with", null) // clean up cache
86
+ }
87
+ element.disabled = false
88
+ return setData(element, "ujs:disabled", null)
89
+ }
90
+
91
+ const isXhrRedirect = function (event) {
92
+ const xhr = event.detail != null ? event.detail[0] : undefined
93
+ return (xhr != null ? xhr.getResponseHeader("X-Xhr-Redirect") : undefined) != null
94
+ }
@@ -0,0 +1,107 @@
1
+ // This code was heavily inspired by the rails-ujs project.
2
+ // Copyright (c) 2007-2021 Rails Core team.
3
+ import { formSubmitSelector } from "../selectors"
4
+ import { ajax, href, isCrossDomain } from "../utils/ajax"
5
+ import { getData, matches, setData } from "../utils/dom"
6
+ import { fire, stopEverything } from "../utils/event"
7
+
8
+ const isRemote = function (element) {
9
+ const value = element.getAttribute("data-remote")
10
+ return value != null && value !== "false"
11
+ }
12
+
13
+ export const handleRemote = function (event: CustomEvent<{ formData: FormData; formControls: HTMLElement[] }>) {
14
+ const element = this
15
+
16
+ if (!isRemote(element)) {
17
+ return true
18
+ }
19
+
20
+ if (!fire(element, "ajax:before")) {
21
+ fire(element, "ajax:stopped")
22
+ return false
23
+ }
24
+
25
+ let method, url, data
26
+
27
+ const withCredentials = element.getAttribute("data-with-credentials")
28
+ const dataType = element.getAttribute("data-type") || "script"
29
+
30
+ if (matches(element, formSubmitSelector)) {
31
+ // memoized value from clicked submit button
32
+ method = getData(element, "ujs:submit-button-formmethod") || element.getAttribute("method") || "GET"
33
+ url = getData(element, "ujs:submit-button-formaction") || element.getAttribute("action") || location.href
34
+
35
+ // strip query string if it's a GET request
36
+ if (method.toUpperCase() === "GET") {
37
+ url = url.replace(/\?.*$/, "")
38
+ data = Array.from<Array<string>, string>(event.detail.formData as any, (e) =>
39
+ e.map(encodeURIComponent).join("=")
40
+ ).join("&")
41
+ } else {
42
+ data = event.detail.formData
43
+ }
44
+
45
+ setData(element, "ujs:submit-button", null)
46
+ setData(element, "ujs:submit-button-formmethod", null)
47
+ setData(element, "ujs:submit-button-formaction", null)
48
+ } else {
49
+ method = element.getAttribute("data-method")
50
+ url = href(element)
51
+ data = element.getAttribute("data-params")
52
+ }
53
+
54
+ ajax({
55
+ type: method,
56
+ url,
57
+ data,
58
+ dataType,
59
+ // stopping the "ajax:beforeSend" event will cancel the ajax request
60
+ beforeSend: (xhr, options) => {
61
+ if (fire(element, "ajax:beforeSend", [xhr, options])) {
62
+ return fire(element, "ajax:send", [xhr])
63
+ } else {
64
+ fire(element, "ajax:stopped")
65
+ return false
66
+ }
67
+ },
68
+ success: (...args) => fire(element, "ajax:success", args),
69
+ error: (...args) => fire(element, "ajax:error", args),
70
+ complete: (...args) => fire(element, "ajax:complete", args),
71
+ crossDomain: isCrossDomain(url),
72
+ withCredentials: withCredentials != null && withCredentials !== "false",
73
+ })
74
+
75
+ return stopEverything(event)
76
+ }
77
+
78
+ export const formSubmitButtonClick = (event) => {
79
+ const button: HTMLButtonElement = event.target
80
+ const { form } = button
81
+
82
+ if (!form) {
83
+ return
84
+ }
85
+
86
+ if (button.name) {
87
+ setData(form, "ujs:submit-button", { name: button.name, value: button.value })
88
+ }
89
+
90
+ setData(form, "ujs:formnovalidate-button", button.formNoValidate)
91
+ setData(form, "ujs:submit-button-formaction", button.getAttribute("formaction"))
92
+ return setData(form, "ujs:submit-button-formmethod", button.getAttribute("formmethod"))
93
+ }
94
+
95
+ export const preventInsignificantClick = (event) => {
96
+ const link: HTMLElement = event.target
97
+
98
+ const method = (link.getAttribute("data-method") || "GET").toUpperCase()
99
+ const data = link.getAttribute("data-params")
100
+ const metaClick = event.metaKey || event.ctrlKey
101
+ const insignificantMetaClick = metaClick && method === "GET" && !data
102
+ const nonPrimaryMouseClick = event.button != null && event.button !== 0
103
+
104
+ if (nonPrimaryMouseClick || insignificantMetaClick) {
105
+ return event.stopImmediatePropagation()
106
+ }
107
+ }
@@ -0,0 +1,6 @@
1
+ export * from "./utils/event"
2
+ export * from "./utils/dom"
3
+ export * from "./features/remote"
4
+ export * from "./selectors"
5
+ export * from "./turbolinks"
6
+ export * from "./start"
@@ -0,0 +1,38 @@
1
+ // This code was heavily inspired by the rails-ujs project.
2
+ // Copyright (c) 2007-2021 Rails Core team.
3
+ export const buttonClickSelector = {
4
+ selector: "sl-button[data-remote]:not([form]), sl-button[data-confirm]:not([form])",
5
+ exclude: "sl-form sl-button",
6
+ }
7
+
8
+ export const formSubmitSelector = "sl-form[data-remote]"
9
+
10
+ export const formInputClickSelector = [
11
+ "sl-form sl-button[submit]",
12
+ "sl-form sl-button:not([type])",
13
+ "sl-button[submit][sl-form]",
14
+ "sl-button[sl-form]:not([type])",
15
+ ].join(", ")
16
+
17
+ export const formDisableSelector = [
18
+ "sl-input[data-disable-with]:not([disabled])",
19
+ "sl-button[data-disable-with]:not([disabled])",
20
+ "sl-textarea[data-disable-with]:not([disabled])",
21
+ "sl-input[data-disable]:not([disabled])",
22
+ "sl-button[data-disable]:not([disabled])",
23
+ "sl-textarea[data-disable]:not([disabled])",
24
+ ].join(", ")
25
+
26
+ export const formEnableSelector = [
27
+ "sl-input[data-disable-with][disabled]",
28
+ "sl-button[data-disable-with][disabled]",
29
+ "sl-textarea[data-disable-with][disabled]",
30
+ "sl-input[data-disable][disabled]",
31
+ "sl-button[data-disable][disabled]",
32
+ "sl-textarea[data-disable][disabled]",
33
+ ].join(", ")
34
+
35
+ export const buttonDisableSelector = [
36
+ "sl-button[data-remote][data-disable-with]",
37
+ "sl-button[data-remote][data-disable]",
38
+ ].join(", ")
@@ -0,0 +1,38 @@
1
+ // This code was heavily inspired by the rails-ujs project.
2
+ // Copyright (c) 2007-2021 Rails Core team.
3
+ import { handleConfirm } from "./features/confirm"
4
+ import { disableElement, enableElement, handleDisabledElement } from "./features/disable"
5
+ import { formSubmitButtonClick, handleRemote, preventInsignificantClick } from "./features/remote"
6
+ import { buttonClickSelector, buttonDisableSelector, formInputClickSelector, formSubmitSelector } from "./selectors"
7
+
8
+ import { delegate } from "./utils/event"
9
+
10
+ export const getDefaultAssetPath = () => {
11
+ const rootUrl = (document.currentScript as any).src.replace(/\/packs.*$/, "")
12
+
13
+ return `${rootUrl}/packs/js/`
14
+ }
15
+
16
+ export const startUjs = () => {
17
+ delegate(document, buttonDisableSelector, "ajax:complete", enableElement)
18
+ delegate(document, buttonDisableSelector, "ajax:stopped", enableElement)
19
+
20
+ delegate(document, buttonClickSelector, "click", preventInsignificantClick)
21
+ delegate(document, buttonClickSelector, "click", handleDisabledElement)
22
+ delegate(document, buttonClickSelector, "click", handleConfirm)
23
+ delegate(document, buttonClickSelector, "click", disableElement)
24
+ delegate(document, buttonClickSelector, "click", handleRemote)
25
+
26
+ delegate(document, formSubmitSelector, "sl-submit", handleDisabledElement)
27
+ delegate(document, formSubmitSelector, "sl-submit", handleConfirm)
28
+ delegate(document, formSubmitSelector, "sl-submit", handleRemote)
29
+
30
+ // simulates a normal form submit:
31
+ delegate(document, formSubmitSelector, "ajax:send", disableElement)
32
+ delegate(document, formSubmitSelector, "ajax:complete", enableElement)
33
+
34
+ delegate(document, formInputClickSelector, "click", preventInsignificantClick)
35
+ delegate(document, formInputClickSelector, "click", handleDisabledElement)
36
+ // delegate(document, formInputClickSelector, "click", handleConfirm)
37
+ delegate(document, formInputClickSelector, "click", formSubmitButtonClick)
38
+ }