feat(turbo): add turbo:morph helper
This commit is contained in:
parent
65bd996f2a
commit
cf81e8ecd5
4 changed files with 61 additions and 3 deletions
|
@ -39,5 +39,15 @@ module TurboStreamHelper
|
|||
def enable(target)
|
||||
dispatch('dom:mutation', { action: :enable, target: target })
|
||||
end
|
||||
|
||||
def morph(target, content = nil, **rendering, &block)
|
||||
template = render_template(target, content, allow_inferred_rendering: true, **rendering, &block)
|
||||
dispatch('dom:mutation', { action: :morph, target: target, html: template })
|
||||
end
|
||||
|
||||
def morph_all(targets, content = nil, **rendering, &block)
|
||||
template = render_template(targets, content, allow_inferred_rendering: true, **rendering, &block)
|
||||
dispatch('dom:mutation', { action: :morph, targets: targets, html: template })
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import invariant from 'tiny-invariant';
|
||||
import { z } from 'zod';
|
||||
import morphdom from 'morphdom';
|
||||
|
||||
import { ApplicationController, Detail } from './application_controller';
|
||||
|
||||
|
@ -18,18 +19,27 @@ export class TurboEventController extends ApplicationController {
|
|||
}
|
||||
}
|
||||
|
||||
const MutationAction = z.enum(['show', 'hide', 'focus', 'enable', 'disable']);
|
||||
const MutationAction = z.enum([
|
||||
'show',
|
||||
'hide',
|
||||
'focus',
|
||||
'enable',
|
||||
'disable',
|
||||
'morph'
|
||||
]);
|
||||
type MutationAction = z.infer<typeof MutationAction>;
|
||||
const Mutation = z.union([
|
||||
z.object({
|
||||
action: MutationAction,
|
||||
delay: z.number().optional(),
|
||||
target: z.string()
|
||||
target: z.string(),
|
||||
html: z.string().optional()
|
||||
}),
|
||||
z.object({
|
||||
action: MutationAction,
|
||||
delay: z.number().optional(),
|
||||
targets: z.string()
|
||||
targets: z.string(),
|
||||
html: z.string().optional()
|
||||
})
|
||||
]);
|
||||
type Mutation = z.infer<typeof Mutation>;
|
||||
|
@ -65,9 +75,41 @@ const Mutations: Record<MutationAction, (mutation: Mutation) => void> = {
|
|||
for (const element of findElements<HTMLInputElement>(mutation)) {
|
||||
element.disabled = false;
|
||||
}
|
||||
},
|
||||
morph: (mutation) => {
|
||||
invariant(mutation.html, 'morph action requires html');
|
||||
for (const element of findElements<HTMLInputElement>(mutation)) {
|
||||
morphdom(element, mutation.html, {
|
||||
onBeforeElUpdated(fromEl, toEl) {
|
||||
if (isTouchedInput(fromEl)) {
|
||||
fromEl.removeAttribute('data-touched');
|
||||
mergeInputValue(
|
||||
fromEl as HTMLInputElement,
|
||||
toEl as HTMLInputElement
|
||||
);
|
||||
}
|
||||
if (fromEl.isEqualNode(toEl)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function mergeInputValue(fromEl: HTMLInputElement, toEl: HTMLInputElement) {
|
||||
toEl.value = fromEl.value;
|
||||
toEl.checked = fromEl.checked;
|
||||
}
|
||||
|
||||
function isTouchedInput(element: HTMLElement): boolean {
|
||||
return (
|
||||
['INPUT', 'TEXTAREA', 'SELECT'].includes(element.tagName) &&
|
||||
!!element.getAttribute('data-touched')
|
||||
);
|
||||
}
|
||||
|
||||
function mutate(mutation: Mutation) {
|
||||
const fn = Mutations[mutation.action];
|
||||
invariant(fn, `Could not find mutation ${mutation.action}`);
|
||||
|
|
|
@ -34,6 +34,7 @@
|
|||
"is-hotkey": "^0.2.0",
|
||||
"maplibre-gl": "^1.15.2",
|
||||
"match-sorter": "^6.2.0",
|
||||
"morphdom": "^2.6.1",
|
||||
"patch-package": "^6.4.7",
|
||||
"react": "^18.0.0",
|
||||
"react-coordinate-input": "^1.0.0",
|
||||
|
|
|
@ -9211,6 +9211,11 @@ moize@^6.0.0:
|
|||
fast-equals "^2.0.1"
|
||||
micro-memoize "^4.0.9"
|
||||
|
||||
morphdom@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/morphdom/-/morphdom-2.6.1.tgz#e868e24f989fa3183004b159aed643e628b4306e"
|
||||
integrity sha512-Y8YRbAEP3eKykroIBWrjcfMw7mmwJfjhqdpSvoqinu8Y702nAwikpXcNFDiIkyvfCLxLM9Wu95RZqo4a9jFBaA==
|
||||
|
||||
move-concurrently@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/move-concurrently/-/move-concurrently-1.0.1.tgz#be2c005fda32e0b29af1f05d7c4b33214c701f92"
|
||||
|
|
Loading…
Reference in a new issue