shoelace-rails 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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
+ }