From e3c061aca02fb8c6e1a3c36182241303b0f1d71f Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 08:28:37 -0800 Subject: [PATCH 01/19] inputs edits --- docs/javascript/display.md | 12 +++- docs/javascript/inputs.md | 137 +++++++++---------------------------- docs/lib/generators.md | 2 +- docs/lib/inputs.md | 107 +++++++++++++++++++++++++++++ 4 files changed, 150 insertions(+), 108 deletions(-) diff --git a/docs/javascript/display.md b/docs/javascript/display.md index 4a6e314ba..cb00ebff1 100644 --- a/docs/javascript/display.md +++ b/docs/javascript/display.md @@ -156,4 +156,14 @@ Inputs.button("Click me", {value: 0, reduce: (i) => displayThere(++i)}) ## view(*element*) -The [`view` function](./inputs#viewelement) is a special type of display function that inserts the given DOM *element* (typically an input), then returns its corresponding value [generator](./generators) via [`Generators.input`](../lib/generators#input(element)). When the user interacts with the input, this triggers the [reactive evaluation](reactivity) of all the JavaScript code that reference this value. +The [`view` function](./inputs#view(element)) is a wrapper for `display` that returns a [value generator](./generators) for the given input element (rather than the input element itself). For example, below we display an input element and expose its value to the page as the variable `text`. + +```js echo +const text = view(html``); +``` + +```js echo +text // Try typing into the box above +``` + +When you type into the textbox, the generator will yield a new value, triggering the [reactive evaluation](./reactivity) of any code blocks that reference `text`. See [Inputs](./inputs) for more. diff --git a/docs/javascript/inputs.md b/docs/javascript/inputs.md index c3cbc9c30..6899ea6d8 100644 --- a/docs/javascript/inputs.md +++ b/docs/javascript/inputs.md @@ -1,156 +1,81 @@ # JavaScript: Inputs -Inputs are user-interface elements that accept data from a user. In a data app, inputs might prompt a viewer to: +Inputs are graphical user interface elements such as dropdowns, radios, sliders, and text boxes that accept data from a user and enable interaction via [reactivity](./reactivity). They can also be custom elements that you design, such as charts that support interactive selection via pointing or brushing. -- Select a URL from a dropdown list to view site traffic for a specific page -- Interactively subset a table of users by typing in a domain name -- Choose a date range to explore software downloads over a period of interest +Inputs might prompt a viewer to: -Inputs can be displayed with the [`view`](#view(element)) function, which is a special type of [display](display) function that additionally returns the input’s value generator, which can then be assigned to a variable for use elsewhere. +- Filter a table of users by typing in a name +- Select a URL from a dropdown to view traffic to a specific page +- Choose a date range to explore data within a period of interest -For example, the radio input below prompts a user to select one from a series of values: +Inputs are typically displayed using the built-in [`view`](#view(element)) function, which [displays](./display) the given element and returns a [value generator](./generators). The generator can then be declared as a [top-level variable](./reactivity) to expose the input’s value to the page. For example, the radio input below prompts the user to select their favorite team: ```js echo const team = view(Inputs.radio(["Metropolis Meteors", "Rockford Peaches", "Bears"], {label: "Favorite team:", value: "Metropolis Meteors"})); ``` -The `team` variable in this example now reactively updates when the user interacts with the radio input, triggering a new evaluation of the dependent code. Select different teams in the radio input above to update the text. +The `team` variable here will reactively update when the user interacts with the radio input, triggering re-evaluation of referencing code blocks. Select different teams in the radio input above to update the text. -```md My favorite baseball team is the ${team}! -``` +```md run=false My favorite baseball team is the ${team}! - -## view(*element*) - -The `view` function used above does two things: (1) it displays the given DOM *element*, then (2) returns its corresponding value [generator](./generators), using [`Generators.input`](../lib/generators#input(element)) under the hood. Use `view` to display an input while also exposing the input’s value as a [reactive variable](./reactivity). You can reference the input’s value anywhere, and the code will run whenever the input changes. - -The `view` function is not limited to Observable Inputs. For example, here is a simple range slider created with [html](../lib/htl): - -```js echo -const topn = view(html``); -``` - -Now we can reference `topn` elsewhere, for example to control how many groups are displayed in a sorted bar chart: - -```js echo -Plot.plot({ - marginLeft: 50, - marks: [ - Plot.barX(olympians, Plot.groupY({x: "count"}, {y: "nationality", sort: {y: "x", reverse: true, limit: topn}})), - Plot.ruleX([0]) - ] -}) -``` - -## Observable Inputs - -The [Observable Inputs](../lib/inputs) library implements commonly used inputs — buttons, sliders, dropdowns, tables, and the like — as functions. Each input function returns an HTML element that emits *input* events for compatibility with [`view`](#view(element)) and [Generators.input](../lib/generators#input(element)). - -### Usage - -Inputs are generally declared as follows: - -```js run=false -const value = view(Inputs.inputName(...)); -``` - -where *value* is the named input value, and *inputName* is the input name (like `radio`, `button`, or `checkbox`). See the full list of [available inputs](../lib/inputs) with live examples, or visit the [Observable Inputs API reference](https://github.com/observablehq/inputs/blob/main/README.md) for more detail and specific input options. - -Options differ between inputs. For example, the checkbox input accepts options to disable all or certain values, sort displayed values, and only display repeated values *once* (among others): - -```js echo -const checkout = view(Inputs.checkbox(["B","A","Z","Z","F","D","G","G","G","Q"], {disabled: ["F", "Q"], sort: true, unique: true, value: "B", label: "Choose categories:"})); -``` - -```js echo -checkout ``` -### Analysis with Observable Inputs - -To demonstrate Observable Inputs for data analysis, we’ll use the `olympians` sample dataset containing records on athletes that participated in the 2016 Rio olympics (from [Matt Riggott](https://flother.is/2017/olympic-games-data/)). +You can implement custom inputs using arbitrary HTML. For example, here is a [range input](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/range) that lets you choose an integer between 1 and 15 (inclusive): ```js echo -Inputs.table(olympians) +const n = view(html``); ``` -Here, we create a subset of columns to simplify outputs: - ```js echo -const columns = olympians.columns.slice(1, -1); // hide the id and info column to simplify +n // Try dragging the slider above ``` -Now let’s wire up our data to a search input. Type whatever you want into the box and search will find matching rows in the data which we can then use in a table below. +
To be compatible with view, custom inputs must emit input events and expose their current value as element.value. See Generators.input for more.
-A few examples to try: **[mal]** will match *sex* = male, but also names that start with “mal”, such as Anna Malova; **[1986]** will match anyone born in 1986 (and a few other results); **[USA gym]** will match USA’s gymnastics team. Each space-separated term in your query is prefix-matched against all columns in the data. +More often, you’ll use a helper library such as [Observable Inputs](../lib/inputs) or [Observable Plot](../lib/plot) to declare inputs. For example, here is [`Inputs.range`](../lib/inputs#range): ```js echo -const search = view(Inputs.search(olympians, { - datalist: ["mal", "1986", "USA gym"], - placeholder: "Search athletes" -})) +const m = view(Inputs.range([1, 15], {label: "Favorite number", step: 1})); ``` ```js echo -Inputs.table(search, {columns}) +m // Try dragging the slider above ``` -If you like, you can sort the table columns by clicking on the column name. Click once to sort ascending, and click again to sort descending. Note that the sort order is temporary: it’ll go away if you reload the page. Specify the column name as the *sort* option above if you want this order to persist. - -For a more structured approach, we can use a select input to choose a sport, then *array*.filter to determine which rows are shown in the table. The *sort* and *unique* options tell the input to show only distinct values and to sort them alphabetically. +To use a chart as an input, you can use Plot’s [pointer interaction](https://observablehq.com/plot/interactions/pointer), say by setting the **tip** option on a mark. In the scatterplot below, the penguin closest to the pointer is exposed as the reactive variable `penguin`. ```js echo -const sport = view(Inputs.select(olympians.map(d => d.sport), {sort: true, unique: true, label: "sport"})); +const penguin = view(Plot.dot(penguins, {x: "culmen_length_mm", y: "flipper_length_mm", tip: true}).plot()); ``` ```js echo -const selectedAthletes = display(olympians.filter(d => d.sport === sport)); +penguin ``` -```js echo -Inputs.table(selectedAthletes, {columns}) -``` +In the future, Plot will support more interaction methods, including brushing. Please upvote [#5](https://github.com/observablehq/plot/issues/5) if you are interested in this feature. -To visualize a column of data as a histogram, use the value of the select input with [Observable Plot](https://observablehq.com/plot/). +## view(*element*) -```js echo -Plot.plot({ - x: { - domain: [1.3, 2.2] - }, - marks: [ - Plot.rectY(selectedAthletes, Plot.binX({y: "count"}, {x: "height", fill: "steelblue"})), - Plot.ruleY([0]) - ] -}) -``` +The `view` function used above does two things: -You can also pass grouped data to the select input as a [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) from key to array of values, say using [d3.group](https://d3js.org/d3-array/group). The value of the select input in this mode is the data in the selected group. Note that *unique* is no longer required, and that *sort* works here, too, sorting the keys of the map returned by d3.group. +1. it [displays](./display) the given DOM *element*, and then +2. returns its corresponding [value generator](./generators). -```js echo -const groups = display(d3.group(olympians, d => d.sport)); -``` +The `view` function uses [`Generators.input`](../lib/generators#input(element)) under the hood. You can also call `Generators.input` directly, say if you want to declare the input as a top-level variable while placing it elsewhere on the page, say using an [inline expression](../javascript#inline-expressions). ```js echo -const sportAthletes = view(Inputs.select(groups, {sort: true, label: "sport"})); +const nameInput = html``; +const name = Generators.input(nameInput); ``` -```js echo -Inputs.table(sportAthletes, {columns}) -``` +Enter your name: ${nameInput}. -The select input works well for categorical data, such as sports or nationalities, but how about quantitative dimensions such as height or weight? Here’s a range input that lets you pick a target weight; we then filter the table rows for any athlete within 10% of the target weight. Notice that some columns, such as sport, are strongly correlated with weight. +Hi ${name || "anonymous"}! -```js echo -const weight = view(Inputs.range(d3.extent(olympians, d => d.weight), {step: 1, label: "weight (kg)"})); -``` +```md run=false +Enter your name: ${nameInput}. -```js echo -Inputs.table(olympians.filter(d => d.weight < weight * 1.1 && weight * 0.9 < d.weight), {sort: "weight", columns}) +Hi ${name || "anonymous"}! ``` - -### License - -Observable Inputs are released under the [ISC license](https://github.com/observablehq/inputs/blob/main/LICENSE) and depend only on [Hypertext Literal](../lib/htl), our tagged template literal for safely generating dynamic HTML. If you are interested in contributing or wish to report an issue, please see [our repository](https://github.com/observablehq/inputs). For recent changes, please see our [release notes](https://github.com/observablehq/inputs/releases). \ No newline at end of file diff --git a/docs/lib/generators.md b/docs/lib/generators.md index 6c26ce0be..251ffbf72 100644 --- a/docs/lib/generators.md +++ b/docs/lib/generators.md @@ -8,7 +8,7 @@ import {Generators} from "npm:@observablehq/stdlib"; ## input(*element*) -Returns an async generator that yields whenever the given *element* emits an *input* event, with the given *element*’s current value. (It’s a bit fancier than that because we special-case a few element types.) The built-in [`view` function](<../javascript/display#view(element)>) uses this. +Returns an async generator that yields whenever the given *element* emits an *input* event, with the given *element*’s current value. (It’s a bit fancier than that because we special-case a few element types.) The built-in [`view` function](<../javascript/inputs#view(element)>) uses this. ```js echo const nameInput = display(document.createElement("input")); diff --git a/docs/lib/inputs.md b/docs/lib/inputs.md index 50fa8779a..1a612aa41 100644 --- a/docs/lib/inputs.md +++ b/docs/lib/inputs.md @@ -1,5 +1,7 @@ # Observable Inputs +Observable Inputs implements commonly used inputs — buttons, sliders, dropdowns, tables, and the like — as functions. Each input function returns an HTML element that emits *input* events for compatibility with [`view`](#view(element)) and [Generators.input](../lib/generators#input(element)). + [Observable Inputs](https://github.com/observablehq/inputs) provides “lightweight interface components — buttons, sliders, dropdowns, tables, and the like — to help you explore data and build interactive displays.” Observable Inputs is available by default as `Inputs` in Markdown, but you can import it explicitly like so: ```js echo @@ -12,6 +14,111 @@ Or, just import the specific inputs you want: import {Button, Color} from "npm:@observablehq/inputs"; ``` +### Usage + +Inputs are generally declared as follows: + +```js run=false +const value = view(Inputs.inputName(...)); +``` + +where *value* is the named input value, and *inputName* is the input name (like `radio`, `button`, or `checkbox`). See the full list of [available inputs](../lib/inputs) with live examples, or visit the [Observable Inputs API reference](https://github.com/observablehq/inputs/blob/main/README.md) for more detail and specific input options. + +Options differ between inputs. For example, the checkbox input accepts options to disable all or certain values, sort displayed values, and only display repeated values *once* (among others): + +```js echo +const checkout = view(Inputs.checkbox(["B","A","Z","Z","F","D","G","G","G","Q"], {disabled: ["F", "Q"], sort: true, unique: true, value: "B", label: "Choose categories:"})); +``` + +```js echo +checkout +``` + +### Analysis with Observable Inputs + +To demonstrate Observable Inputs for data analysis, we’ll use the `olympians` sample dataset containing records on athletes that participated in the 2016 Rio olympics (from [Matt Riggott](https://flother.is/2017/olympic-games-data/)). + +```js echo +Inputs.table(olympians) +``` + +Here, we create a subset of columns to simplify outputs: + +```js echo +const columns = olympians.columns.slice(1, -1); // hide the id and info column to simplify +``` + +Now let’s wire up our data to a search input. Type whatever you want into the box and search will find matching rows in the data which we can then use in a table below. + +A few examples to try: **[mal]** will match *sex* = male, but also names that start with “mal”, such as Anna Malova; **[1986]** will match anyone born in 1986 (and a few other results); **[USA gym]** will match USA’s gymnastics team. Each space-separated term in your query is prefix-matched against all columns in the data. + +```js echo +const search = view(Inputs.search(olympians, { + datalist: ["mal", "1986", "USA gym"], + placeholder: "Search athletes" +})) +``` + +```js echo +Inputs.table(search, {columns}) +``` + +If you like, you can sort the table columns by clicking on the column name. Click once to sort ascending, and click again to sort descending. Note that the sort order is temporary: it’ll go away if you reload the page. Specify the column name as the *sort* option above if you want this order to persist. + +For a more structured approach, we can use a select input to choose a sport, then *array*.filter to determine which rows are shown in the table. The *sort* and *unique* options tell the input to show only distinct values and to sort them alphabetically. + +```js echo +const sport = view(Inputs.select(olympians.map(d => d.sport), {sort: true, unique: true, label: "sport"})); +``` + +```js echo +const selectedAthletes = display(olympians.filter(d => d.sport === sport)); +``` + +```js echo +Inputs.table(selectedAthletes, {columns}) +``` + +To visualize a column of data as a histogram, use the value of the select input with [Observable Plot](https://observablehq.com/plot/). + +```js echo +Plot.plot({ + x: { + domain: [1.3, 2.2] + }, + marks: [ + Plot.rectY(selectedAthletes, Plot.binX({y: "count"}, {x: "height", fill: "steelblue"})), + Plot.ruleY([0]) + ] +}) +``` + +You can also pass grouped data to the select input as a [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) from key to array of values, say using [d3.group](https://d3js.org/d3-array/group). The value of the select input in this mode is the data in the selected group. Note that *unique* is no longer required, and that *sort* works here, too, sorting the keys of the map returned by d3.group. + +```js echo +const groups = display(d3.group(olympians, d => d.sport)); +``` + +```js echo +const sportAthletes = view(Inputs.select(groups, {sort: true, label: "sport"})); +``` + +```js echo +Inputs.table(sportAthletes, {columns}) +``` + +The select input works well for categorical data, such as sports or nationalities, but how about quantitative dimensions such as height or weight? Here’s a range input that lets you pick a target weight; we then filter the table rows for any athlete within 10% of the target weight. Notice that some columns, such as sport, are strongly correlated with weight. + +```js echo +const weight = view(Inputs.range(d3.extent(olympians, d => d.weight), {step: 1, label: "weight (kg)"})); +``` + +```js echo +Inputs.table(olympians.filter(d => d.weight < weight * 1.1 && weight * 0.9 < d.weight), {sort: "weight", columns}) +``` + +## Basic inputs + These basic inputs will get you started. * [Button](#button) - do something when a button is clicked From cc54f0ce352c64ad8c1eb66369b44f72f03a96af Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 12:09:30 -0800 Subject: [PATCH 02/19] tweak inline example --- docs/javascript/inputs.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/javascript/inputs.md b/docs/javascript/inputs.md index 6899ea6d8..ceaf75971 100644 --- a/docs/javascript/inputs.md +++ b/docs/javascript/inputs.md @@ -63,19 +63,23 @@ The `view` function used above does two things: 1. it [displays](./display) the given DOM *element*, and then 2. returns its corresponding [value generator](./generators). -The `view` function uses [`Generators.input`](../lib/generators#input(element)) under the hood. You can also call `Generators.input` directly, say if you want to declare the input as a top-level variable while placing it elsewhere on the page, say using an [inline expression](../javascript#inline-expressions). +The `view` function uses [`Generators.input`](../lib/generators#input(element)) under the hood. You can also call `Generators.input` directly, say to declare the input as a top-level variable without immediately displaying it: ```js echo const nameInput = html``; const name = Generators.input(nameInput); ``` -Enter your name: ${nameInput}. +As a top-level variable, you can then display the input anywhere you like, such as within a [card](../css/card) using an [inline expression](../javascript#inline-expressions). And you can reference the input’s value reactively anywhere, too. -Hi ${name || "anonymous"}! +
+
Enter your name: ${nameInput}
+
Hi ${name || "anonymous"}!
+
-```md run=false -Enter your name: ${nameInput}. - -Hi ${name || "anonymous"}! +```html run=false +
+
Enter your name: ${nameInput}
+
Hi ${name || "anonymous"}!
+
``` From dd6e5e362eeb14cf0335d557434cf3f1245a9226 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 12:49:26 -0800 Subject: [PATCH 03/19] trim inputs overview --- docs/lib/inputs.md | 339 +++++++++------------------------------------ 1 file changed, 64 insertions(+), 275 deletions(-) diff --git a/docs/lib/inputs.md b/docs/lib/inputs.md index 1a612aa41..3ae24ff64 100644 --- a/docs/lib/inputs.md +++ b/docs/lib/inputs.md @@ -1,7 +1,5 @@ # Observable Inputs -Observable Inputs implements commonly used inputs — buttons, sliders, dropdowns, tables, and the like — as functions. Each input function returns an HTML element that emits *input* events for compatibility with [`view`](#view(element)) and [Generators.input](../lib/generators#input(element)). - [Observable Inputs](https://github.com/observablehq/inputs) provides “lightweight interface components — buttons, sliders, dropdowns, tables, and the like — to help you explore data and build interactive displays.” Observable Inputs is available by default as `Inputs` in Markdown, but you can import it explicitly like so: ```js echo @@ -11,331 +9,122 @@ import * as Inputs from "npm:@observablehq/inputs"; Or, just import the specific inputs you want: ```js echo -import {Button, Color} from "npm:@observablehq/inputs"; -``` - -### Usage - -Inputs are generally declared as follows: - -```js run=false -const value = view(Inputs.inputName(...)); +import {button, color} from "npm:@observablehq/inputs"; ``` -where *value* is the named input value, and *inputName* is the input name (like `radio`, `button`, or `checkbox`). See the full list of [available inputs](../lib/inputs) with live examples, or visit the [Observable Inputs API reference](https://github.com/observablehq/inputs/blob/main/README.md) for more detail and specific input options. - -Options differ between inputs. For example, the checkbox input accepts options to disable all or certain values, sort displayed values, and only display repeated values *once* (among others): +Inputs are typically passed to the [`view` function](<../javascript/inputs#view(element)>) for display, while exposing the input’s [value generator](../javascript/generators) as a [reactive variable](../javascript/reactivity). Options differ between inputs. For example, the checkbox input accepts options to disable all or certain values, sort displayed values, and only display repeated values _once_ (among others): ```js echo -const checkout = view(Inputs.checkbox(["B","A","Z","Z","F","D","G","G","G","Q"], {disabled: ["F", "Q"], sort: true, unique: true, value: "B", label: "Choose categories:"})); +const checkout = view( + Inputs.checkbox(["B", "A", "Z", "Z", "⚠️F", "D", "G", "G", "G", "⚠️Q"], { + disabled: ["⚠️F", "⚠️Q"], + sort: true, + unique: true, + value: "B", + label: "Choose categories:" + }) +); ``` ```js echo checkout ``` -### Analysis with Observable Inputs +To demonstrate Observable Inputs, let’s look at a sample dataset of athletes from the 2016 Rio olympics via [Matt Riggott](https://flother.is/2017/olympic-games-data/). Here’s a [table input](../inputs/table) — always a good starting point for an agnostic view of the data: -To demonstrate Observable Inputs for data analysis, we’ll use the `olympians` sample dataset containing records on athletes that participated in the 2016 Rio olympics (from [Matt Riggott](https://flother.is/2017/olympic-games-data/)). +```js +const olympians = await d3.csv("https://static.observableusercontent.com/files/31ca24545a0603dce099d10ee89ee5ae72d29fa55e8fc7c9ffb5ded87ac83060d80f1d9e21f4ae8eb04c1e8940b7287d179fe8060d887fb1f055f430e210007c", (d) => (delete d.id, delete d.info, d3.autoType(d))); +``` ```js echo Inputs.table(olympians) ``` -Here, we create a subset of columns to simplify outputs: - -```js echo -const columns = olympians.columns.slice(1, -1); // hide the id and info column to simplify -``` +
Tables can be inputs, too! The value of the table is the subset of rows that you select using the checkboxes in the first column.
-Now let’s wire up our data to a search input. Type whatever you want into the box and search will find matching rows in the data which we can then use in a table below. +Now let’s wire up the table to a [search input](../inputs/search). Type anything into the box and the search input will find the matching rows in the data. The value of the search input is the subset of rows that match the query. -A few examples to try: **[mal]** will match *sex* = male, but also names that start with “mal”, such as Anna Malova; **[1986]** will match anyone born in 1986 (and a few other results); **[USA gym]** will match USA’s gymnastics team. Each space-separated term in your query is prefix-matched against all columns in the data. +A few examples to try: **[mal]** will match _sex_ = male, but also names that start with “mal”, such as Anna Malova; **[1986]** will match anyone born in 1986 (and a few other results); **[USA gym]** will match USA’s gymnastics team. Each space-separated term in your query is prefix-matched against all columns in the data. ```js echo -const search = view(Inputs.search(olympians, { +const searchResults = view(Inputs.search(olympians, { datalist: ["mal", "1986", "USA gym"], placeholder: "Search athletes" })) ``` ```js echo -Inputs.table(search, {columns}) +Inputs.table(searchResults) ``` -If you like, you can sort the table columns by clicking on the column name. Click once to sort ascending, and click again to sort descending. Note that the sort order is temporary: it’ll go away if you reload the page. Specify the column name as the *sort* option above if you want this order to persist. - -For a more structured approach, we can use a select input to choose a sport, then *array*.filter to determine which rows are shown in the table. The *sort* and *unique* options tell the input to show only distinct values and to sort them alphabetically. - -```js echo -const sport = view(Inputs.select(olympians.map(d => d.sport), {sort: true, unique: true, label: "sport"})); -``` +You can sort columns by clicking on the column name: click once to sort ascending, and click again to sort descending. Note that the sort order is temporary: it’ll go away if you reload the page. Specify the column name as the _sort_ option above if you want this order to persist. -```js echo -const selectedAthletes = display(olympians.filter(d => d.sport === sport)); -``` +For a more structured approach, we can use a select input to choose a sport, then _array_.filter to determine which rows are shown in the table. The _sort_ and _unique_ options tell the input to show only distinct values and to sort them alphabetically. Try comparing the **[gymnastics]** and **[basketball]** sports. ```js echo -Inputs.table(selectedAthletes, {columns}) +const sport = view( + Inputs.select( + olympians.filter((d) => d.weight && d.height).map((d) => d.sport), + {sort: true, unique: true, label: "sport"} + ) +); ``` -To visualize a column of data as a histogram, use the value of the select input with [Observable Plot](https://observablehq.com/plot/). - ```js echo Plot.plot({ - x: { - domain: [1.3, 2.2] - }, + title: `How ${sport} athletes compare`, marks: [ - Plot.rectY(selectedAthletes, Plot.binX({y: "count"}, {x: "height", fill: "steelblue"})), - Plot.ruleY([0]) + Plot.dot(olympians, {x: "weight", y: "height"}), + Plot.dot(olympians.filter((d) => d.sport === sport), {x: "weight", y: "height", stroke: "red"}) ] }) ``` -You can also pass grouped data to the select input as a [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) from key to array of values, say using [d3.group](https://d3js.org/d3-array/group). The value of the select input in this mode is the data in the selected group. Note that *unique* is no longer required, and that *sort* works here, too, sorting the keys of the map returned by d3.group. +You can pass grouped data to a [select input](../inputs/select) as a [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map) from key to array of values, say using [d3.group](https://d3js.org/d3-array/group). The value of the select input in this mode is the data in the selected group. Note that _unique_ is no longer required, and that _sort_ works here, too, sorting the keys of the map returned by d3.group. ```js echo -const groups = display(d3.group(olympians, d => d.sport)); +const sportAthletes = view( + Inputs.select( + d3.group(olympians, (d) => d.sport), + {sort: true, label: "sport"} + ) +); ``` ```js echo -const sportAthletes = view(Inputs.select(groups, {sort: true, label: "sport"})); +Inputs.table(sportAthletes) ``` -```js echo -Inputs.table(sportAthletes, {columns}) -``` - -The select input works well for categorical data, such as sports or nationalities, but how about quantitative dimensions such as height or weight? Here’s a range input that lets you pick a target weight; we then filter the table rows for any athlete within 10% of the target weight. Notice that some columns, such as sport, are strongly correlated with weight. +The select input works well for categorical data, such as sports or nationalities, but how about quantitative dimensions such as height or weight? Here’s a [range input](../inputs/range) that lets you pick a target weight; we then filter the table rows for any athlete within 10% of the target weight. Notice that some columns, such as sport, are strongly correlated with weight. ```js echo -const weight = view(Inputs.range(d3.extent(olympians, d => d.weight), {step: 1, label: "weight (kg)"})); +const weight = view( + Inputs.range( + d3.extent(olympians, (d) => d.weight), + {step: 1, label: "weight (kg)"} + ) +); ``` ```js echo -Inputs.table(olympians.filter(d => d.weight < weight * 1.1 && weight * 0.9 < d.weight), {sort: "weight", columns}) -``` - -## Basic inputs - -These basic inputs will get you started. - -* [Button](#button) - do something when a button is clicked -* [Toggle](#toggle) - toggle between two values (on or off) -* [Checkbox](#checkbox) - choose any from a set -* [Radio](#radio) - choose one from a set -* [Range](#range) or [Number](https://observablehq.com/@observablehq/input-range) - choose a number in a range (slider) -* [Select](#select) - choose one or any from a set (drop-down menu) -* [Text](#text) - enter freeform single-line text -* [Textarea](#textarea) - enter freeform multi-line text -* [Date](#date) or [Datetime](https://observablehq.com/@observablehq/input-date) - choose a date -* [Color](#color) - choose a color -* [File](#file) - choose a local file - -These fancy inputs are designed to work with tabular data such as CSV or TSV [file attachments](./files). - -* [Search](#search) - query a tabular dataset -* [Table](#table) - browse a tabular dataset - ---- - -### Button - -Do something when a button is clicked. [Examples ›](https://observablehq.com/@observablehq/input-button) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#button) - -```js echo -const clicks = view(Inputs.button("Click me")); -``` - -```js -clicks -``` - ---- - -### Toggle - -Toggle between two values (on or off). [Examples ›](https://observablehq.com/@observablehq/input-toggle) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#toggle) - -```js echo -const mute = view(Inputs.toggle({label: "Mute"})); -``` - -```js -mute -``` - ---- - -### Checkbox - -Choose any from a set. [Examples ›](https://observablehq.com/@observablehq/input-checkbox) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#checkbox) - -```js echo -const flavors = view(Inputs.checkbox(["salty", "sweet", "bitter", "sour", "umami"], {label: "Flavors"})); -``` - -```js -flavors -``` - ---- - -### Radio - -Choose one from a set. [Examples ›](https://observablehq.com/@observablehq/input-radio) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#radio) - -```js echo -const flavor = view(Inputs.radio(["salty", "sweet", "bitter", "sour", "umami"], {label: "Flavor"})); -``` - -```js -flavor -``` - ---- - -### Range - -Pick a number. [Examples ›](https://observablehq.com/@observablehq/input-range) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#range) - -```js echo -const n = view(Inputs.range([0, 255], {step: 1, label: "Favorite number"})); -``` - -```js -n -``` - ---- - -### Select - -Choose one, or any, from a menu. [Examples ›](https://observablehq.com/@observablehq/input-select) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#select) - -```js -const capitals = FileAttachment("us-state-capitals.tsv").tsv({typed: true}); -const stateNames = capitals.then((capitals) => capitals.map(d => d.State)); -``` - -```js echo -const homeState = view(Inputs.select([null].concat(stateNames), {label: "Home state"})); -``` - -```js -homeState -``` - -```js echo -const visitedStates = view(Inputs.select(stateNames, {label: "Visited states", multiple: true})); -``` - -```js -visitedStates -``` - ---- - -### Text - -Enter freeform single-line text. [Examples ›](https://observablehq.com/@observablehq/input-text) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#text) - -```js echo -const name = view(Inputs.text({label: "Name", placeholder: "What’s your name?"})); -``` - -```js -name -``` - ---- - -### Textarea - -Enter freeform multi-line text. [Examples ›](https://observablehq.com/@observablehq/input-textarea) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#textarea) - -```js echo -const bio = view(Inputs.textarea({label: "Biography", placeholder: "What’s your story?"})); -``` - -```js -bio -``` - ---- - -### Date - -Choose a date, or a date and time. [Examples ›](https://observablehq.com/@observablehq/input-date) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#date) - -```js echo -const birthday = view(Inputs.date({label: "Birthday"})); -``` - -```js -birthday -``` - ---- - -### Color - -Choose a color. [Examples ›](https://observablehq.com/@observablehq/input-color) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#color) - -```js echo -const color = view(Inputs.color({label: "Favorite color", value: "#4682b4"})); -``` - -```js -color -``` - ---- - -### File - -Choose a local file. [Examples ›](https://observablehq.com/@observablehq/input-file) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#file) - -```js echo -const file = view(Inputs.file({label: "CSV file", accept: ".csv", required: true})); -``` - -Once a file has been selected, you can read its contents like so: - -```js echo -const data = display(await file.csv({typed: true})); -``` - ---- - -### Search - -Query a tabular dataset. [Examples ›](https://observablehq.com/@observablehq/input-search) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#search) - -```js echo -const search = view(Inputs.search(capitals, {placeholder: "Search U.S. capitals"})); -``` - -```js -search // see table below! -``` - ---- - -### Table - -Browse a tabular dataset. [Examples ›](https://observablehq.com/@observablehq/input-table) [API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#table) - -```js echo -const rows = view(Inputs.table(search)); -``` - -```js -rows // click a checkbox in the leftmost column +Inputs.table( + olympians.filter((d) => d.weight < weight * 1.1 && weight * 0.9 < d.weight), + {sort: "weight"} +) ``` ---- +For more, see the individual input pages: - +- [Button](../inputs/button) - do something when a button is clicked +- [Toggle](../inputs/toggle) - toggle between two values (on or off) +- [Checkbox](../inputs/checkbox) - choose any from a set +- [Radio](../inputs/radio) - choose one from a set +- [Range](../inputs/range) or [Number](../inputs/range) - choose a number in a range (slider) +- [Select](../inputs/select) - choose one or any from a set (drop-down menu) +- [Text](../inputs/text) - enter freeform single-line text +- [Textarea](../inputs/textarea) - enter freeform multi-line text +- [Date](../inputs/date) or [Datetime](../inputs/date) - choose a date +- [Color](../inputs/color) - choose a color +- [File](../inputs/file) - choose a local file +- [Search](../inputs/search) - query a tabular dataset +- [Table](../inputs/table) - browse a tabular dataset From 7bdf9cadfe12c2e7d209c0830addf4ef23c53338 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 13:05:18 -0800 Subject: [PATCH 04/19] button input --- docs/inputs/button.md | 46 ++++++++++++++++--------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/docs/inputs/button.md b/docs/inputs/button.md index 31d782ec7..5ad87c441 100644 --- a/docs/inputs/button.md +++ b/docs/inputs/button.md @@ -1,44 +1,34 @@ # Button input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#button) +API · Source · The button input emits an *input* event when you click it. Buttons may be used to trigger the evaluation of cells, say to restart an animation. For example, below is an animation that progressively hides a bar. Clicking the button will restart the animation. -The button input emits an *input* event when you click it. Buttons may be used to trigger the evaluation of cells, say to restart an animation. - -For example, below is an animation (using [yield](../javascript/generators)) that progressively hides a bar. - -```js echo -import * as Inputs from "npm:@observablehq/inputs"; -``` + ```js echo -const width = 360; -const height = 20; -const style = "max-width: 100%; border: solid 1px black;"; +const replay = view(Inputs.button("Replay")); ``` -```html - -``` + The code block below references replay, so it will run automatically whenever the replay button is clicked. -```js echo -const replay = view(Inputs.button("Replay")); +```js const canvas = document.querySelector("#canvas"); const context = canvas.getContext("2d"); +context.fillStyle = getComputedStyle(canvas).color; ``` - The code block below references replay, so it will run automatically whenever the replay button is clicked. If you click the button while the animation is still running, the animation will be interrupted and restart from the beginning. - ```js echo -replay; +replay; // run this block when the button is clicked const progress = (function* () { - for (let i = width; i >= 0; --i) { - context.clearRect(0, 0, width, height); - context.fillRect(0, 0, i, height); - yield context.canvas; + for (let i = canvas.width; i >= 0; --i) { + context.clearRect(0, 0, canvas.width, canvas.height); + context.fillRect(0, 0, i, canvas.height); + yield canvas; } })(); ``` +
The progress top-level variable is declared as a generator. This causes the Observable runtime to automatically advance the generator on each animation frame. If you prefer, you could write this animation using a standard requestAnimationFrame loop, but generators are nice because the animation will automatically be interrupted when the code is invalidated.
+ You can also use buttons to count clicks. While the value of a button is often not needed, it defaults to zero and is incremented each time the button is clicked. ```js echo @@ -49,15 +39,13 @@ const clicks = view(Inputs.button("Click me")); clicks ``` -Interpolate input values into Markdown using [inline expressions](../javascript#inline-expressions): - -You have clicked ${clicks} times. +You have clicked ${clicks} times. ```md You have clicked ${clicks} times. ``` -You can change this behavior by specifying the *value* and *reduce* options: *value* is the initial value, and *reduce* is called whenever the button is clicked, being passed the current value and returning the new value. The value of the button below is the last time the button was clicked, or null if the button has not been clicked. +While buttons count clicks by default, you can change the behavior by specifying the *value* and *reduce* options: *value* is the initial value, and *reduce* is called whenever the button is clicked, being passed the current value and returning the new value. The value of the button below is the last time the button was clicked, or null if the button has not been clicked. ```js echo const time = view(Inputs.button("Update", {value: null, reduce: () => new Date})); @@ -83,7 +71,7 @@ const counter = view(Inputs.button([ counter ``` -The first argument to `Inputs.button()` is the contents of the button. It’s not required, but it’s strongly encouraged. +The first argument to `Inputs.button()` is the contents of the button. It’s not required, but is strongly encouraged. ```js echo const x = view(Inputs.button()); @@ -126,4 +114,4 @@ The available button input options are: * *value* - the initial value; defaults to 0 or null if *required* is false. * *reduce* - a function to update the value on click; by default returns *value* + 1. * *width* - the width of the input (not including the label). -* *disabled* - whether input is disabled; defaults to false. \ No newline at end of file +* *disabled* - whether input is disabled; defaults to false. From 92a9b1894ca13100d76cfbb10da3c3573a096ec1 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 13:25:37 -0800 Subject: [PATCH 05/19] checkbox edits --- docs/inputs/button.md | 10 ++--- docs/inputs/checkbox.md | 83 +++++++++++++++++++++++++++-------------- 2 files changed, 59 insertions(+), 34 deletions(-) diff --git a/docs/inputs/button.md b/docs/inputs/button.md index 5ad87c441..1a0301db4 100644 --- a/docs/inputs/button.md +++ b/docs/inputs/button.md @@ -109,9 +109,9 @@ Inputs.button("Copy to clipboard", {value: null, reduce: () => navigator.clipboa The available button input options are: -* *label* - a label; either a string or an HTML element. +* *label* - a label; either a string or an HTML element * *required* - if true, the initial value defaults to undefined. -* *value* - the initial value; defaults to 0 or null if *required* is false. -* *reduce* - a function to update the value on click; by default returns *value* + 1. -* *width* - the width of the input (not including the label). -* *disabled* - whether input is disabled; defaults to false. +* *value* - the initial value; defaults to 0 or null if *required* is false +* *reduce* - a function to update the value on click; by default returns *value* + 1 +* *width* - the width of the input (not including the label) +* *disabled* - whether input is disabled; defaults to false diff --git a/docs/inputs/checkbox.md b/docs/inputs/checkbox.md index 54403f697..a9fc7de5e 100644 --- a/docs/inputs/checkbox.md +++ b/docs/inputs/checkbox.md @@ -1,8 +1,6 @@ # Checkbox input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#checkbox) - -The checkbox input allows the user to choose any of a given set of values. (See the [radio](./radio) input for single-choice.) A checkbox is recommended over a [select](./select) input when the number of values to choose from is small — say, seven or fewer — because all choices will be visible up-front, improving usability. For zero or one choice, see the [toggle](./toggle) input. +API · Source · The checkbox input allows the user to choose any of a given set of values. (See the [radio](./radio) input for single-choice.) A checkbox is recommended over a [select](./select) input when the number of values to choose from is small — say, seven or fewer — because all choices will be visible up-front, improving usability. For zero or one choice, see the [toggle](./toggle) input. The initial value of a checkbox defaults to an empty array. You can override this by specifying the *value* option, which should also be an array (or iterable). @@ -11,11 +9,7 @@ const colors = view(Inputs.checkbox(["red", "green", "blue"], {label: "color"})) ``` ```js echo -colors -``` - -```html echo -
${colors.map(color => html`
`)} +colors ``` A checkbox’s values need not be strings: they can be anything. Specify a *format* function to control how these values are presented to the reader. @@ -31,7 +25,7 @@ const teams = [ ``` ```js echo -const watching = view(Inputs.checkbox(teams, {label: "Watching", format: x => x.name})); +const watching = view(Inputs.checkbox(teams, {label: "Watching", format: (x) => x.name})); ``` ```js echo @@ -51,17 +45,38 @@ vowels The *format* function, like the *label*, can return either a text string or an HTML element. This allows extensive control over the appearance of the checkbox, if desired. ```js echo -const colors2 = view(Inputs.checkbox(["red", "green", "blue"], {value: ["red"], label: html`Colors`, format: x => html`${x}`})); +const colors2 = view( + Inputs.checkbox(["red", "green", "blue"], { + value: ["red"], + label: html`Colors`, + format: (x) => + html`${x}` + }) +); ``` ```js echo colors2 ``` -If the checkbox’s data are specified as a Map, the values will be the map’s values while the keys will be the displayed options. (This behavior can be customized by passing *keyof* and *valueof* function options.) Below, the displayed sizes are named, but the value is the corresponding number of fluid ounces. +If the checkbox’s data are specified as a [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), the values will be the map’s values while the keys will be the displayed options. (This behavior can be customized by passing *keyof* and *valueof* function options.) Below, the displayed sizes are named, but the value is the corresponding number of fluid ounces. ```js echo -const sizes = view(Inputs.checkbox(new Map([["Short", 8], ["Tall", 12], ["Grande", 16], ["Venti", 20]]), {value: [12], label: "Size"})); +const sizes = view( + Inputs.checkbox( + new Map([ + ["Short", 8], + ["Tall", 12], + ["Grande", 16], + ["Venti", 20] + ]), + {value: [12], label: "Size"} + ) +); ``` ```js echo @@ -71,10 +86,17 @@ sizes Since the *format* function is passed elements from the data, it can access both the key and value from the corresponding Map entry. ```js echo -const size2 = view(Inputs.checkbox( - new Map([["Short", 8], ["Tall", 12], ["Grande", 16], ["Venti", 20]]), - {value: [12], label: "Size", format: ([name, value]) => `${name} (${value} oz)`} -)); +const size2 = view( + Inputs.checkbox( + new Map([ + ["Short", 8], + ["Tall", 12], + ["Grande", 16], + ["Venti", 20] + ]), + {value: [12], label: "Size", format: ([name, value]) => `${name} (${value} oz)`} + ) +); ``` ```js echo @@ -84,14 +106,19 @@ size2 Passing a Map to checkbox is especially useful in conjunction with [d3.group](https://d3js.org/d3-array/group). For example, given a the sample `olympians` dataset of Olympic athletes, we can use d3.group to group them by gold medal count, and then checkbox to select the athletes for the chosen count. Note that the value of the checkbox will be an array of arrays, since d3.group returns a Map from key to array; use [*array*.flat](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/flat) to merge these arrays if desired. ```js echo -const goldAthletes = view(Inputs.checkbox(d3.group(olympians, d => d.gold), {label: "Gold medal count", sort: "descending", key: [4, 5]})); +const goldAthletes = view( + Inputs.checkbox( + d3.group(olympians, (d) => d.gold), + {label: "Gold medal count", sort: "descending", key: [4, 5]} + ) +); ``` ```js echo goldAthletes.flat() ``` -If the *sort* and *unique* options are specified, the checkbox’s keys will be sorted and duplicate keys will be discarded, respectively. +If the *sort* and *unique* options are specified, the checkbox’s keys will be sorted and duplicate keys will be discarded, respectively. ```js echo const bases = view(Inputs.checkbox("GATTACA", {sort: true, unique: true})); @@ -107,14 +134,12 @@ bases The available checkbox input options are: -* *label* - a label; either a string or an HTML element. -* *sort* - true, “ascending”, “descending”, or a comparator function to sort keys; defaults to false. -* *unique* - true to only show unique keys; defaults to false. -* *locale* - the current locale; defaults to English. -* *format* - a format function; defaults to [formatLocaleAuto](https://github.com/observablehq/inputs/blob/main/README.md#inputsformatlocaleautolocale) composed with *keyof*. -* *keyof* - a function to return the key for the given element in *data*. -* *valueof* - a function to return the value of the given element in *data*. -* *value* - the initial value, an array; defaults to an empty array (no selection). -* *disabled* - whether input is disabled, or the disabled values; defaults to false. - - \ No newline at end of file +* *label* - a label; either a string or an HTML element +* *sort* - true, *ascending*, *descending*, or a comparator to sort keys; defaults to false +* *unique* - true to only show unique keys; defaults to false +* *locale* - the current locale; defaults to English +* *format* - a format function; defaults to [formatLocaleAuto](https://github.com/observablehq/inputs/blob/main/README.md#inputsformatlocaleautolocale) composed with *keyof* +* *keyof* - a function to return the key for the given element in *data* +* *valueof* - a function to return the value of the given element in *data* +* *value* - the initial value, an array; defaults to an empty array (no selection) +* *disabled* - whether input is disabled, or the disabled values; defaults to false From e53c678bfabc055185b8a9e5bd191bf098878565 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 13:27:21 -0800 Subject: [PATCH 06/19] color input --- docs/inputs/color.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/inputs/color.md b/docs/inputs/color.md index 66c7cc6dd..849d1d2dc 100644 --- a/docs/inputs/color.md +++ b/docs/inputs/color.md @@ -1,8 +1,6 @@ # Color input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#inputscoloroptions) - -The color input specifies an RGB color as a hexadecimal string `#rrggbb`. The initial value defaults to black (`#000000`) and can be specified with the *value* option. +API · Source · The color input specifies an RGB color as a hexadecimal string `#rrggbb`. The initial value defaults to black (`#000000`) and can be specified with the *value* option. ```js echo const color = view(Inputs.color({label: "Favorite color", value: "#4682b4"})); @@ -44,4 +42,4 @@ disabled **Inputs.color(*options*)** -Like [Inputs.text](./text), but where *type* is color. The color value is represented as an RGB hexadecimal string such as #ff00ff. This type of input does not support the following options: *placeholder*, *pattern*, *spellcheck*, *autocomplete*, *autocapitalize*, *min*, *max*, *minlength*, *maxlength*. \ No newline at end of file +Like [Inputs.text](./text), but where *type* is color. The color value is represented as an RGB hexadecimal string such as #ff00ff. This type of input does not support the following options: *placeholder*, *pattern*, *spellcheck*, *autocomplete*, *autocapitalize*, *min*, *max*, *minlength*, *maxlength*. From be63517c888618639f3ba245d0eea850b74031a8 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 13:30:35 -0800 Subject: [PATCH 07/19] date input --- docs/inputs/date.md | 24 +++++++++++------------- observablehq.config.ts | 2 +- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/docs/inputs/date.md b/docs/inputs/date.md index 68a425af0..faa67e483 100644 --- a/docs/inputs/date.md +++ b/docs/inputs/date.md @@ -1,8 +1,6 @@ # Date input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#date) - -The date input specifies a date. +API · Source · The date input specifies a date. ```js echo const date = view(Inputs.date()); @@ -86,15 +84,15 @@ readonly The available date input options are: -* *label* - a label; either a string or an HTML element. -* *value* - the initial value, as a JavaScript Date or formatted as an ISO string (yyyy-mm-dd); defaults to null. -* *min* - [minimum value](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/min) attribute. -* *max* - [maximum value](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/max) attribute. -* *required* - if true, the input must be a valid date. -* *validate* - a function to check whether the text input is valid. -* *width* - the width of the input (not including the label). -* *submit* - whether to require explicit submission before updating; defaults to false. -* *readonly* - whether input is readonly; defaults to false. -* *disabled* - whether input is disabled; defaults to false. +* *label* - a label; either a string or an HTML element +* *value* - the initial value as a [Date](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date) or `YYYY-MM-DD` string; defaults to null +* *min* - [minimum value](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/min) attribute +* *max* - [maximum value](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/max) attribute +* *required* - if true, the input must be a valid date +* *validate* - a function to check whether the text input is valid +* *width* - the width of the input (not including the label) +* *submit* - whether to require explicit submission; defaults to false +* *readonly* - whether input is readonly; defaults to false +* *disabled* - whether input is disabled; defaults to false The value of the input is a Date instance at UTC midnight of the specified date, or null if no (valid) value has been specified. Note that the displayed date format is [based on the browser’s locale](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/date). diff --git a/observablehq.config.ts b/observablehq.config.ts index e5dbbb35d..088c111bb 100644 --- a/observablehq.config.ts +++ b/observablehq.config.ts @@ -41,7 +41,7 @@ export default { {name: "Button", path: "/inputs/button"}, {name: "Checkbox", path: "/inputs/checkbox"}, {name: "Color", path: "/inputs/color"}, - {name: "Date/Datetime", path: "/inputs/date"}, + {name: "Date", path: "/inputs/date"}, {name: "File", path: "/inputs/file"}, {name: "Form", path: "/inputs/form"}, {name: "Radio", path: "/inputs/radio"}, From 6983d88732cde7cb963ec7d081f28e77f1af4ce7 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 13:39:10 -0800 Subject: [PATCH 08/19] file edits --- docs/inputs/file.md | 36 ++++++++++++++++-------------------- 1 file changed, 16 insertions(+), 20 deletions(-) diff --git a/docs/inputs/file.md b/docs/inputs/file.md index ecbfcd0f8..897f9f227 100644 --- a/docs/inputs/file.md +++ b/docs/inputs/file.md @@ -1,8 +1,6 @@ # File input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#file) - -The file input specifies a local file. The exposed value provides the same interface as an Observable [file attachment](../javascript/files) for convenient parsing in various formats such as text, image, JSON, CSV, ZIP, and XLSX; however, the file is not uploaded and is only available temporarily in memory. +API · Source · The file input specifies a local file and is intended for prompting the user to select a file from their own machine. The exposed value provides the same interface as [`FileAttachment`](../javascript/files) for convenient parsing in various formats such as text, image, JSON, CSV, ZIP, and XLSX. By default, any file is allowed, and the value of the input resolves to null. @@ -26,7 +24,7 @@ Once a file has been selected, you can read its contents like so: ```js echo -const data = display(await csvfile.csv({typed: true})); +csvfile.csv({typed: true}) ``` Here are examples of other supported file types. @@ -36,7 +34,7 @@ const jsonfile = view(Inputs.file({label: "JSON file", accept: ".json", required ``` ```js echo -const data = display(await jsonfile.json()); +jsonfile.json() ``` ```js echo @@ -44,7 +42,7 @@ const textfile = view(Inputs.file({label: "Text file", accept: ".txt", required: ``` ```js echo -const data = display(await textfile.text()); +textfile.text() ``` ```js echo @@ -52,7 +50,7 @@ const imgfile = view(Inputs.file({label: "Image file", accept: ".png,.jpg", requ ``` ```js echo -const image = display(await imgfile.image()); +imgfile.image() ``` ```js echo @@ -60,7 +58,7 @@ const xlsxfile = view(Inputs.file({label: "Excel file", accept: ".xlsx", require ``` ```js echo -const workbook = display(await xlsxfile.xlsx()); +xlsxfile.xlsx() ``` ```js echo @@ -68,7 +66,7 @@ const zipfile = view(Inputs.file({label: "ZIP archive", accept: ".zip", required ``` ```js echo -const archive = display(await zipfile.zip()) +zipfile.zip() ``` The *multiple* option allows the user to pick multiple files. In this mode, the exposed value is an array of files instead of a single file. @@ -87,15 +85,13 @@ files The available file input options are: -* *label* - a label; either a string or an HTML element. -* *required* - if true, a valid file must be selected. -* *validate* - a function to check whether the file input is valid. -* *accept* - the [acceptable file types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept). -* *capture* - for [capturing image or video data](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#capture). -* *multiple* - whether to allow multiple files to be selected; defaults to false. -* *width* - the width of the input (not including the label). -* *disabled* - whether input is disabled; defaults to false. - -Note that the value of file input cannot be set programmatically; it can only be changed by the user. +* *label* - a label; either a string or an HTML element +* *required* - if true, a valid file must be selected +* *validate* - a function to check whether the file input is valid +* *accept* - the [acceptable file types](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#accept) +* *capture* - for [capturing image or video data](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/file#capture) +* *multiple* - whether to allow multiple files to be selected; defaults to false +* *width* - the width of the input (not including the label) +* *disabled* - whether input is disabled; defaults to false - +
The value of file input cannot be set programmatically; it can only be changed by the user.
From 99915dc57f091013ab4d5578a9d31fb4cc7fa017 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 13:40:20 -0800 Subject: [PATCH 09/19] form edits --- docs/inputs/form.md | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/docs/inputs/form.md b/docs/inputs/form.md index 0e75a9106..e38250ab3 100644 --- a/docs/inputs/form.md +++ b/docs/inputs/form.md @@ -1,8 +1,6 @@ # Form input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#inputsforminputs-options) - -The form input combines a number of inputs into a single compound input. It’s intended for a more compact display of closely-related inputs, say for a color’s red, green, and blue channels. +API · Source · The form input combines a number of inputs into a single compound input. It’s intended for a more compact display of closely-related inputs, say for a color’s red, green, and blue channels. ```js echo const rgb = view(Inputs.form([ @@ -36,6 +34,6 @@ rgb2 The available form input options are: -* *template* - a function that takes the given *inputs* and returns an HTML element to display. +* *template* - a function that takes the given *inputs* and returns an HTML element -If the *template* object is not specified, the given inputs are wrapped in a DIV. \ No newline at end of file +If the *template* object is not specified, the given inputs are wrapped in a DIV. From 8cfbc475660dcbab2f595777f870bdcf94a3a50b Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 13:43:41 -0800 Subject: [PATCH 10/19] radio edits --- docs/inputs/radio.md | 81 +++++++++++++++++++++++++++++--------------- 1 file changed, 54 insertions(+), 27 deletions(-) diff --git a/docs/inputs/radio.md b/docs/inputs/radio.md index ce86428b5..c8abd0f31 100644 --- a/docs/inputs/radio.md +++ b/docs/inputs/radio.md @@ -1,8 +1,6 @@ # Radio input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#radio) - -The radio input allows the user to choose one of a given set of values. (See the [checkbox](./checkbox) input for multiple-choice.) A radio is recommended over a [select](./select) input when the number of values to choose from is small — say, seven or fewer — because all choices will be visible up-front, improving usability. +API · Source · The radio input allows the user to choose one of a given set of values. (See the [checkbox](./checkbox) input for multiple-choice.) A radio is recommended over a [select](./select) input when the number of values to choose from is small — say, seven or fewer — because all choices will be visible up-front, improving usability. ```js echo const color = view(Inputs.radio(["red", "green", "blue"], {label: "color"})); @@ -12,14 +10,10 @@ const color = view(Inputs.radio(["red", "green", "blue"], {label: "color"})); color ``` -```html echo -
-``` - Note that a radio cannot be cleared by the user once selected; if you wish to allow no selection, include null in the allowed values. ```js echo -const vote = view(Inputs.radio(["Yea", "Nay", null], {value: null, format: x => x ?? "Abstain"})); +const vote = view(Inputs.radio(["Yea", "Nay", null], {value: null, format: (x) => x ?? "Abstain"})); ``` ```js echo @@ -59,17 +53,38 @@ vowel The *format* function, like the *label*, can return either a text string or an HTML element. This allows extensive control over the appearance of the radio, if desired. ```js echo -const color2 = view(Inputs.radio(["red", "green", "blue"], {value: "red", label: html`Color`, format: x => html`${x}`})); +const color2 = view( + Inputs.radio(["red", "green", "blue"], { + value: "red", + label: html`Colors`, + format: (x) => + html`${x}` + }) +); ``` ```js echo color2 ``` -If the radio’s data are specified as a Map, the values will be the map’s values while the keys will be the displayed options. (This behavior can be customized by passing *keyof* and *valueof* function options.) Below, the displayed sizes are named, but the value is the corresponding number of fluid ounces. +If the radio’s data are specified as a [Map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Map), the values will be the map’s values while the keys will be the displayed options. (This behavior can be customized by passing *keyof* and *valueof* function options.) Below, the displayed sizes are named, but the value is the corresponding number of fluid ounces. ```js echo -const size = view(Inputs.radio(new Map([["Short", 8], ["Tall", 12], ["Grande", 16], ["Venti", 20]]), {value: 12, label: "Size"})); +const size = view( + Inputs.radio( + new Map([ + ["Short", 8], + ["Tall", 12], + ["Grande", 16], + ["Venti", 20] + ]), + {value: 12, label: "Size"} + ) +); ``` ```js echo @@ -79,27 +94,39 @@ size Since the *format* function is passed elements from the data, it can access both the key and value from the corresponding Map entry. ```js echo -const size2 = view(Inputs.radio( - new Map([["Short", 8], ["Tall", 12], ["Grande", 16], ["Venti", 20]]), - {value: 12, label: "Size", format: ([name, value]) => `${name} (${value} oz)`} -)); +const size2 = view( + Inputs.radio( + new Map([ + ["Short", 8], + ["Tall", 12], + ["Grande", 16], + ["Venti", 20] + ]), + {value: 12, label: "Size", format: ([name, value]) => `${name} (${value} oz)`} + ) +); ``` ```js echo size2 ``` -Passing a Map to radio is especially useful in conjunction with [d3.group](https://d3js.org/d3-array/group). For example, given a tabular dataset of Olympic athletes (`olympians`), we can use d3.group to group them by gold medal count, and then a radio input to select the athletes for the chosen count. +Passing a Map to radio is especially useful in conjunction with [d3.group](https://d3js.org/d3-array/group). For example, given a tabular dataset of Olympic athletes (`olympians`), we can use d3.group to group them by gold medal count, and then a radio input to select the athletes for the chosen count. ```js echo -const goldAthletes = view(Inputs.radio(d3.group(olympians, d => d.gold), {label: "Gold medal count", sort: "descending"})); +const goldAthletes = view( + Inputs.radio( + d3.group(olympians, (d) => d.gold), + {label: "Gold medal count", sort: "descending"} + ) +); ``` ```js echo goldAthletes ``` -If the *sort* and *unique* options are specified, the radio’s keys will be sorted and duplicate keys will be discarded, respectively. +If the *sort* and *unique* options are specified, the radio’s keys will be sorted and duplicate keys will be discarded, respectively. ```js echo const base = view(Inputs.radio("GATTACA", {sort: true, unique: true})); @@ -115,12 +142,12 @@ base The available radio input options are: -* *label* - a label; either a string or an HTML element. -* *sort* - true, “ascending”, “descending”, or a comparator function to sort keys; defaults to false. -* *unique* - true to only show unique keys; defaults to false. -* *locale* - the current locale; defaults to English. -* *format* - a format function; defaults to [formatLocaleAuto](https://github.com/observablehq/inputs/blob/main/README.md#inputsformatlocaleautolocale) composed with *keyof*. -* *keyof* - a function to return the key for the given element in *data*. -* *valueof* - a function to return the value of the given element in *data*. -* *value* - the initial value; defaults to null (no selection). -* *disabled* - whether input is disabled, or the disabled values; defaults to false. \ No newline at end of file +* *label* - a label; either a string or an HTML element +* *sort* - true, *ascending*, *descending*, or a comparator to sort keys; defaults to false +* *unique* - true to only show unique keys; defaults to false +* *locale* - the current locale; defaults to English +* *format* - a format function; defaults to [formatLocaleAuto](https://github.com/observablehq/inputs/blob/main/README.md#inputsformatlocaleautolocale) composed with *keyof* +* *keyof* - a function to return the key for the given element in *data* +* *valueof* - a function to return the value of the given element in *data* +* *value* - the initial value; defaults to null (no selection) +* *disabled* - whether input is disabled, or the disabled values; defaults to false From f50530d663aafed5a77ac59a6865f76a12815920 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 13:45:35 -0800 Subject: [PATCH 11/19] range edits --- docs/inputs/range.md | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/docs/inputs/range.md b/docs/inputs/range.md index 3a098bf80..d058deba4 100644 --- a/docs/inputs/range.md +++ b/docs/inputs/range.md @@ -1,8 +1,6 @@ # Range input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#range) - -The range input specifies a number between the given *min* and *max* (inclusive). This number can be adjusted roughly by sliding, or precisely by typing. A range input is also known as a slider. +API · Source · The range input specifies a number between the given *min* and *max* (inclusive). This number can be adjusted roughly by sliding, or precisely by typing. A range input is also known as a slider. By default, a range chooses a floating point number between 0 and 1 with full precision, which is often more precision than desired. @@ -14,8 +12,6 @@ const x = view(Inputs.range()); x ``` -The current value of *x* is ${x.toLocaleString("en")}. - The *step* option is strongly encouraged to set the desired precision (the interval between adjacent values). For integers, use *step* = 1. The up and down buttons in the number input will only work if a *step* is specified. To change the extent, pass [*min*, *max*] as the first argument. ```js echo @@ -102,16 +98,16 @@ d The available range input options are: -* *label* - a label; either a string or an HTML element. -* *step* - the step (precision); the interval between adjacent values. -* *format* - a format function; defaults to [formatTrim](https://github.com/observablehq/inputs?tab=readme-ov-file#inputsformattrimnumber). -* *placeholder* - a placeholder string for when the input is empty. -* *transform* - an optional non-linear transform. -* *invert* - the inverse transform. -* *validate* - a function to check whether the number input is valid. -* *value* - the initial value; defaults to (*min* + *max*) / 2. -* *width* - the width of the input (not including the label). -* *disabled* - whether input is disabled; defaults to false. +* *label* - a label; either a string or an HTML element +* *step* - the step (precision); the interval between adjacent values +* *format* - a format function; defaults to [formatTrim](https://github.com/observablehq/inputs?tab=readme-ov-file#inputsformattrimnumber) +* *placeholder* - a placeholder string for when the input is empty +* *transform* - an optional non-linear transform +* *invert* - the inverse transform +* *validate* - a function to check whether the number input is valid +* *value* - the initial value; defaults to (*min* + *max*) / 2 +* *width* - the width of the input (not including the label) +* *disabled* - whether input is disabled; defaults to false The given *value* is clamped to the given extent, and rounded if *step* is defined. However, note that the *min*, *max* and *step* options affect only the slider behavior, the number input’s buttons, and whether the browser shows a warning if a typed number is invalid; they do not constrain the typed number. @@ -119,4 +115,4 @@ If *validate* is not defined, [*number*.checkValidity](https://html.spec.whatwg. The *format* function should return a string value that is compatible with native number parsing. Hence, the default [formatTrim](https://github.com/observablehq/inputs?tab=readme-ov-file#inputsformattrimnumber) is recommended. -If a *transform* function is specified, an inverse transform function *invert* is strongly recommended. If *invert* is not provided, the Range will fallback to Newton’s method, but this may be slow or inaccurate. Passing Math.sqrt, Math.log, or Math.exp as a *transform* will automatically supply the corresponding *invert*. If *min* is greater than *max*, *i.e.* if the extent is inverted, then *transform* and *invert* will default to `value => -value`. \ No newline at end of file +If a *transform* function is specified, an inverse transform function *invert* is strongly recommended. If *invert* is not provided, the Range will fallback to Newton’s method, but this may be slow or inaccurate. Passing Math.sqrt, Math.log, or Math.exp as a *transform* will automatically supply the corresponding *invert*. If *min* is greater than *max*, *i.e.* if the extent is inverted, then *transform* and *invert* will default to `(value) => -value`. From 08d4b7007247b2097163abbd752d65f79932c10d Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 13:54:43 -0800 Subject: [PATCH 12/19] search edits --- docs/inputs/search.md | 72 +++++++++++++++++-------------------------- 1 file changed, 28 insertions(+), 44 deletions(-) diff --git a/docs/inputs/search.md b/docs/inputs/search.md index bc0cdeb3a..186cd7cb0 100644 --- a/docs/inputs/search.md +++ b/docs/inputs/search.md @@ -1,52 +1,36 @@ # Search input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#search) - -The search input allows freeform, full-text search of a tabular dataset (or a single column of values) using a simple query parser. It is often paired with a [table input](./table). +API · Source · The search input allows freeform, full-text search of a tabular dataset (or a single column of values) using a simple query parser. It is often paired with a [table input](./table). By default, the query is split into terms separated by spaces; each term is then prefix-matched against the property values (the fields) of each row in the data. Try searching for “gen” below to find Gentoo penguins. ```js echo -const search = view(Inputs.search(penguins, {placeholder: "Search penguins..."})); +const search = view(Inputs.search(penguins, {placeholder: "Search penguins…"})); ``` ```js echo search ``` -Or, as a table: +Or, as a table: ```js echo Inputs.table(search) ``` - - -If you search for multiple terms, such as “gen bis” (for Gentoos on the Biscoe Islands) or “gen fem” (for female Gentoos), every term must match: there’s an implicit logical AND. - -The search input is designed to work with other [inputs](../javascript/inputs) and especially [tables](./table). You can also refer to the current search results from any cell using a [view](../javascript/inputs#view(element)). For example, to compute the median body mass of the matching penguins: - -```js echo -d3.median(search, d => d.body_mass_g) -``` +If you search for multiple terms, such as “gen bis” (for Gentoos on the Biscoe Islands) or “gen fem” (for female Gentoos), every term must match: there’s an implicit logical AND. If you’d like different search syntax or behavior, pass the *filter* option. This function is passed the current search query and returns the [filter test function](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter) to be applied to the data. @@ -56,19 +40,19 @@ If you’d like different search syntax or behavior, pass the *filter* option. T The available search input options are: -* *label* - a label; either a string or an HTML element. -* *query* - the initial search terms; defaults to the empty string. -* *placeholder* - a placeholder string for when the query is empty. -* *columns* - an array of columns to search; defaults to *data*.columns. -* *locale* - the current locale; defaults to English. -* *format* - a function to show the number of results. -* *spellcheck* - whether to activate the browser’s spell-checker. -* *autocomplete* - the [autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) attribute, as text or boolean (true for on, false for off). -* *autocapitalize* - the [autocapitalize](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autocapitalize) attribute, as text or boolean (true for on, false for off). -* *filter* - the filter factory: a function that receives the query and returns a filter. -* *width* - the width of the input (not including the label). -* *datalist* - an iterable of suggested values. -* *disabled* - whether input is disabled; defaults to false. -* *required* - if true, the search’s value is all *data* if no query; defaults to true. - -If a *filter* function is specified, it is invoked whenever the query changes; the function it returns is then passed each element from *data*, along with its zero-based index, and should return a truthy value if the given element matches the query. The default filter splits the current query into space-separated tokens and checks that each token matches the beginning of at least one string in the data’s columns, case-insensitive. For example, the query [hello world] will match the string “Worldwide Hello Services” but not “hello”. \ No newline at end of file +* *label* - a label; either a string or an HTML element +* *query* - the initial search terms; defaults to the empty string +* *placeholder* - a placeholder string for when the query is empty +* *columns* - an array of columns to search; defaults to *data*.columns +* *locale* - the current locale; defaults to English +* *format* - a function to show the number of results +* *spellcheck* - whether to activate the browser’s spell-checker +* *autocomplete* - the [autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) attribute, as text or boolean +* *autocapitalize* - the [autocapitalize](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autocapitalize) attribute, as text or boolean +* *filter* - the filter factory: a function that receives the query and returns a filter +* *width* - the width of the input (not including the label) +* *datalist* - an iterable of suggested values +* *disabled* - whether input is disabled; defaults to false +* *required* - if true, the search’s value is all *data* if no query; defaults to true + +If a *filter* function is specified, it is invoked whenever the query changes; the function it returns is then passed each element from *data*, along with its zero-based index, and should return a truthy value if the given element matches the query. The default filter splits the current query into space-separated tokens and checks that each token matches the beginning of at least one string in the data’s columns, case-insensitive. For example, the query [hello world] will match the string “Worldwide Hello Services” but not “hello”. From 0a9cb6d01302585c3a74902ce1abaaa3e59a4a89 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 13:58:01 -0800 Subject: [PATCH 13/19] select edits --- docs/inputs/select.md | 94 +++++++++++++++++++++++++++---------------- 1 file changed, 59 insertions(+), 35 deletions(-) diff --git a/docs/inputs/select.md b/docs/inputs/select.md index dd81012c3..33270ccb1 100644 --- a/docs/inputs/select.md +++ b/docs/inputs/select.md @@ -1,8 +1,6 @@ # Select input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#select) - -The select input allows the user to choose from a given set of values. A select is recommended over a [radio](./radio) or [checkbox](./checkbox) input when the number of values to choose from is large — say, eight or more — to save space. +API · Source · The select input allows the user to choose from a given set of values. A select is recommended over a [radio](./radio) or [checkbox](./checkbox) input when the number of values to choose from is large — say, eight or more — to save space. The default appearance of a select is a drop-down menu that allows you to choose a single value. The initial value is the first of the allowed values, but you can override this by specifying the *value* option. @@ -18,10 +16,6 @@ const color = view(Inputs.select(x11colors, {value: "steelblue", label: "Favorit color ``` -```html echo -
-``` - If the *multiple* option is true, the select will allow multiple values to be selected and the value of the select will be the array of selected values. The initial value is the empty array. You can choose a range of values by dragging or Shift-clicking, and select or deselect a value by Command-clicking. ```js echo @@ -32,16 +26,16 @@ const colors = view(Inputs.select(x11colors, {multiple: true, label: "Favorite c colors ``` -```html echo -
${colors.map(color => html`
${color}`)} -``` - The *multiple* option can also be a number indicating the desired size: the number of rows to show. If *multiple* is true, the size defaults to the number of allowed values, or ten, whichever is fewer. ```js echo const fewerColors = view(Inputs.select(x11colors, {multiple: 4, label: "Favorite colors"})); ``` +```js echo +fewerColors +``` + For single-choice selects, if you wish to allow no choice to be selected, we recommend including null as an explicit option. ```js echo @@ -65,7 +59,13 @@ const teams = [ ``` ```js echo -const favorite = view(Inputs.select(teams, {label: "Favorite team", format: x => x.name, value: teams.find(t => t.name === "Warriors")})); +const favorite = view( + Inputs.select(teams, { + label: "Favorite team", + format: (t) => t.name, + value: teams.find((t) => t.name === "Warriors") + }) +); ``` ```js echo @@ -75,7 +75,17 @@ favorite If the select’s data are specified as a Map, the values will be the map’s values while the keys will be the displayed options. (This behavior can be customized by passing *keyof* and *valueof* function options.) Below, the displayed sizes are named, but the value is the corresponding number of fluid ounces. ```js echo -const size = view(Inputs.select(new Map([["Short", 8], ["Tall", 12], ["Grande", 16], ["Venti", 20]]), {value: 12, label: "Size"})); +const size = view( + Inputs.select( + new Map([ + ["Short", 8], + ["Tall", 12], + ["Grande", 16], + ["Venti", 20] + ]), + {value: 12, label: "Size"} + ) +); ``` ```js echo @@ -85,7 +95,17 @@ size Since the *format* function is passed elements from the data, it can access both the key and value from the corresponding Map entry. ```js echo -const size2 = view(Inputs.select(new Map([["Short", 8], ["Tall", 12], ["Grande", 16], ["Venti", 20]]), {value: 12, label: "Size", format: ([name, value]) => `${name} (${value} oz)`})); +const size2 = view( + Inputs.select( + new Map([ + ["Short", 8], + ["Tall", 12], + ["Grande", 16], + ["Venti", 20] + ]), + {value: 12, label: "Size", format: ([name, value]) => `${name} (${value} oz)`} + ) +); ``` ```js echo @@ -95,17 +115,27 @@ size2 Passing a Map to select is especially useful in conjunction with [d3.group](https://d3js.org/d3-array/group). For example, given a tabular dataset of Olympic athletes (`olympians`), we can use d3.group to group them by sport, and then the select input to select only athletes for the chosen sport. ```js echo -const sportAthletes = view(Inputs.select(d3.group(olympians, d => d.sport), {label: "Sport"})); +const sportAthletes = view( + Inputs.select( + d3.group(olympians, (d) => d.sport), + {label: "Sport"} + ) +); ``` ```js echo sportAthletes ``` -If the *sort* and *unique* options are specified, the select’s keys will be sorted and duplicate keys will be discarded, respectively. +If the *sort* and *unique* options are specified, the select’s keys will be sorted and duplicate keys will be discarded, respectively. ```js echo -const sport = view(Inputs.select(olympians.map(d => d.sport), {label: "Sport", sort: true, unique: true})); +const sport = view( + Inputs.select( + olympians.map((d) => d.sport), + {label: "Sport", sort: true, unique: true} + ) +); ``` ```js echo @@ -128,21 +158,15 @@ Inputs.select(["A", "E", "I", "O", "U", "Y"], {label: "Vowel", disabled: ["Y"]}) The available select input options are: -* *label* - a label; either a string or an HTML element. -* *multiple* - whether to allow multiple choice; defaults to false. -* *size* - if *multiple* is true, the number of options to show. -* *sort* - true, “ascending”, “descending”, or a comparator function to sort keys; defaults to false. -* *unique* - true to only show unique keys; defaults to false. -* *locale* - the current locale; defaults to English. -* *format* - a format function; defaults to [formatLocaleAuto](https://github.com/observablehq/inputs/blob/main/README.md#inputsformatlocaleautolocale) composed with *keyof*. -* *keyof* - a function to return the key for the given element in *data*. -* *valueof* - a function to return the value of the given element in *data*. -* *value* - the initial value, an array if multiple choice is allowed. -* *width* - the width of the input (not including the label). -* *disabled* - whether input is disabled, or the disabled values; defaults to false. - - - - - - +* *label* - a label; either a string or an HTML element +* *multiple* - whether to allow multiple choice; defaults to false +* *size* - if *multiple* is true, the number of options to show +* *sort* - true, *ascending*, *descending*, or a comparator to sort keys; defaults to false +* *unique* - true to only show unique keys; defaults to false +* *locale* - the current locale; defaults to English +* *format* - a format function; defaults to [formatLocaleAuto](https://github.com/observablehq/inputs/blob/main/README.md#inputsformatlocaleautolocale) composed with *keyof* +* *keyof* - a function to return the key for the given element in *data* +* *valueof* - a function to return the value of the given element in *data* +* *value* - the initial value, an array if multiple choice is allowed +* *width* - the width of the input (not including the label) +* *disabled* - whether input is disabled, or the disabled values; defaults to false From 55a420174ba7e2eba94055b0be434167b2cadb0b Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 14:03:42 -0800 Subject: [PATCH 14/19] table edits --- docs/inputs/table.md | 78 ++++++++++++++++++++++---------------------- 1 file changed, 39 insertions(+), 39 deletions(-) diff --git a/docs/inputs/table.md b/docs/inputs/table.md index 718839956..93dec0603 100644 --- a/docs/inputs/table.md +++ b/docs/inputs/table.md @@ -1,8 +1,6 @@ # Table input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#table) - -The table input displays tabular data. It’s fast: rows are rendered lazily on scroll. It sorts: click a header to sort, and click again to reverse. And it selects: click a checkbox on any row, and the selected rows are exported as a view value. (And for searching, see the [search input](./search).) +API · Source · The table input displays tabular data. It’s fast: rows are rendered lazily on scroll. It sorts: click a header to sort, and click again to reverse. And it selects: click a checkbox on any row, and the selected rows are exported as a view value. (And for searching, see the [search input](./search).) By default, all columns are visible. Only the first dozen rows are initially visible, but you can scroll to see more. Column headers are fixed for readability. @@ -72,8 +70,8 @@ column. - Numbers are formatted using [_number_.toLocaleString](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Number/toLocaleString); - Dates are formatted in [ISO8601](https://en.wikipedia.org/wiki/ISO_8601); -- Undefined and NaN values are empty; -- Everything else is displayed as-is. +- undefined and NaN values are empty; +- everything else is displayed as-is. To override the default formatting, pass _format_ options for the desired columns. @@ -81,14 +79,14 @@ columns. ```js echo Inputs.table(penguins, { format: { - culmen_length_mm: x => x.toFixed(1), - culmen_depth_mm: x => x.toFixed(1), - sex: x => x === "MALE" ? "M" : x === "FEMALE" ? "F" : "" + culmen_length_mm: (x) => x.toFixed(1), + culmen_depth_mm: (x) => x.toFixed(1), + sex: (x) => x === "MALE" ? "M" : x === "FEMALE" ? "F" : "" } }) ``` -The _format_ function can return a text string or an HTML element. +The _format_ function can return a text string or an HTML element. For example, this can be used to render inline visualizations such as bars or [sparklines](https://observablehq.com/@mbostock/covid-cases-by-state). ```js echo @@ -98,22 +96,24 @@ Inputs.table(penguins, { culmen_depth_mm: sparkbar(d3.max(penguins, d => d.culmen_depth_mm)), flipper_length_mm: sparkbar(d3.max(penguins, d => d.flipper_length_mm)), body_mass_g: sparkbar(d3.max(penguins, d => d.body_mass_g)), - sex: x => x.toLowerCase() + sex: (x) => x.toLowerCase() } }) ``` ```js echo function sparkbar(max) { - return x => htl.html`
${x.toLocaleString("en")}` + justify-content: end;">${x.toLocaleString("en-US")}` } ``` @@ -163,29 +163,29 @@ Inputs.table(penguins, { You can preselect some rows in the table by setting the _value_ option to an array of references to the actual objects in your data. -For example, to preselect the first two items, you could write: +For example, to preselect the first two items, you could set _value_ to: -```js echo run=false -{ value: penguins.slice(0, 2)} +```js echo +penguins.slice(0, 2) ``` -or, if you just want to preselect the rows 1, 3, 7 and 9: +or, if you want to preselect the rows 1, 3, 7 and 9: -```js echo run=false -{ value: penguins.filter((_,i)=> [1, 3, 7, 9].includes(i))} +```js echo +[1, 3, 7, 9].map((i) => penguins[i]) ``` The _multiple_ option allows multiple rows to be selected (defaults to true). When false, only one row can be selected. To set the initial value in that case, just reference the preselected object: -```js echo run=false -{ multiple: false, value: penguins[4] } +```js echo +penguins[4] ``` ```js echo Inputs.table(penguins, { - value: penguins.filter((_, i) => [1, 3, 7, 9].includes(i)), + value: [1, 3, 7, 9].map((i) => penguins[i]), multiple: true }) ``` @@ -198,20 +198,20 @@ Thanks to [Ilyá Belsky](https://observablehq.com/@oluckyman) and [Brett Cooper] The available table input options are: -* *columns* - the columns (property names) to show; defaults to *data*.columns. -* *value* - a subset of *data* to use as the initial selection (checked rows), or a *data* item if *multiple* is false. -* *rows* - the maximum number of rows to show; defaults to 11.5. -* *sort* - the column to sort by; defaults to null (input order). -* *reverse* - whether to reverse the initial sort (descending instead of ascending). -* *format* - an object of column name to format function. -* *align* - an object of column name to “left”, “right”, or “center”. -* *header* - an object of column name to corresponding header; either a string or HTML element. -* *width* - the table width, or an object of column name to width. -* *maxWidth* - the maximum table width, if any. -* *height* - the fixed table height, if any. -* *maxHeight* - the maximum table height, if any; defaults to (*rows* + 1) * 22 - 1. -* *layout* - the [table layout](https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout); defaults to fixed for ≤12 columns. -* *required* - if true, the table’s value is all *data* if no selection; defaults to true. -* *multiple* - if true, allow multiple rows to be selected; defaults to true. - -If *width* is “auto”, the table width will be based on the table contents; note that this may cause the table to resize as rows are lazily rendered. \ No newline at end of file +* *columns* - the columns (property names) to show; defaults to *data*.columns +* *value* - a subset of *data* to use as the initial selection (checked rows), or a *data* item if *multiple* is false +* *rows* - the maximum number of rows to show; defaults to 11.5 +* *sort* - the column to sort by; defaults to null (input order) +* *reverse* - whether to reverse the initial sort (descending instead of ascending) +* *format* - an object of column name to format function +* *align* - an object of column name to *left*, *right*, or *center* +* *header* - an object of column name to corresponding header; either a string or HTML element +* *width* - the table width, or an object of column name to width +* *maxWidth* - the maximum table width, if any +* *height* - the fixed table height, if any +* *maxHeight* - the maximum table height, if any; defaults to (*rows* + 1) * 22 - 1 +* *layout* - the [table layout](https://developer.mozilla.org/en-US/docs/Web/CSS/table-layout); defaults to fixed for ≤12 columns +* *required* - if true, the table’s value is all *data* if no selection; defaults to true +* *multiple* - if true, allow multiple rows to be selected; defaults to true + +If *width* is *auto*, the table width will be based on the table contents; note that this may cause the table to resize as rows are lazily rendered. From f9c43cdbb7dc21b8e93dc4689121cca9b5a7768f Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 14:07:33 -0800 Subject: [PATCH 15/19] text edits --- docs/inputs/text.md | 81 ++++++++++++++++++++++++--------------------- 1 file changed, 44 insertions(+), 37 deletions(-) diff --git a/docs/inputs/text.md b/docs/inputs/text.md index 915840bcd..adf49368f 100644 --- a/docs/inputs/text.md +++ b/docs/inputs/text.md @@ -1,8 +1,6 @@ # Text input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#text) - -The text input allows freeform single-line text entry. (For multiple lines, see the [text area](./textarea)) input. +API · Source · The text input allows freeform single-line text entry. For multiple lines, see the [text area](./textarea) input. In its most basic form, a text input is a blank box whose value is the empty string. The text’s value changes as the user types into the box. @@ -17,7 +15,13 @@ text We recommend providing a *label* and *placeholder* to improve usability. You can also supply an initial *value* if desired. ```js echo -const name = view(Inputs.text({label: "Name", placeholder: "Enter your name", value: "Anonymous"})); +const name = view( + Inputs.text({ + label: "Name", + placeholder: "Enter your name", + value: "Anonymous" + }) +); ``` ```js echo @@ -27,7 +31,12 @@ name The *label* may be either a text string or an HTML element, if more control over styling is desired. ```js echo -const signature = view(Inputs.text({label: html`Fancy`, placeholder: "What’s your fancy?"})); +const signature = view( + Inputs.text({ + label: html`Fancy`, + placeholder: "What’s your fancy?" + }) +); ``` For specific classes of text, such as email addresses and telephone numbers, you can supply one of the [HTML5 input types](https://developer.mozilla.org/en-US/docs/Learn/Forms/HTML5_input_types), such as email, tel (for a telephone number), or url, as the *type* option. Or, use a convenience method: Inputs.email, Inputs.password, Inputs.tel, or Inputs.url. @@ -40,20 +49,18 @@ const password = view(Inputs.password({label: "Password", value: "open sesame"}) password ``` -The HTML5 *pattern*, *spellcheck*, *minlength*, and *maxlength* options are also supported. If the user enters invalid input, the browser may display a warning (e.g., “Enter an email address”). You can also check whether the current value is valid by calling [*form*.checkValidity](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-checkvalidity). - -```js echo -const email = view(Inputs.text({type: "email", label: "Email", placeholder: "Enter your email"})); -``` - - - If the input will trigger some expensive calculation, such as fetching from a remote server, the *submit* option can be used to defer the text input from reporting the new value until the user clicks the Submit button or hits Enter. The value of *submit* can also be the desired contents of the submit button (a string or HTML). ```js echo @@ -74,7 +81,7 @@ const capitals = FileAttachment("us-state-capitals.tsv").tsv({typed: true}); const state = view(Inputs.text({ label: "U.S. state", placeholder: "Enter state name", - datalist: capitals.map(d => d.State) + datalist: capitals.map((d) => d.State) })); ``` @@ -98,24 +105,24 @@ fixed The available text input options are: -* *label* - a label; either a string or an HTML element. -* *type* - the [input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types), such as “password” or “email”; defaults to “text”. -* *value* - the initial value; defaults to the empty string. -* *placeholder* - the [placeholder](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/placeholder) attribute. -* *spellcheck* - whether to activate the browser’s spell-checker. -* *autocomplete* - the [autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) attribute, as text or boolean (true for on, false for off). -* *autocapitalize* - the [autocapitalize](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autocapitalize) attribute, as text or boolean (true for on, false for off). -* *pattern* - the [pattern](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern) attribute. -* *minlength* - [minimum length](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/minlength) attribute. -* *maxlength* - [maximum length](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/maxlength) attribute. -* *min* - [minimum value](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/min) attribute; formatted appropriately, *e.g.* yyyy-mm-dd for the date type. -* *max* - [maximum value](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/max) attribute. -* *required* - if true, the input must be non-empty; defaults to *minlength* > 0. -* *validate* - a function to check whether the text input is valid. -* *width* - the width of the input (not including the label). -* *submit* - whether to require explicit submission before updating; defaults to false. -* *datalist* - an iterable of suggested values. -* *readonly* - whether input is readonly; defaults to false. -* *disabled* - whether input is disabled; defaults to false. - -If *validate* is not defined, [*text*.checkValidity](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-checkvalidity) is used. While the input is not considered valid, changes to the input will not be reported. \ No newline at end of file +* *label* - a label; either a string or an HTML element +* *type* - the [input type](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#input_types), such as “password” or “email”; defaults to “text” +* *value* - the initial value; defaults to the empty string +* *placeholder* - the [placeholder](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/placeholder) attribute +* *spellcheck* - whether to activate the browser’s spell-checker +* *autocomplete* - the [autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) attribute, as text or boolean +* *autocapitalize* - the [autocapitalize](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autocapitalize) attribute, as text or boolean +* *pattern* - the [pattern](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/pattern) attribute +* *minlength* - [minimum length](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/minlength) attribute +* *maxlength* - [maximum length](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/maxlength) attribute +* *min* - [minimum value](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/min) attribute (`YYYY-MM-DD` for the date type) +* *max* - [maximum value](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/max) attribute +* *required* - if true, the input must be non-empty; defaults to *minlength* > 0 +* *validate* - a function to check whether the text input is valid +* *width* - the width of the input (not including the label) +* *submit* - whether to require explicit submission; defaults to false +* *datalist* - an iterable of suggested values +* *readonly* - whether input is readonly; defaults to false +* *disabled* - whether input is disabled; defaults to false + +If *validate* is not defined, [*text*.checkValidity](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-checkvalidity) is used. While the input is not considered valid, changes to the input will not be reported. From 2ca8a55caa5ee6a2903e9091936827ccab5d9d75 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 14:08:46 -0800 Subject: [PATCH 16/19] textarea edits --- docs/inputs/textarea.md | 42 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/docs/inputs/textarea.md b/docs/inputs/textarea.md index 129aa5dc9..895e0f445 100644 --- a/docs/inputs/textarea.md +++ b/docs/inputs/textarea.md @@ -1,8 +1,6 @@ # Text area input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#textarea) - -The textarea input allows freeform multi-line text entry. (For a single line, see the [text](./text) input). +API · Source · The textarea input allows freeform multi-line text entry. For a single line, see the [text](./text) input. In its most basic form, a textarea is a blank box whose value is the empty string. The textarea’s value changes as the user types into the box. @@ -34,7 +32,7 @@ const essay = view(Inputs.textarea({label: "Essay", rows: 6, minlength: 40, subm essay ``` -The HTML5 *spellcheck*, *minlength*, and *maxlength* options are supported. If the user enters invalid input, the browser may display a warning (e.g., “Use at least 80 characters”). You can also check whether the current value is valid by calling [*form*.checkValidity](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-checkvalidity). +The HTML5 *spellcheck*, *minlength*, and *maxlength* options are supported. If the user enters invalid input, the browser may display a warning (e.g., “Use at least 80 characters”). To prevent a textarea’s value from being changed, use the *disabled* option. @@ -46,28 +44,28 @@ const fixed = view(Inputs.textarea({label: "Fixed value", value: "Can’t edit m fixed ``` -## Options +## Options **Inputs.textarea(*options*)** The available text area options are: -* *label* - a label; either a string or an HTML element. -* *value* - the initial value; defaults to the empty string. -* *placeholder* - the [placeholder](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/placeholder) attribute. -* *spellcheck* - whether to activate the browser’s spell-checker. -* *autocomplete* - the [autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) attribute, as text or boolean (true for on, false for off). -* *autocapitalize* - the [autocapitalize](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autocapitalize) attribute, as text or boolean (true for on, false for off). -* *minlength* - [minimum length](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/minlength) attribute. -* *maxlength* - [maximum length](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/maxlength) attribute. -* *required* - if true, the input must be non-empty; defaults to *minlength* > 0. -* *validate* - a function to check whether the text input is valid. -* *width* - the width of the input (not including the label). -* *rows* - the number of rows of text to show. -* *resize* - if true, allow vertical resizing; defaults to *rows* < 12. -* *submit* - whether to require explicit submission before updating; defaults to false. -* *readonly* - whether input is readonly; defaults to false. -* *disabled* - whether input is disabled; defaults to false. -* *monospace* - if true, use a monospace font. +* *label* - a label; either a string or an HTML element +* *value* - the initial value; defaults to the empty string +* *placeholder* - the [placeholder](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/placeholder) attribute +* *spellcheck* - whether to activate the browser’s spell-checker +* *autocomplete* - the [autocomplete](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/autocomplete) attribute, as text or boolean +* *autocapitalize* - the [autocapitalize](https://developer.mozilla.org/en-US/docs/Web/HTML/Global_attributes/autocapitalize) attribute, as text or boolean +* *minlength* - [minimum length](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/minlength) attribute +* *maxlength* - [maximum length](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/maxlength) attribute +* *required* - if true, the input must be non-empty; defaults to *minlength* > 0 +* *validate* - a function to check whether the text input is valid +* *width* - the width of the input (not including the label) +* *rows* - the number of rows of text to show +* *resize* - if true, allow vertical resizing; defaults to *rows* < 12 +* *submit* - whether to require explicit submission; defaults to false +* *readonly* - whether input is readonly; defaults to false +* *disabled* - whether input is disabled; defaults to false +* *monospace* - if true, use a monospace font If *validate* is not defined, [*text*.checkValidity](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#dom-cva-checkvalidity) is used. While the input is not considered valid, changes to the input will not be reported. From c684da76b00f0b7f4cdb58f78b47eefa39e6f01b Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 14:09:39 -0800 Subject: [PATCH 17/19] toggle edits --- docs/inputs/toggle.md | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/docs/inputs/toggle.md b/docs/inputs/toggle.md index b11b3772d..65cef8ef5 100644 --- a/docs/inputs/toggle.md +++ b/docs/inputs/toggle.md @@ -1,8 +1,6 @@ # Toggle input -[API Reference ›](https://github.com/observablehq/inputs/blob/main/README.md#toggle) - -The toggle input allows the user to choose one of two values, representing on or off. It is a specialized form of the [checkbox input](./checkbox). +API · Source · The toggle input allows the user to choose one of two values, representing on or off. It is a specialized form of the [checkbox input](./checkbox). The initial value of a toggle defaults to false. You can override this by specifying the *value* option. @@ -50,7 +48,7 @@ frozen The available toggle input options are: -* *label* - a label; either a string or an HTML element. -* *values* - the two values to toggle between; defaults to [true, false]. -* *value* - the initial value; defaults to the second value (false). -* *disabled* - whether input is disabled; defaults to false. \ No newline at end of file +* *label* - a label; either a string or an HTML element +* *values* - the two values to toggle between; defaults to [true, false] +* *value* - the initial value; defaults to the second value (false) +* *disabled* - whether input is disabled; defaults to false From 92c49bd5d1991dab3d4d17f1fc051b03bb9958c3 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 14:17:00 -0800 Subject: [PATCH 18/19] tweak page titles --- docs/lib/dot.md | 2 +- docs/lib/xlsx.md | 2 +- observablehq.config.ts | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/lib/dot.md b/docs/lib/dot.md index 42da63a6b..80f19e246 100644 --- a/docs/lib/dot.md +++ b/docs/lib/dot.md @@ -1,4 +1,4 @@ -# DOT +# DOT (Graphviz) [DOT](https://graphviz.org/doc/info/lang.html) is a language for expressing node-link diagrams using [Graphviz](https://graphviz.org). Observable provides a `dot` tagged template literal powered by [Viz.js](https://github.com/mdaines/viz-js). This is available by default in Markdown, or you can import it like so: diff --git a/docs/lib/xlsx.md b/docs/lib/xlsx.md index d55743f17..3c6ebba43 100644 --- a/docs/lib/xlsx.md +++ b/docs/lib/xlsx.md @@ -1,4 +1,4 @@ -# XLSX +# Microsoft Excel (XLSX) [`FileAttachment`](../javascript/files) supports the [Microsoft Excel Open XML format](https://en.wikipedia.org/wiki/Office_Open_XML) via the `file.xlsx` method. This is implemented using the MIT-licensed [ExcelJS](https://github.com/exceljs/exceljs) library. diff --git a/observablehq.config.ts b/observablehq.config.ts index 088c111bb..11d542ec2 100644 --- a/observablehq.config.ts +++ b/observablehq.config.ts @@ -69,7 +69,7 @@ export default { {name: "Lodash", path: "/lib/lodash"}, {name: "Mapbox GL JS", path: "/lib/mapbox-gl"}, {name: "Mermaid", path: "/lib/mermaid"}, - {name: "Microsoft Excel", path: "/lib/xlsx"}, + {name: "Microsoft Excel (XLSX)", path: "/lib/xlsx"}, {name: "Observable Generators", path: "/lib/generators"}, {name: "Observable Inputs", path: "/lib/inputs"}, {name: "Observable Plot", path: "/lib/plot"}, From 1cb27273b2abfc51bfaf8981d2f821ada3be3073 Mon Sep 17 00:00:00 2001 From: Mike Bostock Date: Wed, 14 Feb 2024 14:19:27 -0800 Subject: [PATCH 19/19] sponge for light hero --- docs/index.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/index.md b/docs/index.md index f9604e6e7..58955e6f9 100644 --- a/docs/index.md +++ b/docs/index.md @@ -98,6 +98,12 @@ index: false margin-left: 0.25rem; } +@media (prefers-color-scheme: light) { + h1 { + --theme-red: #d75c48; + } +} +