Skip to content

Commit

Permalink
Merge pull request #9447 from quarto-dev/feature/listing-img-lazy
Browse files Browse the repository at this point in the history
listings - add 'lazy: false' to item to control lazy loading of images
  • Loading branch information
cscheid authored Apr 22, 2024
2 parents 84704e6 + d0ff78c commit e32ea02
Show file tree
Hide file tree
Showing 28 changed files with 317 additions and 18 deletions.
1 change: 1 addition & 0 deletions news/changelog-1.5.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ All changes included in 1.5:
- ([#8715](https://github.com/quarto-dev/quarto-cli/issues/8715)): Listings should respect `image: false`
- ([#8860](https://github.com/quarto-dev/quarto-cli/discussions/8860)): Don't show duplicate author names.
- ([#9030](https://github.com/quarto-dev/quarto-cli/discussions/9030)): Warn (rather than error) when listing globs produce an empty listing (as this is permissable).
- ([#9447](https://github.com/quarto-dev/quarto-cli/pull/9447)): Add support for the boolean `image-lazy-loading` option to enable lazy loading of images in listings (default: `true`).

## Manuscripts

Expand Down
6 changes: 6 additions & 0 deletions src/project/types/website/listing/website-listing-read.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import {
kImageAlign,
kImageAlt,
kImageHeight,
kImageLazyLoading,
kImagePlaceholder,
kInclude,
kListing,
Expand Down Expand Up @@ -117,6 +118,7 @@ import {
isProjectDraft,
projectDraftMode,
} from "../website-utils.ts";
import { kFieldImageLazyLoading } from "./website-listing-shared.ts";

// Defaults (a card listing that contains everything
// in the source document's directory)
Expand Down Expand Up @@ -1117,6 +1119,9 @@ async function listItemFromFile(
: undefined;

const imageAlt = documentMeta?.[kImageAlt] as string | undefined;
const imageLazyLoading = documentMeta?.[kImageLazyLoading] as
| boolean
| undefined;

const date = documentMeta?.date
? parsePandocDate(resolveDate(input, documentMeta?.date) as string)
Expand Down Expand Up @@ -1170,6 +1175,7 @@ async function listItemFromFile(
[kFieldCategories]: categories,
[kFieldImage]: image,
[kFieldImageAlt]: imageAlt,
[kFieldImageLazyLoading]: imageLazyLoading,
[kFieldDescription]: description,
[kFieldFileName]: filename,
[kFieldFileModified]: filemodified,
Expand Down
5 changes: 5 additions & 0 deletions src/project/types/website/listing/website-listing-shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ export const kImageAlign = "image-align";
// Alt text for the item's image
export const kImageAlt = "image-alt";

// Lazy loading for the item's image. If unset, the default is true.
export const kImageLazyLoading = "image-lazy-loading";

// The placeholder image for the item
export const kImagePlaceholder = "image-placeholder";

Expand Down Expand Up @@ -101,6 +104,7 @@ export const kFieldFileModified = "file-modified";
export const kFieldDate = "date";
export const kFieldImage = "image";
export const kFieldImageAlt = "image-alt";
export const kFieldImageLazyLoading = "image-lazy-loading";
export const kFieldDescription = "description";
export const kFieldReadingTime = "reading-time";
export const kFieldWordCount = "word-count";
Expand Down Expand Up @@ -211,6 +215,7 @@ export interface ListingItem extends Record<string, unknown> {
date?: Date;
image?: string;
[kImageAlt]?: string;
[kImageLazyLoading]?: boolean;
path?: string;
filename?: string;
[kFieldFileModified]?: Date;
Expand Down
7 changes: 4 additions & 3 deletions src/project/types/website/listing/website-listing-template.ts
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export function templateMarkdownHandler(
// For file modified specifically, include the time portion
const includeTime = field === kFieldFileModified;

const date = typeof (dateRaw) === "string"
const date = typeof dateRaw === "string"
? parsePandocDate(dateRaw as string)
: dateRaw as Date;
if (date) {
Expand Down Expand Up @@ -422,6 +422,7 @@ export function reshapeListing(
src: string,
classes: string,
alt?: string,
lazy?: boolean,
) => {
const pageSize = listing[kPageSize];
const classAttr = classes ? `class="${classes}"` : "";
Expand All @@ -430,8 +431,8 @@ export function reshapeListing(
: "";
const altAttr = alt ? `alt="${encodeAttributeValue(alt)}"` : "";
const srcAttr = itemNumber > pageSize ? "data-src" : "src";

return `<img ${srcAttr}="${src}" ${classAttr} ${styleAttr} ${altAttr}>`;
const lazyAttr = lazy === false ? "" : "loading='lazy' ";
return `<img ${lazyAttr}${srcAttr}="${src}" ${classAttr} ${styleAttr} ${altAttr}>`;
};
utilities.imgPlaceholder = (
itemNumber: number,
Expand Down
33 changes: 29 additions & 4 deletions src/resources/editor/tools/vs-code.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -10597,6 +10597,12 @@ var require_yaml_intelligence_resources = __commonJS({
description: "The default image to use if an item in the listing doesn't have an image."
}
},
"image-lazy-loading": {
boolean: {
description: "If false, images in the listing will be loaded immediately. If true, images will be loaded as they come into view.",
default: true
}
},
"image-align": {
enum: [
"left",
Expand Down Expand Up @@ -18472,6 +18478,20 @@ var require_yaml_intelligence_resources = __commonJS({
]
},
description: "The alt text for preview image on this page."
},
{
name: "image-lazy-loading",
schema: "boolean",
tags: {
formats: [
"$html-doc"
]
},
description: {
short: "If true, the preview image will only load when it comes into view.",
long: 'Enables lazy loading for the preview image. If true, the preview image element \nwill have `loading="lazy"`, and will only load when it comes into view.\n\nIf false, the preview image will load immediately.\n'
},
default: true
}
],
"schema/extension.yml": [
Expand Down Expand Up @@ -21151,6 +21171,7 @@ var require_yaml_intelligence_resources = __commonJS({
"The name to display in the UI.",
"The name of the language the kernel implements.",
"The name of the kernel.",
"Configures the Julia engine.",
"Arguments to pass to the Julia worker process.",
"Environment variables to pass to the Julia worker process.",
"Set Knitr options.",
Expand Down Expand Up @@ -22744,7 +22765,11 @@ var require_yaml_intelligence_resources = __commonJS({
},
"Disambiguating year suffix in author-date styles (e.g.&nbsp;\u201Ca\u201D in \u201CDoe,\n1999a\u201D).",
"Manuscript configuration",
"internal-schema-hack"
"internal-schema-hack",
{
short: "If true, the preview image will only load when it comes into\nview.",
long: 'Enables lazy loading for the preview image. If true, the preview\nimage element will have <code>loading="lazy"</code>, and will only load\nwhen it comes into view.\nIf false, the preview image will load immediately.'
}
],
"schema/external-schemas.yml": [
{
Expand Down Expand Up @@ -22973,12 +22998,12 @@ var require_yaml_intelligence_resources = __commonJS({
mermaid: "%%"
},
"handlers/mermaid/schema.yml": {
_internalId: 183687,
_internalId: 186197,
type: "object",
description: "be an object",
properties: {
"mermaid-format": {
_internalId: 183679,
_internalId: 186189,
type: "enum",
enum: [
"png",
Expand All @@ -22994,7 +23019,7 @@ var require_yaml_intelligence_resources = __commonJS({
exhaustiveCompletions: true
},
theme: {
_internalId: 183686,
_internalId: 186196,
type: "anyOf",
anyOf: [
{
Expand Down
33 changes: 29 additions & 4 deletions src/resources/editor/tools/yaml/web-worker.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

33 changes: 29 additions & 4 deletions src/resources/editor/tools/yaml/yaml-intelligence-resources.json
Original file line number Diff line number Diff line change
Expand Up @@ -3569,6 +3569,12 @@
"description": "The default image to use if an item in the listing doesn't have an image."
}
},
"image-lazy-loading": {
"boolean": {
"description": "If false, images in the listing will be loaded immediately. If true, images will be loaded as they come into view.",
"default": true
}
},
"image-align": {
"enum": [
"left",
Expand Down Expand Up @@ -11444,6 +11450,20 @@
]
},
"description": "The alt text for preview image on this page."
},
{
"name": "image-lazy-loading",
"schema": "boolean",
"tags": {
"formats": [
"$html-doc"
]
},
"description": {
"short": "If true, the preview image will only load when it comes into view.",
"long": "Enables lazy loading for the preview image. If true, the preview image element \nwill have `loading=\"lazy\"`, and will only load when it comes into view.\n\nIf false, the preview image will load immediately.\n"
},
"default": true
}
],
"schema/extension.yml": [
Expand Down Expand Up @@ -14123,6 +14143,7 @@
"The name to display in the UI.",
"The name of the language the kernel implements.",
"The name of the kernel.",
"Configures the Julia engine.",
"Arguments to pass to the Julia worker process.",
"Environment variables to pass to the Julia worker process.",
"Set Knitr options.",
Expand Down Expand Up @@ -15716,7 +15737,11 @@
},
"Disambiguating year suffix in author-date styles (e.g.&nbsp;“a” in “Doe,\n1999a”).",
"Manuscript configuration",
"internal-schema-hack"
"internal-schema-hack",
{
"short": "If true, the preview image will only load when it comes into\nview.",
"long": "Enables lazy loading for the preview image. If true, the preview\nimage element will have <code>loading=\"lazy\"</code>, and will only load\nwhen it comes into view.\nIf false, the preview image will load immediately."
}
],
"schema/external-schemas.yml": [
{
Expand Down Expand Up @@ -15945,12 +15970,12 @@
"mermaid": "%%"
},
"handlers/mermaid/schema.yml": {
"_internalId": 183687,
"_internalId": 186197,
"type": "object",
"description": "be an object",
"properties": {
"mermaid-format": {
"_internalId": 183679,
"_internalId": 186189,
"type": "enum",
"enum": [
"png",
Expand All @@ -15966,7 +15991,7 @@
"exhaustiveCompletions": true
},
"theme": {
"_internalId": 183686,
"_internalId": 186196,
"type": "anyOf",
"anyOf": [
{
Expand Down
2 changes: 1 addition & 1 deletion src/resources/projects/website/listing/item-default.ejs.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ print(`<div class="metadata-value listing-${field}">${listing.utilities.outputLi
<div class="thumbnail">
<a href="<%- item.path %>" class="no-external">
<% if (item.image) { %>
<%= listing.utilities.img(itemNumber, item.image, "thumbnail-image", item['image-alt']) %>
<%= listing.utilities.img(itemNumber, item.image, "thumbnail-image", item['image-alt'], item['image-lazy-loading'] ?? listing['image-lazy-loading']) %>
<% } else { %>
<%= listing.utilities.imgPlaceholder(itemNumber, item.outputHref) %>
<% } %>
Expand Down
2 changes: 1 addition & 1 deletion src/resources/projects/website/listing/item-grid.ejs.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ return !["title", "image", "image-alt", "date", "author", "subtitle", "descripti
<% if (item.image) { %>

<p class="card-img-top">
<%= listing.utilities.img(itemNumber, item.image, "thumbnail-image card-img", item['image-alt']) %>
<%= listing.utilities.img(itemNumber, item.image, "thumbnail-image card-img", item['image-alt'], item['image-lazy-loading'] ?? listing['image-lazy-loading']) %>
</p>
<% } else { %>
<%= listing.utilities.imgPlaceholder(itemNumber, item.outputHref) %>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ let value = readField(item, field);

if (field === "image") {
if (item.image) {
value = listing.utilities.img(itemNumber, item[field], "", item['image-alt']);
value = listing.utilities.img(itemNumber, item[field], "", item['image-alt'], item['image-lazy-loading'] ?? listing['image-lazy-loading']);
} else {
value = listing.utilities.imgPlaceholder(itemNumber, item.outputHref);
}
Expand Down
4 changes: 4 additions & 0 deletions src/resources/schema/definitions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -1579,6 +1579,10 @@
image-placeholder:
string:
description: "The default image to use if an item in the listing doesn't have an image."
image-lazy-loading:
boolean:
description: "If false, images in the listing will be loaded immediately. If true, images will be loaded as they come into view."
default: true
image-align:
enum: [left, right]
description: In `default` type listings, whether to place the image on the right or left side of the post content (`left` or `right`).
Expand Down
13 changes: 13 additions & 0 deletions src/resources/schema/document-website.yml
Original file line number Diff line number Diff line change
Expand Up @@ -62,3 +62,16 @@
tags:
formats: [$html-doc]
description: The alt text for preview image on this page.

- name: image-lazy-loading
schema: boolean
tags:
formats: [$html-doc]
description:
short: If true, the preview image will only load when it comes into view.
long: |
Enables lazy loading for the preview image. If true, the preview image element
will have `loading="lazy"`, and will only load when it comes into view.
If false, the preview image will load immediately.
default: true
2 changes: 2 additions & 0 deletions src/resources/types/schema-types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -797,6 +797,8 @@ Learn more about supported date formatting values [here](https://deno.land/std@0
Defaults to 175. */;
"image-placeholder"?:
string /* The default image to use if an item in the listing doesn't have an image. */;
"image-lazy-loading"?:
boolean /* If false, images in the listing will be loaded immediately. If true, images will be loaded as they come into view. */;
"image-align"?:
| "left"
| "right" /* In `default` type listings, whether to place the image on the right or left side of the post content (`left` or `right`). */;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/.quarto/
Loading

0 comments on commit e32ea02

Please sign in to comment.