package gallery
import (
"fmt"
"html/template"
)
// The preview heights: how tall each example's own document is, so the
// box the widget draws it in has its height before the frame arrives,
// scripts or no scripts, and a wrong number is a test failure rather
// than a jump on screen. Measured, not guessed: the two height drives
// in heights_browser_test.go read every frame's content off a real
// engine and name the number to write here; FIESTA_SWEEP=full prints
// the whole table. Keyed /, /row for a
// grouped widget, and demo for the demo application.
//
// A box holds what a reader opens in it as well as what is there at
// rest: a menu, the enhanced select's list, the date and time fields'
// calendar or clock. The drives open each in turn in a box made tall
// enough that a menu's dvh cap is not the frame, and the number here is
// the larger of the two readings, so a box sized for the closed state
// is a short box. Rastrillo reserves the same room by hand. The one
// exception is a frame in previewRestHeights, whose open menus need far
// more than its page: there the box rests at the closed reading and
// view.js grows it to this one while a menu is open.
//
// Seeded once by the widget task to prove the measurement; calibrated
// once the shell, the frame padding, the stage and the demo were all
// in, which is when the drives started failing a short box; and swept
// again over all twelve locales once the samples were translated, which
// is where previewMobileLocaleHeights comes from: the languages whose
// samples wrap taller than English's on a phone.
// previewHeight is what an example gets when the table has nothing to
// say about it.
const previewHeight = 220
// previewHeights is each key's Desktop height, in CSS pixels at its
// class's virtual width: the tallest of its content at rest and with
// each thing in it open, over the twelve locales, with slack.
var previewHeights = map[string]int{
"actor/a-person": 80,
"actor/an-agent-acting-for-a-person": 80,
"api-tokens/four-tokens-two-of-them-live": 990,
"api-tokens/just-created-the-secret-shown-once": 1120,
"api-tokens/no-tokens-yet": 670,
"back-nav/back-to-a-record": 100,
"badge/row": 100,
"box/default": 220,
"bulk-bar/three-rows-selected": 210,
"button/default": 270,
"callout/info": 140,
"callout/negative": 160,
"callout/negative-announced-as-an-alert": 160,
"callout/positive": 160,
"callout/warning": 160,
"choice-field/pick-any": 280,
"choice-field/pick-one": 280,
"confirm-form/destructive": 170,
"confirm-form/ordinary-default-cancel-wording": 170,
"demo": 760,
"detail-list/four-facts-one-machine-ish": 230,
"dropdown/a-filter-menu-with-one-choice-applied": 200,
"dropdown/menu-idiom": 210,
"empty-state/sentence-only": 160,
"empty-state/with-a-post-call-to-action": 240,
"error-page/403-forbidden": 350,
"error-page/404-not-found": 350,
"error-page/422-unprocessable": 330,
"error-page/500-server-error": 400,
"error-page/503-unavailable": 350,
"error-page/anything-else-the-generic-pair": 330,
"field-check/disabled": 160,
"field-check/off": 160,
"field-check/on": 160,
"field-date/bounded": 430,
"field-date/enhanced-input-type-tomorrow-or-in-3-weeks": 430,
"field-date/plain-the-bare-native-input": 190,
"field-date/required-with-an-error": 430,
"field-daterange/date-and-time-seeded": 460,
"field-daterange/dates-only-with-an-error-on-the-end": 460,
"field-datetime/bounded": 430,
"field-datetime/enhanced-input-type-tomorrow-or-in-3-weeks": 430,
"field-datetime/plain-the-bare-native-input": 190,
"field-datetime/required-with-an-error": 430,
"field-money/bare": 190,
"field-money/with-a-hint": 210,
"field-money/with-an-error": 210,
"field-select/hand-written-with-optgroups": 400,
"field-select/required-with-an-error": 210,
"field-select/three-options-a-native-select": 180,
"field-select/twelve-options-plain-the-enhancement-opted-out": 180,
"field-select/twelve-options-the-filterable-combobox": 400,
"field-text/bare": 190,
"field-text/required": 190,
"field-text/the-headline-field": 190,
"field-text/with-a-hint": 210,
"field-text/with-an-error": 210,
"field-textarea/bare": 230,
"field-textarea/required": 230,
"field-textarea/with-a-hint": 260,
"field-textarea/with-an-error": 260,
"field-time/bounded": 440,
"field-time/enhanced-input-type-half-seven-or-noon": 440,
"field-time/plain-the-bare-native-input": 190,
"field-time/required-with-an-error": 440,
"field-url/bare": 190,
"field-url/with-a-hint": 210,
"field-url/with-an-error": 210,
"field/bare": 190,
"field/required": 190,
"field/short": 190,
"field/the-headline-field": 190,
"field/with-a-hint": 190,
"field/with-an-error": 210,
"field/with-help-under-the-control": 210,
"form-error/a-rejected-submission": 100,
"form-foot/idle-the-button-before-anything-happens": 180,
"form-foot/save-and-cancel": 180,
"form-foot/submit-only": 180,
"form-foot/working-what-busy-js-writes-on-the-way-out": 180,
"form-layout/default": 380,
"format-abbr/default": 170,
"format-addresses/default": 220,
"format-dates/default": 150,
"format-durations/default": 150,
"format-filesizes/default": 150,
"format-identifiers/default": 170,
"format-money/default": 210,
"format-numbers/default": 140,
"format-output/default": 230,
"format-people/default": 120,
"format-ratios/default": 280,
"help/default": 100,
"job-status/failed": 80,
"job-status/finished": 80,
"job-status/running": 90,
"layout-column/default": 920,
"layout-console-page/default": 680,
"layout-console/default": 680,
"layout-sidebar-page/default": 560,
"layout-sidebar/default": 560,
"layout-stage/default": 460,
"layout-topbar/default": 660,
"list-bar-search/empty-accessible-name-from-the-catalog": 120,
"list-bar-search/with-a-query": 120,
"list-bar-search/with-a-query-on-page-4": 120,
"list-bar/search-with-a-filter-dropdown": 210,
"list-grid/default": 280,
"list-row-action/a-short-status-beside-the-name": 120,
"list-row-action/lead-marker-status-and-an-action": 120,
"list-row-action/name-and-meta-line-only": 120,
"list-search-submit/default-label": 130,
"locale-menu/twelve-languages-current-highlighted": 440,
"media-grid/screenshots-a-logo-and-a-banner": 720,
"media-grid/select-mode-one-tile-selected": 400,
"meter/row": 100,
"modal/default": 250,
"notice/a-saved-message": 120,
"page-header/title-only": 120,
"page-header/title-subhead-and-a-primary-action": 150,
"pagination/first-page-default-accessible-name": 120,
"pagination/middle-of-a-long-list": 120,
"person/large-the-show-header-size": 100,
"person/name-and-email": 90,
"person/no-initial": 90,
"row-menu/links-a-form-and-a-destructive-item": 210,
"row-menu/links-only-for-a-list-inside-a-selection-form": 180,
"scheme-menu/standalone": 210,
"scheme-menu/three-choices-dark-current": 210,
"seg-tabs/buttons-that-switch-in-place": 110,
"seg-tabs/two-sections": 110,
"side-nav/four-sections-the-first-open": 210,
"selbox/default": 90,
"shell-sidebar/default": 260,
"shell-topbar/default": 210,
"signin/a-passkey": 470,
"signin/after-the-link-is-sent": 390,
"signin/after-the-link-is-sent-in-another-browser": 390,
"signin/an-address-that-does-not-look-right": 390,
"signin/asking-for-an-address": 420,
"signin/coming-back-with-an-email-link": 390,
"signin/coming-back-with-keymail": 390,
"signin/email-and-password": 390,
"signin/google-apple-and-the-rest": 310,
"signin/on-the-way-to-keymail": 390,
"signin/when-keymail-could-not-confirm-it": 410,
"signin/when-keymail-was-offered-and-a-link-went-out": 390,
"snippet/a-line-to-copy": 110,
"stat-band/default": 190,
"stat-band/two-to-a-line-on-a-phone": 200,
"stat/a-shortened-number": 140,
"stat/a-smaller-number": 140,
"stat/the-big-number": 190,
"stat/when-going-down-is-good": 180,
"stat/when-going-up-is-bad": 180,
"stat/when-you-cannot-say": 180,
"status-pill/row": 100,
"tblock/default": 270,
}
// previewMobileHeights overrides the 1.25× factor for the examples
// whose Mobile layout is taller than the factor allows: one that
// reflows onto a different axis at 390px, and, since Mobile lays out
// at the stage's own width below 390px, one whose text wraps taller at
// the narrowest phone frame, a 320px viewport. Seeded from the sweep at
// 1500px with Mobile chosen and at a 320px viewport, over the twelve
// locales, whose samples are all English until they are translated.
var previewMobileHeights = map[string]int{
"api-tokens/four-tokens-two-of-them-live": 1850,
"api-tokens/just-created-the-secret-shown-once": 2080,
"badge/row": 252,
"button/default": 410,
"callout/negative": 240,
"callout/warning": 240,
"format-abbr/default": 220,
"format-numbers/default": 220,
"layout-column/default": 1330,
"layout-console-page/default": 1130,
"layout-sidebar-page/default": 1000,
"layout-stage/default": 640,
"layout-topbar/default": 1070,
"list-bar/search-with-a-filter-dropdown": 310,
"list-row-action/a-short-status-beside-the-name": 300,
"list-row-action/lead-marker-status-and-an-action": 240,
"media-grid/screenshots-a-logo-and-a-banner": 1590,
"media-grid/select-mode-one-tile-selected": 850,
"meter/row": 370,
"page-header/title-subhead-and-a-primary-action": 220,
"pagination/middle-of-a-long-list": 180,
"side-nav/four-sections-the-first-open": 300,
"signin/a-passkey": 730,
"signin/after-the-link-is-sent": 600,
"signin/after-the-link-is-sent-in-another-browser": 570,
"signin/an-address-that-does-not-look-right": 610,
"signin/asking-for-an-address": 660,
"signin/coming-back-with-an-email-link": 560,
"signin/coming-back-with-keymail": 580,
"signin/on-the-way-to-keymail": 550,
"signin/when-keymail-could-not-confirm-it": 660,
"signin/when-keymail-was-offered-and-a-link-went-out": 660,
"snippet/a-line-to-copy": 180,
"stat-band/default": 460,
"stat-band/two-to-a-line-on-a-phone": 310,
"status-pill/row": 180,
}
// previewMobileLocaleHeights is a language's own Mobile height for a
// frame whose content wraps taller in that language than
// previewMobileHeights allows, read for that language's pages only.
var previewMobileLocaleHeights = map[string]map[string]int{
"ar": {
"api-tokens/four-tokens-two-of-them-live": 1880,
"list-row-action/a-short-status-beside-the-name": 320,
"stat-band/two-to-a-line-on-a-phone": 330,
},
"bn": {
"form-layout/default": 510,
},
"es": {
"api-tokens/four-tokens-two-of-them-live": 1880,
"api-tokens/just-created-the-secret-shown-once": 2110,
"box/default": 310,
"callout/info": 210,
"callout/negative-announced-as-an-alert": 240,
"empty-state/with-a-post-call-to-action": 350,
"error-page/403-forbidden": 470,
"error-page/404-not-found": 440,
"error-page/422-unprocessable": 470,
"error-page/500-server-error": 520,
"form-layout/default": 570,
"format-abbr/default": 250,
"list-row-action/a-short-status-beside-the-name": 410,
"signin/after-the-link-is-sent-in-another-browser": 620,
"signin/on-the-way-to-keymail": 580,
"signin/when-keymail-could-not-confirm-it": 700,
"signin/when-keymail-was-offered-and-a-link-went-out": 710,
},
"ga": {
"api-tokens/four-tokens-two-of-them-live": 2040,
"api-tokens/just-created-the-secret-shown-once": 2290,
"box/default": 320,
"button/default": 450,
"callout/positive": 240,
"empty-state/with-a-post-call-to-action": 350,
"error-page/403-forbidden": 500,
"error-page/404-not-found": 450,
"error-page/422-unprocessable": 440,
"error-page/500-server-error": 520,
"form-layout/default": 510,
"format-abbr/default": 270,
"format-addresses/default": 320,
"layout-column/default": 1390,
"layout-console-page/default": 1240,
"layout-topbar/default": 1180,
"list-row-action/lead-marker-status-and-an-action": 310,
"modal/default": 350,
"side-nav/four-sections-the-first-open": 320,
"signin/after-the-link-is-sent": 630,
"signin/after-the-link-is-sent-in-another-browser": 630,
"signin/google-apple-and-the-rest": 430,
"signin/on-the-way-to-keymail": 590,
"signin/when-keymail-could-not-confirm-it": 730,
"signin/when-keymail-was-offered-and-a-link-went-out": 720,
"snippet/a-line-to-copy": 200,
"stat-band/two-to-a-line-on-a-phone": 330,
},
"hi": {
"list-row-action/a-short-status-beside-the-name": 350,
},
"ja": {
"api-tokens/four-tokens-two-of-them-live": 1900,
"api-tokens/just-created-the-secret-shown-once": 2130,
"callout/negative-announced-as-an-alert": 240,
"error-page/403-forbidden": 470,
"error-page/500-server-error": 520,
"form-layout/default": 530,
"format-abbr/default": 250,
"list-row-action/a-short-status-beside-the-name": 530,
},
"pt-PT": {
"api-tokens/four-tokens-two-of-them-live": 1900,
"api-tokens/just-created-the-secret-shown-once": 2130,
"button/default": 450,
"callout/positive": 240,
"callout/warning": 290,
"empty-state/with-a-post-call-to-action": 350,
"error-page/404-not-found": 440,
"error-page/500-server-error": 520,
"form-layout/default": 570,
"format-abbr/default": 250,
"list-row-action/a-short-status-beside-the-name": 390,
"signin/after-the-link-is-sent": 630,
"signin/after-the-link-is-sent-in-another-browser": 630,
"signin/on-the-way-to-keymail": 590,
"signin/when-keymail-could-not-confirm-it": 730,
"signin/when-keymail-was-offered-and-a-link-went-out": 690,
"tblock/default": 360,
},
"ru": {
"api-tokens/four-tokens-two-of-them-live": 2020,
"api-tokens/just-created-the-secret-shown-once": 2270,
"button/default": 450,
"callout/info": 210,
"callout/positive": 240,
"callout/warning": 290,
"error-page/403-forbidden": 520,
"error-page/404-not-found": 440,
"error-page/422-unprocessable": 500,
"error-page/500-server-error": 550,
"form-layout/default": 510,
"format-abbr/default": 270,
"format-addresses/default": 350,
"job-status/failed": 130,
"layout-column/default": 1400,
"layout-console-page/default": 1200,
"layout-topbar/default": 1140,
"list-row-action/a-short-status-beside-the-name": 330,
"list-row-action/lead-marker-status-and-an-action": 290,
"modal/default": 350,
"seg-tabs/buttons-that-switch-in-place": 170,
"seg-tabs/two-sections": 170,
"side-nav/four-sections-the-first-open": 320,
"signin/after-the-link-is-sent": 640,
"signin/after-the-link-is-sent-in-another-browser": 640,
"signin/on-the-way-to-keymail": 590,
"signin/when-keymail-could-not-confirm-it": 700,
"signin/when-keymail-was-offered-and-a-link-went-out": 730,
"snippet/a-line-to-copy": 220,
"stat-band/two-to-a-line-on-a-phone": 330,
"status-pill/row": 310,
},
"vi": {
"api-tokens/four-tokens-two-of-them-live": 1990,
"api-tokens/just-created-the-secret-shown-once": 2230,
"callout/info": 210,
"error-page/403-forbidden": 440,
"job-status/failed": 130,
"list-row-action/a-short-status-beside-the-name": 420,
"signin/after-the-link-is-sent-in-another-browser": 600,
"signin/when-keymail-was-offered-and-a-link-went-out": 690,
},
}
// previewReserved is the room a box is given beyond what any sweep
// measures, written by hand with its reason, and never printed by a
// sweep: a floor under the measured table. The modal's is Rastrillo's
// 620px: a modal wants a window to be modal over, and its panel is
// sized from the window (85vh, at most 640px), so a box that only just
// holds the panel shows a modal in a letterbox rather than one over a
// page. The drives still read the panel for clipping at whatever the
// box comes to.
var previewReserved = map[string]int{
"modal/default": 620,
}
// restHeights is a box's Desktop and Mobile heights while nothing in
// its frame is open.
type restHeights struct{ Desktop, Mobile int }
// previewRestHeights is for a frame whose open menu needs far more room
// than its page: the column's footer menus open downward under the
// page, so a box sized for them open is mostly empty at rest. The main
// tables keep the open height; view.js shows these while no menu in
// the frame is open and the open height while one is. With scripts
// off the box keeps the open height, since nothing could grow it.
var previewRestHeights = map[string]restHeights{
"layout-column/default": {Desktop: 650, Mobile: 1110},
}
// heightOf is one key's Desktop height: the measured table's, or the
// default, raised to its reservation where it has one.
func heightOf(key string) int {
h, ok := previewHeights[key]
if !ok {
h = previewHeight
}
if r := previewReserved[key]; r > h {
h = r
}
return h
}
// previewStyle writes one widget's two virtual heights. Mobile is a
// quarter taller than Desktop unless the tables say otherwise: one
// factor rather than a second table, since a component this small has
// one column either way, and the exceptions are the ones whose whole
// shape is a row that becomes a column.
func previewStyle(locale, key string) template.CSS {
h, m := previewHeightsFor(locale, key)
css := fmt.Sprintf("--ds-h: %dpx; --ds-hm: %dpx", h, m)
if r, ok := previewRestHeights[key]; ok {
css += fmt.Sprintf("; --ds-hr: %dpx; --ds-hmr: %dpx", r.Desktop, r.Mobile)
}
return template.CSS(css)
}
// previewHeightsFor is one widget's Desktop and Mobile heights in
// locale, as previewStyle writes them.
func previewHeightsFor(locale, key string) (desktop, mobile int) {
h := heightOf(key)
m, ok := previewMobileHeights[key]
if !ok {
m = h * 5 / 4
}
if l, ok := previewMobileLocaleHeights[locale][key]; ok && l > m {
m = l
}
return h, m
}