@cogenta/theme-docs 0.3.3 → 0.5.3
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/dist/src/index.d.ts +2 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +2 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/render/blocks/accordion.d.ts +8 -0
- package/dist/src/render/blocks/accordion.d.ts.map +1 -1
- package/dist/src/render/blocks/accordion.js +10 -10
- package/dist/src/render/blocks/accordion.js.map +1 -1
- package/dist/src/render/blocks/collection-list.d.ts +3 -11
- package/dist/src/render/blocks/collection-list.d.ts.map +1 -1
- package/dist/src/render/blocks/collection-list.js +69 -68
- package/dist/src/render/blocks/collection-list.js.map +1 -1
- package/dist/src/render/blocks/cta.d.ts +6 -0
- package/dist/src/render/blocks/cta.d.ts.map +1 -1
- package/dist/src/render/blocks/cta.js +9 -4
- package/dist/src/render/blocks/cta.js.map +1 -1
- package/dist/src/render/blocks/embed.d.ts.map +1 -1
- package/dist/src/render/blocks/embed.js +14 -13
- package/dist/src/render/blocks/embed.js.map +1 -1
- package/dist/src/render/blocks/faq.d.ts +7 -0
- package/dist/src/render/blocks/faq.d.ts.map +1 -1
- package/dist/src/render/blocks/faq.js +10 -10
- package/dist/src/render/blocks/faq.js.map +1 -1
- package/dist/src/render/blocks/feature-grid.d.ts.map +1 -1
- package/dist/src/render/blocks/feature-grid.js +19 -19
- package/dist/src/render/blocks/feature-grid.js.map +1 -1
- package/dist/src/render/blocks/gallery.d.ts +8 -4
- package/dist/src/render/blocks/gallery.d.ts.map +1 -1
- package/dist/src/render/blocks/gallery.js +19 -13
- package/dist/src/render/blocks/gallery.js.map +1 -1
- package/dist/src/render/blocks/hero.d.ts +12 -9
- package/dist/src/render/blocks/hero.d.ts.map +1 -1
- package/dist/src/render/blocks/hero.js +22 -20
- package/dist/src/render/blocks/hero.js.map +1 -1
- package/dist/src/render/blocks/logo-strip.d.ts +6 -0
- package/dist/src/render/blocks/logo-strip.d.ts.map +1 -1
- package/dist/src/render/blocks/logo-strip.js +6 -10
- package/dist/src/render/blocks/logo-strip.js.map +1 -1
- package/dist/src/render/blocks/logos.d.ts +10 -0
- package/dist/src/render/blocks/logos.d.ts.map +1 -1
- package/dist/src/render/blocks/logos.js +20 -17
- package/dist/src/render/blocks/logos.js.map +1 -1
- package/dist/src/render/blocks/media-figure.d.ts +0 -6
- package/dist/src/render/blocks/media-figure.d.ts.map +1 -1
- package/dist/src/render/blocks/media-figure.js +28 -16
- package/dist/src/render/blocks/media-figure.js.map +1 -1
- package/dist/src/render/blocks/pricing-table.d.ts.map +1 -1
- package/dist/src/render/blocks/pricing-table.js +16 -20
- package/dist/src/render/blocks/pricing-table.js.map +1 -1
- package/dist/src/render/blocks/prose.d.ts +13 -1
- package/dist/src/render/blocks/prose.d.ts.map +1 -1
- package/dist/src/render/blocks/prose.js +12 -31
- package/dist/src/render/blocks/prose.js.map +1 -1
- package/dist/src/render/blocks/quote.d.ts +5 -3
- package/dist/src/render/blocks/quote.d.ts.map +1 -1
- package/dist/src/render/blocks/quote.js +10 -14
- package/dist/src/render/blocks/quote.js.map +1 -1
- package/dist/src/render/blocks/stat-counter.d.ts +5 -0
- package/dist/src/render/blocks/stat-counter.d.ts.map +1 -1
- package/dist/src/render/blocks/stat-counter.js +6 -10
- package/dist/src/render/blocks/stat-counter.js.map +1 -1
- package/dist/src/render/blocks/stats.d.ts +10 -0
- package/dist/src/render/blocks/stats.d.ts.map +1 -1
- package/dist/src/render/blocks/stats.js +13 -8
- package/dist/src/render/blocks/stats.js.map +1 -1
- package/dist/src/render/blocks/testimonial.d.ts +4 -5
- package/dist/src/render/blocks/testimonial.d.ts.map +1 -1
- package/dist/src/render/blocks/testimonial.js +9 -13
- package/dist/src/render/blocks/testimonial.js.map +1 -1
- package/dist/src/render/chrome.d.ts +36 -1
- package/dist/src/render/chrome.d.ts.map +1 -1
- package/dist/src/render/chrome.js +122 -80
- package/dist/src/render/chrome.js.map +1 -1
- package/dist/src/render/doc-index.d.ts +25 -0
- package/dist/src/render/doc-index.d.ts.map +1 -0
- package/dist/src/render/doc-index.js +49 -0
- package/dist/src/render/doc-index.js.map +1 -0
- package/dist/src/render/doc-page.d.ts +6 -0
- package/dist/src/render/doc-page.d.ts.map +1 -0
- package/dist/src/render/doc-page.js +127 -0
- package/dist/src/render/doc-page.js.map +1 -0
- package/dist/src/render/layout.d.ts +23 -0
- package/dist/src/render/layout.d.ts.map +1 -0
- package/dist/src/render/layout.js +40 -0
- package/dist/src/render/layout.js.map +1 -0
- package/dist/src/render/render-block.d.ts +9 -6
- package/dist/src/render/render-block.d.ts.map +1 -1
- package/dist/src/render/render-block.js +47 -100
- package/dist/src/render/render-block.js.map +1 -1
- package/dist/src/render/rich-text.d.ts +26 -0
- package/dist/src/render/rich-text.d.ts.map +1 -0
- package/dist/src/render/rich-text.js +325 -0
- package/dist/src/render/rich-text.js.map +1 -0
- package/dist/src/render/search.d.ts +15 -0
- package/dist/src/render/search.d.ts.map +1 -0
- package/dist/src/render/search.js +31 -0
- package/dist/src/render/search.js.map +1 -0
- package/dist/src/render/strings.d.ts +51 -0
- package/dist/src/render/strings.d.ts.map +1 -0
- package/dist/src/render/strings.js +60 -0
- package/dist/src/render/strings.js.map +1 -0
- package/dist/src/render/term-archive.d.ts +5 -5
- package/dist/src/render/term-archive.d.ts.map +1 -1
- package/dist/src/render/term-archive.js +27 -16
- package/dist/src/render/term-archive.js.map +1 -1
- package/dist/src/render/widget-areas.d.ts +22 -0
- package/dist/src/render/widget-areas.d.ts.map +1 -0
- package/dist/src/render/widget-areas.js +29 -0
- package/dist/src/render/widget-areas.js.map +1 -0
- package/package.json +4 -4
- package/src/index.ts +2 -0
- package/src/render/blocks/accordion.ts +31 -42
- package/src/render/blocks/collection-list.ts +132 -123
- package/src/render/blocks/cta.ts +23 -18
- package/src/render/blocks/embed.ts +37 -28
- package/src/render/blocks/faq.ts +26 -37
- package/src/render/blocks/feature-grid.ts +34 -42
- package/src/render/blocks/gallery.ts +33 -29
- package/src/render/blocks/hero.ts +41 -45
- package/src/render/blocks/logo-strip.ts +21 -21
- package/src/render/blocks/logos.ts +32 -42
- package/src/render/blocks/media-figure.ts +47 -25
- package/src/render/blocks/pricing-table.ts +34 -41
- package/src/render/blocks/prose.ts +25 -40
- package/src/render/blocks/quote.ts +25 -19
- package/src/render/blocks/stat-counter.ts +27 -29
- package/src/render/blocks/stats.ts +35 -32
- package/src/render/blocks/testimonial.ts +18 -19
- package/src/render/chrome.ts +170 -83
- package/src/render/doc-index.ts +65 -0
- package/src/render/doc-page.ts +303 -0
- package/src/render/layout.ts +73 -0
- package/src/render/render-block.ts +67 -191
- package/src/render/rich-text.ts +446 -0
- package/src/render/search.ts +41 -0
- package/src/render/strings.ts +67 -0
- package/src/render/term-archive.ts +62 -38
- package/src/render/widget-areas.ts +33 -0
- package/src/styles/archive.css +21 -27
- package/src/styles/base.css +438 -838
- package/src/styles/blocks.css +658 -916
- package/src/styles/chrome.css +439 -0
- package/src/styles/docs.css +404 -0
- package/src/styles/rich.css +337 -0
- package/src/styles/theme.css +28 -23
- package/src/styles/tokens.css +209 -215
- package/src/styles/widgets.css +890 -0
- package/tokens.json +15 -15
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The widget areas this theme names (contract D `theme@1.6`). Declaring them
|
|
3
|
+
* is what tells the host this theme draws the footer columns inside its own
|
|
4
|
+
* footer; the page areas are placed by the host in the shared
|
|
5
|
+
* `cg-sidebar-layout` markup, which `styles/widgets.css` sets on this theme's
|
|
6
|
+
* own documentation grid.
|
|
7
|
+
*
|
|
8
|
+
* A documentation page already has two columns of its own beside the
|
|
9
|
+
* article: the navigation on the left and "On this page" on the right. The
|
|
10
|
+
* sidebar never becomes a fourth column next to them. It is the right-hand
|
|
11
|
+
* rail, shared with "On this page": the contents stay in view while the page
|
|
12
|
+
* is read, and the widgets sit at the foot of the rail, level with the end of
|
|
13
|
+
* the article, where a reader who finished the page looks for help and for
|
|
14
|
+
* what comes next. On a page without its own contents, and beside search
|
|
15
|
+
* results or an archive, the widgets are the rail.
|
|
16
|
+
*/
|
|
17
|
+
export declare const widgetAreas: readonly {
|
|
18
|
+
readonly id: string;
|
|
19
|
+
readonly label: string;
|
|
20
|
+
readonly description?: string;
|
|
21
|
+
}[];
|
|
22
|
+
//# sourceMappingURL=widget-areas.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-areas.d.ts","sourceRoot":"","sources":["../../../src/render/widget-areas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,WAAW,EAAE,SAAS;IACjC,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAA;IACnB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAA;CAC9B,EAYA,CAAA"}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The widget areas this theme names (contract D `theme@1.6`). Declaring them
|
|
3
|
+
* is what tells the host this theme draws the footer columns inside its own
|
|
4
|
+
* footer; the page areas are placed by the host in the shared
|
|
5
|
+
* `cg-sidebar-layout` markup, which `styles/widgets.css` sets on this theme's
|
|
6
|
+
* own documentation grid.
|
|
7
|
+
*
|
|
8
|
+
* A documentation page already has two columns of its own beside the
|
|
9
|
+
* article: the navigation on the left and "On this page" on the right. The
|
|
10
|
+
* sidebar never becomes a fourth column next to them. It is the right-hand
|
|
11
|
+
* rail, shared with "On this page": the contents stay in view while the page
|
|
12
|
+
* is read, and the widgets sit at the foot of the rail, level with the end of
|
|
13
|
+
* the article, where a reader who finished the page looks for help and for
|
|
14
|
+
* what comes next. On a page without its own contents, and beside search
|
|
15
|
+
* results or an archive, the widgets are the rail.
|
|
16
|
+
*/
|
|
17
|
+
export const widgetAreas = [
|
|
18
|
+
{
|
|
19
|
+
id: 'sidebar',
|
|
20
|
+
label: 'Page rail',
|
|
21
|
+
description: 'The right-hand rail: under “On this page” and level with the end of a documentation page, beside search results and archives.',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
id: 'content-after',
|
|
25
|
+
label: 'After the page',
|
|
26
|
+
description: 'In the reading column, after a page and its previous and next links.',
|
|
27
|
+
},
|
|
28
|
+
];
|
|
29
|
+
//# sourceMappingURL=widget-areas.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"widget-areas.js","sourceRoot":"","sources":["../../../src/render/widget-areas.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AACH,MAAM,CAAC,MAAM,WAAW,GAIlB;IACJ;QACE,EAAE,EAAE,SAAS;QACb,KAAK,EAAE,WAAW;QAClB,WAAW,EACT,+HAA+H;KAClI;IACD;QACE,EAAE,EAAE,eAAe;QACnB,KAAK,EAAE,gBAAgB;QACvB,WAAW,EAAE,sEAAsE;KACpF;CACF,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cogenta/theme-docs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.3",
|
|
4
4
|
"description": "A dense, reference-first documentation theme for Cogenta: a sticky sidebar-navigated doc layout, real code blocks, and a genuine dark mode — zero client JavaScript.",
|
|
5
5
|
"license": "MPL-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -36,9 +36,9 @@
|
|
|
36
36
|
"directory": "packages/theme-docs"
|
|
37
37
|
},
|
|
38
38
|
"dependencies": {
|
|
39
|
-
"@cogenta/
|
|
40
|
-
"@cogenta/theme-kit": "0.
|
|
41
|
-
"@cogenta/
|
|
39
|
+
"@cogenta/blocks": "1.1.3",
|
|
40
|
+
"@cogenta/theme-kit": "0.5.3",
|
|
41
|
+
"@cogenta/render": "0.3.5"
|
|
42
42
|
},
|
|
43
43
|
"peerDependencies": {
|
|
44
44
|
"astro": "^7.2.1"
|
package/src/index.ts
CHANGED
|
@@ -17,4 +17,6 @@ export * from './render/blocks/stats.js'
|
|
|
17
17
|
export * from './render/blocks/testimonial.js'
|
|
18
18
|
export * from './render/chrome.js'
|
|
19
19
|
export * from './render/render-block.js'
|
|
20
|
+
export { headingAnchors, keyChord, slugify } from './render/rich-text.js'
|
|
20
21
|
export * from './render/term-archive.js'
|
|
22
|
+
export * from './render/widget-areas.js'
|
|
@@ -1,51 +1,40 @@
|
|
|
1
|
-
import type { AccordionBlock
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
h,
|
|
6
|
-
heading,
|
|
7
|
-
type RenderContext,
|
|
8
|
-
renderRichText,
|
|
9
|
-
} from '@cogenta/theme-kit'
|
|
1
|
+
import type { AccordionBlock } from '@cogenta/blocks'
|
|
2
|
+
import { type HtmlElement, h, type RenderContext } from '@cogenta/theme-kit'
|
|
3
|
+
import { section, sectionHead } from '../layout.js'
|
|
4
|
+
import { renderDocsRichText } from '../rich-text.js'
|
|
10
5
|
|
|
11
6
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
7
|
+
* Detail a reader opens when they need it (every error code, the fields of a
|
|
8
|
+
* response) as ruled rows on the reading measure.
|
|
9
|
+
*
|
|
10
|
+
* Every row is a `<details>`: it opens without a script, and the browser's
|
|
11
|
+
* own find-in-page still reaches a closed answer. The plus that turns into a
|
|
12
|
+
* minus is two hairlines drawn by the stylesheet, not a glyph.
|
|
15
13
|
*/
|
|
16
|
-
function renderItem(item: AccordionItem, ctx: RenderContext): HtmlElement {
|
|
17
|
-
return h(
|
|
18
|
-
'li',
|
|
19
|
-
{ class: 'cg-panels__item' },
|
|
20
|
-
h(
|
|
21
|
-
'details',
|
|
22
|
-
{ class: 'cg-panels__details' },
|
|
23
|
-
h(
|
|
24
|
-
'summary',
|
|
25
|
-
{ class: 'cg-panels__question' },
|
|
26
|
-
h('span', { class: 'cg-panels__question-text' }, item.question),
|
|
27
|
-
h('span', { class: 'cg-panels__chevron', 'aria-hidden': 'true' }),
|
|
28
|
-
),
|
|
29
|
-
h('div', { class: 'cg-panels__answer' }, renderRichText(ctx, item.answer)),
|
|
30
|
-
),
|
|
31
|
-
)
|
|
32
|
-
}
|
|
33
|
-
|
|
34
14
|
export function renderAccordion(block: AccordionBlock, ctx: RenderContext): HtmlElement {
|
|
35
|
-
return
|
|
15
|
+
return section(
|
|
36
16
|
'section',
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
{ class: 'cg-panels__title', 'data-field': 'title' },
|
|
43
|
-
block.title,
|
|
44
|
-
),
|
|
17
|
+
'accordion',
|
|
18
|
+
'cd-accordion',
|
|
19
|
+
{},
|
|
20
|
+
'div',
|
|
21
|
+
sectionHead('accordion', block.title),
|
|
45
22
|
h(
|
|
46
|
-
'
|
|
47
|
-
{ class: '
|
|
48
|
-
block.items.map((item) =>
|
|
23
|
+
'div',
|
|
24
|
+
{ class: 'cd-accordion__items' },
|
|
25
|
+
block.items.map((item) =>
|
|
26
|
+
h(
|
|
27
|
+
'details',
|
|
28
|
+
{ class: 'cd-accordion__item' },
|
|
29
|
+
h(
|
|
30
|
+
'summary',
|
|
31
|
+
{ class: 'cd-accordion__summary' },
|
|
32
|
+
h('span', { class: 'cd-accordion__question' }, item.question),
|
|
33
|
+
h('span', { class: 'cd-accordion__sign', 'aria-hidden': 'true' }),
|
|
34
|
+
),
|
|
35
|
+
h('div', { class: 'cd-accordion__answer cd-rich' }, renderDocsRichText(ctx, item.answer)),
|
|
36
|
+
),
|
|
37
|
+
),
|
|
49
38
|
),
|
|
50
39
|
)
|
|
51
40
|
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import type { CollectionListBlock } from '@cogenta/blocks'
|
|
2
2
|
import {
|
|
3
|
-
blockHeadingTag,
|
|
4
3
|
buildCollectionListQuery,
|
|
5
4
|
type ContentEntry,
|
|
6
5
|
entryDate,
|
|
@@ -8,119 +7,122 @@ import {
|
|
|
8
7
|
entryHref,
|
|
9
8
|
entryImage,
|
|
10
9
|
entryTitle,
|
|
11
|
-
type HeadingTag,
|
|
12
10
|
type HtmlElement,
|
|
13
11
|
h,
|
|
14
|
-
heading,
|
|
15
12
|
nestedHeadingTag,
|
|
13
|
+
type QueryRequest,
|
|
16
14
|
type RenderContext,
|
|
17
15
|
renderImageSource,
|
|
18
16
|
} from '@cogenta/theme-kit'
|
|
17
|
+
import { DOC_PAGE_COLLECTION, groupDocPages } from '../doc-index.js'
|
|
18
|
+
import { longDate, section, sectionHead } from '../layout.js'
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
* (
|
|
25
|
-
*
|
|
26
|
-
*
|
|
27
|
-
*
|
|
21
|
+
* A list of entries, read by what they are as much as by the layout chosen.
|
|
22
|
+
*
|
|
23
|
+
* - **Doc pages: the documentation, by section.** The home page's "Browse"
|
|
24
|
+
* block. One column per section (sections in the order the documentation
|
|
25
|
+
* reads, pages by their own `order`), the section named in a small heading
|
|
26
|
+
* over a hairline, its pages as a plain column of links. Up to four columns
|
|
27
|
+
* on a desk, two on a tablet, one on a phone. The layout an editor picked
|
|
28
|
+
* is ignored on purpose: a table of contents is not a carousel.
|
|
29
|
+
* - **`list`: an index.** Ruled rows: the title, the summary under it, and a
|
|
30
|
+
* date on the right when the entry has one, in tabular numerals.
|
|
31
|
+
* - **`grid`**: three columns without cards. A picture, when there is one,
|
|
32
|
+
* in the hairline frame at 16:10, then the title, the summary and the date.
|
|
33
|
+
* - **`carousel`**: the same items in one row that scrolls sideways.
|
|
34
|
+
*
|
|
35
|
+
* Each picture repeats the link of its title, so it is taken out of the tab
|
|
36
|
+
* order and hidden from assistive technology: one link per entry.
|
|
28
37
|
*/
|
|
29
|
-
export { buildCollectionListQuery as query }
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* The general-purpose row: a cover image (`entryImage`, contract D
|
|
33
|
-
* `theme@1.4`) when the collection carries one, title, excerpt and date —
|
|
34
|
-
* this is what a non-docs collection placed in this block gets.
|
|
35
|
-
*/
|
|
36
|
-
function renderRow(entry: ContentEntry, ctx: RenderContext, tag: HeadingTag): HtmlElement {
|
|
37
|
-
const date = entryDate(entry)
|
|
38
|
-
const excerpt = entryExcerpt(entry)
|
|
39
|
-
const image = entryImage(entry, ctx)
|
|
40
|
-
return h(
|
|
41
|
-
'li',
|
|
42
|
-
{ class: 'cg-list__row' },
|
|
43
|
-
image === undefined
|
|
44
|
-
? null
|
|
45
|
-
: h(
|
|
46
|
-
'div',
|
|
47
|
-
{ class: 'cg-list__thumb' },
|
|
48
|
-
renderImageSource(image, { loading: 'lazy', className: 'cg-list__thumb-image' }),
|
|
49
|
-
),
|
|
50
|
-
h(
|
|
51
|
-
'div',
|
|
52
|
-
{ class: 'cg-list__body' },
|
|
53
|
-
heading(
|
|
54
|
-
tag,
|
|
55
|
-
{ class: 'cg-list__title' },
|
|
56
|
-
h('a', { class: 'cg-list__link', href: entryHref(entry, ctx) }, entryTitle(entry, ctx)),
|
|
57
|
-
),
|
|
58
|
-
date === undefined
|
|
59
|
-
? null
|
|
60
|
-
: h(
|
|
61
|
-
'time',
|
|
62
|
-
{ class: 'cg-list__date', datetime: date },
|
|
63
|
-
new Intl.DateTimeFormat(ctx.locale, { dateStyle: 'long' }).format(new Date(date)),
|
|
64
|
-
),
|
|
65
|
-
excerpt === undefined ? null : h('p', { class: 'cg-list__excerpt' }, excerpt),
|
|
66
|
-
),
|
|
67
|
-
)
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* `doc_page`'s own layout: a compact, multi-column index grouped by the
|
|
72
|
-
* entry's own `section` field and ordered by its own `order` field within
|
|
73
|
-
* each group — neither is a valid `collectionList.sort.field` (contract B
|
|
74
|
-
* fixes that union to `id`/`createdAt`/`updatedAt`), so the theme re-groups
|
|
75
|
-
* and re-sorts the already-fetched slice itself rather than asking for a
|
|
76
|
-
* sort the contract does not offer. Sections are ordered alphabetically for
|
|
77
|
-
* a deterministic page; entries within a section fall back to the order
|
|
78
|
-
* they were fetched in when `order` is absent or not a number.
|
|
79
|
-
*/
|
|
80
|
-
function renderGuidesIndex(entries: readonly ContentEntry[], ctx: RenderContext): HtmlElement {
|
|
81
|
-
const groups = new Map<string, ContentEntry[]>()
|
|
82
|
-
entries.forEach((entry) => {
|
|
83
|
-
const section =
|
|
84
|
-
typeof entry.section === 'string' && entry.section !== ''
|
|
85
|
-
? entry.section
|
|
86
|
-
: ctx.t('entry.untitled')
|
|
87
|
-
const list = groups.get(section) ?? []
|
|
88
|
-
list.push(entry)
|
|
89
|
-
groups.set(section, list)
|
|
90
|
-
})
|
|
91
|
-
|
|
92
|
-
const sortedSections = [...groups.entries()].sort(([a], [b]) => a.localeCompare(b))
|
|
93
38
|
|
|
39
|
+
function renderDocIndex(
|
|
40
|
+
entries: readonly ContentEntry[],
|
|
41
|
+
ctx: RenderContext,
|
|
42
|
+
tag: string,
|
|
43
|
+
): HtmlElement {
|
|
44
|
+
const groups = groupDocPages(entries, ctx)
|
|
94
45
|
return h(
|
|
95
46
|
'div',
|
|
96
|
-
{ class: '
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
const orderA = typeof a.order === 'number' ? a.order : Number.POSITIVE_INFINITY
|
|
100
|
-
const orderB = typeof b.order === 'number' ? b.order : Number.POSITIVE_INFINITY
|
|
101
|
-
return orderA - orderB
|
|
102
|
-
})
|
|
103
|
-
return h(
|
|
47
|
+
{ class: 'cd-browse', 'data-count': String(Math.min(groups.length, 4)) },
|
|
48
|
+
groups.map((group) =>
|
|
49
|
+
h(
|
|
104
50
|
'div',
|
|
105
|
-
{ class: '
|
|
106
|
-
h(
|
|
51
|
+
{ class: 'cd-browse__group' },
|
|
52
|
+
h(tag, { class: 'cd-browse__heading' }, group.section),
|
|
107
53
|
h(
|
|
108
54
|
'ul',
|
|
109
|
-
{ class: '
|
|
110
|
-
|
|
55
|
+
{ class: 'cd-browse__links' },
|
|
56
|
+
group.entries.map((entry) =>
|
|
111
57
|
h(
|
|
112
58
|
'li',
|
|
113
|
-
{ class: '
|
|
59
|
+
{ class: 'cd-browse__item' },
|
|
114
60
|
h(
|
|
115
61
|
'a',
|
|
116
|
-
{ class: '
|
|
62
|
+
{ class: 'cd-browse__link', href: entryHref(entry, ctx) },
|
|
117
63
|
entryTitle(entry, ctx),
|
|
118
64
|
),
|
|
119
65
|
),
|
|
120
66
|
),
|
|
121
67
|
),
|
|
122
|
-
)
|
|
123
|
-
|
|
68
|
+
),
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function dateOf(entry: ContentEntry, ctx: RenderContext, className: string): HtmlElement | null {
|
|
74
|
+
const date = entryDate(entry)
|
|
75
|
+
if (date === undefined) return null
|
|
76
|
+
return h('time', { class: className, datetime: date }, longDate(date, ctx.locale))
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function indexRow(entry: ContentEntry, ctx: RenderContext, tag: string): HtmlElement {
|
|
80
|
+
const excerpt = entryExcerpt(entry)
|
|
81
|
+
return h(
|
|
82
|
+
'li',
|
|
83
|
+
{ class: 'cd-index__item' },
|
|
84
|
+
h(
|
|
85
|
+
'div',
|
|
86
|
+
{ class: 'cd-index__words' },
|
|
87
|
+
h(
|
|
88
|
+
tag,
|
|
89
|
+
{ class: 'cd-index__title' },
|
|
90
|
+
h('a', { class: 'cd-index__link', href: entryHref(entry, ctx) }, entryTitle(entry, ctx)),
|
|
91
|
+
),
|
|
92
|
+
excerpt === undefined ? null : h('p', { class: 'cd-index__text' }, excerpt),
|
|
93
|
+
),
|
|
94
|
+
dateOf(entry, ctx, 'cd-index__date'),
|
|
95
|
+
)
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function card(entry: ContentEntry, ctx: RenderContext, tag: string): HtmlElement {
|
|
99
|
+
const excerpt = entryExcerpt(entry)
|
|
100
|
+
const source = entryImage(entry, ctx)
|
|
101
|
+
return h(
|
|
102
|
+
'li',
|
|
103
|
+
{ class: 'cd-cards__item' },
|
|
104
|
+
source === undefined
|
|
105
|
+
? null
|
|
106
|
+
: h(
|
|
107
|
+
'a',
|
|
108
|
+
{
|
|
109
|
+
class: 'cd-cards__media cd-frame',
|
|
110
|
+
href: entryHref(entry, ctx),
|
|
111
|
+
tabindex: -1,
|
|
112
|
+
'aria-hidden': 'true',
|
|
113
|
+
},
|
|
114
|
+
renderImageSource(source, {
|
|
115
|
+
className: 'cd-frame__image',
|
|
116
|
+
sizes: '(min-width: 64rem) 24rem, (min-width: 40rem) 50vw, 100vw',
|
|
117
|
+
}),
|
|
118
|
+
),
|
|
119
|
+
h(
|
|
120
|
+
tag,
|
|
121
|
+
{ class: 'cd-cards__title' },
|
|
122
|
+
h('a', { class: 'cd-cards__link', href: entryHref(entry, ctx) }, entryTitle(entry, ctx)),
|
|
123
|
+
),
|
|
124
|
+
excerpt === undefined ? null : h('p', { class: 'cd-cards__text' }, excerpt),
|
|
125
|
+
dateOf(entry, ctx, 'cd-cards__date'),
|
|
124
126
|
)
|
|
125
127
|
}
|
|
126
128
|
|
|
@@ -129,42 +131,49 @@ export function renderCollectionList(
|
|
|
129
131
|
ctx: RenderContext,
|
|
130
132
|
entries: readonly ContentEntry[],
|
|
131
133
|
): HtmlElement {
|
|
132
|
-
const
|
|
133
|
-
const
|
|
134
|
-
const
|
|
134
|
+
const tag = nestedHeadingTag('collectionList', block.title !== undefined)
|
|
135
|
+
const docs = block.collection === DOC_PAGE_COLLECTION
|
|
136
|
+
const shape = entries.length === 0 ? 'empty' : docs ? 'docs' : block.layout
|
|
135
137
|
|
|
136
138
|
const body =
|
|
137
|
-
|
|
138
|
-
? h('p', { class: '
|
|
139
|
-
:
|
|
140
|
-
?
|
|
141
|
-
:
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
139
|
+
shape === 'empty'
|
|
140
|
+
? h('p', { class: 'cd-empty' }, ctx.t('collection.empty'))
|
|
141
|
+
: shape === 'docs'
|
|
142
|
+
? renderDocIndex(entries, ctx, tag)
|
|
143
|
+
: shape === 'list'
|
|
144
|
+
? h(
|
|
145
|
+
'ul',
|
|
146
|
+
{ class: 'cd-index' },
|
|
147
|
+
entries.map((entry) => indexRow(entry, ctx, tag)),
|
|
148
|
+
)
|
|
149
|
+
: h(
|
|
150
|
+
'ul',
|
|
151
|
+
{
|
|
152
|
+
class: 'cd-cards',
|
|
153
|
+
'data-carousel': shape === 'carousel' ? 'true' : 'false',
|
|
154
|
+
...(shape === 'carousel'
|
|
155
|
+
? {
|
|
156
|
+
role: 'region',
|
|
157
|
+
'aria-label': block.title ?? ctx.t('collection.carousel'),
|
|
158
|
+
tabindex: 0,
|
|
159
|
+
}
|
|
160
|
+
: {}),
|
|
161
|
+
},
|
|
162
|
+
entries.map((entry) => card(entry, ctx, tag)),
|
|
163
|
+
)
|
|
146
164
|
|
|
147
|
-
return
|
|
165
|
+
return section(
|
|
148
166
|
'section',
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
)
|
|
156
|
-
: null,
|
|
157
|
-
block.layout === 'carousel' && !isGuidesIndex
|
|
158
|
-
? h(
|
|
159
|
-
'div',
|
|
160
|
-
{
|
|
161
|
-
class: 'cg-list__viewport',
|
|
162
|
-
role: 'region',
|
|
163
|
-
'aria-label': block.title ?? ctx.t('collection.carousel'),
|
|
164
|
-
tabindex: '0',
|
|
165
|
-
},
|
|
166
|
-
body,
|
|
167
|
-
)
|
|
168
|
-
: body,
|
|
167
|
+
'collectionList',
|
|
168
|
+
'cd-list',
|
|
169
|
+
{ 'data-layout': block.layout, 'data-shape': shape },
|
|
170
|
+
'div',
|
|
171
|
+
sectionHead('collectionList', block.title),
|
|
172
|
+
body,
|
|
169
173
|
)
|
|
170
174
|
}
|
|
175
|
+
|
|
176
|
+
/** The query this block needs, fetched by the host before any markup is built. */
|
|
177
|
+
export function query(block: CollectionListBlock): QueryRequest {
|
|
178
|
+
return buildCollectionListQuery(block)
|
|
179
|
+
}
|
package/src/render/blocks/cta.ts
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
import type { CtaBlock } from '@cogenta/blocks'
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
blockHeadingTag,
|
|
5
|
-
type HtmlElement,
|
|
6
|
-
h,
|
|
7
|
-
heading,
|
|
8
|
-
type RenderContext,
|
|
9
|
-
} from '@cogenta/theme-kit'
|
|
2
|
+
import { actionList, type HtmlElement, h, type RenderContext } from '@cogenta/theme-kit'
|
|
3
|
+
import { optionalText, section } from '../layout.js'
|
|
10
4
|
|
|
5
|
+
/**
|
|
6
|
+
* A closing line: "Stuck on something", "Upgrading from an older version".
|
|
7
|
+
* Between two hairlines held to the width of the content (never a coloured
|
|
8
|
+
* band, never a rule across the gutters): the title and a sentence on the
|
|
9
|
+
* first seven columns, the actions after them on a wide screen.
|
|
10
|
+
*/
|
|
11
11
|
export function renderCta(block: CtaBlock, ctx: RenderContext): HtmlElement {
|
|
12
|
-
return
|
|
12
|
+
return section(
|
|
13
13
|
'section',
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
14
|
+
'cta',
|
|
15
|
+
'cd-cta',
|
|
16
|
+
{},
|
|
17
|
+
'div',
|
|
18
|
+
h(
|
|
19
|
+
'div',
|
|
20
|
+
{ class: 'cd-cta__panel' },
|
|
21
|
+
h(
|
|
22
|
+
'div',
|
|
23
|
+
{ class: 'cd-cta__words' },
|
|
24
|
+
h('h2', { class: 'cd-cta__title', 'data-field': 'title' }, block.title),
|
|
25
|
+
optionalText('p', 'cd-cta__text', block.text, { 'data-field': 'text' }),
|
|
26
|
+
),
|
|
27
|
+
h('div', { class: 'cd-cta__actions' }, actionList(ctx, block.actions, block.title)),
|
|
19
28
|
),
|
|
20
|
-
block.text === undefined
|
|
21
|
-
? null
|
|
22
|
-
: h('p', { class: 'cg-cta__text', 'data-field': 'text' }, block.text),
|
|
23
|
-
actionList(ctx, block.actions, block.title),
|
|
24
29
|
)
|
|
25
30
|
}
|
|
@@ -2,11 +2,13 @@ import type { EmbedBlock } from '@cogenta/blocks'
|
|
|
2
2
|
import { aspectRatio, type HtmlElement, h, type RenderContext } from '@cogenta/theme-kit'
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
*
|
|
5
|
+
* A recorded walkthrough, a talk, a post: embedded in the hairline frame.
|
|
6
6
|
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
7
|
+
* Nothing here contacts a third party before the visitor has consented. When
|
|
8
|
+
* `consentRequired` is true (and for a provider this theme cannot embed) the
|
|
9
|
+
* frame holds a short notice and a link to the original instead: no
|
|
10
|
+
* `<iframe>`, no `<script>`, no preconnect, no poster fetched from the
|
|
11
|
+
* provider.
|
|
10
12
|
*/
|
|
11
13
|
|
|
12
14
|
function frameSource(provider: EmbedBlock['provider'], rawUrl: string): string | null {
|
|
@@ -42,16 +44,15 @@ function frameSource(provider: EmbedBlock['provider'], rawUrl: string): string |
|
|
|
42
44
|
}
|
|
43
45
|
}
|
|
44
46
|
|
|
45
|
-
function
|
|
47
|
+
function notice(block: EmbedBlock, ctx: RenderContext, reason: string): HtmlElement {
|
|
46
48
|
return h(
|
|
47
49
|
'div',
|
|
48
|
-
{ class: '
|
|
49
|
-
h('p', { class: '
|
|
50
|
-
h('p', { class: 'cg-embed__notice' }, reason),
|
|
50
|
+
{ class: 'cd-embed__notice' },
|
|
51
|
+
h('p', { class: 'cd-embed__reason' }, reason),
|
|
51
52
|
h(
|
|
52
53
|
'a',
|
|
53
54
|
{
|
|
54
|
-
class: '
|
|
55
|
+
class: 'cd-arrow-link cd-embed__link',
|
|
55
56
|
href: ctx.link(block.url),
|
|
56
57
|
rel: 'noopener noreferrer nofollow',
|
|
57
58
|
},
|
|
@@ -67,28 +68,36 @@ export function renderEmbed(block: EmbedBlock, ctx: RenderContext): HtmlElement
|
|
|
67
68
|
return h(
|
|
68
69
|
'div',
|
|
69
70
|
{
|
|
70
|
-
class: '
|
|
71
|
+
class: 'cd-section cd-embed',
|
|
71
72
|
'data-block': 'embed',
|
|
72
73
|
'data-provider': block.provider,
|
|
73
74
|
'data-consent': block.consentRequired ? 'required' : 'not-required',
|
|
74
|
-
style: `--cg-ratio:${ratio}`,
|
|
75
75
|
},
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
76
|
+
h(
|
|
77
|
+
'div',
|
|
78
|
+
{ class: 'cd-container cd-embed__inner' },
|
|
79
|
+
h(
|
|
80
|
+
'div',
|
|
81
|
+
{ class: 'cd-embed__frame cd-frame', style: `aspect-ratio:${ratio}` },
|
|
82
|
+
source === null
|
|
83
|
+
? notice(
|
|
84
|
+
block,
|
|
85
|
+
ctx,
|
|
86
|
+
block.consentRequired
|
|
87
|
+
? ctx.t('embed.consentRequired', { provider: block.provider })
|
|
88
|
+
: ctx.t('embed.unsupported', { provider: block.provider }),
|
|
89
|
+
)
|
|
90
|
+
: h('iframe', {
|
|
91
|
+
class: 'cd-embed__iframe',
|
|
92
|
+
src: source,
|
|
93
|
+
title: ctx.t('embed.title', { provider: block.provider }),
|
|
94
|
+
loading: 'lazy',
|
|
95
|
+
referrerpolicy: 'strict-origin-when-cross-origin',
|
|
96
|
+
allow:
|
|
97
|
+
'accelerometer; clipboard-write; encrypted-media; picture-in-picture; fullscreen',
|
|
98
|
+
allowfullscreen: true,
|
|
99
|
+
}),
|
|
100
|
+
),
|
|
101
|
+
),
|
|
93
102
|
)
|
|
94
103
|
}
|