id,page,ref,title,content,breadcrumbs,references javascript_plugins:accessibility,javascript_plugins,accessibility,Accessibility,"Custom fields are responsible for preserving the accessibility of the form: The visible field label should name the control. Use field.labelId with aria-labelledby when wrapping or replacing the visible input. Field metadata should remain available to assistive technology. Use field.descriptionId with aria-describedby . Keyboard users must be able to operate every part of the custom field. If the field opens an inline picker or other nested UI, Escape should close that nested UI first and return focus to a sensible element. If a control performs asynchronous loading, expose loading and error states in the UI. Use appropriate ARIA live regions where the state change is important to understand the field. If a plugin hides field.input , the replacement UI must still make the current value and available actions clear. Plugins should not submit the row themselves from inside makeColumnField() controls. Datasette owns the insert/edit dialog lifecycle, form submission, API call, error handling and row refresh.","[""JavaScript plugins"", ""JavaScript plugin objects"", ""makeColumnField(context)""]",[] javascript_plugins:context-object,javascript_plugins,context-object,Context object,"makeColumnField(context) is called with a context object describing the field. The current context object has these keys: mode - string ""insert"" or ""edit"" . database - string or null The database name. table - string or null The table name. tableUrl - string or null The path to the table page, including any configured base URL prefix . column - string The column name. columnType - object or null The configured Datasette column type for this column, if one exists. This is null if no column type has been configured. If present, this object has exactly these keys: type - string The registered column type name , matching the name attribute of the Python ColumnType subclass. config - object Configuration for this specific column type assignment. This is {} if no configuration has been set. sqliteType - string or null The SQLite affinity for this column, if known. This is one of ""TEXT"" , ""INTEGER"" , ""REAL"" , ""BLOB"" , ""NUMERIC"" or null if Datasette could not determine the affinity. notNull - boolean True if the column is defined as NOT NULL . isPk - boolean True if this column is part of the table's primary key. defaultExpression - string or null The SQLite default expression for the column, if available. This is null if the column has no SQLite default. For example, a column defined with DEFAULT (datetime('now')) will have ""datetime('now')"" here. This is the expression from the table schema, not the actual value SQLite will insert. form - HTMLFormElement or null The row insert/edit form element. dialog - HTMLDialogElement or null The modal dialog element.","[""JavaScript plugins"", ""JavaScript plugin objects"", ""makeColumnField(context)""]",[] javascript_plugins:example-textarea-backed-custom-element,javascript_plugins,example-textarea-backed-custom-element,Example: textarea-backed custom element,"This example handles a markdown-editor column type by asking Datasette for a textarea and wrapping that textarea in a custom Web Component element: document.addEventListener(""datasette_init"", function (event) { event.detail.registerPlugin(""markdown-editor"", { version: ""0.1"", makeColumnField(context) { if (!context.columnType || context.columnType.type !== ""markdown-editor"") { return; } return { useTextarea: true, render(field) { const editor = document.createElement(""my-markdown-editor""); editor.appendChild(field.input); if (field.labelId) { field.input.setAttribute(""aria-labelledby"", field.labelId); } if (field.descriptionId) { field.input.setAttribute(""aria-describedby"", field.descriptionId); } return editor; }, focus(field) { const editor = field.root.querySelector(""my-markdown-editor""); if (editor && editor.focus) { editor.focus(); } else { field.input.focus(); } } }; } }); });","[""JavaScript plugins"", ""JavaScript plugin objects"", ""makeColumnField(context)""]",[] javascript_plugins:javascript-plugins-makeabovetablepanelconfigs,javascript_plugins,javascript-plugins-makeabovetablepanelconfigs,makeAboveTablePanelConfigs(),"This method should return a JavaScript array of objects defining additional panels to be added to the top of the table page. Each object should have the following: id - string A unique string ID for the panel, for example map-panel label - string A human-readable label for the panel render(node) - function A function that will be called with a DOM node to render the panel into This example shows how a plugin might define a single panel: document.addEventListener('datasette_init', function(ev) { ev.detail.registerPlugin('panel-plugin', { version: 0.1, makeAboveTablePanelConfigs: () => { return [ { id: 'first-panel', label: 'First panel', render: node => { node.innerHTML = '

My custom panel

This is a custom panel that I added using a JavaScript plugin

'; } } ] } }); }); When a page with a table loads, all registered plugins that implement makeAboveTablePanelConfigs() will be called and panels they return will be added to the top of the table page.","[""JavaScript plugins"", ""JavaScript plugin objects""]",[] javascript_plugins:javascript-plugins-makecolumnactions,javascript_plugins,javascript-plugins-makecolumnactions,makeColumnActions(columnDetails),"This method, if present, will be called when Datasette is rendering the cog action menu icons that appear at the top of the table view. By default these include options like ""Sort ascending/descending"" and ""Facet by this"", but plugins can return additional actions to be included in this menu. The method will be called with a columnDetails object with the following keys: columnName - string The name of the column columnNotNull - boolean True if the column is defined as NOT NULL columnType - string The SQLite data type of the column isPk - boolean True if the column is part of the primary key It should return a JavaScript array of objects each with a label and onClick property: label - string The human-readable label for the action onClick(evt) - function A function that will be called when the action is clicked The evt object passed to the onClick is the standard browser event object that triggered the click. This example plugin adds two menu items - one to copy the column name to the clipboard and another that displays the column metadata in an alert() window: document.addEventListener('datasette_init', function(ev) { ev.detail.registerPlugin('column-name-plugin', { version: 0.1, makeColumnActions: (columnDetails) => { return [ { label: 'Copy column to clipboard', onClick: async (evt) => { await navigator.clipboard.writeText(columnDetails.columnName) } }, { label: 'Alert column metadata', onClick: () => alert(JSON.stringify(columnDetails, null, 2)) } ]; } }); });","[""JavaScript plugins"", ""JavaScript plugin objects""]",[] javascript_plugins:javascript-plugins-makecolumnfield,javascript_plugins,javascript-plugins-makecolumnfield,makeColumnField(context),"This method, if present, can provide a custom form field for a column in Datasette's row insert and edit dialogs. It is designed for plugins that register custom column types using the Python register_column_types() plugin hook. For example, a plugin that defines a file column type can use makeColumnField() to replace a plain text input with a file picker, and a plugin that defines a rich text column type can use it to enhance the field with an editor. Datasette calls makeColumnField(context) on each registered JavaScript plugin when it renders an editable insert/edit field. Plugins should inspect the context object and only return a control object if they can handle that field. Otherwise, use a bare return; . The first plugin to return a truthy control object is used for that field. Plugins are called in registration order. If a plugin raises an exception, Datasette logs the error to the browser console and continues to the next plugin. The row dialog tracks the value that will be sent to the insert/update API. The context object describes the column and form environment; custom controls should read and write field values using the field helper object passed to render(field) .","[""JavaScript plugins"", ""JavaScript plugin objects""]",[] javascript_plugins:javascript-plugins-makejumpsections,javascript_plugins,javascript-plugins-makejumpsections,makeJumpSections(context),"This method should return a JavaScript array of objects defining additional sections to be added to the blank state of the / jump menu, before the user starts typing a search. It should return an array of objects, each with the following: id - string A unique string ID for the section, for example agent-chat render(node, context) - function A function that will be called with a DOM node to render the section into Datasette passes a context object to both makeJumpSections(context) and render(node, context) . It has the following keys: navigationSearch The custom element instance. container - only for render() The .results-container element used by the jump menu. input - only for render() The .search-input element used by the jump menu. This example shows how a plugin might add a button for starting a new chat: document.addEventListener('datasette_init', function(ev) { ev.detail.registerPlugin('agent-plugin', { version: 0.1, makeJumpSections: (context) => { return [ { id: 'agent-chat', render: (node, context) => { node.innerHTML = ''; node.querySelector('button').addEventListener('click', () => { location.href = '/-/agent/new'; }); } } ]; } }); });","[""JavaScript plugins"", ""JavaScript plugin objects""]",[] javascript_plugins:lazy-loading-large-controls,javascript_plugins,lazy-loading-large-controls,Lazy loading large controls,"The JavaScript file that registers makeColumnField() should be small. If the actual control is large, load it from inside render() using dynamic import() . That way the heavier code is only downloaded after a user opens an insert/edit dialog containing a matching column type. const editorUrl = new URL(""./editor.js"", import.meta.url).href; document.addEventListener(""datasette_init"", function (event) { event.detail.registerPlugin(""my-editor"", { version: ""0.1"", makeColumnField(context) { if (!context.columnType || context.columnType.type !== ""my-editor"") { return; } return { useTextarea: true, render(field) { import(editorUrl).then(function () { // Enhance field.input here. }); return field.input; } }; } }); });","[""JavaScript plugins"", ""JavaScript plugin objects"", ""makeColumnField(context)""]",[] javascript_plugins:returned-control-object,javascript_plugins,returned-control-object,Returned control object,"A plugin that wants to handle a field should return an object. Datasette currently recognizes these properties: useTextarea - boolean, optional If true, Datasette creates a