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.
- checksums.yaml +7 -0
- data/.github/workflows/main.yml +85 -0
- data/.gitignore +20 -0
- data/Appraisals +25 -0
- data/CHANGELOG.md +3 -0
- data/CODE_OF_CONDUCT.md +84 -0
- data/Gemfile +11 -0
- data/LICENSE.txt +21 -0
- data/README.md +178 -0
- data/Rakefile +27 -0
- data/app/helpers/shoelace/form_helper.rb +451 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/dist/.keep +0 -0
- data/dist/types/.keep +0 -0
- data/gemfiles/rails_50.gemfile +12 -0
- data/gemfiles/rails_51.gemfile +11 -0
- data/gemfiles/rails_52.gemfile +11 -0
- data/gemfiles/rails_60.gemfile +11 -0
- data/gemfiles/rails_61.gemfile +11 -0
- data/gemfiles/rails_70.gemfile +11 -0
- data/gemfiles/rails_edge.gemfile +14 -0
- data/lib/shoelace/engine.rb +8 -0
- data/lib/shoelace/rails/version.rb +7 -0
- data/lib/shoelace/rails.rb +10 -0
- data/lib/shoelace/testing.rb +40 -0
- data/package.json +50 -0
- data/rollup.config.js +49 -0
- data/shoelace-rails.gemspec +35 -0
- data/src/index.ts +2 -0
- data/src/turbo/index.ts +6 -0
- data/src/turbo/polyfills/formdata-event.js +27 -0
- data/src/turbo/sl-turbo-form.ts +110 -0
- data/src/turbolinks/features/confirm.ts +42 -0
- data/src/turbolinks/features/disable.ts +94 -0
- data/src/turbolinks/features/remote.ts +107 -0
- data/src/turbolinks/index.ts +6 -0
- data/src/turbolinks/selectors.ts +38 -0
- data/src/turbolinks/start.ts +38 -0
- data/src/turbolinks/turbolinks.ts +78 -0
- data/src/turbolinks/utils/ajax.ts +146 -0
- data/src/turbolinks/utils/csp.ts +20 -0
- data/src/turbolinks/utils/csrf.ts +33 -0
- data/src/turbolinks/utils/dom.ts +40 -0
- data/src/turbolinks/utils/event.ts +57 -0
- data/src/turbolinks/utils/form.ts +58 -0
- data/test/dummy_app/Gemfile +19 -0
- data/test/dummy_app/Rakefile +6 -0
- data/test/dummy_app/app/controllers/hotwire_forms_controller.rb +46 -0
- data/test/dummy_app/app/controllers/turbolinks_forms_controller.rb +37 -0
- data/test/dummy_app/app/models/user.rb +16 -0
- data/test/dummy_app/app/packs/entrypoints/hotwire.js +1 -0
- data/test/dummy_app/app/packs/entrypoints/turbolinks.js +5 -0
- data/test/dummy_app/app/views/hotwire_forms/form.html.erb +45 -0
- data/test/dummy_app/app/views/hotwire_forms/show.html.erb +5 -0
- data/test/dummy_app/app/views/layouts/application.html.erb +39 -0
- data/test/dummy_app/app/views/turbolinks_forms/form.html.erb +44 -0
- data/test/dummy_app/app/views/turbolinks_forms/show.html.erb +5 -0
- data/test/dummy_app/bin/rails +5 -0
- data/test/dummy_app/bin/webpack +18 -0
- data/test/dummy_app/bin/yarn +18 -0
- data/test/dummy_app/config/application.rb +16 -0
- data/test/dummy_app/config/boot.rb +4 -0
- data/test/dummy_app/config/environment.rb +2 -0
- data/test/dummy_app/config/environments/development.rb +10 -0
- data/test/dummy_app/config/environments/test.rb +18 -0
- data/test/dummy_app/config/routes.rb +4 -0
- data/test/dummy_app/config/webpack/development.js +5 -0
- data/test/dummy_app/config/webpack/production.js +1 -0
- data/test/dummy_app/config/webpack/test.js +5 -0
- data/test/dummy_app/config/webpacker.yml +33 -0
- data/test/dummy_app/config.ru +6 -0
- data/test/dummy_app/package.json +24 -0
- data/test/dummy_app/test/system/hotwire_form_test.rb +65 -0
- data/test/dummy_app/test/system/turbolinks_form_test.rb +39 -0
- data/test/dummy_app/test/test_helper.rb +68 -0
- data/test/helpers/form_helper_test.rb +397 -0
- data/test/test_helper.rb +18 -0
- data/tsconfig.json +19 -0
- data/yarn.lock +249 -0
- 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
data/src/turbo/index.ts
ADDED
@@ -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,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
|
+
}
|