@cogenta/theme-restaurant 0.3.2 → 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 +3 -0
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/index.js +3 -0
- package/dist/src/index.js.map +1 -1
- package/dist/src/render/blocks/accordion.d.ts +7 -0
- package/dist/src/render/blocks/accordion.d.ts.map +1 -1
- package/dist/src/render/blocks/accordion.js +10 -16
- package/dist/src/render/blocks/accordion.js.map +1 -1
- package/dist/src/render/blocks/collection-list.d.ts +4 -11
- package/dist/src/render/blocks/collection-list.d.ts.map +1 -1
- package/dist/src/render/blocks/collection-list.js +96 -96
- package/dist/src/render/blocks/collection-list.js.map +1 -1
- package/dist/src/render/blocks/cta.d.ts +7 -4
- package/dist/src/render/blocks/cta.d.ts.map +1 -1
- package/dist/src/render/blocks/cta.js +9 -7
- 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 +30 -28
- package/dist/src/render/blocks/embed.js.map +1 -1
- package/dist/src/render/blocks/faq.d.ts +13 -1
- package/dist/src/render/blocks/faq.d.ts.map +1 -1
- package/dist/src/render/blocks/faq.js +15 -11
- 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 +43 -16
- package/dist/src/render/blocks/feature-grid.js.map +1 -1
- package/dist/src/render/blocks/gallery.d.ts +0 -7
- package/dist/src/render/blocks/gallery.d.ts.map +1 -1
- package/dist/src/render/blocks/gallery.js +28 -10
- package/dist/src/render/blocks/gallery.js.map +1 -1
- package/dist/src/render/blocks/hero.d.ts +16 -9
- package/dist/src/render/blocks/hero.d.ts.map +1 -1
- package/dist/src/render/blocks/hero.js +27 -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 +7 -9
- package/dist/src/render/blocks/logo-strip.js.map +1 -1
- package/dist/src/render/blocks/logos.d.ts +8 -0
- package/dist/src/render/blocks/logos.d.ts.map +1 -1
- package/dist/src/render/blocks/logos.js +18 -18
- package/dist/src/render/blocks/logos.js.map +1 -1
- package/dist/src/render/blocks/media-figure.d.ts +14 -3
- package/dist/src/render/blocks/media-figure.d.ts.map +1 -1
- package/dist/src/render/blocks/media-figure.js +34 -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 +17 -22
- package/dist/src/render/blocks/pricing-table.js.map +1 -1
- package/dist/src/render/blocks/prose.d.ts +11 -6
- package/dist/src/render/blocks/prose.d.ts.map +1 -1
- package/dist/src/render/blocks/prose.js +13 -7
- package/dist/src/render/blocks/prose.js.map +1 -1
- package/dist/src/render/blocks/quote.d.ts +6 -2
- package/dist/src/render/blocks/quote.d.ts.map +1 -1
- package/dist/src/render/blocks/quote.js +14 -12
- package/dist/src/render/blocks/quote.js.map +1 -1
- package/dist/src/render/blocks/stat-counter.d.ts +6 -0
- package/dist/src/render/blocks/stat-counter.d.ts.map +1 -1
- package/dist/src/render/blocks/stat-counter.js +7 -10
- package/dist/src/render/blocks/stat-counter.js.map +1 -1
- package/dist/src/render/blocks/stats.d.ts +7 -0
- package/dist/src/render/blocks/stats.d.ts.map +1 -1
- package/dist/src/render/blocks/stats.js +8 -16
- package/dist/src/render/blocks/stats.js.map +1 -1
- package/dist/src/render/blocks/testimonial.d.ts +5 -11
- package/dist/src/render/blocks/testimonial.d.ts.map +1 -1
- package/dist/src/render/blocks/testimonial.js +9 -17
- package/dist/src/render/blocks/testimonial.js.map +1 -1
- package/dist/src/render/chrome.d.ts.map +1 -1
- package/dist/src/render/chrome.js +101 -84
- package/dist/src/render/chrome.js.map +1 -1
- package/dist/src/render/dish.d.ts +13 -0
- package/dist/src/render/dish.d.ts.map +1 -0
- package/dist/src/render/dish.js +84 -0
- package/dist/src/render/dish.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 +33 -0
- package/dist/src/render/layout.js.map +1 -0
- package/dist/src/render/menu.d.ts +53 -0
- package/dist/src/render/menu.d.ts.map +1 -0
- package/dist/src/render/menu.js +99 -0
- package/dist/src/render/menu.js.map +1 -0
- package/dist/src/render/render-block.d.ts +19 -0
- package/dist/src/render/render-block.d.ts.map +1 -1
- package/dist/src/render/render-block.js +50 -39
- package/dist/src/render/render-block.js.map +1 -1
- package/dist/src/render/strings.d.ts +15 -0
- package/dist/src/render/strings.d.ts.map +1 -0
- package/dist/src/render/strings.js +23 -0
- package/dist/src/render/strings.js.map +1 -0
- package/dist/src/render/term-archive.d.ts +10 -4
- package/dist/src/render/term-archive.d.ts.map +1 -1
- package/dist/src/render/term-archive.js +25 -16
- package/dist/src/render/term-archive.js.map +1 -1
- package/dist/src/render/widget-areas.d.ts +13 -0
- package/dist/src/render/widget-areas.d.ts.map +1 -0
- package/dist/src/render/widget-areas.js +20 -0
- package/dist/src/render/widget-areas.js.map +1 -0
- package/dist/theme.config.d.ts +1 -1
- package/dist/theme.config.js +2 -2
- package/dist/theme.config.js.map +1 -1
- package/package.json +5 -5
- package/src/index.ts +3 -0
- package/src/render/blocks/accordion.ts +18 -47
- package/src/render/blocks/collection-list.ts +245 -133
- package/src/render/blocks/cta.ts +20 -11
- package/src/render/blocks/embed.ts +45 -36
- package/src/render/blocks/faq.ts +32 -25
- package/src/render/blocks/feature-grid.ts +83 -33
- package/src/render/blocks/gallery.ts +39 -21
- package/src/render/blocks/hero.ts +41 -34
- package/src/render/blocks/logo-strip.ts +27 -25
- package/src/render/blocks/logos.ts +34 -43
- package/src/render/blocks/media-figure.ts +49 -24
- package/src/render/blocks/pricing-table.ts +30 -42
- package/src/render/blocks/prose.ts +18 -9
- package/src/render/blocks/quote.ts +29 -19
- package/src/render/blocks/stat-counter.ts +24 -29
- package/src/render/blocks/stats.ts +30 -40
- package/src/render/blocks/testimonial.ts +17 -22
- package/src/render/chrome.ts +113 -95
- package/src/render/dish.ts +195 -0
- package/src/render/layout.ts +64 -0
- package/src/render/menu.ts +117 -0
- package/src/render/render-block.ts +51 -40
- package/src/render/strings.ts +44 -0
- package/src/render/term-archive.ts +78 -44
- package/src/render/widget-areas.ts +23 -0
- package/src/styles/archive.css +46 -56
- package/src/styles/base.css +807 -554
- package/src/styles/blocks.css +723 -641
- package/src/styles/menu.css +482 -0
- package/src/styles/theme.css +24 -25
- package/src/styles/tokens.css +239 -225
- package/src/styles/widgets.css +853 -0
- package/tokens.json +16 -16
|
@@ -1,107 +1,124 @@
|
|
|
1
|
-
import { escapeAttribute, escapeText, renderBrandMark, renderSocialLinks, renderThemeToggle, serialize, } from '@cogenta/theme-kit';
|
|
1
|
+
import { escapeAttribute, escapeText, renderBrandMark, renderFooterWidgets, renderSocialLinks, renderThemeToggle, serialize, } from '@cogenta/theme-kit';
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* rather than retrofitted: `headerAction` renders as a button beside the
|
|
5
|
-
* primary nav, `tagline`/`social`/`footerNote` fill the footer's brand
|
|
6
|
-
* column, and — the one thing none of the four L23 themes had yet — a
|
|
7
|
-
* genuinely CSS-only mobile menu.
|
|
3
|
+
* The restaurant's header and footer (`theme@1.4`).
|
|
8
4
|
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
* the
|
|
14
|
-
*
|
|
5
|
+
* Header: one quiet row on the grid, under a hairline. The restaurant's name
|
|
6
|
+
* on the left in the display serif (or its uploaded logo), the pages in words
|
|
7
|
+
* at the text size, then the `headerAction` ("Reserve") as the one underlined
|
|
8
|
+
* link after a hairline, and the light/dark control. The action sits outside
|
|
9
|
+
* the navigation list on purpose: on a phone it stays in the header row
|
|
10
|
+
* beside the menu toggle, because booking a table is what most visitors came
|
|
11
|
+
* to do.
|
|
15
12
|
*
|
|
16
|
-
*
|
|
17
|
-
* the header
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
*
|
|
13
|
+
* Below the wide breakpoint the pages become a panel of large serif links
|
|
14
|
+
* under the header, opened by a CSS-only toggle: a visually hidden checkbox
|
|
15
|
+
* paired with a `<label>`, placed before the `<nav>` so the sibling
|
|
16
|
+
* combinator reaches it. One `<nav>` in the markup, retiled by CSS. Under
|
|
17
|
+
* the links the open panel repeats the `footerNote` (the address card), which
|
|
18
|
+
* the stylesheet shows in the panel only.
|
|
19
|
+
*
|
|
20
|
+
* Footer: a charcoal band. The name and the `tagline`; the `footerNote` set as
|
|
21
|
+
* the restaurant's address card (a blank line starts a new paragraph, a line
|
|
22
|
+
* break stays a line break, so an address and a set of opening hours keep
|
|
23
|
+
* their shape); the footer navigation; the social profiles with their names;
|
|
24
|
+
* the footer widget columns (`theme@1.6`), when a site has placed any, as a
|
|
25
|
+
* second tier under a hairline; then the legal line, with the copyright year and the name beside the host's
|
|
26
|
+
* `brandingHtml`, placed once, as received. Every `theme@1.4` field renders
|
|
27
|
+
* only when present.
|
|
21
28
|
*/
|
|
22
|
-
function navItems(links) {
|
|
29
|
+
function navItems(links, className) {
|
|
23
30
|
return links
|
|
24
31
|
.filter((link) => link.href !== null || link.kind === 'submenu-placeholder')
|
|
25
32
|
.map((link) => {
|
|
26
33
|
const label = escapeText(link.label);
|
|
27
34
|
const titleAttr = link.title === null ? '' : ` title="${escapeAttribute(link.title)}"`;
|
|
28
|
-
if (link.href === null)
|
|
29
|
-
return `<li><span${titleAttr}>${label}</span></li>`;
|
|
30
|
-
|
|
35
|
+
if (link.href === null) {
|
|
36
|
+
return `<li class="${className}"><span${titleAttr}>${label}</span></li>`;
|
|
37
|
+
}
|
|
31
38
|
const target = link.openInNewTab ? ' target="_blank" rel="noopener"' : '';
|
|
32
|
-
return `<li><a href="${href}"${target}${titleAttr}>${label}</a></li>`;
|
|
39
|
+
return `<li class="${className}"><a href="${escapeAttribute(link.href)}"${target}${titleAttr}>${label}</a></li>`;
|
|
33
40
|
})
|
|
34
41
|
.join('');
|
|
35
42
|
}
|
|
36
|
-
/**
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* The mobile drawer — the same nav links and header action as the desktop
|
|
49
|
-
* header, in one `<details>` disclosure. Absent entirely when there is
|
|
50
|
-
* nothing to show, exactly like the desktop `<nav>` above it.
|
|
51
|
-
*/
|
|
52
|
-
function renderMobileNav(input) {
|
|
53
|
-
const items = navItems(input.headerNav);
|
|
54
|
-
const actionItem = input.headerAction === undefined ? '' : `<li>${renderHeaderAction(input.headerAction)}</li>`;
|
|
55
|
-
if (items === '' && actionItem === '')
|
|
43
|
+
/** `footerNote` as paragraphs at blank lines, each keeping its own line breaks. */
|
|
44
|
+
function renderFooterNote(note, className) {
|
|
45
|
+
const paragraphs = note
|
|
46
|
+
.replace(/\r\n?/g, '\n')
|
|
47
|
+
.split(/\n\s*\n/)
|
|
48
|
+
.map((paragraph) => paragraph
|
|
49
|
+
.split('\n')
|
|
50
|
+
.map((line) => line.trim())
|
|
51
|
+
.filter((line) => line !== ''))
|
|
52
|
+
.filter((lines) => lines.length > 0);
|
|
53
|
+
if (paragraphs.length === 0)
|
|
56
54
|
return '';
|
|
57
|
-
return
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
`</details>`);
|
|
55
|
+
return `<div class="${className}">${paragraphs
|
|
56
|
+
.map((lines) => `<p>${lines.map((line) => escapeText(line)).join('<br>')}</p>`)
|
|
57
|
+
.join('')}</div>`;
|
|
61
58
|
}
|
|
62
59
|
export function renderChrome(input) {
|
|
63
|
-
const
|
|
64
|
-
const
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
// footer keeps the site's name in text, so a site whose logo fails to
|
|
68
|
-
// load is still named somewhere on every page.
|
|
69
|
-
const mark = renderBrandMark(input.brand, { className: 'cg-site-header__logo' }) ?? siteNameText;
|
|
70
|
-
const themeToggle = serialize(renderThemeToggle(input.locale, { className: 'cg-theme-toggle' }));
|
|
71
|
-
const headerAction = renderHeaderAction(input.headerAction);
|
|
72
|
-
const mobileNav = renderMobileNav(input);
|
|
73
|
-
const header = `<header class="cg-site-header"><div class="cg-site-header__inner">` +
|
|
74
|
-
`<a class="cg-site-header__home" href="${escapeAttribute(input.homeHref)}">${mark}</a>` +
|
|
75
|
-
`${headerNav === ''
|
|
76
|
-
? ''
|
|
77
|
-
: `<nav class="cg-nav" aria-label="Primary"><ul class="cg-nav__items">${headerNav}</ul></nav>`}` +
|
|
78
|
-
`${themeToggle}` +
|
|
79
|
-
`${headerAction}` +
|
|
80
|
-
`${mobileNav}` +
|
|
81
|
-
`</div></header>`;
|
|
82
|
-
const tagline = input.tagline === undefined
|
|
60
|
+
const siteName = escapeText(input.site.name);
|
|
61
|
+
const home = escapeAttribute(input.homeHref);
|
|
62
|
+
const links = navItems(input.headerNav, 'cr-header__item');
|
|
63
|
+
const action = input.headerAction === undefined
|
|
83
64
|
? ''
|
|
84
|
-
: `<
|
|
85
|
-
const
|
|
65
|
+
: `<a class="cr-header__action" href="${escapeAttribute(input.headerAction.href)}">${escapeText(input.headerAction.label)}</a>`;
|
|
66
|
+
const toggle = serialize(renderThemeToggle(input.locale, { className: 'cg-theme-toggle' }));
|
|
67
|
+
const logo = renderBrandMark(input.brand, { className: 'cr-header__logo' });
|
|
68
|
+
const mark = logo ?? `<span class="cr-header__wordmark">${siteName}</span>`;
|
|
69
|
+
// On a phone the open panel also carries the address card, because the
|
|
70
|
+
// address and the hours are what a guest on the street opens the menu for.
|
|
71
|
+
// It is the footer's own note, shown only in the panel.
|
|
72
|
+
const panelCard = input.footerNote === undefined ? '' : renderFooterNote(input.footerNote, 'cr-header__card');
|
|
73
|
+
const menu = links === ''
|
|
86
74
|
? ''
|
|
87
|
-
:
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
75
|
+
: `<input type="checkbox" id="cr-nav-toggle" class="cr-nav-toggle-input" aria-label="Navigation">` +
|
|
76
|
+
`<label for="cr-nav-toggle" class="cr-nav-toggle-label" aria-hidden="true">` +
|
|
77
|
+
`<span class="cr-nav-toggle-bar"></span><span class="cr-nav-toggle-bar"></span>` +
|
|
78
|
+
`</label>` +
|
|
79
|
+
`<nav class="cr-header__nav" id="cr-nav" aria-label="Primary">` +
|
|
80
|
+
`<ul class="cr-header__links">${links}</ul>` +
|
|
81
|
+
`${panelCard}` +
|
|
82
|
+
`</nav>`;
|
|
83
|
+
const header = `<header class="cr-header" data-nav="${links === '' ? 'none' : 'links'}" data-action="${action === '' ? 'none' : 'link'}">` +
|
|
84
|
+
`<div class="cr-header__inner">` +
|
|
85
|
+
`<a class="cr-header__brand" href="${home}">${mark}</a>` +
|
|
86
|
+
`${menu}` +
|
|
87
|
+
`${action}` +
|
|
88
|
+
`${toggle}` +
|
|
89
|
+
`</div></header>`;
|
|
90
|
+
const tagline = input.tagline === undefined
|
|
92
91
|
? ''
|
|
93
|
-
: `<p class="
|
|
94
|
-
const
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
92
|
+
: `<p class="cr-footer__tagline" data-field="tagline">${escapeText(input.tagline)}</p>`;
|
|
93
|
+
const note = input.footerNote === undefined ? '' : renderFooterNote(input.footerNote, 'cr-footer__note');
|
|
94
|
+
const footerLinks = navItems(input.footerNav, 'cr-footer__item');
|
|
95
|
+
const social = renderSocialLinks(input.social, {
|
|
96
|
+
className: 'cr-footer__social',
|
|
97
|
+
itemClassName: 'cr-footer__social-item',
|
|
98
|
+
});
|
|
99
|
+
const year = new Date().getFullYear();
|
|
100
|
+
// The footer widget columns (`theme@1.6`): a row of their own under the
|
|
101
|
+
// name, the address card, the pages and the profiles, above the legal
|
|
102
|
+
// line, on the footer's own twelve columns.
|
|
103
|
+
const footerWidgets = renderFooterWidgets(input.widgets, {
|
|
104
|
+
className: 'cr-footer__widgets',
|
|
105
|
+
headingLevel: 'h2',
|
|
106
|
+
});
|
|
107
|
+
const footer = `<footer class="cr-footer"><div class="cr-footer__inner">` +
|
|
108
|
+
`<div class="cr-footer__about">` +
|
|
109
|
+
`<a class="cr-footer__name" href="${home}">${siteName}</a>` +
|
|
110
|
+
`${tagline}` +
|
|
111
|
+
`</div>` +
|
|
112
|
+
`${note}` +
|
|
113
|
+
`${footerLinks === ''
|
|
99
114
|
? ''
|
|
100
|
-
: `<nav class="
|
|
101
|
-
`<div class="
|
|
102
|
-
|
|
103
|
-
`<
|
|
104
|
-
`<
|
|
115
|
+
: `<nav class="cr-footer__nav" aria-label="Footer"><ul class="cr-footer__links">${footerLinks}</ul></nav>`}` +
|
|
116
|
+
`${social === null ? '' : `<div class="cr-footer__follow">${serialize(social)}</div>`}` +
|
|
117
|
+
`${footerWidgets === null ? '' : serialize(footerWidgets)}` +
|
|
118
|
+
`<div class="cr-footer__legal">` +
|
|
119
|
+
`<p class="cr-footer__copyright">© ${year} ${siteName}</p>` +
|
|
120
|
+
`${input.brandingHtml === '' ? '' : `<div class="cr-footer__branding">${input.brandingHtml}</div>`}` +
|
|
121
|
+
`</div>` +
|
|
105
122
|
`</div></footer>`;
|
|
106
123
|
return { header, footer };
|
|
107
124
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"chrome.js","sourceRoot":"","sources":["../../../src/render/chrome.ts"],"names":[],"mappings":"AAAA,OAAO,EAIL,eAAe,EACf,UAAU,EACV,eAAe,EACf,iBAAiB,EACjB,iBAAiB,EACjB,SAAS,GACV,MAAM,oBAAoB,CAAA;AAE3B
|
|
1
|
+
{"version":3,"file":"chrome.js","sourceRoot":"","sources":["../../../src/render/chrome.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type HtmlElement, type PageContent, type PageEntryMeta, type RenderContext } from '@cogenta/theme-kit';
|
|
2
|
+
export declare function isDishPage(entry: PageEntryMeta | undefined): entry is PageEntryMeta;
|
|
3
|
+
export declare function renderDishHeader(page: PageContent, entry: PageEntryMeta, ctx: RenderContext): HtmlElement;
|
|
4
|
+
/**
|
|
5
|
+
* Any other page: its title large and light on the grid, the entry's summary
|
|
6
|
+
* under it in the serif italic when it has one. A dated entry (a note from
|
|
7
|
+
* the kitchen) also carries its date, author and reading time in one small
|
|
8
|
+
* line, and its cover photograph under the title. An undated entry with a
|
|
9
|
+
* picture keeps the picture for the lists that link to it: its own page
|
|
10
|
+
* opens on words.
|
|
11
|
+
*/
|
|
12
|
+
export declare function renderPageHeader(page: PageContent, ctx: RenderContext): HtmlElement;
|
|
13
|
+
//# sourceMappingURL=dish.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dish.d.ts","sourceRoot":"","sources":["../../../src/render/dish.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,WAAW,EAEhB,KAAK,WAAW,EAEhB,KAAK,aAAa,EAClB,KAAK,aAAa,EAEnB,MAAM,oBAAoB,CAAA;AAmC3B,wBAAgB,UAAU,CAAC,KAAK,EAAE,aAAa,GAAG,SAAS,GAAG,KAAK,IAAI,aAAa,CAEnF;AAmCD,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,WAAW,EACjB,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,aAAa,GACjB,WAAW,CAmDb;AA0BD;;;;;;;GAOG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,EAAE,GAAG,EAAE,aAAa,GAAG,WAAW,CAyBnF"}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { h, renderImageSource, } from '@cogenta/theme-kit';
|
|
2
|
+
import { currencyOf, DETAIL_FIELDS, formatPrice, isVegetarian, priceOf, sectionOf, textOf, } from './menu.js';
|
|
3
|
+
import { menuString } from './strings.js';
|
|
4
|
+
export function isDishPage(entry) {
|
|
5
|
+
return entry !== undefined && priceOf(entry.fields?.price) !== undefined;
|
|
6
|
+
}
|
|
7
|
+
function details(fields, locale) {
|
|
8
|
+
const rows = DETAIL_FIELDS.flatMap((name) => {
|
|
9
|
+
const value = textOf(fields[name]);
|
|
10
|
+
if (value === undefined)
|
|
11
|
+
return [];
|
|
12
|
+
return [
|
|
13
|
+
h('div', { class: 'cr-dish__fact', 'data-fact': name }, h('dt', { class: 'cr-dish__fact-label' }, menuString(locale, name)), h('dd', { class: 'cr-dish__fact-value' }, value)),
|
|
14
|
+
];
|
|
15
|
+
});
|
|
16
|
+
if (rows.length === 0)
|
|
17
|
+
return null;
|
|
18
|
+
return h('div', { class: 'cr-dish__details' }, h('h2', { class: 'cg-visually-hidden' }, menuString(locale, 'details')), h('dl', { class: 'cr-dish__facts' }, rows));
|
|
19
|
+
}
|
|
20
|
+
function terms(entry) {
|
|
21
|
+
if (entry.terms === undefined || entry.terms.length === 0)
|
|
22
|
+
return null;
|
|
23
|
+
return h('ul', { class: 'cr-dish__terms' }, entry.terms.map((term) => h('li', {}, term.href === null ? term.label : h('a', { href: term.href }, term.label))));
|
|
24
|
+
}
|
|
25
|
+
export function renderDishHeader(page, entry, ctx) {
|
|
26
|
+
const fields = entry.fields ?? {};
|
|
27
|
+
const price = priceOf(fields.price);
|
|
28
|
+
const section = sectionOf(fields);
|
|
29
|
+
const image = entry.image;
|
|
30
|
+
return h('article', { class: 'cr-dish', 'data-media': String(image !== undefined) }, h('div', { class: 'cr-container cr-dish__inner' }, image === undefined
|
|
31
|
+
? null
|
|
32
|
+
: h('figure', { class: 'cr-dish__media' }, renderImageSource(image, {
|
|
33
|
+
className: 'cr-dish__image',
|
|
34
|
+
loading: 'eager',
|
|
35
|
+
sizes: '(min-width: 64rem) 48vw, 100vw',
|
|
36
|
+
})), h('div', { class: 'cr-dish__info' }, section === undefined ? null : h('p', { class: 'cr-dish__section' }, section), h('h1', { class: 'cr-dish__title' }, page.title), entry.excerpt === undefined ? null : h('p', { class: 'cr-dish__summary' }, entry.excerpt), h('p', { class: 'cr-dish__price-line' }, h('span', { class: 'cr-dish__price' }, h('span', { class: 'cg-visually-hidden' }, `${menuString(ctx.locale, 'price')} `), h('data', { value: String(price) }, formatPrice(price, currencyOf(fields.currency), ctx.locale))), isVegetarian(fields)
|
|
37
|
+
? h('span', { class: 'cr-dish__diet' }, menuString(ctx.locale, 'vegetarian'))
|
|
38
|
+
: null), details(fields, ctx.locale), terms(entry))));
|
|
39
|
+
}
|
|
40
|
+
function formatDate(iso, locale) {
|
|
41
|
+
try {
|
|
42
|
+
return new Intl.DateTimeFormat(locale, { dateStyle: 'long' }).format(new Date(iso));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
return iso;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Date, author and reading time, for a dated entry only: an opening-hours
|
|
50
|
+
* page is not something a guest reads against the clock.
|
|
51
|
+
*/
|
|
52
|
+
function pageMeta(entry, ctx) {
|
|
53
|
+
if (entry.publishedAt === undefined)
|
|
54
|
+
return null;
|
|
55
|
+
const parts = [
|
|
56
|
+
h('time', { datetime: entry.publishedAt }, formatDate(entry.publishedAt, ctx.locale)),
|
|
57
|
+
];
|
|
58
|
+
if (entry.author !== undefined)
|
|
59
|
+
parts.push(h('span', {}, entry.author.name));
|
|
60
|
+
if (entry.readingMinutes !== undefined) {
|
|
61
|
+
parts.push(h('span', {}, ctx.t('entry.readingTime', { minutes: entry.readingMinutes })));
|
|
62
|
+
}
|
|
63
|
+
return h('p', { class: 'cr-page-head__meta' }, parts);
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Any other page: its title large and light on the grid, the entry's summary
|
|
67
|
+
* under it in the serif italic when it has one. A dated entry (a note from
|
|
68
|
+
* the kitchen) also carries its date, author and reading time in one small
|
|
69
|
+
* line, and its cover photograph under the title. An undated entry with a
|
|
70
|
+
* picture keeps the picture for the lists that link to it: its own page
|
|
71
|
+
* opens on words.
|
|
72
|
+
*/
|
|
73
|
+
export function renderPageHeader(page, ctx) {
|
|
74
|
+
const entry = page.entry;
|
|
75
|
+
const cover = entry?.publishedAt === undefined ? undefined : entry.image;
|
|
76
|
+
return h('header', { class: 'cr-page-head', 'data-cover': String(cover !== undefined) }, h('div', { class: 'cr-container cr-page-head__inner' }, h('h1', { class: 'cr-page-head__title' }, page.title), entry?.excerpt === undefined ? null : h('p', { class: 'cr-page-head__lead' }, entry.excerpt), entry === undefined ? null : pageMeta(entry, ctx), cover === undefined
|
|
77
|
+
? null
|
|
78
|
+
: h('figure', { class: 'cr-page-head__cover' }, renderImageSource(cover, {
|
|
79
|
+
className: 'cr-page-head__image',
|
|
80
|
+
loading: 'eager',
|
|
81
|
+
sizes: '(min-width: 90rem) 86rem, 100vw',
|
|
82
|
+
}))));
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=dish.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dish.js","sourceRoot":"","sources":["../../../src/render/dish.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type Attributes, type Child, type HtmlElement } from '@cogenta/theme-kit';
|
|
2
|
+
/**
|
|
3
|
+
* The frame every block of this theme renders into.
|
|
4
|
+
*
|
|
5
|
+
* The outer element carries the one section rhythm (`--cr-section`, the same
|
|
6
|
+
* for every block) and paints edge to edge when a variant asks for a band.
|
|
7
|
+
* The inner element is the twelve-column container, so a section title, a
|
|
8
|
+
* dish name, a caption and a price all start on the same line.
|
|
9
|
+
*
|
|
10
|
+
* `inner` is `figure` for a block whose semantics are a figure (a quotation
|
|
11
|
+
* and its attribution, an image and its caption): `<figcaption>` must be a
|
|
12
|
+
* direct child of its `<figure>`.
|
|
13
|
+
*/
|
|
14
|
+
export declare function section(outer: 'section' | 'div', block: string, className: string, attrs: Attributes, inner: 'div' | 'figure', ...children: readonly Child[]): HtmlElement;
|
|
15
|
+
/**
|
|
16
|
+
* A titled block's heading: set in the display serif at the section size, on
|
|
17
|
+
* the first column of the grid. A dining room lets the food and the room
|
|
18
|
+
* carry the page, so a section names itself in a few words and nothing else.
|
|
19
|
+
*/
|
|
20
|
+
export declare function sectionHead(blockName: string, title: string | undefined): HtmlElement | null;
|
|
21
|
+
/** A paragraph that renders only when the text is there: no empty `<p>` on the page. */
|
|
22
|
+
export declare function optionalText(tag: string, className: string, value: string | undefined, attrs?: Attributes): HtmlElement | null;
|
|
23
|
+
//# sourceMappingURL=layout.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.d.ts","sourceRoot":"","sources":["../../../src/render/layout.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,UAAU,EAEf,KAAK,KAAK,EACV,KAAK,WAAW,EAGjB,MAAM,oBAAoB,CAAA;AAE3B;;;;;;;;;;;GAWG;AACH,wBAAgB,OAAO,CACrB,KAAK,EAAE,SAAS,GAAG,KAAK,EACxB,KAAK,EAAE,MAAM,EACb,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,UAAU,EACjB,KAAK,EAAE,KAAK,GAAG,QAAQ,EACvB,GAAG,QAAQ,EAAE,SAAS,KAAK,EAAE,GAC5B,WAAW,CAMb;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,SAAS,GAAG,WAAW,GAAG,IAAI,CAW5F;AAED,wFAAwF;AACxF,wBAAgB,YAAY,CAC1B,GAAG,EAAE,MAAM,EACX,SAAS,EAAE,MAAM,EACjB,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,KAAK,GAAE,UAAe,GACrB,WAAW,GAAG,IAAI,CAGpB"}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { blockHeadingTag, h, heading, } from '@cogenta/theme-kit';
|
|
2
|
+
/**
|
|
3
|
+
* The frame every block of this theme renders into.
|
|
4
|
+
*
|
|
5
|
+
* The outer element carries the one section rhythm (`--cr-section`, the same
|
|
6
|
+
* for every block) and paints edge to edge when a variant asks for a band.
|
|
7
|
+
* The inner element is the twelve-column container, so a section title, a
|
|
8
|
+
* dish name, a caption and a price all start on the same line.
|
|
9
|
+
*
|
|
10
|
+
* `inner` is `figure` for a block whose semantics are a figure (a quotation
|
|
11
|
+
* and its attribution, an image and its caption): `<figcaption>` must be a
|
|
12
|
+
* direct child of its `<figure>`.
|
|
13
|
+
*/
|
|
14
|
+
export function section(outer, block, className, attrs, inner, ...children) {
|
|
15
|
+
return h(outer, { class: `cr-section ${className}`, 'data-block': block, ...attrs }, h(inner, { class: `cr-container ${className}__inner` }, ...children));
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* A titled block's heading: set in the display serif at the section size, on
|
|
19
|
+
* the first column of the grid. A dining room lets the food and the room
|
|
20
|
+
* carry the page, so a section names itself in a few words and nothing else.
|
|
21
|
+
*/
|
|
22
|
+
export function sectionHead(blockName, title) {
|
|
23
|
+
if (title === undefined)
|
|
24
|
+
return null;
|
|
25
|
+
return h('div', { class: 'cr-head' }, heading(blockHeadingTag(blockName) ?? 'h2', { class: 'cr-head__title', 'data-field': 'title' }, title));
|
|
26
|
+
}
|
|
27
|
+
/** A paragraph that renders only when the text is there: no empty `<p>` on the page. */
|
|
28
|
+
export function optionalText(tag, className, value, attrs = {}) {
|
|
29
|
+
if (value === undefined || value.trim() === '')
|
|
30
|
+
return null;
|
|
31
|
+
return h(tag, { class: className, ...attrs }, value);
|
|
32
|
+
}
|
|
33
|
+
//# sourceMappingURL=layout.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"layout.js","sourceRoot":"","sources":["../../../src/render/layout.ts"],"names":[],"mappings":"AAAA,OAAO,EAEL,eAAe,EAGf,CAAC,EACD,OAAO,GACR,MAAM,oBAAoB,CAAA;AAE3B;;;;;;;;;;;GAWG;AACH,MAAM,UAAU,OAAO,CACrB,KAAwB,EACxB,KAAa,EACb,SAAiB,EACjB,KAAiB,EACjB,KAAuB,EACvB,GAAG,QAA0B;IAE7B,OAAO,CAAC,CACN,KAAK,EACL,EAAE,KAAK,EAAE,cAAc,SAAS,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,KAAK,EAAE,EACnE,CAAC,CAAC,KAAK,EAAE,EAAE,KAAK,EAAE,gBAAgB,SAAS,SAAS,EAAE,EAAE,GAAG,QAAQ,CAAC,CACrE,CAAA;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,SAAiB,EAAE,KAAyB;IACtE,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAA;IACpC,OAAO,CAAC,CACN,KAAK,EACL,EAAE,KAAK,EAAE,SAAS,EAAE,EACpB,OAAO,CACL,eAAe,CAAC,SAAS,CAAC,IAAI,IAAI,EAClC,EAAE,KAAK,EAAE,gBAAgB,EAAE,YAAY,EAAE,OAAO,EAAE,EAClD,KAAK,CACN,CACF,CAAA;AACH,CAAC;AAED,wFAAwF;AACxF,MAAM,UAAU,YAAY,CAC1B,GAAW,EACX,SAAiB,EACjB,KAAyB,EACzB,KAAK,GAAe,EAAE;IAEtB,IAAI,KAAK,KAAK,SAAS,IAAI,KAAK,CAAC,IAAI,EAAE,KAAK,EAAE;QAAE,OAAO,IAAI,CAAA;IAC3D,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,KAAK,EAAE,EAAE,KAAK,CAAC,CAAA;AACtD,CAAC"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a dish out of plain content data.
|
|
3
|
+
*
|
|
4
|
+
* Contract A fixes the system fields and nothing else, so a theme can only
|
|
5
|
+
* recognise a dish by convention: an entry with a numeric `price` is
|
|
6
|
+
* something on the menu, whatever its collection is called, and its
|
|
7
|
+
* `category` (or `section`, or `course`) is the part of the menu it belongs
|
|
8
|
+
* to. The same convention reads a dish in a menu list (the raw entry) and on
|
|
9
|
+
* its own page (`theme@1.5`'s `PageEntryMeta.fields`), so the two can never
|
|
10
|
+
* disagree about a price.
|
|
11
|
+
*
|
|
12
|
+
* Money is shown, never computed: no rounding, no service charge, no
|
|
13
|
+
* conversion. The currency comes from the entry's own `currency` field (an
|
|
14
|
+
* ISO 4217 code); contract B has no currency, so an entry that does not say
|
|
15
|
+
* is shown in euros, the currency every dish this theme's blueprint seeds is
|
|
16
|
+
* priced in. A restaurant that charges in another currency sets the field.
|
|
17
|
+
*/
|
|
18
|
+
export declare const DEFAULT_CURRENCY = "EUR";
|
|
19
|
+
/** A finite, non-negative number, or `undefined` for anything else. */
|
|
20
|
+
export declare function priceOf(value: unknown): number | undefined;
|
|
21
|
+
/** An ISO 4217 code the platform accepts, or the default. */
|
|
22
|
+
export declare function currencyOf(value: unknown): string;
|
|
23
|
+
/**
|
|
24
|
+
* `€16` in English, `16 €` in French: the platform's own currency formatting
|
|
25
|
+
* in the page's locale. A whole amount shows no decimals, a price with cents
|
|
26
|
+
* shows two, so a menu of round prices does not read as a column of `.00`.
|
|
27
|
+
*/
|
|
28
|
+
export declare function formatPrice(amount: number, currency: string, locale: string): string;
|
|
29
|
+
/** A non-empty string, trimmed, or `undefined`. */
|
|
30
|
+
export declare function textOf(value: unknown): string | undefined;
|
|
31
|
+
/** The first field among `names` that holds text. */
|
|
32
|
+
export declare function firstText(source: Readonly<Record<string, unknown>>, names: readonly string[]): string | undefined;
|
|
33
|
+
/** The part of the menu a dish belongs to, under whichever of the usual names a collection gave it. */
|
|
34
|
+
export declare const SECTION_FIELDS: readonly ['category', 'section', 'course'];
|
|
35
|
+
/** The plain details a dish page lists, in the order a guest reads them. */
|
|
36
|
+
export declare const DETAIL_FIELDS: readonly ['sourcing', 'pairing', 'allergens'];
|
|
37
|
+
export type DetailField = (typeof DETAIL_FIELDS)[number];
|
|
38
|
+
export declare function sectionOf(source: Readonly<Record<string, unknown>>): string | undefined;
|
|
39
|
+
/** `vegetarian: true` says so; anything else says nothing. */
|
|
40
|
+
export declare function isVegetarian(source: Readonly<Record<string, unknown>>): boolean;
|
|
41
|
+
export interface MenuSection<T> {
|
|
42
|
+
/** `undefined` for dishes that name no section: one untitled group. */
|
|
43
|
+
readonly title: string | undefined;
|
|
44
|
+
readonly items: readonly T[];
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Groups dishes by section, keeping the order the list arrived in: a section
|
|
48
|
+
* appears where its first dish does, and its dishes keep their own order
|
|
49
|
+
* inside it. The editor orders the menu by ordering the entries; the theme
|
|
50
|
+
* never sorts it alphabetically behind their back.
|
|
51
|
+
*/
|
|
52
|
+
export declare function groupBySection<T extends Readonly<Record<string, unknown>>>(entries: readonly T[]): readonly MenuSection<T>[];
|
|
53
|
+
//# sourceMappingURL=menu.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.d.ts","sourceRoot":"","sources":["../../../src/render/menu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,eAAO,MAAM,gBAAgB,QAAQ,CAAA;AAErC,uEAAuE;AACvE,wBAAgB,OAAO,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAE1D;AAED,6DAA6D;AAC7D,wBAAgB,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAIjD;AAED;;;;GAIG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAapF;AAED,mDAAmD;AACnD,wBAAgB,MAAM,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,GAAG,SAAS,CAIzD;AAED,qDAAqD;AACrD,wBAAgB,SAAS,CACvB,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACzC,KAAK,EAAE,SAAS,MAAM,EAAE,GACvB,MAAM,GAAG,SAAS,CAMpB;AAED,uGAAuG;AACvG,eAAO,MAAM,cAAc,YAAI,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAU,CAAA;AAExE,4EAA4E;AAC5E,eAAO,MAAM,aAAa,YAAI,UAAU,EAAE,SAAS,EAAE,WAAW,CAAU,CAAA;AAE1E,MAAM,MAAM,WAAW,GAAG,CAAC,OAAO,aAAa,CAAC,CAAC,MAAM,CAAC,CAAA;AAExD,wBAAgB,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,MAAM,GAAG,SAAS,CAEvF;AAED,8DAA8D;AAC9D,wBAAgB,YAAY,CAAC,MAAM,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,OAAO,CAE/E;AAED,MAAM,WAAW,WAAW,CAAC,CAAC;IAC5B,uEAAuE;IACvE,QAAQ,CAAC,KAAK,EAAE,MAAM,GAAG,SAAS,CAAA;IAClC,QAAQ,CAAC,KAAK,EAAE,SAAS,CAAC,EAAE,CAAA;CAC7B;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,CAAC,SAAS,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,EACxE,OAAO,EAAE,SAAS,CAAC,EAAE,GACpB,SAAS,WAAW,CAAC,CAAC,CAAC,EAAE,CAc3B"}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Reading a dish out of plain content data.
|
|
3
|
+
*
|
|
4
|
+
* Contract A fixes the system fields and nothing else, so a theme can only
|
|
5
|
+
* recognise a dish by convention: an entry with a numeric `price` is
|
|
6
|
+
* something on the menu, whatever its collection is called, and its
|
|
7
|
+
* `category` (or `section`, or `course`) is the part of the menu it belongs
|
|
8
|
+
* to. The same convention reads a dish in a menu list (the raw entry) and on
|
|
9
|
+
* its own page (`theme@1.5`'s `PageEntryMeta.fields`), so the two can never
|
|
10
|
+
* disagree about a price.
|
|
11
|
+
*
|
|
12
|
+
* Money is shown, never computed: no rounding, no service charge, no
|
|
13
|
+
* conversion. The currency comes from the entry's own `currency` field (an
|
|
14
|
+
* ISO 4217 code); contract B has no currency, so an entry that does not say
|
|
15
|
+
* is shown in euros, the currency every dish this theme's blueprint seeds is
|
|
16
|
+
* priced in. A restaurant that charges in another currency sets the field.
|
|
17
|
+
*/
|
|
18
|
+
export const DEFAULT_CURRENCY = 'EUR';
|
|
19
|
+
/** A finite, non-negative number, or `undefined` for anything else. */
|
|
20
|
+
export function priceOf(value) {
|
|
21
|
+
return typeof value === 'number' && Number.isFinite(value) && value >= 0 ? value : undefined;
|
|
22
|
+
}
|
|
23
|
+
/** An ISO 4217 code the platform accepts, or the default. */
|
|
24
|
+
export function currencyOf(value) {
|
|
25
|
+
if (typeof value !== 'string' || !/^[A-Za-z]{3}$/.test(value.trim()))
|
|
26
|
+
return DEFAULT_CURRENCY;
|
|
27
|
+
const code = value.trim().toUpperCase();
|
|
28
|
+
return Intl.supportedValuesOf('currency').includes(code) ? code : DEFAULT_CURRENCY;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* `€16` in English, `16 €` in French: the platform's own currency formatting
|
|
32
|
+
* in the page's locale. A whole amount shows no decimals, a price with cents
|
|
33
|
+
* shows two, so a menu of round prices does not read as a column of `.00`.
|
|
34
|
+
*/
|
|
35
|
+
export function formatPrice(amount, currency, locale) {
|
|
36
|
+
const whole = Number.isInteger(amount);
|
|
37
|
+
const options = {
|
|
38
|
+
style: 'currency',
|
|
39
|
+
currency,
|
|
40
|
+
minimumFractionDigits: whole ? 0 : 2,
|
|
41
|
+
maximumFractionDigits: whole ? 0 : 2,
|
|
42
|
+
};
|
|
43
|
+
try {
|
|
44
|
+
return new Intl.NumberFormat(locale, options).format(amount);
|
|
45
|
+
}
|
|
46
|
+
catch {
|
|
47
|
+
return new Intl.NumberFormat('en', options).format(amount);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
/** A non-empty string, trimmed, or `undefined`. */
|
|
51
|
+
export function textOf(value) {
|
|
52
|
+
if (typeof value !== 'string')
|
|
53
|
+
return undefined;
|
|
54
|
+
const trimmed = value.trim();
|
|
55
|
+
return trimmed === '' ? undefined : trimmed;
|
|
56
|
+
}
|
|
57
|
+
/** The first field among `names` that holds text. */
|
|
58
|
+
export function firstText(source, names) {
|
|
59
|
+
for (const name of names) {
|
|
60
|
+
const value = textOf(source[name]);
|
|
61
|
+
if (value !== undefined)
|
|
62
|
+
return value;
|
|
63
|
+
}
|
|
64
|
+
return undefined;
|
|
65
|
+
}
|
|
66
|
+
/** The part of the menu a dish belongs to, under whichever of the usual names a collection gave it. */
|
|
67
|
+
export const SECTION_FIELDS = ['category', 'section', 'course'];
|
|
68
|
+
/** The plain details a dish page lists, in the order a guest reads them. */
|
|
69
|
+
export const DETAIL_FIELDS = ['sourcing', 'pairing', 'allergens'];
|
|
70
|
+
export function sectionOf(source) {
|
|
71
|
+
return firstText(source, SECTION_FIELDS);
|
|
72
|
+
}
|
|
73
|
+
/** `vegetarian: true` says so; anything else says nothing. */
|
|
74
|
+
export function isVegetarian(source) {
|
|
75
|
+
return source.vegetarian === true;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Groups dishes by section, keeping the order the list arrived in: a section
|
|
79
|
+
* appears where its first dish does, and its dishes keep their own order
|
|
80
|
+
* inside it. The editor orders the menu by ordering the entries; the theme
|
|
81
|
+
* never sorts it alphabetically behind their back.
|
|
82
|
+
*/
|
|
83
|
+
export function groupBySection(entries) {
|
|
84
|
+
const order = [];
|
|
85
|
+
const groups = new Map();
|
|
86
|
+
for (const entry of entries) {
|
|
87
|
+
const title = sectionOf(entry);
|
|
88
|
+
const group = groups.get(title);
|
|
89
|
+
if (group === undefined) {
|
|
90
|
+
order.push(title);
|
|
91
|
+
groups.set(title, [entry]);
|
|
92
|
+
}
|
|
93
|
+
else {
|
|
94
|
+
group.push(entry);
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return order.map((title) => ({ title, items: groups.get(title) ?? [] }));
|
|
98
|
+
}
|
|
99
|
+
//# sourceMappingURL=menu.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"menu.js","sourceRoot":"","sources":["../../../src/render/menu.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAA;AAErC,uEAAuE;AACvE,MAAM,UAAU,OAAO,CAAC,KAAc;IACpC,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAA;AAC9F,CAAC;AAED,6DAA6D;AAC7D,MAAM,UAAU,UAAU,CAAC,KAAc;IACvC,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,CAAC,eAAe,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QAAE,OAAO,gBAAgB,CAAA;IAC7F,MAAM,IAAI,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC,WAAW,EAAE,CAAA;IACvC,OAAO,IAAI,CAAC,iBAAiB,CAAC,UAAU,CAAC,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,gBAAgB,CAAA;AACpF,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,WAAW,CAAC,MAAc,EAAE,QAAgB,EAAE,MAAc;IAC1E,MAAM,KAAK,GAAG,MAAM,CAAC,SAAS,CAAC,MAAM,CAAC,CAAA;IACtC,MAAM,OAAO,GAA6B;QACxC,KAAK,EAAE,UAAU;QACjB,QAAQ;QACR,qBAAqB,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;QACpC,qBAAqB,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;KACrC,CAAA;IACD,IAAI,CAAC;QACH,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC9D,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC,MAAM,CAAC,MAAM,CAAC,CAAA;IAC5D,CAAC;AACH,CAAC;AAED,mDAAmD;AACnD,MAAM,UAAU,MAAM,CAAC,KAAc;IACnC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,SAAS,CAAA;IAC/C,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAA;IAC5B,OAAO,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,OAAO,CAAA;AAC7C,CAAC;AAED,qDAAqD;AACrD,MAAM,UAAU,SAAS,CACvB,MAAyC,EACzC,KAAwB;IAExB,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,KAAK,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAA;QAClC,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,KAAK,CAAA;IACvC,CAAC;IACD,OAAO,SAAS,CAAA;AAClB,CAAC;AAED,uGAAuG;AACvG,MAAM,CAAC,MAAM,cAAc,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,QAAQ,CAAU,CAAA;AAExE,4EAA4E;AAC5E,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,UAAU,EAAE,SAAS,EAAE,WAAW,CAAU,CAAA;AAI1E,MAAM,UAAU,SAAS,CAAC,MAAyC;IACjE,OAAO,SAAS,CAAC,MAAM,EAAE,cAAc,CAAC,CAAA;AAC1C,CAAC;AAED,8DAA8D;AAC9D,MAAM,UAAU,YAAY,CAAC,MAAyC;IACpE,OAAO,MAAM,CAAC,UAAU,KAAK,IAAI,CAAA;AACnC,CAAC;AAQD;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,OAAqB;IAErB,MAAM,KAAK,GAA2B,EAAE,CAAA;IACxC,MAAM,MAAM,GAAG,IAAI,GAAG,EAA2B,CAAA;IACjD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,CAAA;QAC9B,MAAM,KAAK,GAAG,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAA;QAC/B,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;YACxB,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;YACjB,MAAM,CAAC,GAAG,CAAC,KAAK,EAAE,CAAC,KAAK,CAAC,CAAC,CAAA;QAC5B,CAAC;aAAM,CAAC;YACN,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAA;QACnB,CAAC;IACH,CAAC;IACD,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,CAAA;AAC1E,CAAC"}
|
|
@@ -2,5 +2,24 @@ import { type BlockRegistry, type VocabularyBlock } from '@cogenta/blocks';
|
|
|
2
2
|
import { type FetchedEntries, type HtmlElement, type PageContent, type RenderContext } from '@cogenta/theme-kit';
|
|
3
3
|
export type { FetchedEntries, PageContent };
|
|
4
4
|
export declare function renderBlock(block: VocabularyBlock, ctx: RenderContext, entries?: FetchedEntries, registry?: BlockRegistry): HtmlElement | null;
|
|
5
|
+
/**
|
|
6
|
+
* `<main id="cg-main">` is mandatory: it is the skip link's target, written
|
|
7
|
+
* by the host outside any theme's control.
|
|
8
|
+
*
|
|
9
|
+
* Three openings, and exactly one `h1` in each:
|
|
10
|
+
*
|
|
11
|
+
* - a page whose blocks include a `hero` lets the hero carry the title;
|
|
12
|
+
* - a dish (`isDishPage`: its `theme@1.5` fields carry a price) gets the dish
|
|
13
|
+
* sheet: photograph, section, name, description, price, details;
|
|
14
|
+
* - anything else gets its title set large on the grid.
|
|
15
|
+
*
|
|
16
|
+
* A host older than `theme@1.5` sends no fields, so its dish pages open like
|
|
17
|
+
* any other page: title and description, never a price read from somewhere
|
|
18
|
+
* it was not given. A host older than `theme@1.4` sends no entry at all, and
|
|
19
|
+
* the page opens on its bare title.
|
|
20
|
+
*
|
|
21
|
+
* `withBlockKey` stamps every rendered block with its contract-B `_key`, so
|
|
22
|
+
* the visual page builder (L16) can map a clicked element back to its block.
|
|
23
|
+
*/
|
|
5
24
|
export declare function renderPage(page: PageContent, ctx: RenderContext, entries?: FetchedEntries, registry?: BlockRegistry): HtmlElement;
|
|
6
25
|
//# sourceMappingURL=render-block.d.ts.map
|