diff --git a/examples/google-analytics/docs/components/bigNumber.js b/examples/google-analytics/docs/components/bigNumber.js deleted file mode 100644 index 95156515c..000000000 --- a/examples/google-analytics/docs/components/bigNumber.js +++ /dev/null @@ -1,33 +0,0 @@ -import * as d3 from "npm:d3"; -import {html} from "npm:htl"; - -export function BigNumber( - number, - { - href, - target = "_blank", - title, - format = ",.2~f", - trend, - trendFormat = "+.1~%", - trendColor = trend > 0 ? "green" : trend < 0 ? "red" : "orange", - trendArrow = trend > 0 ? "↗︎" : trend < 0 ? "↘︎" : "→", - plot - } = {} -) { - if (typeof format !== "function") format = typeof number === "string" ? String : d3.format(format); - if (typeof trendFormat !== "function") trendFormat = d3.format(trendFormat); - const div = html`
-
${title}
-
-
${format(number)}
- ${ - trend == null - ? null - : html`
${trendFormat(trend)} ${trendArrow}
` - } -
- ${plot} -
`; - return href == null ? div : html`${div}`; -} diff --git a/examples/google-analytics/docs/components/dailyPlot.js b/examples/google-analytics/docs/components/dailyPlot.js index 48d67abe5..293940c37 100644 --- a/examples/google-analytics/docs/components/dailyPlot.js +++ b/examples/google-analytics/docs/components/dailyPlot.js @@ -1,5 +1,5 @@ -import * as d3 from "npm:d3"; import * as Plot from "npm:@observablehq/plot"; +import * as d3 from "npm:d3"; export const today = d3.utcDay(d3.utcHour.offset(d3.utcHour(), -10)); export const start = d3.utcYear.offset(today, -2); @@ -16,7 +16,7 @@ export function DailyPlot(data, {title, label = title, domain, width, height = 2 y: { grid: true, domain, - label: label + label }, marks: [ Plot.axisY({ diff --git a/examples/google-analytics/docs/components/trend.js b/examples/google-analytics/docs/components/trend.js new file mode 100644 index 000000000..53ba00142 --- /dev/null +++ b/examples/google-analytics/docs/components/trend.js @@ -0,0 +1,31 @@ +import * as d3 from "npm:d3"; +import {html} from "npm:htl"; + +export function trend( + value /*: number */, + { + format = "+d", + positive = "green", + negative = "red", + base = "muted", + positiveSuffix = " ↗︎", + negativeSuffix = " ↘︎", + baseSuffix = "" + } = {} /* + as { + format: string | ((x: number) => string); + positive: string; + negative: string; + base: string; + positiveSuffix: string; + negativeSuffix: string; + baseSuffix: string; + } + */ +) /*: Node */ { + if (typeof format === "string") format = d3.format(format); + if (typeof format !== "function") throw new Error(`unsupported format ${format}`); + return html`${format(value)}${ + value > 0 ? positiveSuffix : value < 0 ? negativeSuffix : baseSuffix + }`; +} diff --git a/examples/google-analytics/docs/index.md b/examples/google-analytics/docs/index.md index a7471ba07..f4450e9a5 100644 --- a/examples/google-analytics/docs/index.md +++ b/examples/google-analytics/docs/index.md @@ -3,7 +3,6 @@ theme: dashboard --- ```js -// Data const summary = FileAttachment("data/google-analytics-summary.csv").csv({typed: true}); const hourly = FileAttachment("data/google-analytics-time-of-day.csv").csv({typed: true}); const channels = FileAttachment("data/google-analytics-channels.csv").csv({typed: true}); @@ -13,16 +12,13 @@ const world = FileAttachment("data/countries-110m.json").json(); ``` ```js -// Imports import {svg} from "npm:htl"; -import {BigNumber} from "./components/bigNumber.js"; import {Marimekko} from "./components/marimekko.js"; +import {trend} from "./components/trend.js"; ``` ```js -// Helpers const bigPercent = d3.format(".0%"); -const percent = d3.format(".2%"); const bigNumber = d3.format(".3s"); const date = d3.utcFormat("%m/%d/%Y"); const color = Plot.scale({ @@ -55,11 +51,10 @@ function getCompareValue(data, metric) { ``` ```js -// Charts function lineChart(data, {width, height, metric}) { return Plot.plot({ width, - height: 97, + height: 94, axis: null, insetTop: 10, insetLeft: -15, @@ -78,11 +73,11 @@ function lineChart(data, {width, height, metric}) { function areaChart(data, {width, height, metric}) { return Plot.plot({ width, - height: 97, + height: 94, axis: null, insetTop: 10, insetLeft: -15, - insetRight: -16.5, + insetRight: -17, marks: [ Plot.ruleY([0]), Plot.areaY(data, { @@ -276,42 +271,48 @@ function worldMap(data, {width, height, title, caption}) { } ``` - - # Google analytics _Summary of metrics from the [Google Analytics Data API](https://developers.google.com/analytics/devguides/reporting/data/v1/quickstart-client-libraries), pulled on ${date(d3.max(summary, d => d.date))}_
-
- ${resize((width) => BigNumber(`${summary[summary.length-1].active28d.toLocaleString("en-US")}`, {title: "Rolling 28-day Active users", plot: areaChart(summary, {width, metric: 'active28d'}), trend: getCompareValue(summary, 'active28d'), trendFormat: bigNumber}))} +
+

Rolling 28-day Active users

+ ${summary[summary.length-1].active28d.toLocaleString("en-US")} + ${trend(getCompareValue(summary, 'active28d'))} + ${resize((width) => areaChart(summary, {width, metric: 'active28d'}))}
-
- ${resize((width) => BigNumber(`${bigPercent(summary[summary.length-1].engagementRate)}`, {title: "Engagement Rate", plot: lineChart(summary, {width, metric: 'engagementRate'}), trend: getCompareValue(summary, 'engagementRate'), trendFormat: percent}))} +
+

Engagement Rate

+ ${bigPercent(summary[summary.length-1].engagementRate)} + ${trend(getCompareValue(summary, "engagementRate"), {format: "+.2%"})} + ${resize((width) => lineChart(summary, {width, metric: "engagementRate"}))}
-
- ${resize((width) => BigNumber(`${bigPercent(summary[summary.length-1].wauPerMau)}`, {title: "WAU to MAU ratio", plot: lineChart(summary, {width, metric: 'wauPerMau'}), trend: getCompareValue(summary, 'wauPerMau'), trendFormat: percent}))} +
+

WAU to MAU ratio

+ ${bigPercent(summary[summary.length-1].wauPerMau)} + ${trend(getCompareValue(summary, "wauPerMau"), {format: "+.2%"})} + ${resize((width) => lineChart(summary, {width, metric: 'wauPerMau'}))}
-
- ${resize((width) => BigNumber(`${summary[summary.length-1].engagedSessions.toLocaleString("en-US")}`, {title: "Engaged Sessions", plot: areaChart(summary, {width, metric: 'engagedSessions'}), trend: getCompareValue(summary, 'engagedSessions'), trendFormat: bigNumber}))} +
+

Engaged Sessions

+ ${summary[summary.length-1].engagedSessions.toLocaleString("en-US")} + ${trend(getCompareValue(summary, 'engagedSessions'))} + ${resize((width) => areaChart(summary, {width, metric: 'engagedSessions'}))}
-
+
${resize((width, height) => horizonChart(channels, {width, height, metric:'active28d', title: 'Active users by channel', caption: 'Rolling 28-day active users', format: 's', z: 'channelGroup', color, order: color.domain.slice(1)}))}
-
+
${resize((width, height) => worldMap(countryData, {width, height, title: "Active users by country", caption: 'Current rolling 28-day active users by country', lookup: countryLookup}))}
-
- ${resize((width, height) => marrimekoChart(filteredChannelBreakdown, {width, height, metric:'active28d', title: 'New vs. returning users by channel', caption: 'Rolling 28-day active users by channel and split by new vs. returning', format: '%', yDim: 'channelGroup', xDim: 'type', color}))} +
+ ${resize((width, height) => marrimekoChart(filteredChannelBreakdown, {width, height: height - 12, metric:'active28d', title: 'New vs. returning users by channel', caption: 'Rolling 28-day active users by channel and split by new vs. returning', format: '%', yDim: 'channelGroup', xDim: 'type', color}))}
-
+
${resize((width, height) => Punchcard(hourly, {width, height, label: "active users"}))}
diff --git a/examples/plot/README.md b/examples/plot/README.md index 80f9697d5..a859a28f5 100644 --- a/examples/plot/README.md +++ b/examples/plot/README.md @@ -26,7 +26,7 @@ The [`npm-downloads.csv.ts`](./docs/data/npm-downloads.csv.ts) loader retrieves ## Big numbers -Key performance indicators are displayed as “big numbers” with, in some cases, a trend indicating growth over one week. See the components/bigNumber.js file. +Key performance indicators are displayed as “big numbers” with, in some cases, a trend indicating growth over one week. Their layout is using the convenience CSS classes _big_, _red_ etc. ## Charts diff --git a/examples/plot/docs/components/bigNumber.js b/examples/plot/docs/components/bigNumber.js deleted file mode 100644 index 621921838..000000000 --- a/examples/plot/docs/components/bigNumber.js +++ /dev/null @@ -1,36 +0,0 @@ -import * as d3 from "npm:d3"; -import {html} from "npm:htl"; - -export function BigNumber( - number, - { - href, - target = "_blank", - title, - format = ",.2~f", - trend, - trendFormat = "+.1~%", - trendColor = trend > 0 ? "green" : trend < 0 ? "red" : "orange", - trendArrow = trend > 0 ? "↗︎" : trend < 0 ? "↘︎" : "→", - trendTitle, - plot - } = {} -) { - if (typeof format !== "function") format = typeof number === "string" ? String : d3.format(format); - if (typeof trendFormat !== "function") trendFormat = d3.format(trendFormat); - const div = html`
-
${title}
-
-
${format(number)}
- ${ - trend == null - ? null - : html`
${trendFormat( - trend - )} ${trendArrow}
` - } -
- ${plot} -
`; - return href == null ? div : html`${div}`; -} diff --git a/examples/plot/docs/components/dailyPlot.js b/examples/plot/docs/components/dailyPlot.js index 012bd7214..0229b2059 100644 --- a/examples/plot/docs/components/dailyPlot.js +++ b/examples/plot/docs/components/dailyPlot.js @@ -1,5 +1,5 @@ -import * as d3 from "npm:d3"; import * as Plot from "npm:@observablehq/plot"; +import * as d3 from "npm:d3"; export const today = d3.utcDay(d3.utcHour.offset(d3.utcHour(), -10)); export const start = d3.utcYear.offset(today, -2); diff --git a/examples/plot/docs/components/trend.js b/examples/plot/docs/components/trend.js new file mode 100644 index 000000000..53ba00142 --- /dev/null +++ b/examples/plot/docs/components/trend.js @@ -0,0 +1,31 @@ +import * as d3 from "npm:d3"; +import {html} from "npm:htl"; + +export function trend( + value /*: number */, + { + format = "+d", + positive = "green", + negative = "red", + base = "muted", + positiveSuffix = " ↗︎", + negativeSuffix = " ↘︎", + baseSuffix = "" + } = {} /* + as { + format: string | ((x: number) => string); + positive: string; + negative: string; + base: string; + positiveSuffix: string; + negativeSuffix: string; + baseSuffix: string; + } + */ +) /*: Node */ { + if (typeof format === "string") format = d3.format(format); + if (typeof format !== "function") throw new Error(`unsupported format ${format}`); + return html`${format(value)}${ + value > 0 ? positiveSuffix : value < 0 ? negativeSuffix : baseSuffix + }`; +} diff --git a/examples/plot/docs/index.md b/examples/plot/docs/index.md index 693635137..0098e8092 100644 --- a/examples/plot/docs/index.md +++ b/examples/plot/docs/index.md @@ -6,7 +6,7 @@ theme: dashboard # Plot ```js -import {BigNumber} from "./components/bigNumber.js"; +import {trend} from "./components/trend.js"; import {DailyPlot, today, start} from "./components/dailyPlot.js"; ``` @@ -43,17 +43,29 @@ const burndown = issues ```
-
${BigNumber(versions.at(-1).version, { - title: "Current release", - href: `https://github.com/observablehq/plot/releases/tag/v${versions.at(-1).version}`, - trend: d3.utcDay.count(versions.at(-1).date, Date.now()), - trendFormat: d => `${d === 0 ? "today" : d===1 ? "yesterday" : `${d} days ago`}`, - trendColor: "#888", - trendArrow: null - })}
-
${BigNumber(stars.length, {title: "GitHub stars", trend: stars.filter((d) => d.starred_at >= lastWeek).length, trendFormat: "+", trendTitle: "Since last week"})}
-
${BigNumber(downloads[0].value, {title: "Daily npm downloads", trend: downloads[7].value ? (downloads[0].value - downloads[7].value) / downloads[7].value : undefined, trendTitle: "Compared to last week"})}
-
${BigNumber(d3.sum(downloads, (d) => d.value), {title: "Total npm downloads"})}
+ +
+

GitHub stars

+ ${stars.length.toLocaleString("en-US")} + ${trend(d3.sum(stars, (d) => d.starred_at >= lastWeek))} +
+
+

Daily npm downloads

+ ${downloads[0].value.toLocaleString("en-US")} + ${trend(downloads[7].value + ? (downloads[0].value - downloads[7].value) / downloads[7].value + : undefined, {format: "+.1%"})} +
+
+

Total npm downloads

+ ${d3.sum(downloads, (d) => d.value).toLocaleString("en-US")} +
@@ -100,10 +112,22 @@ const burndown = issues
-
${BigNumber(issues.filter((d) => !d.pull_request && d.state === "open").length, {title: "Open issues", href: "https://github.com/observablehq/plot/issues"})}
-
${BigNumber(issues.filter((d) => !d.pull_request && d.open >= lastMonth).length, {title: "Opened issues, 28d"})}
-
${BigNumber(issues.filter((d) => !d.pull_request && d.close >= lastMonth).length, {title: "Closed issues, 28d"})}
-
${BigNumber(issues.filter((d) => d.pull_request && d.state === "open" && !d.draft).length, {title: "Open PRs", href: "https://github.com/observablehq/plot/pulls?q=is%3Apr+is%3Aopen+draft%3Afalse"})}
+ +
+

Opened issues, 28d

+ ${d3.sum(issues, (d) => !d.pull_request && d.open >= lastMonth).toLocaleString("en-US")} +
+
+

Closed issues, 28d

+ ${d3.sum(issues, (d) => !d.pull_request && d.close >= lastMonth).toLocaleString("en-US")} +
+