rails new user_delete_app -d postgresql
bin/rails g scaffold user name:string email:string
bin/rails db:create
bin/rails db:migrate
Slim related addition to Gemifile
gem 'slim-rails'
gem 'html2slim'
Hit the following in the terminal
bundle exec erb2slim app/views app/views -d
[Add Tailwind](https://qiita.com/sakusakupg/items/a5f61818b76c7009f8ff#tailwind%E3%82%92%E5%85%A5%E3%82%8C%E3%81%A6%E3%81% BF% E3% 82% 8B)
seed data preparation
db/seeds.rb
User.create(
[
{name: 'Jane Cooper', email: '[email protected]'},
{name: 'Cody Fisher', email: '[email protected]'},
{name: 'Esther Howard', email: '[email protected]'},
{name: 'Jenny Wilson', email: '[email protected]'},
{name: 'Kristin Watson', email: '[email protected]'},
{name: 'Cameron Williamson', email: '[email protected]'}
]
)
Hit the following in the terminal
bin/rails db:seed
slim:app/views/users/index.html.slim
p#notice
= notice
.ml-5
.flex.flex-col.py-5
.-my-2.overflow-x-auto(class="sm:-mx-6 lg:-mx-8")
.py-2.align-middle.inline-block.min-w-full(class="sm:px-6 lg:px-8")
.shadow.overflow-hidden.border-b.border-gray-200(class="sm:rounded-lg")
table.min-w-full.divide-y.divide-gray-200
thead
tr
th.px-6.py-3.bg-gray-200.text-left.text-xs.leading-4.font-medium.text-gray-500.uppercase.tracking-wider
| Name
th.px-6.py-3.bg-gray-200.text-left.text-xs.leading-4.font-medium.text-gray-500.uppercase.tracking-wider
| Email
th.px-6.py-3.bg-gray-200.text-left.text-xs.leading-4.font-medium.text-gray-500.uppercase.tracking-wider
| Show
th.px-6.py-3.bg-gray-200.text-left.text-xs.leading-4.font-medium.text-gray-500.uppercase.tracking-wider
| Edit
/ th.px-6.py-3.bg-gray-200.text-left.text-xs.leading-4.font-medium.text-gray-500.uppercase.tracking-wider
/ | Destroy
tbody.bg-white.divide-y.divide-gray-200
- @users.each do |user|
tr
td.px-6.py-4.whitespace-no-wrap.text-sm.leading-5.text-gray-500
= user.name
td.px-6.py-4.whitespace-no-wrap.text-sm.leading-5.text-gray-500
= user.email
td.px-6.py-4.whitespace-no-wrap.text-sm.leading-5.text-gray-500
= link_to 'Show', user
td.px-6.py-4.whitespace-no-wrap.text-sm.leading-5.text-gray-500
= link_to 'Edit', edit_user_path(user), class: 'disabled'
/ td.px-6.py-4.whitespace-no-wrap.text-sm.leading-5.text-gray-500
/ = link_to 'Destroy', user, method: :delete, data: {confirm: 'Are you sure?'}
.mx-8
= link_to 'New User', new_user_path, class: 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded disabled'
slim:app/views/users/show.html.slim
ruby:
delete_modal_id = "id-#{SecureRandom.uuid}"
delete_btn_id = "id-#{SecureRandom.uuid}"
p#notice
= notice
.min-h-screen.bg-gray-100.m-10
.bg-white.shadow.overflow-hidden.sm:rounded-lg
.px-4.py-5.border-b.border-gray-200.sm:px-6
h3.text-lg.leading-6.font-medium.text-gray-900
|User details
p.mt-1.max-w-2xl.text-sm.leading-5.text-gray-500
|User details screen
.px-4.py-5.sm:p-0
dl
.sm:grid.sm:grid-cols-3.sm:gap-4.sm:px-6.sm:py-5
dt.text-sm.leading-5.font-medium.text-gray-500
| Name:
dd.mt-1.text-sm.leading-5.text-gray-900.sm:mt-0.sm:col-span-2
= @user.name
.mt-8.sm:mt-0.sm:grid.sm:grid-cols-3.sm:gap-4.sm:border-t.sm:border-gray-200.sm:px-6.sm:py-5
dt.text-sm.leading-5.font-medium.text-gray-500
| Email:
dd.mt-1.text-sm.leading-5.text-gray-900.sm:mt-0.sm:col-span-2
= @user.email
.btn-wrapper
button.bg-red-500.hover:bg-red-700.text-white.font-bold.py-2.px-4.m-3.rounded(
type="button"
data-modal-open="##{delete_modal_id}"
)
|Account deletion
.pt-4
= link_to 'Edit', edit_user_path(@user), class: 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded disabled'
| |
= link_to 'Back', users_path, class: 'bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded'
.fixed.z-10.inset-0.overflow-y-auto.hidden(id="#{delete_modal_id}" data-modal)
.flex.items-end.justify-center.min-h-screen.pt-4.px-4.pb-20.text-center.sm:block.sm:p-0
.fixed.inset-0.transition-opacity
.absolute.inset-0.bg-gray-700.opacity-75
span.hidden.sm:inline-block.sm:align-middle.sm:h-screen
.inline-block.align-bottom.bg-white.rounded-lg.px-4.pt-5.pb-4.text-left.overflow-hidden.shadow-xl.transform.transition-all.sm:my-8.sm:align-middle.sm:max-w-lg.sm:w-full.sm:p-6 aria-labelledby="modal-headline" aria-modal="true" role="dialog"
.sm:flex.sm:items-start
.mx-auto.flex-shrink-0.flex.items-center.justify-center.h-12.w-12.rounded-full.bg-red-100.sm:mx-0.sm:h-10.sm:w-10
svg.h-6.w-6.text-red-600 fill="none" stroke="currentColor" viewbox=("0 0 24 24")
path d=("M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z") stroke-linecap="round" stroke-linejoin="round" stroke-width="2"
.mt-3.text-center.sm:mt-0.sm:ml-4.sm:text-left
h3#modal-headline.text-lg.leading-6.font-medium.text-gray-900
|Account deletion
.mt-2
p.text-sm.leading-5.text-gray-500
|To delete, "#{@user.name}Please press the account deletion button.
.mt-5.mx-8
input.shadow.appearance-none.border.rounded.w-full.py-2.px-3.text-gray-700.leading-tight.focus:outline-none.focus:shadow-outline(placeholder="Enter your user name" data-activation-target="##{delete_btn_id}" data-activation-value="#{@user.name}")
.mt-5.sm:mt-4.sm:flex.sm:flex-row-reverse
span.flex.w-full.rounded-md.shadow-sm.sm:ml-3.sm:w-auto
= link_to \
@user,\
method: :delete,\
class: "disabled inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 bg-red-600 text-base leading-6 font-medium text-white shadow-sm hover:bg-red-500 focus:outline-none focus:border-red-700 focus:shadow-outline-red transition ease-in-out duration-150 sm:text-sm sm:leading-5",\
tabindex: -1,\
id: delete_btn_id do
|Account deletion
span.mt-3.flex.w-full.rounded-md.shadow-sm.sm:mt-0.sm:w-auto
button.inline-flex.justify-center.w-full.rounded-md.border.border-gray-300.px-4.py-2.bg-white.text-base.leading-6.font-medium.text-gray-700.shadow-sm.hover:text-gray-500.focus:outline-none.focus:border-blue-300.focus:shadow-outline-blue.transition.ease-in-out.duration-150.sm:text-sm.sm:leading-5(type="button" data-modal-close)
|Cancel
app/javascript/src/js/modal.js
document.addEventListener('click', (e) => {
const clicked = e.target.closest('[data-modal-open]')
if (clicked) {
const targetSelector = clicked.getAttribute('data-modal-open')
const target = document.querySelector(targetSelector)
if (target) {
target.classList.remove('hidden')
}
}
})
document.addEventListener('click', (e) => {
const clicked = e.target.closest('[data-modal-close]')
if (clicked) {
const targetSelector = clicked.getAttribute('data-modal-close')
if (targetSelector) {
const target = document.querySelector(targetSelector)
if (target) {
target.classList.add('hidden')
}
} else {
const target = clicked.closest('[data-modal]')
if (target) {
target.classList.add('hidden')
}
}
}
})
app/javascript/src/scss/application.scss
@import "btn";
app/javascript/src/scss/_btn.scss
.disabled {
@apply opacity-50 cursor-not-allowed pointer-events-none;
}
app/javascript/src/js/activation.js
document.addEventListener('input', (e) => {
const input = e.target
const targetSelector = input.dataset.activationTarget
if (targetSelector) {
const activationValue = input.dataset.activationValue
const targets = document.querySelectorAll(targetSelector)
if (input.value === activationValue) {
targets.forEach((target) => {
target.classList.remove('disabled')
target.removeAttribute('tabindex')
})
} else {
targets.forEach((target) => {
target.classList.add('disabled')
target.setAttribute('tabindex', -1)
})
}
}
})
Place the cursor on input-> click Tab-> click Enter
I was able to delete the button even if it was disabled, so I avoided it with tabindex: -1
.
= link_to \
@user,\
method: :delete,\
class: "disabled inline-flex justify-center w-full rounded-md border border-transparent px-4 py-2 bg-red-600 text-base leading-6 font-medium text-white shadow-sm hover:bg-red-500 focus:outline-none focus:border-red-700 focus:shadow-outline-red transition ease-in-out duration-150 sm:text-sm sm:leading-5",\
tabindex: -1,\
id: delete_btn_id do
|Account deletion
# DELETE /users/1
# DELETE /users/1.json
def destroy
@user.destroy
respond_to do |format|
format.html { redirect_to users_url, notice: 'User was successfully destroyed.' }
format.json { head :no_content }
end
end