@aparte/plugin-cards 0.0.0-stage → 0.29.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +151 -2
- package/dist/carousel-DwgHvriV.js +63 -0
- package/dist/carousel-DwgHvriV.js.map +1 -0
- package/dist/controls-BYUm4aRV.js +62 -0
- package/dist/controls-BYUm4aRV.js.map +1 -0
- package/dist/format-BFq79ydr.js +728 -0
- package/dist/format-BFq79ydr.js.map +1 -0
- package/dist/index.d.ts +30 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +120 -0
- package/dist/index.js.map +1 -0
- package/dist/items/icons.d.ts +12 -0
- package/dist/items/icons.d.ts.map +1 -0
- package/dist/items/index.d.ts +100 -0
- package/dist/items/index.d.ts.map +1 -0
- package/dist/items.js +698 -0
- package/dist/items.js.map +1 -0
- package/dist/kit/carousel.d.ts +17 -0
- package/dist/kit/carousel.d.ts.map +1 -0
- package/dist/kit/controls.d.ts +51 -0
- package/dist/kit/controls.d.ts.map +1 -0
- package/dist/kit/define.d.ts +201 -0
- package/dist/kit/define.d.ts.map +1 -0
- package/dist/kit/dom.d.ts +40 -0
- package/dist/kit/dom.d.ts.map +1 -0
- package/dist/kit/fence.d.ts +39 -0
- package/dist/kit/fence.d.ts.map +1 -0
- package/dist/kit/format.d.ts +49 -0
- package/dist/kit/format.d.ts.map +1 -0
- package/dist/kit/rating.d.ts +19 -0
- package/dist/kit/rating.d.ts.map +1 -0
- package/dist/kit/sparkline.d.ts +19 -0
- package/dist/kit/sparkline.d.ts.map +1 -0
- package/dist/kit/strings.d.ts +139 -0
- package/dist/kit/strings.d.ts.map +1 -0
- package/dist/link/icons.d.ts +16 -0
- package/dist/link/icons.d.ts.map +1 -0
- package/dist/link/index.d.ts +98 -0
- package/dist/link/index.d.ts.map +1 -0
- package/dist/link.js +387 -0
- package/dist/link.js.map +1 -0
- package/dist/media/icons.d.ts +28 -0
- package/dist/media/icons.d.ts.map +1 -0
- package/dist/media/index.d.ts +107 -0
- package/dist/media/index.d.ts.map +1 -0
- package/dist/media.js +610 -0
- package/dist/media.js.map +1 -0
- package/dist/order/icons.d.ts +49 -0
- package/dist/order/icons.d.ts.map +1 -0
- package/dist/order/index.d.ts +204 -0
- package/dist/order/index.d.ts.map +1 -0
- package/dist/order.js +1843 -0
- package/dist/order.js.map +1 -0
- package/dist/place/icons.d.ts +18 -0
- package/dist/place/icons.d.ts.map +1 -0
- package/dist/place/index.d.ts +207 -0
- package/dist/place/index.d.ts.map +1 -0
- package/dist/place.js +1456 -0
- package/dist/place.js.map +1 -0
- package/dist/plan/icons.d.ts +25 -0
- package/dist/plan/icons.d.ts.map +1 -0
- package/dist/plan/index.d.ts +153 -0
- package/dist/plan/index.d.ts.map +1 -0
- package/dist/plan.js +1191 -0
- package/dist/plan.js.map +1 -0
- package/dist/product/icons.d.ts +16 -0
- package/dist/product/icons.d.ts.map +1 -0
- package/dist/product/index.d.ts +133 -0
- package/dist/product/index.d.ts.map +1 -0
- package/dist/product.js +1245 -0
- package/dist/product.js.map +1 -0
- package/dist/rating-CHVajQ2b.js +35 -0
- package/dist/rating-CHVajQ2b.js.map +1 -0
- package/dist/score/icons.d.ts +35 -0
- package/dist/score/icons.d.ts.map +1 -0
- package/dist/score/index.d.ts +185 -0
- package/dist/score/index.d.ts.map +1 -0
- package/dist/score.js +1843 -0
- package/dist/score.js.map +1 -0
- package/dist/sparkline-C-cA9obz.js +56 -0
- package/dist/sparkline-C-cA9obz.js.map +1 -0
- package/dist/stats/icons.d.ts +19 -0
- package/dist/stats/icons.d.ts.map +1 -0
- package/dist/stats/index.d.ts +137 -0
- package/dist/stats/index.d.ts.map +1 -0
- package/dist/stats.js +1085 -0
- package/dist/stats.js.map +1 -0
- package/dist/stock/chart.d.ts +135 -0
- package/dist/stock/chart.d.ts.map +1 -0
- package/dist/stock/icons.d.ts +15 -0
- package/dist/stock/icons.d.ts.map +1 -0
- package/dist/stock/index.d.ts +195 -0
- package/dist/stock/index.d.ts.map +1 -0
- package/dist/stock.js +1013 -0
- package/dist/stock.js.map +1 -0
- package/dist/weather/conditions.d.ts +25 -0
- package/dist/weather/conditions.d.ts.map +1 -0
- package/dist/weather/icons.d.ts +12 -0
- package/dist/weather/icons.d.ts.map +1 -0
- package/dist/weather/index.d.ts +146 -0
- package/dist/weather/index.d.ts.map +1 -0
- package/dist/weather.js +1162 -0
- package/dist/weather.js.map +1 -0
- package/package.json +137 -3
package/dist/media.js
ADDED
|
@@ -0,0 +1,610 @@
|
|
|
1
|
+
import { c as carousel } from "./carousel-DwgHvriV.js";
|
|
2
|
+
import { b as defineCard, q as str, r as word, e as el, h as formatNumber, m as glyph, k as formatUnit, s as safeUrl, w as webUrl, p as num, K as KIT_WORDS } from "./format-BFq79ydr.js";
|
|
3
|
+
const svg = (body) => `<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.6" stroke-linecap="round" stroke-linejoin="round" xmlns="http://www.w3.org/2000/svg">${body}</svg>`;
|
|
4
|
+
const IMAGE = svg('<rect x="3.5" y="4.5" width="17" height="15" rx="2.5"/><circle cx="9" cy="9.5" r="1.6"/><path d="m4 17.5 4.6-4.6a1.4 1.4 0 0 1 2 0l3.4 3.4 1.6-1.6a1.4 1.4 0 0 1 2 0l2.9 2.9"/>');
|
|
5
|
+
const FILM = svg('<rect x="3.5" y="5" width="17" height="14" rx="2.5"/><path d="M7.5 5v14M16.5 5v14M3.5 9.5h4M3.5 14.5h4M16.5 9.5h4M16.5 14.5h4"/>');
|
|
6
|
+
const NOTE = svg('<path d="M9.5 17.5V6l10-2v11.5"/><circle cx="7" cy="17.5" r="2.5"/><circle cx="17" cy="15.5" r="2.5"/>');
|
|
7
|
+
const FRAMES = svg('<rect x="7" y="3.5" width="13.5" height="11.5" rx="2.2"/><path d="M17 18.5a2 2 0 0 1-2 2H5.5a2 2 0 0 1-2-2V9a2 2 0 0 1 2-2"/><path d="m7.5 13 3-3 2.5 2.5 1.5-1.5 3.5 3.5"/>');
|
|
8
|
+
const PLAY = '<svg viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M8.5 5.6v12.8a1 1 0 0 0 1.5.86l10.2-6.4a1 1 0 0 0 0-1.72L10 4.74a1 1 0 0 0-1.5.86z"/></svg>';
|
|
9
|
+
const EXPAND = svg('<path d="M14.5 4.5h5v5M9.5 19.5h-5v-5M19.5 4.5l-6 6M4.5 19.5l6-6"/>');
|
|
10
|
+
const EXTERNAL = svg('<path d="M13.5 4.5h6v6M19.5 4.5l-8 8"/><path d="M17.5 13.5v4a2 2 0 0 1-2 2h-9a2 2 0 0 1-2-2v-9a2 2 0 0 1 2-2h4"/>');
|
|
11
|
+
const CAMERA = svg('<path d="M4.5 8h3l1.6-2.3h5.8L16.5 8h3a1 1 0 0 1 1 1v9a1 1 0 0 1-1 1h-15a1 1 0 0 1-1-1V9a1 1 0 0 1 1-1z"/><circle cx="12" cy="13" r="3.3"/>');
|
|
12
|
+
function kindGlyph(kind) {
|
|
13
|
+
return kind === "video" ? FILM : kind === "audio" ? NOTE : IMAGE;
|
|
14
|
+
}
|
|
15
|
+
const mediaStyles = '/* =========================================\n The media card — @aparte/plugin-cards/media\n =========================================\n Three variants of one card, chosen by `data-variant` on its root:\n single one medium, large: an image at its own shape within a height (a button that\n opens the lightbox), a video in the browser\'s player, a sound under a header\n with its cover and a waveform, a YouTube page as a poster that links out.\n One medium is drawn this way whatever the variant.\n gallery a mosaic: two to six tiles, the first larger from three, "+n" on the last\n when there are more. Several media in `single` are drawn this way too.\n strip square thumbnails that scroll sideways.\n Every tile opens the lightbox: a native <dialog> wearing core\'s `.aparte-dialog`, its\n picture on a dark stage.\n Every colour is a core token or a `--aparte-media-*` one here. The stage is black in\n either theme — what a photo is looked at on — and so is the scrim over a picture: they\n sit on images, not on the page\'s ground.\n */\n\n.aparte-media-card {\n /* The tallest a picture is drawn on the card. */\n --aparte-media-max-block: 22rem;\n /* The seams of the mosaic. */\n --aparte-media-gap: 2px;\n /* The tint of a placeholder, a cover without a picture, the waveform. */\n --aparte-media-tint: var(--aparte-primary);\n --aparte-media-stage: #0b0b0d;\n --aparte-media-on-stage: #ffffff;\n --aparte-media-scrim: rgb(0 0 0 / 0.55);\n --aparte-media-lightbox-block: min(72dvh, 44rem);\n}\n\n.aparte-media-card:has(> .aparte-media--single) { --aparte-rich-card-max-inline-size: 24rem; }\n.aparte-media-card:has(> .aparte-media--strip) { --aparte-rich-card-max-inline-size: 30rem; }\n\n.aparte-media {\n min-inline-size: 0;\n margin: 0;\n color: var(--aparte-text);\n}\n\n/* ── What stands behind a picture ────────────────────────────────────── */\n\n.aparte-media__placeholder {\n position: absolute;\n inset: 0;\n z-index: 0;\n display: grid;\n place-items: center;\n background:\n radial-gradient(circle at 30% 20%, color-mix(in oklab, var(--aparte-media-tint) 14%, transparent), transparent 60%),\n color-mix(in oklab, var(--aparte-media-tint) 8%, var(--aparte-surface-2));\n color: color-mix(in oklab, var(--aparte-media-tint) 55%, var(--aparte-text-muted));\n}\n\n.aparte-media__placeholder-glyph { --aparte-rich-card-glyph-size: 1.75rem; opacity: 0.85; }\n\n.aparte-media__img {\n position: relative;\n z-index: 1;\n display: block;\n}\n\n/* ── One image, at its own shape ─────────────────────────────────────── */\n\n.aparte-media--single {\n display: flex;\n flex-direction: column;\n padding: var(--aparte-space-4) var(--aparte-space-4) var(--aparte-space-5);\n}\n\n.aparte-media__frame {\n position: relative;\n display: block;\n inline-size: 100%;\n margin: 0;\n padding: 0;\n overflow: hidden;\n isolation: isolate;\n border: 0;\n border-radius: var(--aparte-radius-lg);\n background: var(--aparte-surface-2);\n color: inherit;\n font: inherit;\n}\n\n/* Nothing known of its shape: the picture sets the frame\'s size, within the card and the height. */\n.aparte-media__frame[data-kind="image"] {\n align-self: center;\n inline-size: fit-content;\n max-inline-size: 100%;\n cursor: zoom-in;\n}\n\n.aparte-media__frame[data-kind="image"] > .aparte-media__img {\n inline-size: auto;\n block-size: auto;\n max-inline-size: 100%;\n max-block-size: var(--aparte-media-max-block);\n}\n\n/* Its shape known: the frame is drawn at it from the start — as wide as the card allows,\n narrower when the height would pass the limit — and the picture fills it. */\n.aparte-media__frame[data-kind="image"][data-sized] {\n inline-size: min(100%, calc(var(--aparte-media-max-block) * var(--aparte-media-ratio-n)));\n aspect-ratio: var(--aparte-media-ratio);\n}\n\n.aparte-media__frame[data-kind="image"][data-sized] > .aparte-media__img {\n inline-size: 100%;\n block-size: 100%;\n max-block-size: none;\n object-fit: cover;\n}\n\n/* Loading with no shape known, or failed: a box of a reasonable size around the placeholder. */\n.aparte-media__frame[data-kind="image"]:not([data-sized]):is([data-loading], [data-broken]) {\n inline-size: 100%;\n min-block-size: 10rem;\n}\n\n.aparte-media__frame[data-kind="image"] > .aparte-media__img {\n transition: transform var(--aparte-duration-slower, 0.3s) ease, filter var(--aparte-duration-slower, 0.3s) ease;\n}\n\n.aparte-media__frame[data-kind="image"]:hover > .aparte-media__img { transform: scale(1.025); }\n\n.aparte-media__frame:focus-visible {\n outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);\n outline-offset: var(--aparte-focus-outline-offset);\n}\n\n/* "See it larger": a frosted disc in the corner, shown on hover and focus. */\n.aparte-media__zoom {\n --aparte-rich-card-glyph-size: 2rem;\n position: absolute;\n inset-block-start: var(--aparte-space-4);\n inset-inline-end: var(--aparte-space-4);\n z-index: 2;\n padding: 0.4rem;\n border-radius: var(--aparte-radius-full);\n background: var(--aparte-media-scrim);\n color: var(--aparte-media-on-stage);\n backdrop-filter: blur(6px);\n opacity: 0;\n transition: opacity var(--aparte-duration-base, 0.15s) ease;\n}\n\n.aparte-media__frame:hover > .aparte-media__zoom,\n.aparte-media__frame:focus-visible > .aparte-media__zoom { opacity: 1; }\n\n.aparte-media__frame[data-broken] > .aparte-media__zoom { display: none; }\n\n/* ── A video ─────────────────────────────────────────────────────────── */\n\n.aparte-media__frame--video,\n.aparte-media__external,\n.aparte-media__lb-video {\n aspect-ratio: var(--aparte-media-ratio, 16 / 9);\n max-block-size: var(--aparte-media-max-block);\n background: var(--aparte-media-stage);\n}\n\n.aparte-media__video {\n display: block;\n inline-size: 100%;\n block-size: 100%;\n object-fit: contain;\n background: var(--aparte-media-stage);\n}\n\n/* A page that plays it elsewhere: its poster, a disc with the triangle, the site, the length. */\n.aparte-media__external {\n color: var(--aparte-media-on-stage);\n text-decoration: none;\n}\n\n.aparte-media__external > .aparte-media__img {\n position: absolute;\n inset: 0;\n inline-size: 100%;\n block-size: 100%;\n object-fit: cover;\n transition: transform var(--aparte-duration-slower, 0.3s) ease;\n}\n\n.aparte-media__external:hover > .aparte-media__img { transform: scale(1.03); }\n\n.aparte-media__external:focus-visible {\n outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);\n outline-offset: var(--aparte-focus-outline-offset);\n}\n\n.aparte-media__play {\n position: absolute;\n inset-block-start: 50%;\n inset-inline-start: 50%;\n z-index: 2;\n display: grid;\n place-items: center;\n inline-size: 3.5rem;\n block-size: 3.5rem;\n border-radius: var(--aparte-radius-full);\n background: var(--aparte-media-scrim);\n color: var(--aparte-media-on-stage);\n box-shadow: 0 0 0 1px rgb(255 255 255 / 0.25) inset, 0 6px 20px -6px rgb(0 0 0 / 0.6);\n backdrop-filter: blur(6px);\n translate: -50% -50%;\n transition: background-color var(--aparte-duration-base, 0.15s) ease, scale var(--aparte-duration-base, 0.15s) ease;\n}\n\n/* `translate` is physical: in a right-to-left page the start edge is the right one. */\n.aparte-media__play:dir(rtl) { translate: 50% -50%; }\n\n.aparte-media__play-glyph { --aparte-rich-card-glyph-size: 1.5rem; margin-inline-start: 0.15rem; }\n.aparte-media__play:dir(rtl) .aparte-media__play-glyph { margin-inline: 0 0.15rem; transform: scaleX(-1); }\n\n.aparte-media__play--sm { inline-size: 2.5rem; block-size: 2.5rem; }\n.aparte-media__play--sm .aparte-media__play-glyph { --aparte-rich-card-glyph-size: 1.1rem; }\n\n.aparte-media__external:hover .aparte-media__play,\n.aparte-media__tile:hover .aparte-media__play {\n background: var(--aparte-primary);\n scale: 1.08;\n}\n\n.aparte-media__site,\n.aparte-media__duration {\n position: absolute;\n inset-block-end: var(--aparte-space-4);\n z-index: 2;\n display: inline-flex;\n align-items: center;\n gap: var(--aparte-space-2);\n padding: var(--aparte-space-1) var(--aparte-space-3);\n border-radius: var(--aparte-radius-sm);\n background: var(--aparte-media-scrim);\n color: var(--aparte-media-on-stage);\n font-size: var(--aparte-font-size-xs);\n font-weight: var(--aparte-font-weight-semibold);\n line-height: 1.3;\n white-space: nowrap;\n}\n\n.aparte-media__site { inset-inline-start: var(--aparte-space-4); }\n.aparte-media__duration { inset-inline-end: var(--aparte-space-4); }\n.aparte-media__site-glyph { --aparte-rich-card-glyph-size: 0.8rem; }\n\n/* ── A sound ─────────────────────────────────────────────────────────── */\n\n.aparte-media__audio {\n display: grid;\n gap: var(--aparte-space-4);\n padding: var(--aparte-space-3) var(--aparte-space-2) 0;\n}\n\n.aparte-media__audio-head {\n display: flex;\n align-items: center;\n gap: var(--aparte-space-5);\n min-inline-size: 0;\n}\n\n.aparte-media__cover {\n position: relative;\n flex: none;\n inline-size: 3.5rem;\n block-size: 3.5rem;\n overflow: hidden;\n border-radius: var(--aparte-radius-lg);\n box-shadow: 0 4px 14px -8px color-mix(in oklab, var(--aparte-media-tint) 70%, black);\n}\n\n.aparte-media__cover > .aparte-media__img {\n position: absolute;\n inset: 0;\n inline-size: 100%;\n block-size: 100%;\n object-fit: cover;\n}\n\n.aparte-media__cover > .aparte-media__placeholder {\n background: linear-gradient(135deg, color-mix(in oklab, var(--aparte-media-tint) 30%, var(--aparte-surface-1)), color-mix(in oklab, var(--aparte-media-tint) 10%, var(--aparte-surface-2)));\n}\n\n.aparte-media__audio-text {\n display: flex;\n flex: 1;\n flex-direction: column;\n gap: var(--aparte-space-1);\n min-inline-size: 0;\n}\n\n.aparte-media__audio-title,\n.aparte-media__audio-credit {\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.aparte-media__audio-title {\n font-size: var(--aparte-font-size-base);\n font-weight: var(--aparte-font-weight-semibold);\n}\n\n.aparte-media__audio-credit,\n.aparte-media__audio-length {\n color: var(--aparte-text-muted);\n font-size: var(--aparte-font-size-sm);\n}\n\n.aparte-media__audio-length { flex: none; }\n\n/* A waveform that is only decoration: bars cut by a mask, their tops following a fixed\n envelope cut by a polygon. It shows no real level, so it says nothing to a reader. */\n.aparte-media__wave {\n display: block;\n block-size: 2.25rem;\n background: linear-gradient(90deg,\n var(--aparte-media-tint) 0%,\n color-mix(in oklab, var(--aparte-media-tint) 70%, transparent) 50%,\n color-mix(in oklab, var(--aparte-media-tint) 28%, transparent) 50%,\n color-mix(in oklab, var(--aparte-media-tint) 28%, transparent) 100%);\n background-size: 200% 100%;\n background-position: 100% 0;\n -webkit-mask-image: repeating-linear-gradient(90deg, #000 0 3px, transparent 3px 6px);\n mask-image: repeating-linear-gradient(90deg, #000 0 3px, transparent 3px 6px);\n clip-path: polygon(\n 0% 35%, 5% 22.5%, 10% 30%, 15% 10%, 20% 20%, 25% 2.5%, 30% 15%, 35% 27.5%, 40% 7.5%, 45% 25%, 50% 12.5%,\n 55% 32.5%, 60% 5%, 65% 20%, 70% 30%, 75% 15%, 80% 25%, 85% 10%, 90% 27.5%, 95% 35%, 100% 37.5%,\n 100% 62.5%, 95% 65%, 90% 72.5%, 85% 90%, 80% 75%, 75% 85%, 70% 70%, 65% 80%, 60% 95%, 55% 67.5%,\n 50% 87.5%, 45% 75%, 40% 92.5%, 35% 72.5%, 30% 85%, 25% 97.5%, 20% 80%, 15% 90%, 10% 70%, 5% 77.5%, 0% 65%);\n}\n\n/* While it plays, the colour sweeps across the bars. */\n.aparte-media__audio[data-playing] .aparte-media__wave {\n animation: aparte-media-wave 2.4s linear infinite;\n}\n\n@keyframes aparte-media-wave {\n from { background-position: 100% 0; }\n to { background-position: 0% 0; }\n}\n\n.aparte-media__player {\n display: block;\n inline-size: 100%;\n block-size: 2.5rem;\n}\n\n/* ── Under a medium ──────────────────────────────────────────────────── */\n\n.aparte-media__figcaption {\n display: flex;\n flex-direction: column;\n gap: var(--aparte-space-1);\n min-inline-size: 0;\n padding: var(--aparte-space-4) var(--aparte-space-2) 0;\n}\n\n.aparte-media__title {\n font-size: var(--aparte-font-size-base);\n font-weight: var(--aparte-font-weight-semibold);\n letter-spacing: -0.005em;\n line-height: 1.3;\n overflow-wrap: anywhere;\n}\n\n.aparte-media__caption {\n display: -webkit-box;\n margin: 0;\n overflow: hidden;\n color: var(--aparte-text-muted);\n font-size: var(--aparte-font-size-md);\n line-height: 1.45;\n overflow-wrap: anywhere;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 4;\n}\n\n.aparte-media__meta {\n display: flex;\n flex-wrap: wrap;\n align-items: center;\n gap: var(--aparte-space-2) var(--aparte-space-5);\n margin-block-start: var(--aparte-space-2);\n color: var(--aparte-text-muted);\n font-size: var(--aparte-font-size-sm);\n}\n\n.aparte-media__credit {\n display: inline-flex;\n align-items: center;\n gap: var(--aparte-space-2);\n min-inline-size: 0;\n}\n\n.aparte-media__source {\n display: inline-flex;\n align-items: center;\n gap: var(--aparte-space-1);\n border-radius: var(--aparte-radius-xs);\n color: var(--aparte-primary-ink, var(--aparte-text));\n font-weight: var(--aparte-font-weight-medium);\n text-decoration: none;\n}\n\n.aparte-media__source:hover {\n text-decoration: underline;\n text-decoration-thickness: 1px;\n text-underline-offset: 0.18em;\n}\n\n.aparte-media__source:focus-visible {\n outline: var(--aparte-focus-outline-width) solid var(--aparte-border-focus);\n outline-offset: var(--aparte-focus-outline-offset);\n}\n\n.aparte-media__line-glyph { --aparte-rich-card-glyph-size: 0.95rem; }\n.aparte-media__source .aparte-media__line-glyph { --aparte-rich-card-glyph-size: 0.8rem; }\n\n/* ── Tiles: the mosaic\'s and the strip\'s ─────────────────────────────── */\n\n.aparte-media__tile {\n position: relative;\n display: block;\n inline-size: 100%;\n block-size: 100%;\n margin: 0;\n padding: 0;\n overflow: hidden;\n border: 0;\n background: var(--aparte-surface-2);\n color: inherit;\n font: inherit;\n cursor: zoom-in;\n}\n\n.aparte-media__tile > .aparte-media__img {\n position: absolute;\n inset: 0;\n inline-size: 100%;\n block-size: 100%;\n object-fit: cover;\n transition: transform var(--aparte-duration-slower, 0.3s) ease, filter var(--aparte-duration-slower, 0.3s) ease;\n}\n\n.aparte-media__tile:hover > .aparte-media__img {\n transform: scale(1.05);\n filter: brightness(1.05);\n}\n\n/* The focus ring is drawn inside the tile, over the picture: tiles touch each other and the\n card\'s edge, so a ring outside would be cut. Two rings, so it reads on any picture. */\n.aparte-media__tile::after {\n content: "";\n position: absolute;\n inset: 0;\n z-index: 4;\n border-radius: inherit;\n pointer-events: none;\n}\n\n.aparte-media__tile:focus-visible { outline: none; }\n\n.aparte-media__tile:focus-visible::after {\n box-shadow:\n inset 0 0 0 var(--aparte-focus-outline-width) var(--aparte-border-focus),\n inset 0 0 0 calc(var(--aparte-focus-outline-width) * 2) var(--aparte-surface-1);\n}\n\n.aparte-media__tile[data-kind="audio"] { cursor: pointer; }\n\n.aparte-media__tile-title {\n position: absolute;\n inset-inline: var(--aparte-space-3);\n inset-block-end: var(--aparte-space-3);\n z-index: 2;\n overflow: hidden;\n color: var(--aparte-text);\n font-size: var(--aparte-font-size-xs);\n font-weight: var(--aparte-font-weight-semibold);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.aparte-media__tile .aparte-media__duration {\n inset-block-end: var(--aparte-space-3);\n inset-inline-end: var(--aparte-space-3);\n}\n\n/* "+3": how many more the lightbox holds, over the last tile. */\n.aparte-media__more {\n position: absolute;\n inset: 0;\n z-index: 3;\n display: grid;\n place-items: center;\n background: var(--aparte-media-scrim);\n color: var(--aparte-media-on-stage);\n font-size: var(--aparte-font-size-2xl);\n font-weight: var(--aparte-font-weight-semibold);\n letter-spacing: -0.01em;\n backdrop-filter: blur(1.5px);\n transition: background-color var(--aparte-duration-base, 0.15s) ease;\n}\n\n.aparte-media__tile:hover > .aparte-media__more { background: rgb(0 0 0 / 0.42); }\n\n/* ── Gallery: the mosaic ─────────────────────────────────────────────── */\n\n.aparte-media--gallery {\n display: flex;\n flex-direction: column;\n}\n\n/* A six-column grid laid out per count, so every count fills its box with no hole:\n 2 two squares\n 3 a large square, two small ones stacked beside it\n 4 a wide one over three\n 5 two over three\n 6 a large square, two beside it, three under it */\n.aparte-media__grid {\n display: grid;\n grid-template-columns: repeat(6, minmax(0, 1fr));\n gap: var(--aparte-media-gap);\n margin: 0;\n padding: 0;\n list-style: none;\n aspect-ratio: 3 / 2;\n}\n\n.aparte-media__grid > * {\n display: flex;\n grid-column: span 2;\n min-inline-size: 0;\n min-block-size: 0;\n}\n\n.aparte-media__grid > * > .aparte-media__tile { flex: 1; }\n\n.aparte-media__grid[data-count="2"] { grid-template-rows: minmax(0, 1fr); aspect-ratio: 2 / 1; }\n.aparte-media__grid[data-count="2"] > * { grid-column: span 3; }\n\n.aparte-media__grid[data-count="3"] { grid-template-rows: repeat(2, minmax(0, 1fr)); }\n.aparte-media__grid[data-count="3"] > :first-child { grid-column: span 4; grid-row: span 2; }\n\n.aparte-media__grid[data-count="4"] { grid-template-rows: minmax(0, 2fr) minmax(0, 1fr); aspect-ratio: 4 / 3; }\n.aparte-media__grid[data-count="4"] > :first-child { grid-column: span 6; }\n\n.aparte-media__grid[data-count="5"] { grid-template-rows: repeat(2, minmax(0, 1fr)); }\n.aparte-media__grid[data-count="5"] > :nth-child(-n + 2) { grid-column: span 3; }\n\n.aparte-media__grid[data-count="6"] { grid-template-rows: repeat(3, minmax(0, 1fr)); aspect-ratio: 1; }\n.aparte-media__grid[data-count="6"] > :first-child { grid-column: span 4; grid-row: span 2; }\n\n/* The collection\'s line: under the mosaic, over the strip. */\n.aparte-media__heading {\n display: flex;\n align-items: center;\n gap: var(--aparte-space-3);\n min-inline-size: 0;\n color: var(--aparte-text-muted);\n font-size: var(--aparte-font-size-md);\n}\n\n.aparte-media--gallery > .aparte-media__heading { padding: var(--aparte-space-4) var(--aparte-space-6) var(--aparte-space-5); }\n\n.aparte-media__heading-title {\n overflow: hidden;\n color: var(--aparte-text);\n font-weight: var(--aparte-font-weight-semibold);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.aparte-media__heading-title + .aparte-media__count::before {\n content: "·";\n margin-inline-end: var(--aparte-space-3);\n}\n\n.aparte-media__count { flex: none; }\n\n/* ── Strip: square thumbnails in the kit\'s carousel ──────────────────── */\n\n.aparte-media--strip {\n display: grid;\n gap: var(--aparte-space-5);\n padding: var(--aparte-space-5) var(--aparte-space-6) var(--aparte-space-6);\n}\n\n.aparte-media--strip .aparte-rich-card__carousel {\n --aparte-rich-card-slide-size: 7rem;\n --aparte-rich-card-slide-gap: var(--aparte-space-4);\n}\n\n.aparte-media__thumb {\n display: flex;\n flex-direction: column;\n gap: var(--aparte-space-2);\n min-inline-size: 0;\n}\n\n.aparte-media__thumb > .aparte-media__tile {\n block-size: auto;\n aspect-ratio: 1;\n border-radius: var(--aparte-radius-lg);\n box-shadow: 0 0 0 var(--aparte-border-width) color-mix(in oklab, var(--aparte-text) 8%, transparent);\n transition: translate var(--aparte-duration-base, 0.15s) ease, box-shadow var(--aparte-duration-base, 0.15s) ease;\n}\n\n.aparte-media__thumb > .aparte-media__tile:hover {\n translate: 0 -2px;\n box-shadow: 0 10px 20px -14px rgb(0 0 0 / 0.55);\n}\n\n.aparte-media__thumb-title {\n overflow: hidden;\n color: var(--aparte-text-muted);\n font-size: var(--aparte-font-size-sm);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n/* ── The lightbox ────────────────────────────────────────────────────── */\n\n/* Two classes: it must win over the recipe\'s `--lg` whatever order the sheets land in. */\n.aparte-dialog.aparte-media__lightbox { --aparte-dialog-width: 56rem; }\n\n.aparte-media__lb-head { padding-block-end: var(--aparte-space-4); }\n\n.aparte-media__lb-heading {\n display: flex;\n flex: 1;\n align-items: baseline;\n gap: var(--aparte-space-4);\n min-inline-size: 0;\n}\n\n.aparte-media__lb-title {\n flex: 0 1 auto;\n overflow: hidden;\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.aparte-media__lb-counter {\n flex: none;\n color: var(--aparte-text-muted);\n font-size: var(--aparte-font-size-sm);\n font-variant-numeric: tabular-nums;\n}\n\n/* The stage: the picture on black, as large as the window lets it be. */\n.aparte-media__lb-body {\n position: relative;\n display: grid;\n min-block-size: 12rem;\n background: var(--aparte-media-stage);\n}\n\n.aparte-media__lb-stage {\n display: grid;\n place-items: center;\n min-inline-size: 0;\n max-block-size: var(--aparte-media-lightbox-block);\n}\n\n.aparte-media__lb-picture {\n position: relative;\n display: grid;\n place-items: center;\n inline-size: 100%;\n min-block-size: 12rem;\n}\n\n.aparte-media__lb-picture > .aparte-media__placeholder {\n background: none;\n color: rgb(255 255 255 / 0.4);\n}\n\n.aparte-media__lb-picture:not([data-loading], [data-broken]) > .aparte-media__placeholder { display: none; }\n\n.aparte-media__lb-img {\n position: relative;\n z-index: 1;\n display: block;\n inline-size: auto;\n block-size: auto;\n max-inline-size: 100%;\n max-block-size: var(--aparte-media-lightbox-block);\n object-fit: contain;\n}\n\n.aparte-media__lb-video {\n inline-size: 100%;\n max-block-size: var(--aparte-media-lightbox-block);\n}\n\n.aparte-media__lb-stage > .aparte-media__external {\n inline-size: 100%;\n max-block-size: var(--aparte-media-lightbox-block);\n border-radius: 0;\n}\n\n.aparte-media__lb-stage > .aparte-media__audio {\n inline-size: min(100% - var(--aparte-space-8) * 2, 28rem);\n margin: var(--aparte-space-8);\n padding: var(--aparte-space-6);\n border-radius: var(--aparte-radius-xl);\n background: var(--aparte-surface-1);\n}\n\n.aparte-media__lb-nav {\n position: absolute;\n inset-block-start: 50%;\n z-index: 2;\n translate: 0 -50%;\n box-shadow: 0 2px 12px -2px rgb(0 0 0 / 0.45);\n}\n\n.aparte-media__lb-nav--prev { inset-inline-start: var(--aparte-space-5); }\n.aparte-media__lb-nav--next { inset-inline-end: var(--aparte-space-5); }\n.aparte-media__lb-nav:dir(rtl) { transform: scaleX(-1); }\n\n/* At either end the button stays where it is, and stays focusable — a disabled button\n would drop the keyboard\'s focus out of the dialog — but says it leads nowhere. */\n.aparte-media__lb-nav[aria-disabled="true"] {\n opacity: 0.35;\n cursor: default;\n}\n\n.aparte-media__lb-caption {\n display: flex;\n flex-direction: column;\n gap: var(--aparte-space-1);\n padding: var(--aparte-space-5) var(--aparte-space-6) var(--aparte-space-6);\n}\n\n.aparte-media__lb-caption[hidden] { display: none; }\n\n.aparte-media__lb-caption > .aparte-media__caption { -webkit-line-clamp: 6; }\n\n/* ── Loading ─────────────────────────────────────────────────────────── */\n\n.aparte-media-card[data-state="loading"] .aparte-media__sk-frame {\n block-size: auto;\n aspect-ratio: 4 / 3;\n border-radius: var(--aparte-radius-lg);\n}\n\n.aparte-media-card[data-state="loading"] .aparte-media__sk-cell {\n block-size: auto;\n border-radius: 0;\n}\n\n.aparte-media-card[data-state="loading"] .aparte-media__sk-line { inline-size: 60%; }\n.aparte-media-card[data-state="loading"] .aparte-media__sk-line--short { inline-size: 35%; }\n\n.aparte-media__sk-row {\n display: flex;\n gap: var(--aparte-space-4);\n overflow: hidden;\n}\n\n.aparte-media-card[data-state="loading"] .aparte-media__sk-thumb {\n flex: none;\n inline-size: 7rem;\n block-size: 7rem;\n border-radius: var(--aparte-radius-lg);\n}\n\n/* ── Small screens, and stillness ────────────────────────────────────── */\n\n@media (max-width: 380px) {\n .aparte-media-card { --aparte-media-max-block: 18rem; }\n .aparte-media--single { padding: var(--aparte-space-3) var(--aparte-space-3) var(--aparte-space-4); }\n .aparte-media--strip { padding: var(--aparte-space-4) var(--aparte-space-5) var(--aparte-space-5); }\n .aparte-media--strip .aparte-rich-card__carousel { --aparte-rich-card-slide-size: 6rem; }\n .aparte-media__play { inline-size: 3rem; block-size: 3rem; }\n .aparte-media__lb-nav--prev { inset-inline-start: var(--aparte-space-3); }\n .aparte-media__lb-nav--next { inset-inline-end: var(--aparte-space-3); }\n}\n\n@media (prefers-reduced-motion: reduce) {\n .aparte-media__frame:hover > .aparte-media__img,\n .aparte-media__tile:hover > .aparte-media__img,\n .aparte-media__external:hover > .aparte-media__img { transform: none; }\n .aparte-media__thumb > .aparte-media__tile:hover { translate: none; }\n .aparte-media__external:hover .aparte-media__play,\n .aparte-media__tile:hover .aparte-media__play { scale: none; }\n .aparte-media__audio[data-playing] .aparte-media__wave { animation: none; }\n}\n';
|
|
16
|
+
const M = {
|
|
17
|
+
cardsMediaTitle: { en: "Media", fr: "Médias", de: "Medien", es: "Multimedia", it: "Contenuti multimediali", ja: "メディア", ko: "미디어", pt: "Mídia", ru: "Медиа", zh: "媒体" },
|
|
18
|
+
cardsMediaPhotoCount: { en: "{n} photos", fr: "{n} photos", de: "{n} Fotos", es: "{n} fotos", it: "{n} foto", ja: "写真{n}枚", ko: "사진 {n}장", pt: "{n} fotos", ru: "Фото: {n}", zh: "{n} 张照片" },
|
|
19
|
+
cardsMediaVideoCount: { en: "{n} videos", fr: "{n} vidéos", de: "{n} Videos", es: "{n} vídeos", it: "{n} video", ja: "動画{n}本", ko: "동영상 {n}개", pt: "{n} vídeos", ru: "Видео: {n}", zh: "{n} 个视频" },
|
|
20
|
+
cardsMediaCount: { en: "{n} items", fr: "{n} éléments", de: "{n} Elemente", es: "{n} elementos", it: "{n} elementi", ja: "メディア{n}件", ko: "미디어 {n}개", pt: "{n} itens", ru: "Файлов: {n}", zh: "{n} 个媒体" },
|
|
21
|
+
cardsMediaImage: { en: "Image", fr: "Image", de: "Bild", es: "Imagen", it: "Immagine", ja: "画像", ko: "이미지", pt: "Imagem", ru: "Изображение", zh: "图片" },
|
|
22
|
+
cardsMediaVideo: { en: "Video", fr: "Vidéo", de: "Video", es: "Vídeo", it: "Video", ja: "動画", ko: "동영상", pt: "Vídeo", ru: "Видео", zh: "视频" },
|
|
23
|
+
cardsMediaAudio: { en: "Audio", fr: "Audio", de: "Audio", es: "Audio", it: "Audio", ja: "音声", ko: "오디오", pt: "Áudio", ru: "Аудио", zh: "音频" },
|
|
24
|
+
cardsMediaMore: { en: "{n} more", fr: "{n} de plus", de: "{n} weitere", es: "{n} más", it: "altri {n}", ja: "ほか{n}件", ko: "{n}개 더", pt: "mais {n}", ru: "ещё {n}", zh: "另外 {n} 个" },
|
|
25
|
+
cardsMediaView: { en: "View larger", fr: "Agrandir", de: "Vergrößern", es: "Ampliar", it: "Ingrandisci", ja: "拡大表示", ko: "크게 보기", pt: "Ampliar", ru: "Увеличить", zh: "放大查看" },
|
|
26
|
+
cardsMediaClose: { en: "Close", fr: "Fermer", de: "Schließen", es: "Cerrar", it: "Chiudi", ja: "閉じる", ko: "닫기", pt: "Fechar", ru: "Закрыть", zh: "关闭" },
|
|
27
|
+
cardsMediaWatchOn: { en: "Watch on {site}", fr: "Regarder sur {site}", de: "Auf {site} ansehen", es: "Ver en {site}", it: "Guarda su {site}", ja: "{site}で見る", ko: "{site}에서 보기", pt: "Assistir no {site}", ru: "Смотреть на {site}", zh: "在 {site} 上观看" }
|
|
28
|
+
};
|
|
29
|
+
const KIND_WORD = { image: "cardsMediaImage", video: "cardsMediaVideo", audio: "cardsMediaAudio" };
|
|
30
|
+
const MAX_MEDIA = 24;
|
|
31
|
+
const GALLERY_TILES = 6;
|
|
32
|
+
const IMAGE_EXT = /\.(jpe?g|png|gif|webp|avif|svg|bmp|ico|heic|heif|jxl)$/i;
|
|
33
|
+
const VIDEO_EXT = /\.(mp4|m4v|webm|mov|ogv|mkv|m3u8|mpd)$/i;
|
|
34
|
+
const AUDIO_EXT = /\.(mp3|m4a|aac|wav|ogg|oga|opus|flac|weba)$/i;
|
|
35
|
+
function videoSite(url) {
|
|
36
|
+
const host = new URL(url).hostname.replace(/^(www|m)\./, "");
|
|
37
|
+
if (host === "youtu.be" || /(^|\.)youtube(-nocookie)?\.com$/.test(host)) return "YouTube";
|
|
38
|
+
if (/(^|\.)vimeo\.com$/.test(host)) return "Vimeo";
|
|
39
|
+
return void 0;
|
|
40
|
+
}
|
|
41
|
+
function kindOfWord(value) {
|
|
42
|
+
const s = str(value)?.toLowerCase();
|
|
43
|
+
if (!s) return void 0;
|
|
44
|
+
if (/^image\/|^(image|photo|picture|img|gif|illustration|drawing)s?$/.test(s)) return "image";
|
|
45
|
+
if (/^video\/|^application\/(x-mpegurl|vnd\.apple\.mpegurl|dash\+xml)$|^(video|movie|clip|film)s?$/.test(s)) return "video";
|
|
46
|
+
if (/^audio\/|^(audio|sound|music|song|track|podcast|voice|recording)s?$/.test(s)) return "audio";
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
function streamType(m) {
|
|
50
|
+
if (m.mimeType && /^application\/(x-mpegurl|vnd\.apple\.mpegurl|dash\+xml)$/i.test(m.mimeType)) return m.mimeType;
|
|
51
|
+
const path = new URL(m.src).pathname;
|
|
52
|
+
if (/\.m3u8$/i.test(path)) return "application/vnd.apple.mpegurl";
|
|
53
|
+
if (/\.mpd$/i.test(path)) return "application/dash+xml";
|
|
54
|
+
return void 0;
|
|
55
|
+
}
|
|
56
|
+
function playsHere(m) {
|
|
57
|
+
const type = streamType(m);
|
|
58
|
+
return !type || document.createElement("video").canPlayType(type) !== "";
|
|
59
|
+
}
|
|
60
|
+
function kindOfUrl(url) {
|
|
61
|
+
const path = new URL(url).pathname;
|
|
62
|
+
if (IMAGE_EXT.test(path)) return "image";
|
|
63
|
+
if (VIDEO_EXT.test(path)) return "video";
|
|
64
|
+
if (AUDIO_EXT.test(path)) return "audio";
|
|
65
|
+
return void 0;
|
|
66
|
+
}
|
|
67
|
+
function readDuration(value) {
|
|
68
|
+
const n = num(value);
|
|
69
|
+
if (n !== void 0) return n >= 0 ? n : void 0;
|
|
70
|
+
const s = str(value);
|
|
71
|
+
if (!s) return void 0;
|
|
72
|
+
const clock = /^(?:(\d+):)?(\d{1,2}):(\d{2})(?:\.\d+)?$/.exec(s);
|
|
73
|
+
if (clock) {
|
|
74
|
+
const [h, m, sec] = [Number(clock[1] ?? 0), Number(clock[2]), Number(clock[3])];
|
|
75
|
+
if (sec > 59 || clock[1] !== void 0 && m > 59) return void 0;
|
|
76
|
+
return h * 3600 + m * 60 + sec;
|
|
77
|
+
}
|
|
78
|
+
const iso = /^PT(?:(\d+(?:\.\d+)?)H)?(?:(\d+(?:\.\d+)?)M)?(?:(\d+(?:\.\d+)?)S)?$/i.exec(s);
|
|
79
|
+
if (iso && (iso[1] || iso[2] || iso[3])) return Number(iso[1] ?? 0) * 3600 + Number(iso[2] ?? 0) * 60 + Number(iso[3] ?? 0);
|
|
80
|
+
return void 0;
|
|
81
|
+
}
|
|
82
|
+
function textOf(value) {
|
|
83
|
+
if (value && typeof value === "object") {
|
|
84
|
+
const o = value;
|
|
85
|
+
return str(o["name"] ?? o["title"] ?? o["text"] ?? o["username"]);
|
|
86
|
+
}
|
|
87
|
+
return str(value);
|
|
88
|
+
}
|
|
89
|
+
function linkOf(value) {
|
|
90
|
+
const first = Array.isArray(value) ? value[0] : value;
|
|
91
|
+
if (!first || typeof first !== "object") return first;
|
|
92
|
+
const o = first;
|
|
93
|
+
for (const key of ["url", "src", "href", "uri", "link", "regular", "large2x", "large", "medium", "original", "full", "small"]) {
|
|
94
|
+
if (typeof o[key] === "string") return o[key];
|
|
95
|
+
}
|
|
96
|
+
return void 0;
|
|
97
|
+
}
|
|
98
|
+
function address(value) {
|
|
99
|
+
const s = str(linkOf(value));
|
|
100
|
+
if (!s || !(/^https?:\/\//i.test(s) || s.startsWith("/"))) return void 0;
|
|
101
|
+
if (s.startsWith("/") && !s.startsWith("//")) {
|
|
102
|
+
const own = safeUrl(s);
|
|
103
|
+
return own && /^https?:/i.test(own) ? own : void 0;
|
|
104
|
+
}
|
|
105
|
+
return webUrl(s.startsWith("//") ? `https:${s}` : s) ?? void 0;
|
|
106
|
+
}
|
|
107
|
+
const positive = (value) => {
|
|
108
|
+
const n = num(value);
|
|
109
|
+
return n !== void 0 && n > 0 ? n : void 0;
|
|
110
|
+
};
|
|
111
|
+
function readMedium(raw) {
|
|
112
|
+
const value = typeof raw === "string" ? { src: raw } : raw;
|
|
113
|
+
if (!value || typeof value !== "object" || Array.isArray(value)) return null;
|
|
114
|
+
const r = value;
|
|
115
|
+
const urls = r["urls"] && typeof r["urls"] === "object" ? r["urls"] : {};
|
|
116
|
+
const videoFile = Array.isArray(r["video_files"]) ? r["video_files"].find((f) => address(f?.["link"])) : void 0;
|
|
117
|
+
const sources = [
|
|
118
|
+
["src", r["src"]],
|
|
119
|
+
["videoFile", videoFile?.["link"]],
|
|
120
|
+
["image", r["image"]],
|
|
121
|
+
["imageUrl", r["imageUrl"] ?? r["image_url"]],
|
|
122
|
+
["urls", urls["regular"] ?? urls["full"] ?? urls["small"] ?? urls["raw"]],
|
|
123
|
+
["contentUrl", r["contentUrl"] ?? r["fileUrl"] ?? r["file_url"]],
|
|
124
|
+
["url", r["url"]],
|
|
125
|
+
["link", r["link"]],
|
|
126
|
+
["href", r["href"]],
|
|
127
|
+
["thumbnail", r["thumbnail"]]
|
|
128
|
+
];
|
|
129
|
+
let src;
|
|
130
|
+
let from = "";
|
|
131
|
+
for (const [key, candidate] of sources) {
|
|
132
|
+
const url = address(candidate);
|
|
133
|
+
if (url) {
|
|
134
|
+
src = url;
|
|
135
|
+
from = key;
|
|
136
|
+
break;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
if (!src) return null;
|
|
140
|
+
const page = [r["page"], r["pageUrl"], r["page_url"], r["links"]?.["html"], from === "url" ? void 0 : r["url"], from === "link" ? void 0 : r["link"], r["sourceUrl"], r["source_url"]].map(address).find((v) => v && v !== src);
|
|
141
|
+
const poster = [r["poster"], r["thumb"], r["preview"], r["previewUrl"], r["preview_url"], urls["small"], from === "thumbnail" ? void 0 : r["thumbnail"], videoFile ? r["image"] : void 0, r["cover"], r["artwork"]].map(address).find((v) => v && v !== src);
|
|
142
|
+
const mimeType = str(r["mimeType"] ?? r["mime_type"] ?? r["mime"] ?? r["contentType"] ?? r["content_type"] ?? r["encodingFormat"] ?? videoFile?.["file_type"]);
|
|
143
|
+
const site = videoSite(src);
|
|
144
|
+
const kind = site ? "video" : kindOfWord(r["kind"] ?? r["mediaType"] ?? r["media_type"] ?? r["type"]) ?? kindOfWord(mimeType) ?? (videoFile ? "video" : void 0) ?? kindOfUrl(src) ?? "image";
|
|
145
|
+
const creditText = textOf(r["credit"] ?? r["author"] ?? r["photographer"] ?? r["artist"] ?? r["creator"] ?? r["user"] ?? r["source"] ?? r["attribution"] ?? r["channelTitle"] ?? r["channel"]);
|
|
146
|
+
const width = positive(r["width"] ?? r["w"]);
|
|
147
|
+
const height = positive(r["height"] ?? r["h"]);
|
|
148
|
+
const ms = positive(r["duration_ms"] ?? r["durationMs"]);
|
|
149
|
+
const duration = ms !== void 0 ? ms / 1e3 : readDuration(r["duration"] ?? r["length"] ?? r["durationSeconds"] ?? r["duration_seconds"]);
|
|
150
|
+
return {
|
|
151
|
+
kind,
|
|
152
|
+
src,
|
|
153
|
+
...site ? { site } : {},
|
|
154
|
+
poster,
|
|
155
|
+
title: textOf(r["title"] ?? r["name"]),
|
|
156
|
+
caption: textOf(r["caption"] ?? r["description"] ?? r["revised_prompt"] ?? r["revisedPrompt"] ?? r["summary"]),
|
|
157
|
+
credit: creditText && !/^https?:\/\//i.test(creditText) ? creditText : void 0,
|
|
158
|
+
alt: str(r["alt"] ?? r["altText"] ?? r["alt_text"] ?? r["alt_description"] ?? r["revised_prompt"]),
|
|
159
|
+
// Both or neither: one side alone says nothing of the shape.
|
|
160
|
+
...width && height ? { width, height } : {},
|
|
161
|
+
duration,
|
|
162
|
+
mimeType: mimeType?.includes("/") ? mimeType : void 0,
|
|
163
|
+
url: page
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
function readMedia(raw) {
|
|
167
|
+
const value = typeof raw === "string" ? [raw] : raw;
|
|
168
|
+
if (!value || typeof value !== "object") return null;
|
|
169
|
+
let title;
|
|
170
|
+
let list;
|
|
171
|
+
if (Array.isArray(value)) {
|
|
172
|
+
list = value;
|
|
173
|
+
} else {
|
|
174
|
+
const r = value;
|
|
175
|
+
const nested = ["items", "media", "images", "photos", "videos", "results", "data", "files"].map((k) => r[k]).find(Array.isArray);
|
|
176
|
+
if (nested) {
|
|
177
|
+
list = nested;
|
|
178
|
+
title = str(r["title"]);
|
|
179
|
+
} else {
|
|
180
|
+
list = [value];
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const items = list.map(readMedium).filter((m) => m !== null).slice(0, MAX_MEDIA);
|
|
184
|
+
return items.length ? { ...title ? { title } : {}, items } : null;
|
|
185
|
+
}
|
|
186
|
+
function formatDuration(config, seconds) {
|
|
187
|
+
const total = Math.round(seconds);
|
|
188
|
+
const h = Math.floor(total / 3600);
|
|
189
|
+
const m = Math.floor(total % 3600 / 60);
|
|
190
|
+
const s = total % 60;
|
|
191
|
+
const plain = (v) => formatNumber(config, v, { useGrouping: false });
|
|
192
|
+
const two = (v) => formatNumber(config, v, { minimumIntegerDigits: 2, useGrouping: false });
|
|
193
|
+
return h ? `${plain(h)}:${two(m)}:${two(s)}` : `${plain(m)}:${two(s)}`;
|
|
194
|
+
}
|
|
195
|
+
function spokenDuration(config, seconds) {
|
|
196
|
+
const total = Math.round(seconds);
|
|
197
|
+
const parts = [[Math.floor(total / 3600), "hour"], [Math.floor(total % 3600 / 60), "minute"], [total % 60, "second"]];
|
|
198
|
+
const said = parts.filter(([v]) => v > 0).map(([v, unit]) => formatUnit(config, v, unit));
|
|
199
|
+
return said.length ? said.join(" ") : formatUnit(config, 0, "second");
|
|
200
|
+
}
|
|
201
|
+
function isoDuration(seconds) {
|
|
202
|
+
const total = Math.round(seconds);
|
|
203
|
+
return `PT${Math.floor(total / 3600)}H${Math.floor(total % 3600 / 60)}M${total % 60}S`;
|
|
204
|
+
}
|
|
205
|
+
const kindWord = (config, kind) => word(config, KIND_WORD[kind], M[KIND_WORD[kind]]);
|
|
206
|
+
function nameOf(config, m, index) {
|
|
207
|
+
const own = m.title ?? m.alt ?? m.caption;
|
|
208
|
+
if (own) return own;
|
|
209
|
+
return index === void 0 ? kindWord(config, m.kind) : `${kindWord(config, m.kind)} ${formatNumber(config, index + 1)}`;
|
|
210
|
+
}
|
|
211
|
+
const altOf = (config, m, index) => m.alt ?? nameOf(config, m, index);
|
|
212
|
+
function countLabel(config, items) {
|
|
213
|
+
const n = formatNumber(config, items.length);
|
|
214
|
+
if (items.every((m) => m.kind === "image")) return word(config, "cardsMediaPhotoCount", M.cardsMediaPhotoCount, { n });
|
|
215
|
+
if (items.every((m) => m.kind === "video")) return word(config, "cardsMediaVideoCount", M.cardsMediaVideoCount, { n });
|
|
216
|
+
return word(config, "cardsMediaCount", M.cardsMediaCount, { n });
|
|
217
|
+
}
|
|
218
|
+
function placeholder(kind) {
|
|
219
|
+
return el(
|
|
220
|
+
"span",
|
|
221
|
+
{ class: "aparte-media__placeholder", attrs: { "aria-hidden": "true" } },
|
|
222
|
+
glyph(kindGlyph(kind), "aparte-rich-card__glyph aparte-media__placeholder-glyph")
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
function picture(ctx, holder, src, alt, className = "aparte-media__img") {
|
|
226
|
+
const img = el("img", { class: className, attrs: { src, loading: "lazy", decoding: "async" } });
|
|
227
|
+
if (alt) ctx.bind(img, alt, "alt");
|
|
228
|
+
else img.setAttribute("alt", "");
|
|
229
|
+
img.addEventListener("load", () => holder.removeAttribute("data-loading"), { once: true });
|
|
230
|
+
img.addEventListener("error", () => {
|
|
231
|
+
img.remove();
|
|
232
|
+
holder.removeAttribute("data-loading");
|
|
233
|
+
holder.setAttribute("data-broken", "");
|
|
234
|
+
}, { once: true });
|
|
235
|
+
return img;
|
|
236
|
+
}
|
|
237
|
+
function reserve(holder, m) {
|
|
238
|
+
if (!m.width || !m.height) return;
|
|
239
|
+
holder.style.setProperty("--aparte-media-ratio", `${m.width} / ${m.height}`);
|
|
240
|
+
holder.style.setProperty("--aparte-media-ratio-n", String(m.width / m.height));
|
|
241
|
+
holder.setAttribute("data-sized", "");
|
|
242
|
+
}
|
|
243
|
+
function durationOf(ctx, seconds, className, hidden = false) {
|
|
244
|
+
const node = el("time", { class: className, attrs: { datetime: isoDuration(seconds), "aria-hidden": hidden ? "true" : void 0 } });
|
|
245
|
+
ctx.bind(node, () => formatDuration(ctx.config, seconds));
|
|
246
|
+
return node;
|
|
247
|
+
}
|
|
248
|
+
const hostOf = (url) => new URL(url).hostname.replace(/^www\./, "");
|
|
249
|
+
function imageFrame(ctx, m, open) {
|
|
250
|
+
const frame = el("button", { class: "aparte-media__frame", attrs: { type: "button", "aria-haspopup": "dialog", "data-kind": "image", "data-loading": "" } });
|
|
251
|
+
reserve(frame, m);
|
|
252
|
+
frame.append(
|
|
253
|
+
placeholder("image"),
|
|
254
|
+
picture(ctx, frame, m.src, () => altOf(ctx.config, m)),
|
|
255
|
+
glyph(EXPAND, "aparte-rich-card__glyph aparte-media__zoom")
|
|
256
|
+
);
|
|
257
|
+
ctx.bind(frame, () => word(ctx.config, "cardsMediaView", M.cardsMediaView), "title");
|
|
258
|
+
frame.addEventListener("click", () => open(frame));
|
|
259
|
+
return frame;
|
|
260
|
+
}
|
|
261
|
+
function videoFrame(ctx, m, className = "aparte-media__frame aparte-media__frame--video") {
|
|
262
|
+
const frame = el("div", { class: className, attrs: { "data-kind": "video" } });
|
|
263
|
+
reserve(frame, m);
|
|
264
|
+
const video = el("video", {
|
|
265
|
+
class: "aparte-media__video",
|
|
266
|
+
attrs: { controls: true, preload: "metadata", playsinline: true, src: m.src, poster: m.poster }
|
|
267
|
+
});
|
|
268
|
+
ctx.bind(video, () => nameOf(ctx.config, m), "aria-label");
|
|
269
|
+
frame.append(video);
|
|
270
|
+
return frame;
|
|
271
|
+
}
|
|
272
|
+
function externalFrame(ctx, m) {
|
|
273
|
+
const href = m.site ? m.src : m.url ?? m.src;
|
|
274
|
+
const site = m.site ?? hostOf(href);
|
|
275
|
+
const frame = el("a", {
|
|
276
|
+
class: "aparte-media__frame aparte-media__external",
|
|
277
|
+
attrs: { href, target: "_blank", rel: "noopener noreferrer", "data-kind": "video" }
|
|
278
|
+
});
|
|
279
|
+
reserve(frame, m);
|
|
280
|
+
frame.append(placeholder("video"));
|
|
281
|
+
if (m.poster) frame.append(picture(ctx, frame, m.poster, null));
|
|
282
|
+
frame.append(
|
|
283
|
+
el("span", { class: "aparte-media__play", attrs: { "aria-hidden": "true" } }, glyph(PLAY, "aparte-rich-card__glyph aparte-media__play-glyph")),
|
|
284
|
+
el("span", { class: "aparte-media__site", attrs: { "aria-hidden": "true" } }, glyph(EXTERNAL, "aparte-rich-card__glyph aparte-media__site-glyph"), site)
|
|
285
|
+
);
|
|
286
|
+
if (m.duration !== void 0) frame.append(durationOf(ctx, m.duration, "aparte-media__duration", true));
|
|
287
|
+
ctx.bind(frame, () => `${nameOf(ctx.config, m)} — ${word(ctx.config, "cardsMediaWatchOn", M.cardsMediaWatchOn, { site })}`, "aria-label");
|
|
288
|
+
return frame;
|
|
289
|
+
}
|
|
290
|
+
function audioBlock(ctx, m) {
|
|
291
|
+
const cover = el("span", { class: "aparte-media__cover", attrs: { "data-kind": "audio" } }, placeholder("audio"));
|
|
292
|
+
if (m.poster) cover.append(picture(ctx, cover, m.poster, null));
|
|
293
|
+
const title = el("div", { class: "aparte-media__audio-title" });
|
|
294
|
+
ctx.bind(title, () => m.title ?? kindWord(ctx.config, "audio"));
|
|
295
|
+
const text = el(
|
|
296
|
+
"div",
|
|
297
|
+
{ class: "aparte-media__audio-text" },
|
|
298
|
+
title,
|
|
299
|
+
m.credit ? el("div", { class: "aparte-media__audio-credit", text: m.credit }) : null
|
|
300
|
+
);
|
|
301
|
+
const head = el(
|
|
302
|
+
"div",
|
|
303
|
+
{ class: "aparte-media__audio-head" },
|
|
304
|
+
cover,
|
|
305
|
+
text,
|
|
306
|
+
m.duration !== void 0 ? durationOf(ctx, m.duration, "aparte-media__audio-length") : null
|
|
307
|
+
);
|
|
308
|
+
const audio = el("audio", { class: "aparte-media__player", attrs: { controls: true, preload: "metadata", src: m.src } });
|
|
309
|
+
ctx.bind(audio, () => nameOf(ctx.config, m), "aria-label");
|
|
310
|
+
const block = el(
|
|
311
|
+
"div",
|
|
312
|
+
{ class: "aparte-media__audio", attrs: { "data-kind": "audio" } },
|
|
313
|
+
head,
|
|
314
|
+
el("span", { class: "aparte-media__wave", attrs: { "aria-hidden": "true" } }),
|
|
315
|
+
audio
|
|
316
|
+
);
|
|
317
|
+
audio.addEventListener("play", () => block.setAttribute("data-playing", ""));
|
|
318
|
+
for (const stop of ["pause", "ended", "emptied"]) audio.addEventListener(stop, () => block.removeAttribute("data-playing"));
|
|
319
|
+
return block;
|
|
320
|
+
}
|
|
321
|
+
function captionOf(m, withTitle, tag = "figcaption") {
|
|
322
|
+
const title = withTitle && m.title ? el("div", { class: "aparte-media__title", text: m.title }) : null;
|
|
323
|
+
const caption = m.caption && m.caption !== m.title ? el("p", { class: "aparte-media__caption", text: m.caption }) : null;
|
|
324
|
+
const credit = m.credit && m.kind !== "audio" ? el("span", { class: "aparte-media__credit" }, glyph(m.kind === "image" ? CAMERA : kindGlyph(m.kind), "aparte-rich-card__glyph aparte-media__line-glyph"), el("span", { text: m.credit })) : null;
|
|
325
|
+
const source = m.url ? el(
|
|
326
|
+
"a",
|
|
327
|
+
{ class: "aparte-media__source", attrs: { href: m.url, target: "_blank", rel: "noopener noreferrer" } },
|
|
328
|
+
el("span", { text: hostOf(m.url) }),
|
|
329
|
+
glyph(EXTERNAL, "aparte-rich-card__glyph aparte-media__line-glyph")
|
|
330
|
+
) : null;
|
|
331
|
+
const meta = credit || source ? el("div", { class: "aparte-media__meta" }, credit, source) : null;
|
|
332
|
+
if (!title && !caption && !meta) return null;
|
|
333
|
+
return el(tag, { class: "aparte-media__figcaption" }, title, caption, meta);
|
|
334
|
+
}
|
|
335
|
+
function stageOf(ctx, m, open, index) {
|
|
336
|
+
if (m.kind === "audio") return audioBlock(ctx, m);
|
|
337
|
+
if (m.site || m.kind === "video" && !playsHere(m)) return externalFrame(ctx, m);
|
|
338
|
+
if (m.kind === "video") return videoFrame(ctx, m, open ? void 0 : "aparte-media__lb-video");
|
|
339
|
+
if (open) return imageFrame(ctx, m, open);
|
|
340
|
+
const box = el("div", { class: "aparte-media__lb-picture", attrs: { "data-loading": "" } }, placeholder("image"));
|
|
341
|
+
const img = picture(ctx, box, m.src, () => altOf(ctx.config, m, index), "aparte-media__lb-img");
|
|
342
|
+
img.removeAttribute("loading");
|
|
343
|
+
box.append(img);
|
|
344
|
+
return box;
|
|
345
|
+
}
|
|
346
|
+
function tileOf(ctx, m, index, more, open) {
|
|
347
|
+
const tile = el("button", { class: "aparte-media__tile", attrs: { type: "button", "aria-haspopup": "dialog", "data-kind": m.kind } });
|
|
348
|
+
tile.append(placeholder(m.kind));
|
|
349
|
+
const still = m.kind === "image" ? m.poster ?? m.src : m.poster;
|
|
350
|
+
if (still) tile.append(picture(ctx, tile, still, null, "aparte-media__img"));
|
|
351
|
+
if (m.kind === "video") {
|
|
352
|
+
tile.append(el(
|
|
353
|
+
"span",
|
|
354
|
+
{ class: "aparte-media__play aparte-media__play--sm", attrs: { "aria-hidden": "true" } },
|
|
355
|
+
glyph(PLAY, "aparte-rich-card__glyph aparte-media__play-glyph")
|
|
356
|
+
));
|
|
357
|
+
}
|
|
358
|
+
if (m.kind === "audio" && m.title) tile.append(el("span", { class: "aparte-media__tile-title", attrs: { "aria-hidden": "true" }, text: m.title }));
|
|
359
|
+
if (m.duration !== void 0 && m.kind !== "image") tile.append(durationOf(ctx, m.duration, "aparte-media__duration", true));
|
|
360
|
+
if (more > 0) {
|
|
361
|
+
const plus = el("span", { class: "aparte-media__more", attrs: { "aria-hidden": "true" } });
|
|
362
|
+
ctx.bind(plus, () => `+${formatNumber(ctx.config, more)}`);
|
|
363
|
+
tile.append(plus);
|
|
364
|
+
}
|
|
365
|
+
ctx.bind(tile, () => {
|
|
366
|
+
const parts = [nameOf(ctx.config, m, index)];
|
|
367
|
+
if (m.kind !== "image" && (m.title ?? m.alt ?? m.caption)) parts.push(kindWord(ctx.config, m.kind));
|
|
368
|
+
if (m.duration !== void 0 && m.kind !== "image") parts.push(spokenDuration(ctx.config, m.duration));
|
|
369
|
+
if (more > 0) parts.push(word(ctx.config, "cardsMediaMore", M.cardsMediaMore, { n: formatNumber(ctx.config, more) }));
|
|
370
|
+
return parts.join(", ");
|
|
371
|
+
}, "aria-label");
|
|
372
|
+
tile.addEventListener("click", () => open(index, tile));
|
|
373
|
+
return tile;
|
|
374
|
+
}
|
|
375
|
+
function headingOf(ctx, data) {
|
|
376
|
+
const count = el("span", { class: "aparte-media__count" });
|
|
377
|
+
ctx.bind(count, () => countLabel(ctx.config, data.items));
|
|
378
|
+
return el(
|
|
379
|
+
"div",
|
|
380
|
+
{ class: "aparte-media__heading" },
|
|
381
|
+
glyph(FRAMES, "aparte-rich-card__glyph aparte-media__line-glyph"),
|
|
382
|
+
data.title ? el("span", { class: "aparte-media__heading-title", text: data.title }) : null,
|
|
383
|
+
count
|
|
384
|
+
);
|
|
385
|
+
}
|
|
386
|
+
let lightboxes = 0;
|
|
387
|
+
function live(ctx, target, compute, attr) {
|
|
388
|
+
ctx.bind(target, compute, attr);
|
|
389
|
+
return () => attr ? target.setAttribute(attr, compute()) : target.textContent = compute();
|
|
390
|
+
}
|
|
391
|
+
function lightbox(ctx, items, host) {
|
|
392
|
+
let dialog = null;
|
|
393
|
+
let index = 0;
|
|
394
|
+
let opener = null;
|
|
395
|
+
let stage;
|
|
396
|
+
let caption;
|
|
397
|
+
const refresh = [];
|
|
398
|
+
const navs = [];
|
|
399
|
+
const many = items.length > 1;
|
|
400
|
+
function shut() {
|
|
401
|
+
if (!dialog) return;
|
|
402
|
+
if (typeof dialog.close === "function") {
|
|
403
|
+
dialog.close();
|
|
404
|
+
} else {
|
|
405
|
+
dialog.removeAttribute("open");
|
|
406
|
+
dialog.dispatchEvent(new Event("close"));
|
|
407
|
+
}
|
|
408
|
+
}
|
|
409
|
+
function show(i) {
|
|
410
|
+
index = Math.max(0, Math.min(items.length - 1, i));
|
|
411
|
+
const m = items[index];
|
|
412
|
+
stage.setAttribute("data-kind", m.kind);
|
|
413
|
+
stage.replaceChildren(stageOf(ctx, m, null, many ? index : void 0));
|
|
414
|
+
caption.replaceChildren(...captionOf(m, false, "div")?.childNodes ?? []);
|
|
415
|
+
caption.hidden = !caption.childNodes.length;
|
|
416
|
+
for (const update of refresh) update();
|
|
417
|
+
for (const [button, dir] of navs) button.setAttribute("aria-disabled", String(dir < 0 ? index === 0 : index === items.length - 1));
|
|
418
|
+
}
|
|
419
|
+
function go(i) {
|
|
420
|
+
if (i >= 0 && i < items.length && i !== index) show(i);
|
|
421
|
+
}
|
|
422
|
+
function navButton(dir) {
|
|
423
|
+
const button = el("button", {
|
|
424
|
+
class: `aparte-btn aparte-btn--icon aparte-btn--circle aparte-btn--surface aparte-media__lb-nav aparte-media__lb-nav--${dir < 0 ? "prev" : "next"}`,
|
|
425
|
+
attrs: { type: "button" }
|
|
426
|
+
});
|
|
427
|
+
button.innerHTML = ctx.config.getIcon(dir < 0 ? "prevBranch" : "nextBranch");
|
|
428
|
+
ctx.bind(button, () => word(ctx.config, dir < 0 ? "cardsPrevious" : "cardsNext", dir < 0 ? KIT_WORDS.cardsPrevious : KIT_WORDS.cardsNext), "aria-label");
|
|
429
|
+
button.addEventListener("click", () => go(index + dir));
|
|
430
|
+
navs.push([button, dir]);
|
|
431
|
+
return button;
|
|
432
|
+
}
|
|
433
|
+
function onKey(event) {
|
|
434
|
+
if (event.target?.closest?.("video, audio")) return;
|
|
435
|
+
const rtl = typeof getComputedStyle === "function" && getComputedStyle(dialog).direction === "rtl";
|
|
436
|
+
let to;
|
|
437
|
+
if (event.key === "ArrowRight" || event.key === "ArrowLeft") to = index + (event.key === "ArrowRight" ? 1 : -1) * (rtl ? -1 : 1);
|
|
438
|
+
else if (event.key === "Home") to = 0;
|
|
439
|
+
else if (event.key === "End") to = items.length - 1;
|
|
440
|
+
else if (event.key === "Escape" && typeof dialog.showModal !== "function") {
|
|
441
|
+
event.preventDefault();
|
|
442
|
+
shut();
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
if (to === void 0) return;
|
|
446
|
+
event.preventDefault();
|
|
447
|
+
go(to);
|
|
448
|
+
}
|
|
449
|
+
function build() {
|
|
450
|
+
const id = `aparte-media-lightbox-${++lightboxes}`;
|
|
451
|
+
const title = el("h2", { class: "aparte-dialog__title aparte-media__lb-title", attrs: { id } });
|
|
452
|
+
refresh.push(live(ctx, title, () => nameOf(ctx.config, items[index], many ? index : void 0)));
|
|
453
|
+
const heading = el("div", { class: "aparte-media__lb-heading" }, title);
|
|
454
|
+
if (many) {
|
|
455
|
+
const counter = el("span", { class: "aparte-media__lb-counter" });
|
|
456
|
+
refresh.push(live(ctx, counter, () => word(ctx.config, "cardsSlide", KIT_WORDS.cardsSlide, {
|
|
457
|
+
n: formatNumber(ctx.config, index + 1),
|
|
458
|
+
total: formatNumber(ctx.config, items.length)
|
|
459
|
+
})));
|
|
460
|
+
heading.append(counter);
|
|
461
|
+
}
|
|
462
|
+
const close = el("button", { class: "aparte-btn aparte-btn--icon aparte-btn--sm aparte-btn--ghost aparte-dialog__close aparte-media__lb-close", attrs: { type: "button" } });
|
|
463
|
+
close.innerHTML = ctx.config.getIcon("close");
|
|
464
|
+
ctx.bind(close, () => word(ctx.config, "cardsMediaClose", M.cardsMediaClose), "aria-label");
|
|
465
|
+
close.addEventListener("click", shut);
|
|
466
|
+
stage = el("div", { class: "aparte-media__lb-stage" });
|
|
467
|
+
caption = el("div", { class: "aparte-media__lb-caption" });
|
|
468
|
+
const body = el("div", { class: "aparte-media__lb-body" }, stage);
|
|
469
|
+
if (many) body.append(navButton(-1), navButton(1));
|
|
470
|
+
const node = el(
|
|
471
|
+
"dialog",
|
|
472
|
+
{ class: "aparte-dialog aparte-dialog--lg aparte-media__lightbox", attrs: { "aria-labelledby": id } },
|
|
473
|
+
el("div", { class: "aparte-dialog__header aparte-media__lb-head" }, heading, close),
|
|
474
|
+
body,
|
|
475
|
+
caption
|
|
476
|
+
);
|
|
477
|
+
node.addEventListener("keydown", onKey);
|
|
478
|
+
node.addEventListener("close", () => {
|
|
479
|
+
for (const media of stage.querySelectorAll("video, audio")) if (!media.paused) media.pause();
|
|
480
|
+
opener?.focus();
|
|
481
|
+
});
|
|
482
|
+
return node;
|
|
483
|
+
}
|
|
484
|
+
return (i, from) => {
|
|
485
|
+
if (!dialog) {
|
|
486
|
+
dialog = build();
|
|
487
|
+
host().append(dialog);
|
|
488
|
+
}
|
|
489
|
+
opener = from;
|
|
490
|
+
show(i);
|
|
491
|
+
if (typeof dialog.showModal === "function") {
|
|
492
|
+
if (!dialog.open) dialog.showModal();
|
|
493
|
+
} else {
|
|
494
|
+
dialog.setAttribute("open", "");
|
|
495
|
+
}
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
function render(data, ctx) {
|
|
499
|
+
const [first] = data.items;
|
|
500
|
+
let root;
|
|
501
|
+
const open = lightbox(ctx, data.items, () => root);
|
|
502
|
+
if (data.items.length === 1) {
|
|
503
|
+
root = el(
|
|
504
|
+
"figure",
|
|
505
|
+
{ class: "aparte-media aparte-media--single", attrs: { "data-kind": first.kind } },
|
|
506
|
+
stageOf(ctx, first, (from) => open(0, from)),
|
|
507
|
+
captionOf(first, first.kind !== "audio")
|
|
508
|
+
);
|
|
509
|
+
return [root];
|
|
510
|
+
}
|
|
511
|
+
if (ctx.variant === "strip") {
|
|
512
|
+
const slides = data.items.map((m, i) => el(
|
|
513
|
+
"div",
|
|
514
|
+
{ class: "aparte-media__thumb" },
|
|
515
|
+
tileOf(ctx, m, i, 0, open),
|
|
516
|
+
m.title ? el("span", { class: "aparte-media__thumb-title", attrs: { "aria-hidden": "true" }, text: m.title }) : null
|
|
517
|
+
));
|
|
518
|
+
root = el(
|
|
519
|
+
"div",
|
|
520
|
+
{ class: "aparte-media aparte-media--strip" },
|
|
521
|
+
headingOf(ctx, data),
|
|
522
|
+
carousel(ctx, slides, { label: () => data.title ?? countLabel(ctx.config, data.items) })
|
|
523
|
+
);
|
|
524
|
+
return [root];
|
|
525
|
+
}
|
|
526
|
+
const shown = data.items.slice(0, GALLERY_TILES);
|
|
527
|
+
const more = data.items.length - shown.length;
|
|
528
|
+
const grid = el(
|
|
529
|
+
"ul",
|
|
530
|
+
{ class: "aparte-media__grid", attrs: { "data-count": shown.length } },
|
|
531
|
+
...shown.map((m, i) => el("li", { class: "aparte-media__cell" }, tileOf(ctx, m, i, i === shown.length - 1 ? more : 0, open)))
|
|
532
|
+
);
|
|
533
|
+
ctx.bind(grid, () => data.title ?? countLabel(ctx.config, data.items), "aria-label");
|
|
534
|
+
root = el("div", { class: "aparte-media aparte-media--gallery" }, grid, headingOf(ctx, data));
|
|
535
|
+
return [root];
|
|
536
|
+
}
|
|
537
|
+
function skeleton(partial2, ctx) {
|
|
538
|
+
const block = (cls, kind = "rect") => el("span", { class: `aparte-skeleton aparte-skeleton--${kind} ${cls}`, attrs: { "aria-hidden": "true" } });
|
|
539
|
+
const known = partial2?.title ?? partial2?.items?.[0]?.title;
|
|
540
|
+
if (ctx.variant === "gallery") {
|
|
541
|
+
return [el(
|
|
542
|
+
"div",
|
|
543
|
+
{ class: "aparte-media aparte-media--gallery" },
|
|
544
|
+
el("div", { class: "aparte-media__grid", attrs: { "data-count": 4 } }, ...Array.from({ length: 4 }, () => block("aparte-media__sk-cell"))),
|
|
545
|
+
el("div", { class: "aparte-media__heading" }, known ? el("span", { class: "aparte-media__heading-title", text: known }) : block("aparte-media__sk-line", "text"))
|
|
546
|
+
)];
|
|
547
|
+
}
|
|
548
|
+
if (ctx.variant === "strip") {
|
|
549
|
+
return [el(
|
|
550
|
+
"div",
|
|
551
|
+
{ class: "aparte-media aparte-media--strip" },
|
|
552
|
+
el("div", { class: "aparte-media__heading" }, known ? el("span", { class: "aparte-media__heading-title", text: known }) : block("aparte-media__sk-line", "text")),
|
|
553
|
+
el("div", { class: "aparte-media__sk-row" }, ...Array.from({ length: 4 }, () => block("aparte-media__sk-thumb")))
|
|
554
|
+
)];
|
|
555
|
+
}
|
|
556
|
+
return [el(
|
|
557
|
+
"div",
|
|
558
|
+
{ class: "aparte-media aparte-media--single" },
|
|
559
|
+
block("aparte-media__sk-frame"),
|
|
560
|
+
el(
|
|
561
|
+
"div",
|
|
562
|
+
{ class: "aparte-media__figcaption" },
|
|
563
|
+
known ? el("div", { class: "aparte-media__title", text: known }) : block("aparte-media__sk-line", "text"),
|
|
564
|
+
block("aparte-media__sk-line aparte-media__sk-line--short", "text")
|
|
565
|
+
)
|
|
566
|
+
)];
|
|
567
|
+
}
|
|
568
|
+
function label(data, ctx) {
|
|
569
|
+
const items = data?.items ?? [];
|
|
570
|
+
if (items.length === 1) return nameOf(ctx.config, items[0]);
|
|
571
|
+
if (items.length > 1) return data.title ?? countLabel(ctx.config, items);
|
|
572
|
+
return data?.title ?? word(ctx.config, "cardsMediaTitle", M.cardsMediaTitle);
|
|
573
|
+
}
|
|
574
|
+
function fallback(data, ctx) {
|
|
575
|
+
const lines = data.items.map((m, i) => {
|
|
576
|
+
const name = nameOf(ctx.config, m, data.items.length > 1 ? i : void 0);
|
|
577
|
+
const credit = m.credit ? ` — ${m.credit}` : "";
|
|
578
|
+
const length = m.duration !== void 0 ? ` (${formatDuration(ctx.config, m.duration)})` : "";
|
|
579
|
+
return `${name}${credit}${length}: ${m.src}`;
|
|
580
|
+
});
|
|
581
|
+
return data.title ? `${data.title}:
|
|
582
|
+
${lines.join("\n")}` : lines.join("\n");
|
|
583
|
+
}
|
|
584
|
+
function partial(input) {
|
|
585
|
+
const title = str(input["prompt"] ?? input["query"] ?? input["q"] ?? input["search"] ?? input["title"]);
|
|
586
|
+
return title ? { title } : null;
|
|
587
|
+
}
|
|
588
|
+
const mediaCard = defineCard({
|
|
589
|
+
name: "media",
|
|
590
|
+
variants: ["single", "gallery", "strip"],
|
|
591
|
+
styles: mediaStyles,
|
|
592
|
+
icon: FRAMES,
|
|
593
|
+
normalize: readMedia,
|
|
594
|
+
render,
|
|
595
|
+
skeleton,
|
|
596
|
+
label,
|
|
597
|
+
fallback,
|
|
598
|
+
partial
|
|
599
|
+
});
|
|
600
|
+
function setupMediaCard(options = {}) {
|
|
601
|
+
return mediaCard.setup(options);
|
|
602
|
+
}
|
|
603
|
+
export {
|
|
604
|
+
formatDuration,
|
|
605
|
+
mediaCard,
|
|
606
|
+
readDuration,
|
|
607
|
+
readMedia,
|
|
608
|
+
setupMediaCard
|
|
609
|
+
};
|
|
610
|
+
//# sourceMappingURL=media.js.map
|