編集中離脱ガード
stimeo--dirty-form
フォームの変更を覚えておき、保存せずに離れようとしたら引き止めます。
フォームを開いたときの値を覚えておき、変わったかどうかを見ています。変わっている間だけ、ページを閉じる操作と、Turbo での画面遷移の両方を引き止めます。変更が元に戻れば引き止めもやめます。引き止め方は選べます。標準の確認ダイアログでもよいですし、自前の確認ダイアログへつなぐこともできます。同じページに複数のフォームがあっても、確認は 1 回だけです。送信が成功したときは、そのとき送った値を「保存済み」とみなします。送信中に足した入力は変更として残るので、うっかり捨ててしまいません。確認ダイアログの見た目は持たず、値の保存もしません。
キーボード操作
このコンポーネント自体はキーボード操作を持ちません。
<%# Dirty-form demo: edit either field and its form gets data-dirty (shown as a badge).
When both forms are dirty, one Turbo visit dispatches both guard events and opens
one native confirm using the first eligible form in live DOM order. "Save" calls
markClean for that form; this catalog has no server, so it stands in for a successful
save. The library owns data-dirty and navigation coordination; the demo only styles
the visible state. %>
<div class="dirty-form-demo">
<form
class="dirty-form"
data-controller="stimeo--dirty-form"
data-stimeo--dirty-form-message-value="<%= t('components.dirty_form.demo.first_message') %>"
aria-labelledby="dirty-form-first-title"
>
<h3 id="dirty-form-first-title" class="dirty-form__title">
<%= t("components.dirty_form.demo.first_title") %>
</h3>
<label class="dirty-form__field">
<span><%= t("components.dirty_form.demo.first_label") %></span>
<input type="text" name="title" value="<%= t('components.dirty_form.demo.first_value') %>">
</label>
<div class="dirty-form__bar">
<span class="dirty-form__badge"><%= t("components.dirty_form.demo.unsaved") %></span>
<button type="button" class="demo-trigger" data-action="click->stimeo--dirty-form#markClean">
<%= t("components.dirty_form.demo.save") %>
</button>
</div>
</form>
<form
class="dirty-form"
data-controller="stimeo--dirty-form"
data-stimeo--dirty-form-message-value="<%= t('components.dirty_form.demo.second_message') %>"
aria-labelledby="dirty-form-second-title"
>
<h3 id="dirty-form-second-title" class="dirty-form__title">
<%= t("components.dirty_form.demo.second_title") %>
</h3>
<label class="dirty-form__field">
<span><%= t("components.dirty_form.demo.second_label") %></span>
<input type="text" name="summary" value="<%= t('components.dirty_form.demo.second_value') %>">
</label>
<div class="dirty-form__bar">
<span class="dirty-form__badge"><%= t("components.dirty_form.demo.unsaved") %></span>
<button type="button" class="demo-trigger" data-action="click->stimeo--dirty-form#markClean">
<%= t("components.dirty_form.demo.save") %>
</button>
</div>
</form>
</div>
/*
* Presentation-only styles for the dirty-form demo.
* The library toggles data-dirty on the form; this CSS reveals the "unsaved" badge
* while that hook is present.
*/
.dirty-form-demo {
display: grid;
gap: 1rem;
max-width: 28rem;
}
.dirty-form {
display: flex;
flex-direction: column;
gap: 0.75rem;
padding: 1rem;
border: 1px solid var(--border);
border-radius: 0.5rem;
}
.dirty-form__title {
margin: 0;
font-size: 1rem;
}
.dirty-form__field {
display: flex;
flex-direction: column;
gap: 0.25rem;
}
.dirty-form__field input {
padding: 0.5rem;
border: 1px solid var(--border);
border-radius: 0.375rem;
font: inherit;
}
.dirty-form__bar {
display: flex;
align-items: center;
gap: 0.75rem;
}
/* The badge appears only while the form is dirty. */
.dirty-form__badge {
display: none;
font-size: 0.8rem;
font-weight: 600;
color: var(--accent-700);
}
.dirty-form[data-dirty] .dirty-form__badge {
display: inline-block;
}
このデモに固有の消費側 JS はありません(挙動はコントローラが担います)。
これらのスタイルは共通のデザイントークン(ライト/ダーク両対応)を使います。共通スタイルも一緒にコピーし、ルート要素の data-theme を切り替えればダークになります。
このコンポーネントを動かすために HTML へ記述する data-* 属性です。ルート要素に下の data-controller を付け、その内側に各 target / value / action を配置します。
ルート要素に付与
data-controller="stimeo--dirty-form"
値(Values)
| 名前 | 説明 | 属性 |
|---|---|---|
message
|
Turbo 遷移時のネイティブ confirm に使う確認メッセージ。 |
data-stimeo--dirty-form-message-value |
confirmBridge
|
confirm の代わりに Turbo 遷移を止め、確認を Confirm Bridge に委ねるか。 |
data-stimeo--dirty-form-confirm-bridge-value |
アクション
| 名前 | アクション |
|---|---|
markClean
|
stimeo--dirty-form#markClean |
acceptRestore
|
stimeo--dirty-form#acceptRestore |
イベント
| 名前 | 説明 | イベント |
|---|---|---|
dirty
|
dirty 状態が変化したときに発火。detail.dirty を伴う。 |
stimeo--dirty-form:dirty |
guard
|
cancelable。Turbo 遷移時、dirty かつ非送信中の各フォームで発火。preventDefault で遷移を中断。 |
stimeo--dirty-form:guard |
状態フック
ライブラリが操作するのはこれらの ARIA / data 属性、カスタムプロパティだけです。見た目は利用側 CSS がこれらに反応して作ります([aria-selected] / [aria-expanded] / var(--stimeo--…) などのセレクタでフックします)。
| フック | 対象 | 意味 |
|---|---|---|
data-dirty |
フォーム(ルート) | connect 時の基準値と異なる間に付与。 |