Skip to content

Commit

Permalink
add examples grid (observablehq#1834)
Browse files Browse the repository at this point in the history
  • Loading branch information
mbostock authored and chaichontat committed Jan 14, 2024
1 parent 1676a64 commit 900412d
Show file tree
Hide file tree
Showing 5 changed files with 206 additions and 3 deletions.
22 changes: 22 additions & 0 deletions docs/.vitepress/theme/CustomLayout.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<script setup>
import DefaultTheme from "vitepress/theme-without-fonts";
import ExamplesGrid from "./ExamplesGrid.vue";
import ObservablePromo from "./ObservablePromo.vue";
const {Layout} = DefaultTheme;
Expand All @@ -9,8 +10,29 @@ const {Layout} = DefaultTheme;

<template>
<Layout>
<template #home-features-before>
<ExamplesGrid />
</template>
<template #home-features-after>
<ObservablePromo />
</template>
</Layout>
</template>

<style>
.VPHome {
overflow-x: hidden; /* iOS */
}
.VPHome .VPFeature {
background-color: rgba(246, 246, 247, 0.5);
-webkit-backdrop-filter: blur(10px); /* Safari */
backdrop-filter: blur(10px);
}
.dark .VPHome .VPFeature {
background-color: rgba(37, 37, 41, 0.5);
}
</style>
132 changes: 132 additions & 0 deletions docs/.vitepress/theme/ExamplesGrid.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
<script setup>
import * as d3 from "d3";
import {ref, shallowRef, onMounted, onUnmounted} from "vue";
import {data} from "./gallery.data.js";
let observer;
let pointerframe;
let clientX;
const n = 60; // maximum number of examples to show
const slice = d3.shuffler(d3.randomLcg(d3.utcDay()))(data.slice()).slice(0, n);
const sample = shallowRef(slice.slice(0, 55)); // initial guess
const container = ref();
const xn = ref(10); // number of rows
const yn = ref(5); // number of columns
const x = ref(0.5); // normalized horizontal pointer position
// Some browsers trigger pointermove more frequently than desirable, so we
// debounce events for a smooth transitions.
function onpointermove(event) {
if (!pointerframe) pointerframe = requestAnimationFrame(afterpointermove);
clientX = event.clientX;
}
function afterpointermove() {
pointerframe = null;
x.value = clientX / document.body.clientWidth;
}
onMounted(() => {
observer = new ResizeObserver(() => {
const w = parseFloat(getComputedStyle(container.value).getPropertyValue("--grid-width"));
xn.value = Math.ceil(document.body.clientWidth / w) + 3; // overflow columns
yn.value = Math.min(Math.round(640 / w + 2), Math.floor(n / xn.value)); // 640 is grid height
sample.value = slice.slice(0, yn.value * xn.value);
});
observer.observe(document.body);
addEventListener("pointermove", onpointermove);
});
onUnmounted(() => {
observer.disconnect();
removeEventListener("pointermove", onpointermove);
});
</script>

<template>
<div :class="$style.examples" ref="container" :style="`transform: translate(${60 - x * 10}vw, 33%);`">
<div v-for="(d, i) in sample" :style="`--delay: ${((i % xn) / xn + (d3.randomLcg(1 / i)()) - 0.4) * 1}s;`">
<a :href="`https://observablehq.com/${d.path}${d.path.includes('?') ? '&' : '?'}intent=fork`" :title="[d.title, d.author].filter(Boolean).join('\n')" target="_blank" :style="`--x: ${(i % xn) - xn / 2 + (Math.floor(i / xn) % 2) * 0.5}; --y: ${Math.floor(i / xn) - yn / 2};`">
<img :src="`https://static.observableusercontent.com/thumbnail/${d.thumbnail}.jpg`" width="640" height="400" />
</a>
</div>
</div>
</template>

<style module>
.examples {
position: relative;
height: 640px;
transition: transform 150ms ease-out;
filter: drop-shadow(0 4px 8px rgba(0,0,0,0.2));
--grid-width: 140px;
}
@media (min-width: 640px) {
.examples {
--grid-width: 160px;
}
}
@media (min-width: 960px) {
.examples {
--grid-width: 200px;
}
}
/* The drop-shadow should not be affected by the hexagon clip-path. */
.examples div {
position: relative;
transition: filter 250ms ease-out;
animation: fade-in 350ms cubic-bezier(0.215, 0.610, 0.355, 1.000) var(--delay) backwards;
}
.examples div:hover {
filter: drop-shadow(0 4px 8px rgba(0, 0, 0, 0.4));
z-index: 3;
}
.examples a {
position: absolute;
--transform: perspective(75em) rotateX(30deg) rotateZ(-7deg) translate(calc(var(--x) * 100%), calc(var(--y) * 86.67%)) scale(1.145);
transform: var(--transform);
animation: drop-in 350ms cubic-bezier(0.215, 0.610, 0.355, 1.000) var(--delay) backwards;
transition: transform 250ms ease-out;
clip-path: polygon(50.0% 100.0%, 93.3% 75.0%, 93.3% 25.0%, 50.0% 0.0%, 6.7% 25.0%, 6.7% 75.0%);
}
.examples a:hover {
transform: var(--transform) translateZ(10px) scale(1.1);
}
.examples img {
aspect-ratio: 1;
object-fit: cover;
width: var(--grid-width);
}
@keyframes fade-in {
from {
filter: blur(20px);
opacity: 0;
}
to {
filter: none;
opacity: 1;
}
}
@keyframes drop-in {
from {
transform: var(--transform) translateY(-100px) translateZ(400px);
}
to {
transform: var(--transform);
}
}
</style>
19 changes: 19 additions & 0 deletions docs/.vitepress/theme/gallery.data.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {Runtime} from "@observablehq/runtime";

export default {
async load() {
const runtime = new Runtime();
const module = runtime.module((await import("https://api.observablehq.com/@observablehq/plot-gallery.js?v=4")).default);
const data = [];
module.define("md", () => String.raw);
module.redefine("previews", () => (chunk) => data.push(...chunk));
const values = [];
for (const output of module._resolve("previews")._outputs) {
if (output._name) {
values.push(module.value(output._name));
}
}
await Promise.all(values);
return data;
}
};
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,8 +36,8 @@
"prepublishOnly": "rm -rf dist && rollup -c",
"postpublish": "git push && git push --tags",
"dev": "vite",
"docs:dev": "vitepress dev docs",
"docs:build": "vitepress build docs",
"docs:dev": "node --experimental-network-imports node_modules/vitepress/dist/node/cli.js dev docs",
"docs:build": "node --experimental-network-imports node_modules/vitepress/dist/node/cli.js build docs",
"docs:preview": "vitepress preview docs"
},
"_moduleAliases": {
Expand All @@ -48,6 +48,7 @@
],
"devDependencies": {
"@esbuild-kit/core-utils": "^3.1.0",
"@observablehq/runtime": "^5.7.3",
"@rollup/plugin-commonjs": "^25.0.2",
"@rollup/plugin-json": "^6.0.0",
"@rollup/plugin-node-resolve": "^15.0.1",
Expand Down
31 changes: 30 additions & 1 deletion yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -521,6 +521,30 @@
"@nodelib/fs.scandir" "2.1.5"
fastq "^1.6.0"

"@observablehq/inspector@^5.0.0":
version "5.0.0"
resolved "https://registry.yarnpkg.com/@observablehq/inspector/-/inspector-5.0.0.tgz#7dec08d4fa20dfb79977ce62f7cc4a814b44e595"
integrity sha512-Vvg/TQdsZTUaeYbH0IKxYEz37FbRO6kdowoz2PrHLQif54NC1CjEihEjg+ZMSBn587GQxTFABu0CGkFZgtR1UQ==
dependencies:
isoformat "^0.2.0"

"@observablehq/runtime@^5.7.3":
version "5.9.1"
resolved "https://registry.yarnpkg.com/@observablehq/runtime/-/runtime-5.9.1.tgz#fd79f9cce8a165e123021ca2f72fe54e61f16d12"
integrity sha512-ACNFixkIFVihIaWrDGXxgjxVj4/cHx26kdfGDVpEX1mEVqgP1SnfQMoJyZoqG23txs+uUcyr8LG37NkMEsNpdw==
dependencies:
"@observablehq/inspector" "^5.0.0"
"@observablehq/stdlib" "^5.0.0"

"@observablehq/stdlib@^5.0.0":
version "5.8.1"
resolved "https://registry.yarnpkg.com/@observablehq/stdlib/-/stdlib-5.8.1.tgz#50002b0d2a021890052d6f96700d86d15ca18c7d"
integrity sha512-ng6QQSzFbPQnMMeCUhUl/EPzpyrwfmGsujztGdPXS1ZYrLoAc9co4rhUC5Vv6dBh8E4yzZkxwNyTs73bLN4alQ==
dependencies:
d3-array "^3.2.0"
d3-dsv "^3.0.1"
d3-require "^1.3.0"

"@one-ini/[email protected]":
version "0.1.1"
resolved "https://registry.yarnpkg.com/@one-ini/wasm/-/wasm-0.1.1.tgz#6013659736c9dbfccc96e8a9c2b3de317df39323"
Expand Down Expand Up @@ -1434,7 +1458,7 @@ d3-delaunay@6:
d3-dispatch "1 - 3"
d3-selection "3"

"d3-dsv@1 - 3", d3-dsv@3:
"d3-dsv@1 - 3", d3-dsv@3, d3-dsv@^3.0.1:
version "3.0.1"
resolved "https://registry.yarnpkg.com/d3-dsv/-/d3-dsv-3.0.1.tgz#c63af978f4d6a0d084a52a673922be2160789b73"
integrity sha512-UG6OvdI5afDIFP9w4G0mNq50dSOsXHJaRE8arAS5o9ApWnIElp8GZw1Dun8vP8OyHOZ/QJUKUJwxiiCCnUwm+Q==
Expand Down Expand Up @@ -1517,6 +1541,11 @@ d3-random@3:
resolved "https://registry.yarnpkg.com/d3-random/-/d3-random-3.0.1.tgz#d4926378d333d9c0bfd1e6fa0194d30aebaa20f4"
integrity sha512-FXMe9GfxTxqd5D6jFsQ+DJ8BJS4E/fT5mqqdjovykEB2oFbTMDVdg1MGFxfQW+FBOGoB++k8swBrgwSHT1cUXQ==

d3-require@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/d3-require/-/d3-require-1.3.0.tgz#2b97f5e2ebcb64ac0c63c11f30056aea1c74f0ec"
integrity sha512-XaNc2azaAwXhGjmCMtxlD+AowpMfLimVsAoTMpqrvb8CWoA4QqyV12mc4Ue6KSoDvfuS831tsumfhDYxGd4FGA==

d3-scale-chromatic@3:
version "3.0.0"
resolved "https://registry.yarnpkg.com/d3-scale-chromatic/-/d3-scale-chromatic-3.0.0.tgz#15b4ceb8ca2bb0dcb6d1a641ee03d59c3b62376a"
Expand Down

0 comments on commit 900412d

Please sign in to comment.