shoelace-rails 0.4.1 → 0.5.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/main.yml +11 -61
- data/.gitignore +1 -6
- data/Appraisals +6 -0
- data/CHANGELOG.md +16 -5
- data/Gemfile +0 -3
- data/Rakefile +2 -17
- data/app/helpers/shoelace/form_helper.rb +38 -2
- data/gemfiles/rails_60.gemfile +0 -1
- data/gemfiles/rails_61.gemfile +0 -1
- data/gemfiles/rails_70.gemfile +0 -1
- data/gemfiles/rails_71.gemfile +10 -0
- data/gemfiles/rails_edge.gemfile +0 -1
- data/lib/shoelace/rails/version.rb +1 -1
- data/shoelace-rails.gemspec +1 -1
- data/test/helpers/form_helper_test.rb +39 -10
- metadata +6 -58
- data/dist/.keep +0 -0
- data/dist/types/.keep +0 -0
- data/package.json +0 -50
- data/rollup.config.js +0 -49
- data/src/index.ts +0 -2
- data/src/turbo/index.ts +0 -6
- data/src/turbo/polyfills/formdata-event.js +0 -27
- data/src/turbo/sl-turbo-form.ts +0 -110
- data/src/turbolinks/features/confirm.ts +0 -42
- data/src/turbolinks/features/disable.ts +0 -94
- data/src/turbolinks/features/remote.ts +0 -107
- data/src/turbolinks/index.ts +0 -6
- data/src/turbolinks/selectors.ts +0 -38
- data/src/turbolinks/start.ts +0 -38
- data/src/turbolinks/turbolinks.ts +0 -78
- data/src/turbolinks/utils/ajax.ts +0 -146
- data/src/turbolinks/utils/csp.ts +0 -20
- data/src/turbolinks/utils/csrf.ts +0 -33
- data/src/turbolinks/utils/dom.ts +0 -40
- data/src/turbolinks/utils/event.ts +0 -57
- data/src/turbolinks/utils/form.ts +0 -58
- data/test/dummy_app/Gemfile +0 -19
- data/test/dummy_app/Rakefile +0 -6
- data/test/dummy_app/app/controllers/hotwire_forms_controller.rb +0 -46
- data/test/dummy_app/app/controllers/turbolinks_forms_controller.rb +0 -37
- data/test/dummy_app/app/models/user.rb +0 -16
- data/test/dummy_app/app/packs/entrypoints/hotwire.js +0 -1
- data/test/dummy_app/app/packs/entrypoints/turbolinks.js +0 -5
- data/test/dummy_app/app/views/hotwire_forms/form.html.erb +0 -45
- data/test/dummy_app/app/views/hotwire_forms/show.html.erb +0 -5
- data/test/dummy_app/app/views/layouts/application.html.erb +0 -39
- data/test/dummy_app/app/views/turbolinks_forms/form.html.erb +0 -44
- data/test/dummy_app/app/views/turbolinks_forms/show.html.erb +0 -5
- data/test/dummy_app/bin/rails +0 -5
- data/test/dummy_app/bin/webpack +0 -18
- data/test/dummy_app/bin/yarn +0 -18
- data/test/dummy_app/config/application.rb +0 -16
- data/test/dummy_app/config/boot.rb +0 -4
- data/test/dummy_app/config/environment.rb +0 -2
- data/test/dummy_app/config/environments/development.rb +0 -10
- data/test/dummy_app/config/environments/test.rb +0 -18
- data/test/dummy_app/config/routes.rb +0 -4
- data/test/dummy_app/config/webpack/development.js +0 -5
- data/test/dummy_app/config/webpack/production.js +0 -1
- data/test/dummy_app/config/webpack/test.js +0 -5
- data/test/dummy_app/config/webpacker.yml +0 -33
- data/test/dummy_app/config.ru +0 -6
- data/test/dummy_app/package.json +0 -24
- data/test/dummy_app/test/system/hotwire_form_test.rb +0 -63
- data/test/dummy_app/test/system/turbolinks_form_test.rb +0 -38
- data/test/dummy_app/test/test_helper.rb +0 -68
- data/tsconfig.json +0 -19
- data/yarn.lock +0 -249
@@ -1,146 +0,0 @@
|
|
1
|
-
// This code was heavily inspired by the rails-ujs project.
|
2
|
-
// Copyright (c) 2007-2021 Rails Core team.
|
3
|
-
import { cspNonce } from "./csp"
|
4
|
-
import { CSRFProtection } from "./csrf"
|
5
|
-
|
6
|
-
const AcceptHeaders = {
|
7
|
-
"*": "*/*",
|
8
|
-
text: "text/plain",
|
9
|
-
html: "text/html",
|
10
|
-
xml: "application/xml, text/xml",
|
11
|
-
json: "application/json, text/javascript",
|
12
|
-
script: "text/javascript, application/javascript, application/ecmascript, application/x-ecmascript",
|
13
|
-
}
|
14
|
-
|
15
|
-
export const ajax = (options) => {
|
16
|
-
options = prepareOptions(options)
|
17
|
-
var xhr = createXHR(options, function () {
|
18
|
-
const response = processResponse(
|
19
|
-
xhr.response != null ? xhr.response : xhr.responseText,
|
20
|
-
xhr.getResponseHeader("Content-Type")
|
21
|
-
)
|
22
|
-
|
23
|
-
if (Math.floor(xhr.status / 100) === 2) {
|
24
|
-
if (typeof options.success === "function") {
|
25
|
-
options.success(response, xhr.statusText, xhr)
|
26
|
-
}
|
27
|
-
} else {
|
28
|
-
if (typeof options.error === "function") {
|
29
|
-
options.error(response, xhr.statusText, xhr)
|
30
|
-
}
|
31
|
-
}
|
32
|
-
return typeof options.complete === "function" ? options.complete(xhr, xhr.statusText) : undefined
|
33
|
-
})
|
34
|
-
|
35
|
-
if (options.beforeSend != null && !options.beforeSend(xhr, options)) {
|
36
|
-
return false
|
37
|
-
}
|
38
|
-
|
39
|
-
if (xhr.readyState === XMLHttpRequest.OPENED) {
|
40
|
-
return xhr.send(options.data)
|
41
|
-
}
|
42
|
-
}
|
43
|
-
|
44
|
-
const prepareOptions = (options) => {
|
45
|
-
options.url = options.url || location.href
|
46
|
-
options.type = options.type.toUpperCase()
|
47
|
-
// append data to url if it's a GET request
|
48
|
-
if (options.type === "GET" && options.data) {
|
49
|
-
if (options.url.indexOf("?") < 0) {
|
50
|
-
options.url += "?" + options.data
|
51
|
-
} else {
|
52
|
-
options.url += "&" + options.data
|
53
|
-
}
|
54
|
-
}
|
55
|
-
|
56
|
-
// Use "*" as default dataType
|
57
|
-
if (AcceptHeaders[options.dataType] == null) {
|
58
|
-
options.dataType = "*"
|
59
|
-
}
|
60
|
-
|
61
|
-
options.accept = AcceptHeaders[options.dataType]
|
62
|
-
if (options.dataType !== "*") {
|
63
|
-
options.accept += ", */*; q=0.01"
|
64
|
-
}
|
65
|
-
|
66
|
-
return options
|
67
|
-
}
|
68
|
-
|
69
|
-
const createXHR = (options, done) => {
|
70
|
-
const xhr = new XMLHttpRequest()
|
71
|
-
|
72
|
-
// Open and set up xhr
|
73
|
-
xhr.open(options.type, options.url, true)
|
74
|
-
xhr.setRequestHeader("Accept", options.accept)
|
75
|
-
|
76
|
-
// Set Content-Type only when sending a string
|
77
|
-
// Sending FormData will automatically set Content-Type to multipart/form-data
|
78
|
-
if (typeof options.data === "string") {
|
79
|
-
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8")
|
80
|
-
}
|
81
|
-
|
82
|
-
if (!options.crossDomain) {
|
83
|
-
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest")
|
84
|
-
CSRFProtection(xhr)
|
85
|
-
}
|
86
|
-
|
87
|
-
xhr.withCredentials = !!options.withCredentials
|
88
|
-
xhr.onreadystatechange = function () {
|
89
|
-
if (xhr.readyState === XMLHttpRequest.DONE) {
|
90
|
-
return done(xhr)
|
91
|
-
}
|
92
|
-
}
|
93
|
-
return xhr
|
94
|
-
}
|
95
|
-
|
96
|
-
const processResponse = (response, type) => {
|
97
|
-
if (typeof response === "string" && typeof type === "string") {
|
98
|
-
if (type.match(/\bjson\b/)) {
|
99
|
-
try {
|
100
|
-
response = JSON.parse(response)
|
101
|
-
} catch (error) {
|
102
|
-
// no-op...
|
103
|
-
}
|
104
|
-
} else if (type.match(/\b(?:java|ecma)script\b/)) {
|
105
|
-
const script = document.createElement("script")
|
106
|
-
script.setAttribute("nonce", cspNonce())
|
107
|
-
script.text = response
|
108
|
-
document.head.appendChild(script).parentNode.removeChild(script)
|
109
|
-
} else if (type.match(/\b(xml|html|svg)\b/)) {
|
110
|
-
const parser = new DOMParser()
|
111
|
-
type = type.replace(/;.+/, "") // remove something like ';charset=utf-8'
|
112
|
-
|
113
|
-
try {
|
114
|
-
response = parser.parseFromString(response, type)
|
115
|
-
} catch (error1) {}
|
116
|
-
}
|
117
|
-
}
|
118
|
-
|
119
|
-
return response
|
120
|
-
}
|
121
|
-
|
122
|
-
// Default way to get an element's href. May be overridden at Rails.href.
|
123
|
-
export const href = (element) => element.href
|
124
|
-
|
125
|
-
// Determines if the request is a cross domain request.
|
126
|
-
export const isCrossDomain = (url) => {
|
127
|
-
const originAnchor = document.createElement("a")
|
128
|
-
originAnchor.href = location.href
|
129
|
-
const urlAnchor = document.createElement("a")
|
130
|
-
|
131
|
-
try {
|
132
|
-
urlAnchor.href = url
|
133
|
-
// If URL protocol is false or is a string containing a single colon
|
134
|
-
// *and* host are false, assume it is not a cross-domain request
|
135
|
-
// (should only be the case for IE7 and IE compatibility mode).
|
136
|
-
// Otherwise, evaluate protocol and host of the URL against the origin
|
137
|
-
// protocol and host.
|
138
|
-
return !(
|
139
|
-
((!urlAnchor.protocol || urlAnchor.protocol === ":") && !urlAnchor.host) ||
|
140
|
-
originAnchor.protocol + "//" + originAnchor.host === urlAnchor.protocol + "//" + urlAnchor.host
|
141
|
-
)
|
142
|
-
} catch (e) {
|
143
|
-
// If there is an error parsing the URL, assume it is crossDomain.
|
144
|
-
return true
|
145
|
-
}
|
146
|
-
}
|
data/src/turbolinks/utils/csp.ts
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
// This code was heavily inspired by the rails-ujs project.
|
2
|
-
// Copyright (c) 2007-2021 Rails Core team.
|
3
|
-
let nonce = null
|
4
|
-
|
5
|
-
const loadCSPNonce = () => {
|
6
|
-
if (nonce) {
|
7
|
-
return nonce
|
8
|
-
}
|
9
|
-
|
10
|
-
const cspMetaTag: HTMLMetaElement = document.querySelector("meta[name=csp-nonce]")
|
11
|
-
|
12
|
-
if (cspMetaTag) {
|
13
|
-
nonce = cspMetaTag.content
|
14
|
-
}
|
15
|
-
|
16
|
-
return nonce
|
17
|
-
}
|
18
|
-
|
19
|
-
// Returns the Content-Security-Policy nonce for inline scripts.
|
20
|
-
export const cspNonce = () => (nonce != null ? nonce : loadCSPNonce())
|
@@ -1,33 +0,0 @@
|
|
1
|
-
// This code was heavily inspired by the rails-ujs project.
|
2
|
-
// Copyright (c) 2007-2021 Rails Core team.
|
3
|
-
const $ = (selector) => Array.prototype.slice.call(document.querySelectorAll(selector))
|
4
|
-
|
5
|
-
// Up-to-date Cross-Site Request Forgery token
|
6
|
-
export const csrfToken = () => {
|
7
|
-
const meta: HTMLMetaElement = document.querySelector("meta[name=csrf-token]")
|
8
|
-
return meta && meta.content
|
9
|
-
}
|
10
|
-
|
11
|
-
// URL param that must contain the CSRF token
|
12
|
-
export const csrfParam = () => {
|
13
|
-
const meta: HTMLMetaElement = document.querySelector("meta[name=csrf-param]")
|
14
|
-
return meta && meta.content
|
15
|
-
}
|
16
|
-
|
17
|
-
// Make sure that every Ajax request sends the CSRF token
|
18
|
-
export const CSRFProtection = (xhr) => {
|
19
|
-
const token = csrfToken()
|
20
|
-
if (token != null) {
|
21
|
-
return xhr.setRequestHeader("X-CSRF-Token", token)
|
22
|
-
}
|
23
|
-
}
|
24
|
-
|
25
|
-
// Make sure that all forms have actual up-to-date tokens (cached forms contain old ones)
|
26
|
-
export const refreshCSRFTokens = () => {
|
27
|
-
const token = csrfToken()
|
28
|
-
const param = csrfParam()
|
29
|
-
|
30
|
-
if (token != null && param != null) {
|
31
|
-
return $('sl-form input[name="' + param + '"]').forEach((input) => (input.value = token))
|
32
|
-
}
|
33
|
-
}
|
data/src/turbolinks/utils/dom.ts
DELETED
@@ -1,40 +0,0 @@
|
|
1
|
-
// This code was heavily inspired by the rails-ujs project.
|
2
|
-
// Copyright (c) 2007-2021 Rails Core team.
|
3
|
-
const elementPrototype = Element.prototype as any
|
4
|
-
|
5
|
-
const m: (this: Element, selector: string) => boolean =
|
6
|
-
elementPrototype.matches ||
|
7
|
-
elementPrototype.matchesSelector ||
|
8
|
-
elementPrototype.mozMatchesSelector ||
|
9
|
-
elementPrototype.msMatchesSelector ||
|
10
|
-
elementPrototype.oMatchesSelector ||
|
11
|
-
elementPrototype.webkitMatchesSelector
|
12
|
-
|
13
|
-
// Checks if the given native dom element matches the selector
|
14
|
-
// element::
|
15
|
-
// native DOM element
|
16
|
-
// selector::
|
17
|
-
// CSS selector string or
|
18
|
-
// a JavaScript object with `selector` and `exclude` properties
|
19
|
-
// Examples: "form", { selector: "form", exclude: "form[data-remote='true']"}
|
20
|
-
export const matches = (element, selector) => {
|
21
|
-
if (selector.exclude != null) {
|
22
|
-
return m.call(element, selector.selector) && !m.call(element, selector.exclude)
|
23
|
-
} else {
|
24
|
-
return m.call(element, selector)
|
25
|
-
}
|
26
|
-
}
|
27
|
-
|
28
|
-
// get and set data on a given element using "expando properties"
|
29
|
-
// See: https://developer.mozilla.org/en-US/docs/Glossary/Expando
|
30
|
-
const expando = "_ujsData"
|
31
|
-
|
32
|
-
export const getData = (element, key) => (element[expando] != null ? element[expando][key] : undefined)
|
33
|
-
|
34
|
-
export const setData = (element, key, value) => {
|
35
|
-
if (element[expando] == null) {
|
36
|
-
element[expando] = {}
|
37
|
-
}
|
38
|
-
|
39
|
-
return (element[expando][key] = value)
|
40
|
-
}
|
@@ -1,57 +0,0 @@
|
|
1
|
-
// This code was heavily inspired by the rails-ujs project.
|
2
|
-
// Copyright (c) 2007-2021 Rails Core team.
|
3
|
-
import { matches } from "./dom"
|
4
|
-
|
5
|
-
// Triggers a custom event on an element and returns false if the event result is false
|
6
|
-
// obj::
|
7
|
-
// a native DOM element
|
8
|
-
// name::
|
9
|
-
// string that corresponds to the event you want to trigger
|
10
|
-
// e.g. 'click', 'submit'
|
11
|
-
// data::
|
12
|
-
// data you want to pass when you dispatch an event
|
13
|
-
export const fire = (obj, name, data = {}) => {
|
14
|
-
const event = new CustomEvent(name, {
|
15
|
-
bubbles: true,
|
16
|
-
cancelable: true,
|
17
|
-
detail: data,
|
18
|
-
})
|
19
|
-
|
20
|
-
obj.dispatchEvent(event)
|
21
|
-
return !event.defaultPrevented
|
22
|
-
}
|
23
|
-
|
24
|
-
// Helper function, needed to provide consistent behavior in IE
|
25
|
-
export const stopEverything = (event) => {
|
26
|
-
fire(event.target, "ujs:everythingStopped")
|
27
|
-
|
28
|
-
event.preventDefault()
|
29
|
-
event.stopPropagation()
|
30
|
-
|
31
|
-
return event.stopImmediatePropagation()
|
32
|
-
}
|
33
|
-
|
34
|
-
// Delegates events
|
35
|
-
// to a specified parent `element`, which fires event `handler`
|
36
|
-
// for the specified `selector` when an event of `eventType` is triggered
|
37
|
-
// element::
|
38
|
-
// parent element that will listen for events e.g. document
|
39
|
-
// selector::
|
40
|
-
// CSS selector; or an object that has `selector` and `exclude` properties (see: Rails.matches)
|
41
|
-
// eventType::
|
42
|
-
// string representing the event e.g. 'submit', 'click'
|
43
|
-
// handler::
|
44
|
-
// the event handler to be called
|
45
|
-
export const delegate = (element, selector, eventType, handler) =>
|
46
|
-
element.addEventListener(eventType, function (event) {
|
47
|
-
let { target } = event
|
48
|
-
|
49
|
-
while (!!(target instanceof Element) && !matches(target, selector)) {
|
50
|
-
target = target.parentNode
|
51
|
-
}
|
52
|
-
|
53
|
-
if (target instanceof Element && handler.call(target, event) === false) {
|
54
|
-
event.preventDefault()
|
55
|
-
return event.stopPropagation()
|
56
|
-
}
|
57
|
-
})
|
@@ -1,58 +0,0 @@
|
|
1
|
-
// This code was heavily inspired by the rails-ujs project.
|
2
|
-
// Copyright (c) 2007-2021 Rails Core team.
|
3
|
-
import { matches } from "./dom"
|
4
|
-
|
5
|
-
const toArray = (e) => Array.prototype.slice.call(e)
|
6
|
-
|
7
|
-
export const serializeElement = (element, additionalParam) => {
|
8
|
-
let inputs = [element]
|
9
|
-
if (matches(element, "form")) {
|
10
|
-
inputs = toArray(element.elements)
|
11
|
-
}
|
12
|
-
const params = []
|
13
|
-
|
14
|
-
inputs.forEach(function (input) {
|
15
|
-
if (!input.name || input.disabled) {
|
16
|
-
return
|
17
|
-
}
|
18
|
-
|
19
|
-
if (matches(input, "fieldset[disabled] *")) {
|
20
|
-
return
|
21
|
-
}
|
22
|
-
|
23
|
-
if (matches(input, "select")) {
|
24
|
-
return toArray(input.options).forEach(function (option) {
|
25
|
-
if (option.selected) {
|
26
|
-
return params.push({ name: input.name, value: option.value })
|
27
|
-
}
|
28
|
-
})
|
29
|
-
} else if (input.checked || ["radio", "checkbox", "submit"].indexOf(input.type) === -1) {
|
30
|
-
return params.push({ name: input.name, value: input.value })
|
31
|
-
}
|
32
|
-
})
|
33
|
-
|
34
|
-
if (additionalParam) {
|
35
|
-
params.push(additionalParam)
|
36
|
-
}
|
37
|
-
|
38
|
-
return params
|
39
|
-
.map(function (param) {
|
40
|
-
if (param.name != null) {
|
41
|
-
return `${encodeURIComponent(param.name)}=${encodeURIComponent(param.value)}`
|
42
|
-
} else {
|
43
|
-
return param
|
44
|
-
}
|
45
|
-
})
|
46
|
-
.join("&")
|
47
|
-
}
|
48
|
-
|
49
|
-
// Helper function that returns form elements that match the specified CSS selector
|
50
|
-
// If form is actually a "form" element this will return associated elements outside the from that have
|
51
|
-
// the html form attribute set
|
52
|
-
export const formElements = (form, selector) => {
|
53
|
-
if (matches(form, "form")) {
|
54
|
-
return toArray(form.elements).filter((el) => matches(el, selector))
|
55
|
-
} else {
|
56
|
-
return toArray(form.querySelectorAll(selector))
|
57
|
-
}
|
58
|
-
}
|
data/test/dummy_app/Gemfile
DELETED
@@ -1,19 +0,0 @@
|
|
1
|
-
source 'https://rubygems.org'
|
2
|
-
|
3
|
-
gem 'rails', '~> 7.0.0'
|
4
|
-
gem 'webrick'
|
5
|
-
gem 'bootsnap', '>= 1.4.4', require: false
|
6
|
-
gem 'rexml'
|
7
|
-
gem 'shoelace-rails', path: '../../../shoelace-rails'
|
8
|
-
gem 'webpacker', '6.0.0.rc.5'
|
9
|
-
|
10
|
-
group :development, :test do
|
11
|
-
gem 'pry-byebug'
|
12
|
-
end
|
13
|
-
|
14
|
-
group :test do
|
15
|
-
gem 'capybara', '>= 3.26'
|
16
|
-
gem 'capybara-shadowdom'
|
17
|
-
gem 'selenium-webdriver', '>= 4.0.0.rc3'
|
18
|
-
gem 'webdrivers'
|
19
|
-
end
|
data/test/dummy_app/Rakefile
DELETED
@@ -1,46 +0,0 @@
|
|
1
|
-
class HotwireFormsController < ActionController::Base
|
2
|
-
layout "application"
|
3
|
-
|
4
|
-
def show
|
5
|
-
@user = User.new(session[:user])
|
6
|
-
end
|
7
|
-
|
8
|
-
# GET /users/new
|
9
|
-
def new
|
10
|
-
@user = User.new(params[:user]&.permit!)
|
11
|
-
|
12
|
-
render 'form'
|
13
|
-
end
|
14
|
-
|
15
|
-
# GET /users/1/edit
|
16
|
-
def edit
|
17
|
-
@user = User.new(name: "Yuki Nishijima")
|
18
|
-
|
19
|
-
render 'form'
|
20
|
-
end
|
21
|
-
|
22
|
-
# POST /users
|
23
|
-
def create
|
24
|
-
@user = User.new(user_params)
|
25
|
-
|
26
|
-
if @user.valid?
|
27
|
-
session[:user] = user_params.to_h
|
28
|
-
redirect_to hotwire_form_path(1)
|
29
|
-
else
|
30
|
-
render 'form', status: 422
|
31
|
-
end
|
32
|
-
end
|
33
|
-
|
34
|
-
# PATCH/PUT /users/1
|
35
|
-
def update
|
36
|
-
@user = User.new(user_params)
|
37
|
-
|
38
|
-
render(@user.valid? ? 'show' : 'form')
|
39
|
-
end
|
40
|
-
|
41
|
-
private
|
42
|
-
|
43
|
-
def user_params
|
44
|
-
params.require(:user).permit!
|
45
|
-
end
|
46
|
-
end
|
@@ -1,37 +0,0 @@
|
|
1
|
-
class TurbolinksFormsController < ActionController::Base
|
2
|
-
layout "application"
|
3
|
-
|
4
|
-
# GET /users/new
|
5
|
-
def new
|
6
|
-
@user = User.new
|
7
|
-
|
8
|
-
render 'form'
|
9
|
-
end
|
10
|
-
|
11
|
-
# GET /users/1/edit
|
12
|
-
def edit
|
13
|
-
@user = User.new(name: "Yuki Nishijima")
|
14
|
-
|
15
|
-
render 'form'
|
16
|
-
end
|
17
|
-
|
18
|
-
# POST /users
|
19
|
-
def create
|
20
|
-
@user = User.new(user_params)
|
21
|
-
|
22
|
-
render(@user.valid? ? 'show' : 'form')
|
23
|
-
end
|
24
|
-
|
25
|
-
# PATCH/PUT /users/1
|
26
|
-
def update
|
27
|
-
@user = User.new(user_params)
|
28
|
-
|
29
|
-
render(@user.valid? ? 'show' : 'form')
|
30
|
-
end
|
31
|
-
|
32
|
-
private
|
33
|
-
|
34
|
-
def user_params
|
35
|
-
params.require(:user).permit!
|
36
|
-
end
|
37
|
-
end
|
@@ -1,16 +0,0 @@
|
|
1
|
-
class User
|
2
|
-
include ActiveModel::Model
|
3
|
-
include ActiveModel::Attributes
|
4
|
-
|
5
|
-
attribute :name
|
6
|
-
attribute :description
|
7
|
-
attribute :color
|
8
|
-
attribute :score
|
9
|
-
attribute :current_city
|
10
|
-
attribute :previous_city
|
11
|
-
attribute :past_cities
|
12
|
-
attribute :remember_me
|
13
|
-
attribute :subscribe_to_emails
|
14
|
-
|
15
|
-
validates :name, presence: true
|
16
|
-
end
|
@@ -1 +0,0 @@
|
|
1
|
-
import "@hotwired/turbo-rails"
|
@@ -1,45 +0,0 @@
|
|
1
|
-
<% locations = { tokyo: "Tokyo", new_york: "New York", london: "London" } %>
|
2
|
-
<%= sl_form_for(@user, url: hotwire_forms_path) do |form| %>
|
3
|
-
<div>
|
4
|
-
<%= form.text_field :name do %>
|
5
|
-
<span slot="help-text" style="color: rgb(var(--sl-color-danger-600));">
|
6
|
-
<%= @user.errors.full_messages_for(:name).first %>
|
7
|
-
</span>
|
8
|
-
<% end %>
|
9
|
-
</div>
|
10
|
-
|
11
|
-
<div>
|
12
|
-
<%= form.color_field :color %>
|
13
|
-
</div>
|
14
|
-
|
15
|
-
<div>
|
16
|
-
<%= form.range_field :score, min: 0, max: 100, step: 1 %>
|
17
|
-
</div>
|
18
|
-
|
19
|
-
<div>
|
20
|
-
<%= form.collection_radio_buttons :current_city, locations, :first, :last %>
|
21
|
-
</div>
|
22
|
-
|
23
|
-
<div>
|
24
|
-
<%= form.collection_select :previous_city, locations, :first, :last, {}, { placeholder: "Select one" } %>
|
25
|
-
</div>
|
26
|
-
|
27
|
-
<div>
|
28
|
-
<%= form.collection_select :past_cities, locations, :first, :last, {}, { placeholder: "Select two or more", multiple: true, clearable: true } %>
|
29
|
-
</div>
|
30
|
-
|
31
|
-
<div>
|
32
|
-
<%= form.check_box :remember_me %>
|
33
|
-
</div>
|
34
|
-
|
35
|
-
<div>
|
36
|
-
<%= form.switch_field :subscribe_to_emails, value: "1" %>
|
37
|
-
</div>
|
38
|
-
|
39
|
-
<div>
|
40
|
-
<%= form.text_area :description %>
|
41
|
-
</div>
|
42
|
-
|
43
|
-
<%= form.submit %>
|
44
|
-
<%= form.submit "Submit without Turbo", data: { turbo: false } %>
|
45
|
-
<% end %>
|
@@ -1,39 +0,0 @@
|
|
1
|
-
<!DOCTYPE html>
|
2
|
-
<html>
|
3
|
-
<head>
|
4
|
-
<title>Shoelace Test</title>
|
5
|
-
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
-
<%= csrf_meta_tags %>
|
7
|
-
<%= csp_meta_tag %>
|
8
|
-
|
9
|
-
<style>
|
10
|
-
body {
|
11
|
-
font-family: var(--sl-font-sans);
|
12
|
-
font-size: var(--sl-font-size-medium);
|
13
|
-
font-weight: var(--sl-font-weight-normal);
|
14
|
-
letter-spacing: var(--sl-letter-spacing-normal);
|
15
|
-
color: var(--sl-color-gray-800);
|
16
|
-
line-height: var(--sl-line-height-normal);
|
17
|
-
}
|
18
|
-
|
19
|
-
sl-form div, sl-turbo-form div {
|
20
|
-
margin-bottom: 2em;
|
21
|
-
}
|
22
|
-
</style>
|
23
|
-
|
24
|
-
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.88/dist/themes/light.css">
|
25
|
-
<script type="module" src="https://cdn.jsdelivr.net/npm/@shoelace-style/shoelace@2.0.0-beta.88/dist/shoelace.js"></script>
|
26
|
-
|
27
|
-
<% if request.path.include?("hotwire") %>
|
28
|
-
<%= javascript_pack_tag 'hotwire' %>
|
29
|
-
<% else %>
|
30
|
-
<%= javascript_pack_tag 'turbolinks' %>
|
31
|
-
<% end %>
|
32
|
-
</head>
|
33
|
-
|
34
|
-
<body>
|
35
|
-
<div style="width: 950px; margin: auto">
|
36
|
-
<%= yield %>
|
37
|
-
</div>
|
38
|
-
</body>
|
39
|
-
</html>
|
@@ -1,44 +0,0 @@
|
|
1
|
-
<% locations = { tokyo: "Tokyo", new_york: "New York", london: "London" } %>
|
2
|
-
<%= sl_form_for(@user, url: turbolinks_forms_path) do |form| %>
|
3
|
-
<div>
|
4
|
-
<%= form.text_field :name do %>
|
5
|
-
<span slot="help-text" style="color: rgb(var(--sl-color-danger-600));">
|
6
|
-
<%= @user.errors.full_messages_for(:name).first %>
|
7
|
-
</span>
|
8
|
-
<% end %>
|
9
|
-
</div>
|
10
|
-
|
11
|
-
<div>
|
12
|
-
<%= form.color_field :color %>
|
13
|
-
</div>
|
14
|
-
|
15
|
-
<div>
|
16
|
-
<%= form.range_field :score, min: 0, max: 100, step: 1 %>
|
17
|
-
</div>
|
18
|
-
|
19
|
-
<div>
|
20
|
-
<%= form.collection_radio_buttons :current_city, locations, :first, :last %>
|
21
|
-
</div>
|
22
|
-
|
23
|
-
<div>
|
24
|
-
<%= form.collection_select :previous_city, locations, :first, :last, {}, { placeholder: "Select one" } %>
|
25
|
-
</div>
|
26
|
-
|
27
|
-
<div>
|
28
|
-
<%= form.collection_select :past_cities, locations, :first, :last, {}, { placeholder: "Select two or more", multiple: true, clearable: true } %>
|
29
|
-
</div>
|
30
|
-
|
31
|
-
<div>
|
32
|
-
<%= form.check_box :remember_me %>
|
33
|
-
</div>
|
34
|
-
|
35
|
-
<div>
|
36
|
-
<%= form.switch_field :subscribe_to_emails, value: "1" %>
|
37
|
-
</div>
|
38
|
-
|
39
|
-
<div>
|
40
|
-
<%= form.text_area :description %>
|
41
|
-
</div>
|
42
|
-
|
43
|
-
<%= form.submit %>
|
44
|
-
<% end %>
|
data/test/dummy_app/bin/rails
DELETED
data/test/dummy_app/bin/webpack
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
ENV["RAILS_ENV"] ||= ENV["RACK_ENV"] || "development"
|
4
|
-
ENV["NODE_ENV"] ||= "development"
|
5
|
-
|
6
|
-
require "pathname"
|
7
|
-
ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
|
8
|
-
Pathname.new(__FILE__).realpath)
|
9
|
-
|
10
|
-
require "bundler/setup"
|
11
|
-
|
12
|
-
require "webpacker"
|
13
|
-
require "webpacker/webpack_runner"
|
14
|
-
|
15
|
-
APP_ROOT = File.expand_path("..", __dir__)
|
16
|
-
Dir.chdir(APP_ROOT) do
|
17
|
-
Webpacker::WebpackRunner.run(ARGV)
|
18
|
-
end
|