Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for data-turbo-confirm #93

Merged
merged 5 commits into from
Jul 21, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,6 @@ RUN gem update --system && gem install bundler

RUN mkdir -p /mnt/external/node_modules /mnt/external/bundle

COPY . /opt/turbo_boost-commands
WORKDIR /opt/turbo_boost-commands
COPY . /app
WORKDIR /app
CMD bin/docker/run/remote
4 changes: 2 additions & 2 deletions app/assets/builds/@turbo-boost/commands.js

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions app/assets/builds/@turbo-boost/commands.js.map

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions app/assets/builds/@turbo-boost/commands.metafile.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{"inputs":{"node_modules/@turbo-boost/streams/app/assets/builds/@turbo-boost/streams.js":{"bytes":47867,"imports":[],"format":"esm"},"app/javascript/meta.js":{"bytes":341,"imports":[],"format":"esm"},"app/javascript/events.js":{"bytes":784,"imports":[{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/state/observable.js":{"bytes":929,"imports":[{"path":"app/javascript/meta.js","kind":"import-statement","original":"../meta"},{"path":"app/javascript/events.js","kind":"import-statement","original":"../events"}],"format":"esm"},"app/javascript/state/index.js":{"bytes":1840,"imports":[{"path":"app/javascript/meta.js","kind":"import-statement","original":"../meta"},{"path":"app/javascript/state/observable.js","kind":"import-statement","original":"./observable"},{"path":"app/javascript/events.js","kind":"import-statement","original":"../events"},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/renderer.js":{"bytes":477,"imports":[],"format":"esm"},"app/javascript/activity.js":{"bytes":283,"imports":[],"format":"esm"},"app/javascript/lifecycle.js":{"bytes":637,"imports":[{"path":"app/javascript/activity.js","kind":"import-statement","original":"./activity"},{"path":"app/javascript/events.js","kind":"import-statement","original":"./events"}],"format":"esm"},"app/javascript/turbo.js":{"bytes":2040,"imports":[{"path":"app/javascript/meta.js","kind":"import-statement","original":"./meta"},{"path":"app/javascript/state/index.js","kind":"import-statement","original":"./state"},{"path":"app/javascript/renderer.js","kind":"import-statement","original":"./renderer"},{"path":"app/javascript/events.js","kind":"import-statement","original":"./events"},{"path":"app/javascript/lifecycle.js","kind":"import-statement","original":"./lifecycle"},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/schema.js":{"bytes":210,"imports":[{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/confirmation.js":{"bytes":919,"imports":[{"path":"app/javascript/events.js","kind":"import-statement","original":"./events"},{"path":"app/javascript/schema.js","kind":"import-statement","original":"./schema"}],"format":"esm"},"app/javascript/delegates.js":{"bytes":874,"imports":[{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/elements.js":{"bytes":1463,"imports":[{"path":"app/javascript/schema.js","kind":"import-statement","original":"./schema"},{"path":"app/javascript/lifecycle.js","kind":"import-statement","original":"./lifecycle"}],"format":"esm"},"app/javascript/drivers/form.js":{"bytes":314,"imports":[{"path":"app/javascript/meta.js","kind":"import-statement","original":"../meta"}],"format":"esm"},"app/javascript/urls.js":{"bytes":241,"imports":[],"format":"esm"},"app/javascript/drivers/frame.js":{"bytes":219,"imports":[{"path":"app/javascript/urls.js","kind":"import-statement","original":"../urls"},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/drivers/method.js":{"bytes":266,"imports":[{"path":"app/javascript/urls.js","kind":"import-statement","original":"../urls"},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/drivers/window.js":{"bytes":1930,"imports":[{"path":"app/javascript/meta.js","kind":"import-statement","original":"../meta"},{"path":"app/javascript/state/index.js","kind":"import-statement","original":"../state"},{"path":"app/javascript/events.js","kind":"import-statement","original":"../events"},{"path":"app/javascript/lifecycle.js","kind":"import-statement","original":"../lifecycle"},{"path":"app/javascript/urls.js","kind":"import-statement","original":"../urls"},{"path":"app/javascript/renderer.js","kind":"import-statement","original":"../renderer"},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"},"app/javascript/drivers/index.js":{"bytes":2064,"imports":[{"path":"app/javascript/elements.js","kind":"import-statement","original":"../elements"},{"path":"app/javascript/drivers/form.js","kind":"import-statement","original":"./form"},{"path":"app/javascript/drivers/frame.js","kind":"import-statement","original":"./frame"},{"path":"app/javascript/drivers/method.js","kind":"import-statement","original":"./method"},{"path":"app/javascript/drivers/window.js","kind":"import-statement","original":"./window"}],"format":"esm"},"app/javascript/logger.js":{"bytes":731,"imports":[{"path":"app/javascript/events.js","kind":"import-statement","original":"./events"}],"format":"esm"},"app/javascript/uuids.js":{"bytes":221,"imports":[],"format":"esm"},"app/javascript/index.js":{"bytes":3513,"imports":[{"path":"node_modules/@turbo-boost/streams/app/assets/builds/@turbo-boost/streams.js","kind":"import-statement","original":"@turbo-boost/streams"},{"path":"app/javascript/turbo.js","kind":"import-statement","original":"./turbo"},{"path":"app/javascript/schema.js","kind":"import-statement","original":"./schema"},{"path":"app/javascript/events.js","kind":"import-statement","original":"./events"},{"path":"app/javascript/activity.js","kind":"import-statement","original":"./activity"},{"path":"app/javascript/confirmation.js","kind":"import-statement","original":"./confirmation"},{"path":"app/javascript/delegates.js","kind":"import-statement","original":"./delegates"},{"path":"app/javascript/drivers/index.js","kind":"import-statement","original":"./drivers"},{"path":"app/javascript/meta.js","kind":"import-statement","original":"./meta"},{"path":"app/javascript/elements.js","kind":"import-statement","original":"./elements"},{"path":"app/javascript/lifecycle.js","kind":"import-statement","original":"./lifecycle"},{"path":"app/javascript/logger.js","kind":"import-statement","original":"./logger"},{"path":"app/javascript/state/index.js","kind":"import-statement","original":"./state"},{"path":"app/javascript/urls.js","kind":"import-statement","original":"./urls"},{"path":"app/javascript/uuids.js","kind":"import-statement","original":"./uuids"},{"path":"<runtime>","kind":"import-statement","external":true}],"format":"esm"}},"outputs":{"app/assets/builds/@turbo-boost/commands.js.map":{"imports":[],"exports":[],"inputs":{},"bytes":238243},"app/assets/builds/@turbo-boost/commands.js":{"imports":[],"exports":["default"],"entryPoint":"app/javascript/index.js","inputs":{"node_modules/@turbo-boost/streams/app/assets/builds/@turbo-boost/streams.js":{"bytesInOutput":47846},"app/javascript/meta.js":{"bytesInOutput":254},"app/javascript/events.js":{"bytesInOutput":500},"app/javascript/state/observable.js":{"bytesInOutput":415},"app/javascript/state/index.js":{"bytesInOutput":794},"app/javascript/renderer.js":{"bytesInOutput":264},"app/javascript/activity.js":{"bytesInOutput":179},"app/javascript/lifecycle.js":{"bytesInOutput":299},"app/javascript/turbo.js":{"bytesInOutput":1031},"app/javascript/schema.js":{"bytesInOutput":166},"app/javascript/confirmation.js":{"bytesInOutput":419},"app/javascript/delegates.js":{"bytesInOutput":440},"app/javascript/elements.js":{"bytesInOutput":783},"app/javascript/drivers/form.js":{"bytesInOutput":188},"app/javascript/urls.js":{"bytesInOutput":167},"app/javascript/drivers/frame.js":{"bytesInOutput":98},"app/javascript/drivers/method.js":{"bytesInOutput":132},"app/javascript/drivers/window.js":{"bytesInOutput":1188},"app/javascript/drivers/index.js":{"bytesInOutput":1002},"app/javascript/logger.js":{"bytesInOutput":408},"app/javascript/uuids.js":{"bytesInOutput":155},"app/javascript/index.js":{"bytesInOutput":1676}},"bytes":58909}}}
31 changes: 31 additions & 0 deletions app/javascript/confirmation.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { commandEvents } from './events'
import schema from './schema'

const confirmation = {
method: message => Promise.resolve(confirm(message))
}

const isTurboMethod = event => event.detail.driver === 'method'

const isTurboForm = event => {
if (event.detail.driver !== 'form') return false

const element = event.target
const frame = element.closest('turbo-frame')
const target = element.closest(`[${schema.frameAttribute}]`)
return !!(frame || target)
}

const shouldDelegate = event => isTurboMethod(event) || isTurboForm(event)

document.addEventListener(commandEvents.start, async event => {
const message = event.target.getAttribute(schema.confirmAttribute)
if (!message) return

event.detail.confirmation = true

if (shouldDelegate(event)) return // delegate confirmation handling to Turbo
if (await confirmation.method(message)) event.preventDefault()
})

export default confirmation
17 changes: 0 additions & 17 deletions app/javascript/drivers/turbo_form.js

This file was deleted.

14 changes: 8 additions & 6 deletions app/javascript/events.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,12 @@ export const stateEvents = {
export const allEvents = { ...commandEvents, ...stateEvents }

export function dispatch (name, target, options = {}) {
options = options || {}
options.detail = options.detail || {}
target = target || document
const evt = new CustomEvent(name, { ...options, bubbles: true })
target.dispatchEvent(evt)
return evt
return new Promise(resolve => {
options = options || {}
options.detail = options.detail || {}
target = target || document
const evt = new CustomEvent(name, { ...options, bubbles: true })
target.dispatchEvent(evt)
resolve(evt)
})
}
12 changes: 7 additions & 5 deletions app/javascript/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import './turbo'
import schema from './schema'
import { dispatch, commandEvents, stateEvents } from './events'
import activity from './activity'
import confirmation from './confirmation'
import delegates from './delegates'
import drivers from './drivers'
import meta from './meta'
Expand All @@ -23,7 +24,7 @@ function buildCommandPayload (id, element) {
}
}

function invokeCommand (event) {
async function invokeCommand (event) {
let element
let payload = {}

Expand All @@ -38,15 +39,15 @@ function invokeCommand (event) {
...buildCommandPayload(commandId, element),
driver: driver.name,
frameId: driver.frame ? driver.frame.id : null,
src: driver.src
src: driver.src,
}

const startEvent = dispatch(commandEvents.start, element, {
const startEvent = await dispatch(commandEvents.start, element, {
cancelable: true,
detail: payload
})

if (startEvent.defaultPrevented)
if (startEvent.defaultPrevented || startEvent.detail.confirmation && event.defaultPrevented)
return dispatch(commandEvents.abort, element, {
detail: {
message: `An event handler for '${commandEvents.start}' prevented default behavior and blocked command invocation!`,
Expand All @@ -60,7 +61,7 @@ function invokeCommand (event) {
...buildCommandPayload(commandId, element),
driver: driver.name,
frameId: driver.frame ? driver.frame.id : null,
src: driver.src
src: driver.src,
}

activity.add(payload)
Expand Down Expand Up @@ -114,6 +115,7 @@ self.TurboBoost = {
}

self.TurboBoost.Commands = {
confirmation,
logger,
schema,
events: commandEvents,
Expand Down
4 changes: 2 additions & 2 deletions app/javascript/logger.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ const logLevels = {
Object.values(events).forEach(name => {
addEventListener(name, event => {
if (logLevels[currentLevel].includes(event.type)) {
const level = currentLevel === 'debug' ? 'log' : currentLevel
console[level](event.type, { target: event.target, detail: event.detail })
const { target, detail } = event
console[currentLevel](event.type, { target, detail })
}
})
})
Expand Down
3 changes: 2 additions & 1 deletion app/javascript/schema.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
const schema = {
frameAttribute: 'data-turbo-frame',
methodAttribute: 'data-turbo-method',
commandAttribute: 'data-turbo-command'
commandAttribute: 'data-turbo-command',
confirmAttribute: 'data-turbo-confirm'
}

export default { ...schema }
40 changes: 40 additions & 0 deletions bin/build.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import * as esbuild from 'esbuild'
import fs from 'fs'

const context = await esbuild.context({
entryPoints: ['app/javascript/index.js'],
external: ['@hotwired/turbo'],
bundle: true,
format: 'esm',
logLevel: 'debug',
metafile: true,
minify: true,
outfile: 'app/assets/builds/@turbo-boost/commands.js',
sourcemap: true,
target: ['chrome79', 'edge44', 'es2020', 'firefox71', 'opera65', 'safari13']
})

const watch = process.argv.includes('--watch')

if (watch) {
context.logLevel = 'verbose'
await context.watch()
} else {
const result = await context.rebuild()
const metafile = 'app/assets/builds/@turbo-boost/commands.metafile.json'

fs.writeFile(metafile, JSON.stringify(result.metafile), ex => {
if (ex) {
console.error('Build failed!❗️')
conosle.error(ex)
} else {
const message = [
'Build succeeded! 🚀',
`|- Metafile saved to ... → ${metafile}`,
'|- Analyze the bundle at → https://esbuild.github.io/analyze/'
]
console.log(message.join('\n'))
}
context.dispose()
})
}
2 changes: 0 additions & 2 deletions bin/docker/run/local
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,6 @@ fi
# ============================================================================================================
# Dependencies
# ============================================================================================================
rm -rf /opt/turbo_boost-commands/node_modules
ln -sf /mnt/external/node_modules /opt/turbo_boost-commands/node_modules
yarn
bundle

Expand Down
2 changes: 1 addition & 1 deletion bin/standardize
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,6 @@

bundle exec magic_frozen_string_literal
bundle exec standardrb --fix
yarn run prettier-standard package.json app/javascript/**/*.js
yarn run prettier-standard package.json bin/build.mjs app/javascript/**/*.js
yarn run rustywind --write test/dummy/app
yarn run rustywind --write --custom-regex "(:\s[\"'])(.+)[\"']" test/dummy/app/views/_tailwind.yml.erb
9 changes: 4 additions & 5 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
version: "3.9"

x-default-env: &default_env
GEM_HOME: /mnt/external/bundle
PLAYWRIGHT_BROWSERS_PATH: 0
RAILS_ENV: development
RAILS_LOG_TO_STDOUT: true
Expand All @@ -17,9 +16,9 @@ x-default-app: &default_app
networks:
- primary
volumes:
- .:/opt/turbo_boost-commands
- bundle:/mnt/external/bundle
- node_modules:/mnt/external/node_modules
- .:/app
- bundle:/usr/local/bundle
- node_modules:/app/node_modules

networks:
primary:
Expand Down Expand Up @@ -61,7 +60,7 @@ services:
tailwind:
<<: *default_app
container_name: turbo_boost-commands-tailwind
working_dir: /opt/turbo_boost-commands/test/dummy
working_dir: /app/test/dummy
command: bin/rails tailwindcss:watch
depends_on:
web:
Expand Down
10 changes: 5 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
],
"main": "app/assets/builds/@turbo-boost/commands.js",
"files": [
"app/assets/builds"
"app/assets/builds/commands.js",
"app/assets/builds/commands.js.map"
],
"repository": "https://github.com/hopsoft/turbo_boost-commands",
"author": "Nate Hopkins (hopsoft) <[email protected]>",
Expand All @@ -23,15 +24,14 @@
"@hotwired/turbo-rails": ">= 7.2.0"
},
"devDependencies": {
"esbuild": "^0.17.3",
"eslint": "^8.19.0",
"esbuild": "^0.18.14",
"flowbite": "1.5.3",
"playwright": "^1.29.2",
"prettier-standard": "^16.4.1",
"rustywind": "^0.16.0"
},
"scripts": {
"build": "esbuild app/javascript/index.js --bundle --minify --sourcemap --format=esm --target=es2020,chrome79,edge44,firefox71,opera65,safari13 --analyze --outfile=app/assets/builds/@turbo-boost/commands.js",
"build:watch": "yarn build -- --watch"
"build": "yarn run prettier-standard package.json bin/build.mjs app/javascript/**/*.js && node bin/build.mjs",
"build:watch": "yarn build --watch"
}
}
Loading
Loading