Clicking any element inside a <datasette-modal> that carries a
data-modal-cancel attribute now calls requestClose("cancel"), so
Cancel buttons no longer need JavaScript wiring. Like other
dismissals this respects the busy property and the closeGuard hook.
The set-column-type, row-delete and create-table dialogs now use the
attribute instead of their own click listeners, and the plugin
documentation example is simplified to match (the regenerated docs
screenshot is unchanged).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TShiUYVMmmF4zyJR6GMw34
docs/generate-datasette-modal-example.sh builds a temporary demo
database and plugins directory containing the createModal() example
from the Modal dialogs documentation, starts a Datasette server, takes
the screenshot with shot-scraper, quantizes it to an 8-bit palette PNG
and stops the server again. Output is byte-identical to the committed
docs/datasette-modal-example.png.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TShiUYVMmmF4zyJR6GMw34
Taken with shot-scraper against a local Datasette instance running the
createModal() example plugin code from the Modal dialogs docs section,
then palette-quantized to keep the file size down (87KB at 1520x920).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TShiUYVMmmF4zyJR6GMw34
All eight modal dialogs (create table, alter table, insert/edit row,
delete row, set column type, column chooser, mobile column actions and
the navigation jump menu) previously each implemented their own <dialog>
creation, header/footer markup, backdrop-click and Escape handling,
busy-state guards, focus restoration and near-identical frame CSS.
This extracts all of that into a new <datasette-modal> web component
(datasette/static/datasette-modal.js) that wraps a native <dialog> and
provides:
- The standard modal frame, header (title + meta chip), footer and
button styles, distributed via a stylesheet adopted into whichever
document or shadow root the element is connected to - so it also
works inside the shadow DOM of column-chooser and navigation-search
- Close on backdrop click and Escape, a busy property that blocks
dismissal during saves, and a closeGuard hook for discard-changes
confirmation prompts
- Focus restoration to the triggering element on close
- datasette-modal-open and datasette-modal-close events
- Per-dialog sizing via --datasette-modal-width /
--datasette-modal-max-height custom properties
The component is exposed as window.DatasetteModal and via a new
datasetteManager.createModal() method, and is documented in
docs/javascript_plugins.rst as a stable public API for plugins.
This removes roughly 1,200 lines of duplicated frame markup, event
wiring and CSS across table.js, edit-tools.js, mobile-column-actions.js,
column-chooser.js, navigation-search.js and app.css, while keeping the
existing dialog ids, class names and inner structure intact.
Also adds Playwright coverage for the column chooser, mobile column
actions and set-column-type dialogs, which previously had none.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01TShiUYVMmmF4zyJR6GMw34
- Simplify JavaScript column field context:
- expose `isPk` instead of `isPrimaryKey`
- expose `defaultExpression` instead of separate SQLite default flags
- remove value/default state from plugin context
- Update field helper behavior:
- `setValue()` no longer dispatches input/change events
- remove dispatch options and `resetValue()`
- add `markClean()` for plugin-normalized initial values
- track clean field state for reliable dirty detection
Also:
- Prompt before closing row insert/edit dialogs when there are unsaved changes
- Map declared SQLite types to affinities, returning `BLOB` for typeless columns and `NUMERIC` for numeric/date/boolean-like declarations
Replace the value/valueType/originalValue/originalValueType fields on makeColumnField() contexts with an explicit field object API for reading, writing, resetting, comparing and validating field values.
Normalize columnType to {type, config}, rename the SQLite default metadata so it is clearly SQLite-specific, and document that plugins submit only string, number, boolean or null values. Plugins that need structured data should serialize it themselves instead of relying on Datasette to special-case JSON.
Move the built-in json column type behavior onto the same plugin API used by external plugins: validate the textarea with field.setValidity() as the value changes, but submit plain text. Harden row edit value comparison so fixing invalid JSON in an existing row is not blocked by the original invalid value.
Update the JavaScript plugin documentation and Node-based tests for the revised field contract.