@vamos-ui/react 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -0
- package/dist/blocks/block-heading.js +1 -1
- package/dist/blocks/block-image.js +1 -1
- package/dist/blocks/block-link.js +2 -2
- package/dist/blocks/index.d.ts +5 -5
- package/dist/blocks/index.js +5 -5
- package/dist/blocks/labels.d.ts +8 -0
- package/dist/blocks/labels.js +60 -0
- package/dist/blocks/section-header.js +1 -1
- package/dist/blocks/section.js +2 -2
- package/dist/blocks/site/_kit-1.js +1 -1
- package/dist/blocks/site/announcement-bar.js +1 -1
- package/dist/blocks/site/article-layout.d.ts +1 -0
- package/dist/blocks/site/article-layout.js +2 -1
- package/dist/blocks/site/blog-list.d.ts +1 -0
- package/dist/blocks/site/blog-list.js +3 -2
- package/dist/blocks/site/contact-dock.js +3 -1
- package/dist/blocks/site/contact-form.js +1 -1
- package/dist/blocks/site/cta.d.ts +2 -2
- package/dist/blocks/site/cta.js +6 -4
- package/dist/blocks/site/feature-grid.d.ts +1 -0
- package/dist/blocks/site/feature-grid.js +5 -4
- package/dist/blocks/site/feature-tabs.js +1 -1
- package/dist/blocks/site/footer.d.ts +11 -2
- package/dist/blocks/site/footer.js +36 -11
- package/dist/blocks/site/gallery-section.d.ts +6 -1
- package/dist/blocks/site/gallery-section.js +5 -4
- package/dist/blocks/site/hero.d.ts +3 -2
- package/dist/blocks/site/hero.js +7 -5
- package/dist/blocks/site/logo-strip.d.ts +1 -0
- package/dist/blocks/site/logo-strip.js +1 -1
- package/dist/blocks/site/map-hours.d.ts +2 -2
- package/dist/blocks/site/map-hours.js +4 -2
- package/dist/blocks/site/media-text.d.ts +4 -1
- package/dist/blocks/site/media-text.js +4 -3
- package/dist/blocks/site/menu-list.d.ts +7 -3
- package/dist/blocks/site/menu-list.js +34 -7
- package/dist/blocks/site/navbar.d.ts +1 -0
- package/dist/blocks/site/navbar.js +6 -5
- package/dist/blocks/site/not-found.js +2 -1
- package/dist/blocks/site/page-intro.d.ts +1 -0
- package/dist/blocks/site/page-intro.js +2 -1
- package/dist/blocks/site/process.d.ts +1 -0
- package/dist/blocks/site/process.js +2 -1
- package/dist/blocks/site/registry-1.d.ts +1 -1
- package/dist/blocks/site/registry-2.d.ts +5 -0
- package/dist/blocks/site/registry-2.js +5 -3
- package/dist/blocks/site/rich-text-section.js +2 -1
- package/dist/blocks/site/shared/heading-lines.d.ts +9 -0
- package/dist/blocks/site/shared/heading-lines.js +32 -0
- package/dist/blocks/site/shared/image-sizes.d.ts +25 -0
- package/dist/blocks/site/shared/image-sizes.js +37 -0
- package/dist/blocks/site/shared/island-keys.d.ts +1 -1
- package/dist/blocks/site/shared/island-keys.js +1 -1
- package/dist/blocks/site/shared/labels-2.d.ts +17 -1
- package/dist/blocks/site/shared/labels-2.js +832 -6
- package/dist/blocks/site/shared/open-now.d.ts +1 -1
- package/dist/blocks/site/shared/open-now.js +1 -1
- package/dist/blocks/site/shared/opening-hours.d.ts +18 -2
- package/dist/blocks/site/shared/opening-hours.js +36 -7
- package/dist/blocks/site/shared/tap.d.ts +9 -0
- package/dist/blocks/site/shared/tap.js +16 -0
- package/dist/blocks/site/site-shell.js +3 -3
- package/dist/blocks/site/team.d.ts +1 -1
- package/dist/blocks/site/team.js +4 -3
- package/dist/blocks/site/testimonials.d.ts +2 -2
- package/dist/blocks/site/testimonials.js +3 -3
- package/dist/business/approval-flow.js +2 -2
- package/dist/business/import-wizard.js +1 -1
- package/dist/business/line-items.js +1 -1
- package/dist/business/multi-step-form.js +2 -2
- package/dist/business/permission-matrix.js +1 -1
- package/dist/business/setup-checklist.js +2 -2
- package/dist/business/signer-status.js +1 -1
- package/dist/charts/bar-chart.js +1 -1
- package/dist/charts/bar-list.js +1 -1
- package/dist/charts/chart-shell.d.ts +9 -2
- package/dist/charts/chart-shell.js +11 -5
- package/dist/charts/chart.js +1 -1
- package/dist/charts/combo-chart.js +5 -7
- package/dist/charts/donut-chart.js +1 -1
- package/dist/charts/funnel.js +1 -1
- package/dist/charts/gauge.js +2 -2
- package/dist/charts/utils.d.ts +10 -0
- package/dist/charts/utils.js +46 -0
- package/dist/components/accordion.d.ts +1 -1
- package/dist/components/accordion.js +8 -8
- package/dist/components/activity-timeline.js +11 -11
- package/dist/components/address-field.js +3 -3
- package/dist/components/alert-dialog.d.ts +1 -1
- package/dist/components/alert-dialog.js +8 -8
- package/dist/components/alert.js +2 -2
- package/dist/components/app-shell.js +3 -3
- package/dist/components/aspect-ratio.d.ts +26 -0
- package/dist/components/aspect-ratio.js +42 -0
- package/dist/components/attachment-list.js +2 -2
- package/dist/components/audit-log.js +1 -1
- package/dist/components/avatar.d.ts +1 -1
- package/dist/components/avatar.js +8 -8
- package/dist/components/backup-codes.js +1 -1
- package/dist/components/badge.js +3 -3
- package/dist/components/breadcrumb.js +10 -9
- package/dist/components/button-group.js +4 -4
- package/dist/components/button.js +4 -4
- package/dist/components/calendar.js +5 -5
- package/dist/components/card.js +8 -8
- package/dist/components/carousel.d.ts +12 -3
- package/dist/components/carousel.js +17 -14
- package/dist/components/checkbox.d.ts +1 -1
- package/dist/components/checkbox.js +6 -6
- package/dist/components/collapsible.d.ts +36 -0
- package/dist/components/collapsible.js +37 -0
- package/dist/components/color-picker.js +3 -3
- package/dist/components/combobox.js +1 -1
- package/dist/components/command.js +9 -9
- package/dist/components/comment-thread.js +3 -3
- package/dist/components/context-menu.d.ts +1 -1
- package/dist/components/context-menu.js +9 -9
- package/dist/components/currency-input.js +1 -1
- package/dist/components/date-picker.js +2 -2
- package/dist/components/date-range-picker.js +1 -1
- package/dist/components/description-list.js +2 -2
- package/dist/components/dialog.d.ts +1 -1
- package/dist/components/dialog.js +9 -9
- package/dist/components/dropdown-menu.d.ts +1 -1
- package/dist/components/dropdown-menu.js +9 -9
- package/dist/components/editor-layout.js +9 -9
- package/dist/components/empty-state.js +2 -2
- package/dist/components/entity-card.js +1 -1
- package/dist/components/file-upload.js +5 -5
- package/dist/components/formatted-input.js +3 -3
- package/dist/components/hierarchy-chart.js +1 -1
- package/dist/components/hover-card.d.ts +1 -1
- package/dist/components/hover-card.js +3 -3
- package/dist/components/iban-input.js +1 -1
- package/dist/components/id-input.js +3 -3
- package/dist/components/image.d.ts +18 -2
- package/dist/components/image.js +16 -9
- package/dist/components/input-otp.js +6 -6
- package/dist/components/input.d.ts +1 -1
- package/dist/components/input.js +6 -6
- package/dist/components/item.js +4 -4
- package/dist/components/kbd.js +3 -3
- package/dist/components/list-layout.js +9 -9
- package/dist/components/menubar.d.ts +96 -0
- package/dist/components/menubar.js +123 -0
- package/dist/components/native-select.js +1 -1
- package/dist/components/navigation-menu.d.ts +1 -1
- package/dist/components/navigation-menu.js +12 -11
- package/dist/components/notification-center.js +4 -4
- package/dist/components/page-header.js +3 -3
- package/dist/components/pagination.js +9 -9
- package/dist/components/phone-input.js +1 -1
- package/dist/components/pin-map.js +1 -1
- package/dist/components/placeholder-art.d.ts +51 -0
- package/dist/components/placeholder-art.js +82 -0
- package/dist/components/popover.d.ts +1 -1
- package/dist/components/popover.js +7 -7
- package/dist/components/progress.js +3 -3
- package/dist/components/qr-code.js +1 -1
- package/dist/components/radio-group.d.ts +1 -1
- package/dist/components/radio-group.js +4 -4
- package/dist/components/record-detail-layout.js +10 -10
- package/dist/components/record-picker.js +2 -2
- package/dist/components/resizable.js +2 -2
- package/dist/components/scroll-area.d.ts +1 -1
- package/dist/components/scroll-area.js +4 -4
- package/dist/components/select.d.ts +1 -1
- package/dist/components/select.js +10 -10
- package/dist/components/separator.d.ts +1 -1
- package/dist/components/separator.js +3 -3
- package/dist/components/settings-layout.js +6 -6
- package/dist/components/sheet.js +3 -3
- package/dist/components/sidebar.js +12 -11
- package/dist/components/signature-pad.js +1 -1
- package/dist/components/skeleton.js +5 -5
- package/dist/components/sla-timer.js +2 -2
- package/dist/components/slider.d.ts +1 -1
- package/dist/components/slider.js +2 -2
- package/dist/components/slug-input.js +1 -1
- package/dist/components/sparkline.js +1 -1
- package/dist/components/spinner.js +2 -2
- package/dist/components/stat.js +5 -5
- package/dist/components/status-picker.js +2 -2
- package/dist/components/stepper.js +1 -1
- package/dist/components/switch.d.ts +1 -1
- package/dist/components/switch.js +3 -3
- package/dist/components/table.js +10 -10
- package/dist/components/tabs.d.ts +1 -1
- package/dist/components/tabs.js +11 -11
- package/dist/components/tag-input.js +1 -1
- package/dist/components/task-list.js +1 -1
- package/dist/components/time-picker.js +3 -3
- package/dist/components/toggle.d.ts +2 -1
- package/dist/components/toggle.js +7 -6
- package/dist/components/tooltip.d.ts +1 -1
- package/dist/components/tooltip.js +5 -4
- package/dist/components/top-bar.js +4 -4
- package/dist/components/tour.js +1 -1
- package/dist/components/tree-select.js +2 -2
- package/dist/components/tree-view.js +3 -3
- package/dist/components/typography.js +11 -11
- package/dist/components/user-picker.js +2 -2
- package/dist/components/version-history.js +3 -3
- package/dist/data-table/data-table.js +1 -1
- package/dist/data-table/filter-bar.js +2 -2
- package/dist/data-table/index.d.ts +15 -8
- package/dist/data-table/index.js +9 -8
- package/dist/data-table/saved-views.js +1 -1
- package/dist/editor/block-editor.js +1 -1
- package/dist/editor/context.js +1 -1
- package/dist/editor/menus.js +1 -1
- package/dist/fonts/font-picker.js +3 -3
- package/dist/fonts/pairing-picker.js +1 -1
- package/dist/fonts/provider.js +1 -1
- package/dist/index.d.ts +188 -90
- package/dist/index.js +94 -90
- package/dist/lib/data-islands.js +1 -1
- package/dist/lib/field-context.js +1 -1
- package/dist/lib/floating-arrow.js +1 -1
- package/dist/lib/islands.js +1 -1
- package/dist/lib/layer.js +3 -3
- package/dist/lib/locale.js +2 -2
- package/dist/lib/menu.js +2 -2
- package/dist/lib/provider.js +4 -4
- package/dist/lib/theme-scope.js +4 -3
- package/dist/lib/value-format.js +1 -1
- package/dist/lib/variants.js +2 -2
- package/dist/locales/ar.d.ts +2087 -0
- package/dist/locales/ar.js +2088 -0
- package/dist/locales/de.d.ts +2087 -0
- package/dist/locales/de.js +2088 -0
- package/dist/locales/en.d.ts +1 -0
- package/dist/locales/en.js +1 -0
- package/dist/locales/es.d.ts +2087 -0
- package/dist/locales/es.js +2087 -0
- package/dist/locales/fr.d.ts +2087 -0
- package/dist/locales/fr.js +2088 -0
- package/dist/locales/tr.d.ts +1 -0
- package/dist/locales/tr.js +1 -0
- package/dist/media/focal-point.js +1 -1
- package/dist/media/gallery.js +1 -1
- package/dist/media/image-cropper.js +1 -1
- package/dist/media/image-field.js +2 -2
- package/dist/media/lightbox.js +4 -3
- package/dist/media/link-preview.js +3 -3
- package/dist/media/media-library.js +4 -4
- package/dist/media/media-picker.js +1 -1
- package/dist/media/media-provider.js +1 -1
- package/dist/media/video-player.js +4 -4
- package/dist/motion/count-up.js +1 -1
- package/dist/motion/motion-scope.js +2 -2
- package/dist/motion/page-transition.js +1 -1
- package/dist/motion/parallax.js +2 -2
- package/dist/motion/pointer.js +3 -3
- package/dist/motion/reveal.js +3 -3
- package/dist/motion/skeleton-swap.js +1 -1
- package/dist/motion/text-reveal.d.ts +3 -1
- package/dist/motion/text-reveal.js +6 -4
- package/dist/motion/ticker.js +1 -1
- package/dist/node_modules/.vite/vitest/da39a3ee5e6b4b0d3255bfef95601890afd80709/results.json +1 -0
- package/dist/render/block-renderer.js +1 -1
- package/dist/scheduler/gantt.js +2 -2
- package/dist/scheduler/resource-calendar.js +2 -2
- package/dist/scheduler/scheduler.js +3 -3
- package/dist/scheduler/time-tracker.js +4 -4
- package/dist/sortable/kanban.js +2 -2
- package/dist/sortable/sortable-list.js +2 -2
- package/dist/sortable/sortable.js +4 -4
- package/dist/styles/blocks.css +18 -9
- package/dist/styles/editors.css +2 -1
- package/dist/styles/media.css +31 -5
- package/dist/styles.css +39 -9
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -23,6 +23,10 @@ export default function Page() {
|
|
|
23
23
|
}
|
|
24
24
|
```
|
|
25
25
|
|
|
26
|
+
Next.js with webpack (`next build --webpack`, the Next.js 15 default): add
|
|
27
|
+
`experimental: { optimizePackageImports: ["@vamos-ui/react"] }` to next.config, or a Server Component
|
|
28
|
+
that imports from the package root ships every client component. Turbopack and Vite need nothing.
|
|
29
|
+
|
|
26
30
|
Prefer to own the source? Copy single components with the CLI: `npx vamos-ui add button`.
|
|
27
31
|
|
|
28
32
|
MIT licensed. “Vamos UI” and the logo are trademarks of Vamos.
|
|
@@ -29,7 +29,7 @@ export function renderEmphasis(text) {
|
|
|
29
29
|
* <BlockHeading level={1} size="display-xl" text="Dinner, *slowly*." />
|
|
30
30
|
* ```
|
|
31
31
|
*/
|
|
32
|
-
export const BlockHeading = React.forwardRef(function BlockHeading({ text, children, level = 2, size = "display-m", editMode, field, labels, locale, className, ...props }, ref) {
|
|
32
|
+
export const BlockHeading = /*#__PURE__*/ React.forwardRef(function BlockHeading({ text, children, level = 2, size = "display-m", editMode, field, labels, locale, className, ...props }, ref) {
|
|
33
33
|
const Tag = `h${level}`;
|
|
34
34
|
const content = children ?? renderEmphasis(text);
|
|
35
35
|
const empty = content === undefined || content === null || content === "";
|
|
@@ -41,7 +41,7 @@ export function BlockImage({ src, alt, width, height, decorative, aspect, fit =
|
|
|
41
41
|
const altText = decorative ? "" : (alt ?? "");
|
|
42
42
|
const Img = components?.Image;
|
|
43
43
|
const img = Img ? (_jsx(Img, { src: src, alt: altText, width: ratio ? undefined : width, height: ratio ? undefined : height, fill: ratio ? true : undefined, sizes: sizes, priority: priority, className: imgClassName })) : (_jsx("img", { src: src, alt: altText, width: width, height: height, sizes: sizes, loading: priority ? "eager" : "lazy", decoding: priority ? "sync" : "async", fetchPriority: priority ? "high" : undefined, className: imgClassName }));
|
|
44
|
-
const frame = (_jsx("span", { "data-ratio": ratio ? "" : undefined, className: cn("vb-image",
|
|
44
|
+
const frame = (_jsx("span", { "data-ratio": ratio ? "" : undefined, className: cn("vb-image", caption ? undefined : className), style: frameStyle, ...(caption ? null : fieldAttr), children: img }));
|
|
45
45
|
if (!caption)
|
|
46
46
|
return frame;
|
|
47
47
|
return (_jsxs("figure", { className: cn("vb-figure", className), ...fieldAttr, children: [frame, _jsx("figcaption", { className: "text-small text-fg-muted", children: caption })] }));
|
|
@@ -14,9 +14,9 @@ import { isExternalUrl, sanitizeHref } from "./schema/safety.js";
|
|
|
14
14
|
* <BlockLink href="https://maps.app.goo.gl/x" newTab>Directions</BlockLink>
|
|
15
15
|
* ```
|
|
16
16
|
*/
|
|
17
|
-
export const BlockLink = React.forwardRef(function BlockLink({ href, newTab, appearance, size = "lg", components, labels, locale, field, editMode, className, children, rel, target, ...props }, ref) {
|
|
17
|
+
export const BlockLink = /*#__PURE__*/ React.forwardRef(function BlockLink({ href, newTab, appearance, size = "lg", components, labels, locale, field, editMode, className, children, rel, target, ...props }, ref) {
|
|
18
18
|
const safe = sanitizeHref(href);
|
|
19
|
-
const classes = cn(appearance === "link" ? "inline-flex items-center gap-1.5 font-medium text-fg underline decoration-line underline-offset-[0.3em] transition-colors hover:decoration-current focus-ring rounded-[2px]" : null, appearance && appearance !== "link" ? cn(buttonVariants({ variant: appearance, size }), appearance === "primary" && "vb-action-primary", appearance === "secondary" && "vb-action-secondary") : null, className);
|
|
19
|
+
const classes = cn(appearance === "link" ? "inline-flex min-h-6 pointer-coarse:min-h-11 items-center gap-1.5 font-medium text-fg underline decoration-line underline-offset-[0.3em] transition-colors hover:decoration-current focus-ring rounded-[2px]" : null, appearance && appearance !== "link" ? cn(buttonVariants({ variant: appearance, size }), appearance === "primary" && "vb-action-primary", appearance === "secondary" && "vb-action-secondary") : null, className);
|
|
20
20
|
const fieldAttr = editMode && field ? { "data-field": field } : undefined;
|
|
21
21
|
if (!safe) {
|
|
22
22
|
if (process.env.NODE_ENV !== "production")
|
package/dist/blocks/index.d.ts
CHANGED
|
@@ -6,7 +6,7 @@ export { BlockLink, type BlockLinkProps } from "./block-link.js";
|
|
|
6
6
|
export { RichText, renderInline, renderRichBlocks, type RichTextProps, type RichTextRenderOptions } from "./rich-text.js";
|
|
7
7
|
export { JsonLd, mergeJsonLd, serializeJsonLd, type JsonLdProps, type JsonLdNode } from "./json-ld.js";
|
|
8
8
|
export { createBlocks, fieldAttrs, type CreateBlocksOptions, type BoundBlocks } from "./create-blocks.js";
|
|
9
|
-
export { blockLabels, blockLabelsTr, blockText, type BlockLabels } from "./labels.js";
|
|
9
|
+
export { blockLabels, blockLabelsTr, blockLabelsDe, blockLabelsFr, blockLabelsEs, blockLabelsAr, blockText, type BlockLabels } from "./labels.js";
|
|
10
10
|
export type { BlockAdapters, BlockBaseProps, BlockComponentProps, BlockRuntime, ImageData, LinkData } from "./types.js";
|
|
11
11
|
export { parseEmphasis, stripEmphasis, type EmphasisPart } from "./schema/emphasis.js";
|
|
12
12
|
export { sanitizeHref, isExternalUrl, linkKindOf } from "./schema/safety.js";
|
|
@@ -35,21 +35,21 @@ export { BlogList, BlogListSkeleton, type BlogListProps, type BlogListVariant, t
|
|
|
35
35
|
export { ArticleLayout, shareUrl, type ArticleLayoutProps, type ArticleVariant, type ArticlePost, type ArticleAuthor, type ShareTarget } from "./site/article-layout.js";
|
|
36
36
|
export { ContactForm, type ContactFormProps, type ContactFormVariant, type ContactInfo } from "./site/contact-form.js";
|
|
37
37
|
export { MapHours, HoursTable, directionsUrl, mapEmbedUrl, localBusinessJsonLd, type MapHoursProps, type MapHoursVariant, type Branch, type DirectionsProvider, type BusinessType, type HoursDisplay } from "./site/map-hours.js";
|
|
38
|
-
export { MenuList, menuJsonLd, ALLERGENS, type MenuListProps, type MenuVariant, type MenuCategory, type MenuItem, type MenuPrice, type MenuTag, type Allergen } from "./site/menu-list.js";
|
|
38
|
+
export { MenuList, menuJsonLd, ALLERGENS, MENU_TAGS, type MenuListProps, type MenuVariant, type MenuCategory, type MenuItem, type MenuPrice, type MenuTag, type Allergen } from "./site/menu-list.js";
|
|
39
39
|
export { GallerySection, type GallerySectionProps, type GalleryVariant, type GalleryImage, type GallerySpan } from "./site/gallery-section.js";
|
|
40
40
|
export { ConsentBanner, ConsentDefaults, type ConsentBannerProps, type ConsentVariant } from "./site/consent-banner.js";
|
|
41
41
|
export { ContactDock, type ContactDockProps, type ContactDockVariant, type DockAction, type MessagingProvider } from "./site/contact-dock.js";
|
|
42
42
|
export { EmbedFacade, type EmbedFacadeProps } from "./site/embed-facade.js";
|
|
43
|
-
export { Footer, type FooterProps, type FooterVariant } from "./site/footer.js";
|
|
43
|
+
export { Footer, footerJsonLd, type FooterProps, type FooterVariant } from "./site/footer.js";
|
|
44
44
|
export { NotFound, type NotFoundProps, type NotFoundVariant, type NotFoundKind } from "./site/not-found.js";
|
|
45
|
-
export { isOpenAt, hoursStatus, hoursOn, weekRows, summarizeHours, orderedWeek, weekStartOf, weekdayName, formatTime, formatRange, zonedMoment, openingHoursJsonLd, describeStatus, type OpenStatus, type OpenState, type WeekRow, type DayHours, type ZonedMoment, type Weekday, type TimeRange } from "./site/shared/opening-hours.js";
|
|
45
|
+
export { isOpenAt, hoursStatus, hoursOn, weekRows, summarizeHours, orderedWeek, weekStartOf, weekdayName, formatTime, formatRange, zonedMoment, openingHoursJsonLd, describeStatus, timeVars, type OpenStatus, type OpenState, type WeekRow, type DayHours, type ZonedMoment, type Weekday, type TimeRange } from "./site/shared/opening-hours.js";
|
|
46
46
|
export { OpenNow, type OpenNowProps } from "./site/shared/open-now.js";
|
|
47
47
|
export { ConsentGate, type ConsentGateProps } from "./site/shared/consent-gate.js";
|
|
48
48
|
export { ConsentSettingsButton } from "./site/shared/consent-island.js";
|
|
49
49
|
export { getConsent, onConsent, readConsent, writeConsent, parseConsent, openConsentPreferences, consentDefaultsScript, consentModeSignals, CONSENT_COOKIE, CONSENT_EVENT, CONSENT_OPEN_EVENT, CONSENT_CATEGORIES, type ConsentCategory, type ConsentRecord, type ConsentChoices } from "./site/shared/consent.js";
|
|
50
50
|
export { validateContactForm, isSpam, contactFieldLabel, HONEYPOT_FIELD, type ContactFieldConfig, type ContactFieldName, type SiteFormState, type SiteFormAction, type ContactValidation } from "./site/shared/site-form.js";
|
|
51
51
|
export { BrandIcon, BRAND_NAMES, type BrandName, type BrandIconProps } from "./site/shared/brand-icons.js";
|
|
52
|
-
export { site2Labels, site2LabelsTr, type Site2Labels, type Site2BlockLabels } from "./site/shared/labels-2.js";
|
|
52
|
+
export { site2Labels, site2LabelsTr, site2LabelsDe, site2LabelsFr, site2LabelsEs, site2LabelsAr, type Site2Labels, type Site2BlockLabels } from "./site/shared/labels-2.js";
|
|
53
53
|
export { SiteShell, SiteShellPage, SiteShellLocale, SiteShellLink, useSiteShell, type SiteShellProps, type SiteShellPageProps, type SiteShellLocaleProps, type SiteShellLinkProps, type SiteShellContextValue } from "./site/site-shell.js";
|
|
54
54
|
export { siteAccent, siteAccentCss, siteAccentKey, isBuiltInAccent, BUILT_IN_SITE_ACCENTS, SITE_TEXT_CONTRAST, type SiteAccentPalette, type SiteAccentOptions } from "./site/shared/site-accent.js";
|
|
55
55
|
export { sitePageHref, siteLocaleHref, parseSiteHref, readSiteLocation, writeSiteLocation, type SiteRouting, type SiteLocation, type SiteRouteParams } from "./site/shared/site-routing.js";
|
package/dist/blocks/index.js
CHANGED
|
@@ -8,7 +8,7 @@ export { BlockLink } from "./block-link.js";
|
|
|
8
8
|
export { RichText, renderInline, renderRichBlocks } from "./rich-text.js";
|
|
9
9
|
export { JsonLd, mergeJsonLd, serializeJsonLd } from "./json-ld.js";
|
|
10
10
|
export { createBlocks, fieldAttrs } from "./create-blocks.js";
|
|
11
|
-
export { blockLabels, blockLabelsTr, blockText } from "./labels.js";
|
|
11
|
+
export { blockLabels, blockLabelsTr, blockLabelsDe, blockLabelsFr, blockLabelsEs, blockLabelsAr, blockText } from "./labels.js";
|
|
12
12
|
// Pure helpers (no zod) re-exported for components
|
|
13
13
|
export { parseEmphasis, stripEmphasis } from "./schema/emphasis.js";
|
|
14
14
|
export { sanitizeHref, isExternalUrl, linkKindOf } from "./schema/safety.js";
|
|
@@ -37,22 +37,22 @@ export { BlogList, BlogListSkeleton } from "./site/blog-list.js";
|
|
|
37
37
|
export { ArticleLayout, shareUrl } from "./site/article-layout.js";
|
|
38
38
|
export { ContactForm } from "./site/contact-form.js";
|
|
39
39
|
export { MapHours, HoursTable, directionsUrl, mapEmbedUrl, localBusinessJsonLd } from "./site/map-hours.js";
|
|
40
|
-
export { MenuList, menuJsonLd, ALLERGENS } from "./site/menu-list.js";
|
|
40
|
+
export { MenuList, menuJsonLd, ALLERGENS, MENU_TAGS } from "./site/menu-list.js";
|
|
41
41
|
export { GallerySection } from "./site/gallery-section.js";
|
|
42
42
|
export { ConsentBanner, ConsentDefaults } from "./site/consent-banner.js";
|
|
43
43
|
export { ContactDock } from "./site/contact-dock.js";
|
|
44
44
|
export { EmbedFacade } from "./site/embed-facade.js";
|
|
45
|
-
export { Footer } from "./site/footer.js";
|
|
45
|
+
export { Footer, footerJsonLd } from "./site/footer.js";
|
|
46
46
|
export { NotFound } from "./site/not-found.js";
|
|
47
47
|
// Shared modules: opening hours, consent, form contract, brand marks, labels
|
|
48
|
-
export { isOpenAt, hoursStatus, hoursOn, weekRows, summarizeHours, orderedWeek, weekStartOf, weekdayName, formatTime, formatRange, zonedMoment, openingHoursJsonLd, describeStatus } from "./site/shared/opening-hours.js";
|
|
48
|
+
export { isOpenAt, hoursStatus, hoursOn, weekRows, summarizeHours, orderedWeek, weekStartOf, weekdayName, formatTime, formatRange, zonedMoment, openingHoursJsonLd, describeStatus, timeVars } from "./site/shared/opening-hours.js";
|
|
49
49
|
export { OpenNow } from "./site/shared/open-now.js";
|
|
50
50
|
export { ConsentGate } from "./site/shared/consent-gate.js";
|
|
51
51
|
export { ConsentSettingsButton } from "./site/shared/consent-island.js";
|
|
52
52
|
export { getConsent, onConsent, readConsent, writeConsent, parseConsent, openConsentPreferences, consentDefaultsScript, consentModeSignals, CONSENT_COOKIE, CONSENT_EVENT, CONSENT_OPEN_EVENT, CONSENT_CATEGORIES } from "./site/shared/consent.js";
|
|
53
53
|
export { validateContactForm, isSpam, contactFieldLabel, HONEYPOT_FIELD } from "./site/shared/site-form.js";
|
|
54
54
|
export { BrandIcon, BRAND_NAMES } from "./site/shared/brand-icons.js";
|
|
55
|
-
export { site2Labels, site2LabelsTr } from "./site/shared/labels-2.js";
|
|
55
|
+
export { site2Labels, site2LabelsTr, site2LabelsDe, site2LabelsFr, site2LabelsEs, site2LabelsAr } from "./site/shared/labels-2.js";
|
|
56
56
|
// Site shell: locale switch, inner pages and a scoped accent for whole-site blocks
|
|
57
57
|
export { SiteShell, SiteShellPage, SiteShellLocale, SiteShellLink, useSiteShell } from "./site/site-shell.js";
|
|
58
58
|
export { siteAccent, siteAccentCss, siteAccentKey, isBuiltInAccent, BUILT_IN_SITE_ACCENTS, SITE_TEXT_CONTRAST } from "./site/shared/site-accent.js";
|
package/dist/blocks/labels.d.ts
CHANGED
|
@@ -16,5 +16,13 @@ export declare const blockLabels: {
|
|
|
16
16
|
export type BlockLabels = typeof blockLabels;
|
|
17
17
|
/** Turkish dictionary for site blocks. */
|
|
18
18
|
export declare const blockLabelsTr: BlockLabels;
|
|
19
|
+
/** German dictionary for site blocks. */
|
|
20
|
+
export declare const blockLabelsDe: BlockLabels;
|
|
21
|
+
/** French dictionary for site blocks. */
|
|
22
|
+
export declare const blockLabelsFr: BlockLabels;
|
|
23
|
+
/** Spanish dictionary for site blocks. */
|
|
24
|
+
export declare const blockLabelsEs: BlockLabels;
|
|
25
|
+
/** Arabic dictionary for site blocks. */
|
|
26
|
+
export declare const blockLabelsAr: BlockLabels;
|
|
19
27
|
/** Label lookup for server components: `labels` prop → English. */
|
|
20
28
|
export declare function blockText(labels?: Partial<BlockLabels>, locale?: string): (key: keyof BlockLabels, vars?: Record<string, unknown>) => string;
|
package/dist/blocks/labels.js
CHANGED
|
@@ -30,6 +30,66 @@ export const blockLabelsTr = {
|
|
|
30
30
|
emptyImage: "Görsel yok",
|
|
31
31
|
codeSample: "Kod örneği",
|
|
32
32
|
};
|
|
33
|
+
/** German dictionary for site blocks. */
|
|
34
|
+
export const blockLabelsDe = {
|
|
35
|
+
opensInNewTab: "(öffnet in einem neuen Tab)",
|
|
36
|
+
skipToContent: "Zum Inhalt springen",
|
|
37
|
+
addTitle: "Titel hinzufügen",
|
|
38
|
+
addText: "Text hinzufügen",
|
|
39
|
+
addImage: "Bild hinzufügen",
|
|
40
|
+
unknownBlock: "Unbekannter Block „{type}“",
|
|
41
|
+
unknownBlockHint: "Dieser Blocktyp ist nicht installiert. Sein Inhalt bleibt erhalten und wird wieder angezeigt, sobald der Block verfügbar ist.",
|
|
42
|
+
invalidBlock: "„{type}“ enthält ungültige Inhalte",
|
|
43
|
+
invalidBlockHint: "{count, plural, one {# Feld wurde} other {# Felder wurden}} durch Standardwerte ersetzt. Korrigieren Sie sie im Editor.",
|
|
44
|
+
newerBlock: "„{type}“ wurde mit einer neueren Version gespeichert (v{v})",
|
|
45
|
+
emptyImage: "Kein Bild",
|
|
46
|
+
codeSample: "Codebeispiel",
|
|
47
|
+
};
|
|
48
|
+
/** French dictionary for site blocks. */
|
|
49
|
+
export const blockLabelsFr = {
|
|
50
|
+
opensInNewTab: "(s’ouvre dans un nouvel onglet)",
|
|
51
|
+
skipToContent: "Aller au contenu",
|
|
52
|
+
addTitle: "Ajouter un titre",
|
|
53
|
+
addText: "Ajouter du texte",
|
|
54
|
+
addImage: "Ajouter une image",
|
|
55
|
+
unknownBlock: "Bloc inconnu « {type} »",
|
|
56
|
+
unknownBlockHint: "Ce type de bloc n’est pas installé. Son contenu est conservé et s’affichera de nouveau dès que le bloc sera disponible.",
|
|
57
|
+
invalidBlock: "« {type} » contient des données non valides",
|
|
58
|
+
invalidBlockHint: "{count, plural, one {# champ a été remplacé} other {# champs ont été remplacés}} par les valeurs par défaut. Corrigez-les dans l’éditeur.",
|
|
59
|
+
newerBlock: "« {type} » a été enregistré avec une version plus récente (v{v})",
|
|
60
|
+
emptyImage: "Aucune image",
|
|
61
|
+
codeSample: "Exemple de code",
|
|
62
|
+
};
|
|
63
|
+
/** Spanish dictionary for site blocks. */
|
|
64
|
+
export const blockLabelsEs = {
|
|
65
|
+
opensInNewTab: "(se abre en una pestaña nueva)",
|
|
66
|
+
skipToContent: "Saltar al contenido",
|
|
67
|
+
addTitle: "Añade un título",
|
|
68
|
+
addText: "Añade texto",
|
|
69
|
+
addImage: "Añade una imagen",
|
|
70
|
+
unknownBlock: "Bloque desconocido «{type}»",
|
|
71
|
+
unknownBlockHint: "Este tipo de bloque no está instalado. Su contenido se conserva y volverá a mostrarse cuando el bloque esté disponible.",
|
|
72
|
+
invalidBlock: "«{type}» tiene contenido no válido",
|
|
73
|
+
invalidBlockHint: "{count, plural, one {Se ha sustituido # campo} other {Se han sustituido # campos}} por valores predeterminados. Corrígelos en el editor.",
|
|
74
|
+
newerBlock: "«{type}» se guardó con una versión más reciente (v{v})",
|
|
75
|
+
emptyImage: "Sin imagen",
|
|
76
|
+
codeSample: "Ejemplo de código",
|
|
77
|
+
};
|
|
78
|
+
/** Arabic dictionary for site blocks. */
|
|
79
|
+
export const blockLabelsAr = {
|
|
80
|
+
opensInNewTab: "(يُفتح في علامة تبويب جديدة)",
|
|
81
|
+
skipToContent: "انتقل إلى المحتوى",
|
|
82
|
+
addTitle: "أضف عنوانًا",
|
|
83
|
+
addText: "أضف نصًا",
|
|
84
|
+
addImage: "أضف صورة",
|
|
85
|
+
unknownBlock: "كتلة غير معروفة «{type}»",
|
|
86
|
+
unknownBlockHint: "نوع الكتلة هذا غير مثبّت. يُحتفظ بمحتواها وستظهر مجددًا عند توفّر الكتلة.",
|
|
87
|
+
invalidBlock: "تحتوي «{type}» على محتوى غير صالح",
|
|
88
|
+
invalidBlockHint: "{count, plural, zero {لم يُستبدل أي حقل} one {استُبدل حقل واحد} two {استُبدل حقلان} few {استُبدلت # حقول} many {استُبدل # حقلًا} other {استُبدل # حقل}} بالقيم الافتراضية. صحّحها في المحرر.",
|
|
89
|
+
newerBlock: "حُفظت «{type}» بإصدار أحدث (v{v})",
|
|
90
|
+
emptyImage: "لا توجد صورة",
|
|
91
|
+
codeSample: "مثال برمجي",
|
|
92
|
+
};
|
|
33
93
|
/** Label lookup for server components: `labels` prop → English. */
|
|
34
94
|
export function blockText(labels, locale = "en-US") {
|
|
35
95
|
const merged = { ...blockLabels };
|
|
@@ -10,7 +10,7 @@ import { BlockHeading, renderEmphasis } from "./block-heading.js";
|
|
|
10
10
|
* <SectionHeader eyebrow="Private dining" title="Rooms for *twelve* or forty" lead="…" align="split" headingId="rooms" />
|
|
11
11
|
* ```
|
|
12
12
|
*/
|
|
13
|
-
export const SectionHeader = React.forwardRef(function SectionHeader({ eyebrow, index, title, lead, actions, align = "start", size = "m", headingLevel = 2, headingId, editMode, labels, locale, className, ...props }, ref) {
|
|
13
|
+
export const SectionHeader = /*#__PURE__*/ React.forwardRef(function SectionHeader({ eyebrow, index, title, lead, actions, align = "start", size = "m", headingLevel = 2, headingId, editMode, labels, locale, className, ...props }, ref) {
|
|
14
14
|
const heading = (_jsx(BlockHeading, { id: headingId, level: headingLevel, size: size === "l" ? "display-l" : "display-m", text: title, editMode: editMode, field: "props.title", labels: labels, locale: locale }));
|
|
15
15
|
const eyebrowEl = eyebrow || index ? (_jsxs("p", { className: cn("flex items-center gap-3 font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", align === "center" && "justify-center"), "data-field": editMode ? "props.eyebrow" : undefined, children: [index ? (_jsxs(_Fragment, { children: [_jsx("span", { className: "tabular text-fg", children: index }), _jsx("span", { "aria-hidden": true, className: "h-px w-6 bg-current opacity-40" })] })) : null, eyebrow ? _jsx("span", { children: renderEmphasis(eyebrow) }) : null] })) : null;
|
|
16
16
|
const leadEl = lead ? (_jsx("p", { className: "vb-lead max-w-(--measure) text-lead text-fg-muted", "data-field": editMode ? "props.lead" : undefined, children: lead })) : null;
|
package/dist/blocks/section.js
CHANGED
|
@@ -46,7 +46,7 @@ export function sectionThemeAttributes(theme) {
|
|
|
46
46
|
* </Section>
|
|
47
47
|
* ```
|
|
48
48
|
*/
|
|
49
|
-
export const Section = React.forwardRef(function Section({ as: Comp = "section", theme, anchor, headingId, blockId, blockType, editMode, priority, components, labels, className, style, children, ...props }, ref) {
|
|
49
|
+
export const Section = /*#__PURE__*/ React.forwardRef(function Section({ as: Comp = "section", theme, anchor, headingId, blockId, blockType, editMode, priority, components, labels, className, style, children, ...props }, ref) {
|
|
50
50
|
const t = sectionThemeAttributes(theme);
|
|
51
51
|
const bg = theme?.surface === "media" ? theme.background : undefined;
|
|
52
52
|
return (_jsxs(Comp, { ref: ref, id: anchor, "aria-labelledby": headingId, className: cn("vb-section", t.className, className), style: t.style || style ? { ...t.style, ...style } : undefined, "data-block-id": editMode ? blockId : undefined, "data-block-type": editMode ? blockType : undefined, ...t.attrs, ...props, children: [bg ? _jsx(SectionMedia, { image: bg, priority: priority, components: components, labels: labels }) : null, children] }));
|
|
@@ -56,7 +56,7 @@ function SectionMedia({ image, priority, components, labels }) {
|
|
|
56
56
|
return (_jsx("div", { className: "vb-section-media", "aria-hidden": "true", children: _jsx(BlockImage, { src: image.src, width: image.width, height: image.height, blurDataURL: image.blurDataURL, focal: image.focal, decorative: true, priority: priority, sizes: "100vw", components: components, labels: labels }) }));
|
|
57
57
|
}
|
|
58
58
|
/** Centres content at a token width with fluid gutters (`--gutter`). Server component. */
|
|
59
|
-
export const Container = React.forwardRef(function Container({ size = "content", className, ...props }, ref) {
|
|
59
|
+
export const Container = /*#__PURE__*/ React.forwardRef(function Container({ size = "content", className, ...props }, ref) {
|
|
60
60
|
return _jsx("div", { ref: ref, "data-size": size === "content" ? undefined : size, className: cn("vb-container", className), ...props });
|
|
61
61
|
});
|
|
62
62
|
/** "Skip to content" — the first focusable element of a site, before the navbar. */
|
|
@@ -126,7 +126,7 @@ export function CtaList({ actions, components, labels, locale, size = "lg", forc
|
|
|
126
126
|
style = "secondary";
|
|
127
127
|
primarySeen = true;
|
|
128
128
|
}
|
|
129
|
-
return (_jsxs(BlockLink, { href: a.link.href, newTab: a.link.newTab, appearance: style, size: size, components: components, labels: labels, locale: locale, editMode: editMode, field: `props.actions.${i}`, "data-track": a.link.kind, children: [a.link.label, style === "link" && arrow ? _jsx(ArrowEnd, {}) : null] }, `${a.link.href}-${i}`));
|
|
129
|
+
return (_jsxs(BlockLink, { href: a.link.href, newTab: a.link.newTab, appearance: style, size: size, components: components, labels: labels, locale: locale, editMode: editMode, field: `props.actions.${i}`, "data-track": a.link.kind, className: style === "link" ? "min-h-6 pointer-coarse:min-h-11" : undefined, children: [a.link.label, style === "link" && arrow ? _jsx(ArrowEnd, {}) : null] }, `${a.link.href}-${i}`));
|
|
130
130
|
}) }));
|
|
131
131
|
}
|
|
132
132
|
/** Logical "→" that mirrors in RTL. Decorative. */
|
|
@@ -33,6 +33,6 @@ export function AnnouncementBar({ variant = "inline", text, link, tone = "neutra
|
|
|
33
33
|
return null;
|
|
34
34
|
const cookie = storageKey ?? `vb_ann_${hash(textOf(text) + textOf(link?.label))}`;
|
|
35
35
|
const centered = variant === "centered";
|
|
36
|
-
const linkEl = link ? (_jsxs(BlockLink, { href: link.href, newTab: link.newTab, components: components, labels: labels, locale: locale, editMode: editMode, field: "props.link", className: "group/ann inline-flex shrink-0 items-center gap-1.5 font-medium underline decoration-current/40 underline-offset-[0.28em] transition-[text-decoration-color] hover:decoration-current focus-ring rounded-[2px]", children: [link.label, _jsx(ArrowEnd, { className: "group-hover/ann:translate-x-0.5 rtl:group-hover/ann:-translate-x-0.5" })] })) : null;
|
|
36
|
+
const linkEl = link ? (_jsxs(BlockLink, { href: link.href, newTab: link.newTab, components: components, labels: labels, locale: locale, editMode: editMode, field: "props.link", className: "group/ann inline-flex min-h-6 shrink-0 items-center gap-1.5 font-medium underline decoration-current/40 underline-offset-[0.28em] transition-[text-decoration-color] hover:decoration-current focus-ring rounded-[2px]", children: [link.label, _jsx(ArrowEnd, { className: "group-hover/ann:translate-x-0.5 rtl:group-hover/ann:-translate-x-0.5" })] })) : null;
|
|
37
37
|
return (_jsx("div", { role: "region", "aria-label": t("announcement"), id: anchor, "data-announcement": "", "data-variant": variant, "data-tone": tone, "data-block-id": editMode ? id : undefined, "data-hidden": editMode && outside ? "" : undefined, className: cn("vb-announcement relative text-small", toneClass[tone], className), children: _jsxs("div", { className: cn("vb-container flex min-h-9 items-center gap-3 py-1.5", centered && "justify-center text-center"), children: [_jsxs("p", { className: cn("flex min-w-0 flex-wrap items-baseline gap-x-3 gap-y-0.5", centered ? "justify-center" : "flex-1"), children: [centered ? (_jsx("span", { className: "font-mono text-eyebrow uppercase tracking-[0.08em] after:ms-3 after:inline-block after:h-[0.7em] after:w-px after:bg-current after:align-[-0.05em] after:opacity-40 after:content-['']", ...field(editMode, "label"), children: label ?? t("notice") })) : null, _jsx("span", { className: "text-pretty", ...field(editMode, "text"), children: text }), linkEl] }), dismissible ? (_jsx(AnnouncementDismiss, { cookie: cookie, label: t("dismiss"), className: centered ? "absolute end-[max(var(--gutter),0.5rem)] top-1/2 -translate-y-1/2" : undefined })) : null] }) }));
|
|
38
38
|
}
|
|
@@ -18,6 +18,7 @@ export interface ArticlePost {
|
|
|
18
18
|
/** Standfirst under the title. */
|
|
19
19
|
dek?: React.ReactNode;
|
|
20
20
|
body: RichDoc;
|
|
21
|
+
/** Cover. `sizes`: wide `100vw`, contained `(min-width: 76rem) 76rem, 100vw`. Above the fold: `priority`. */
|
|
21
22
|
cover?: SiteImage2;
|
|
22
23
|
coverCaption?: React.ReactNode;
|
|
23
24
|
author?: ArticleAuthor;
|
|
@@ -14,6 +14,7 @@ import { BrandIcon } from "./shared/brand-icons.js";
|
|
|
14
14
|
import { CopyLinkButton } from "./shared/copy-link.js";
|
|
15
15
|
import { shareLabelKeys } from "./shared/island-keys.js";
|
|
16
16
|
import { BlogList } from "./blog-list.js";
|
|
17
|
+
import { headingLines } from "./shared/heading-lines.js";
|
|
17
18
|
/** Gives every h2/h3 a unique, locale-aware anchor and collects the h2s for the TOC. */
|
|
18
19
|
function withAnchors(doc, locale) {
|
|
19
20
|
const used = new Set();
|
|
@@ -50,7 +51,7 @@ export function ArticleLayout({ variant = "classic", post, toc: showToc = true,
|
|
|
50
51
|
const minutes = post.readingMinutes ?? readingMinutes(countWords(richToText(post.body)));
|
|
51
52
|
const ctx = { t, locale, components, labels };
|
|
52
53
|
const cover = variant !== "minimal" && post.cover?.src ? post.cover : undefined;
|
|
53
|
-
const header = (_jsxs("header", { className: cn("flex flex-col gap-6", variant === "wide-cover" && "pt-4"), children: [post.breadcrumb?.length ? _jsx(Breadcrumb, { trail: post.breadcrumb, current: post.title, ctx: ctx }) : null, variant === "minimal" ? (_jsx("p", { className: "font-mono text-eyebrow tabular uppercase tracking-[0.08em] text-fg-muted", children: _jsx("time", { dateTime: isoDay(post.publishedAt), children: formatDate2(isoDay(post.publishedAt), locale) }) })) : post.category ? (_jsx("p", { className: "font-mono text-eyebrow uppercase tracking-[0.08em]", children: _jsx(BlockLink, { href: post.category.href, components: components, labels: labels, locale: locale, className: "text-accent-ink hover:underline underline-offset-[0.25em] focus-ring rounded-[2px]", children: post.category.label }) })) : null, _jsx(BlockHeading, { id: headingId, level: headingLevel, size: variant === "minimal" ? "display-xl" : "display-l", text: post.title, editMode: editMode, field: "props.post.title", labels: labels, locale: locale, className: "max-w-[22ch]" }), post.dek ? _jsx("p", { className: "max-w-[58ch] text-lead text-fg-muted text-pretty", children: post.dek }) : null, _jsx(Byline, { post: post, minutes: minutes, ctx: ctx, showDate: variant !== "minimal" })] }));
|
|
54
|
+
const header = (_jsxs("header", { className: cn("flex flex-col gap-6", variant === "wide-cover" && "pt-4"), children: [post.breadcrumb?.length ? _jsx(Breadcrumb, { trail: post.breadcrumb, current: post.title, ctx: ctx }) : null, variant === "minimal" ? (_jsx("p", { className: "font-mono text-eyebrow tabular uppercase tracking-[0.08em] text-fg-muted", children: _jsx("time", { dateTime: isoDay(post.publishedAt), children: formatDate2(isoDay(post.publishedAt), locale) }) })) : post.category ? (_jsx("p", { className: "font-mono text-eyebrow uppercase tracking-[0.08em]", children: _jsx(BlockLink, { href: post.category.href, components: components, labels: labels, locale: locale, className: "text-accent-ink hover:underline underline-offset-[0.25em] focus-ring rounded-[2px]", children: post.category.label }) })) : null, _jsx(BlockHeading, { id: headingId, level: headingLevel, size: variant === "minimal" ? "display-xl" : "display-l", text: headingLines(post.title), editMode: editMode, field: "props.post.title", labels: labels, locale: locale, className: "max-w-[22ch]" }), post.dek ? _jsx("p", { className: "max-w-[58ch] text-lead text-fg-muted text-pretty", children: post.dek }) : null, _jsx(Byline, { post: post, minutes: minutes, ctx: ctx, showDate: variant !== "minimal" })] }));
|
|
54
55
|
const tocNav = tocVisible ? (_jsxs("nav", { "aria-label": t("onThisPage"), className: cn("vb-toc", variant === "wide-cover" ? "border-y border-line py-4" : "flex flex-col gap-3"), children: [_jsx("p", { className: cn("font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", variant === "wide-cover" && "sr-only"), children: t("onThisPage") }), _jsx("ol", { className: cn("text-small", variant === "wide-cover" ? "flex flex-wrap gap-x-6 gap-y-2" : "flex flex-col gap-2 border-s border-line"), children: toc.map((e) => (_jsx("li", { children: _jsx("a", { href: `#${e.id}`, className: cn("vb-toc-link block text-fg-muted transition-colors hover:text-fg focus-ring rounded-[2px]", variant !== "wide-cover" && "-ms-px border-s border-transparent ps-4 hover:border-fg"), children: e.text }) }, e.id))) })] })) : null;
|
|
55
56
|
const body = _jsx(RichText, { doc: doc, size: "lg", components: components, labels: labels, locale: locale, className: "vb-article-body", editMode: editMode, field: "props.post.body" });
|
|
56
57
|
return (_jsxs(_Fragment, { children: [draft ? (_jsxs("p", { role: "note", className: "vb-draft-strip sticky top-0 z-20 px-4 py-2 text-center text-small text-fg", children: [_jsx("span", { className: "me-2 font-mono text-eyebrow uppercase tracking-[0.08em]", children: t("preview") }), t("previewNotice")] })) : null, _jsxs(Section, { as: "article", theme: theme, anchor: anchor, headingId: headingId, blockId: id, blockType: "article", editMode: editMode, className: cn("vb-article", className), "data-variant": variant, children: [variant === "wide-cover" && cover ? (_jsx("div", { className: "vb-article-cover -mt-[calc(var(--section-y)*var(--vb-rhythm))] mb-12 @4xl:mb-16", children: _jsx(BlockImage, { ...cover, alt: cover.alt ?? "", decorative: !cover.alt, aspect: "21:9", priority: true, sizes: "100vw", components: components, caption: post.coverCaption ? _jsx("span", { className: "vb-container block", children: post.coverCaption }) : undefined }) })) : null, _jsxs(Container, { className: "flex flex-col gap-12 @4xl:gap-16", children: [_jsx("div", { className: cn(variant === "classic" ? "@4xl:grid @4xl:grid-cols-12 @4xl:gap-x-(--gutter)" : "mx-auto w-full max-w-(--prose)", variant === "wide-cover" && "max-w-[58rem]"), children: _jsx("div", { className: cn(variant === "classic" && "@4xl:col-span-9"), children: header }) }), variant === "classic" && cover ? (_jsx(BlockImage, { ...cover, alt: cover.alt ?? "", decorative: !cover.alt, aspect: "3:2", priority: true, sizes: "(min-width: 76rem) 76rem, 100vw", className: "vb-article-cover rounded-ui", components: components, caption: post.coverCaption })) : null, variant === "wide-cover" && tocNav ? _jsx("div", { className: "mx-auto w-full max-w-[58rem]", children: tocNav }) : null, _jsxs("div", { className: cn(variant === "classic" ? "grid gap-x-(--gutter) gap-y-10 @4xl:grid-cols-12" : "mx-auto w-full max-w-(--prose)"), children: [variant === "classic" && tocNav ? (_jsx("aside", { className: "@4xl:col-span-3 @4xl:col-start-10 @4xl:row-start-1", children: _jsx("div", { className: "@4xl:sticky @4xl:top-[calc(var(--topbar-h,0px)+2rem)]", children: tocNav }) })) : null, _jsxs("div", { className: cn("flex min-w-0 flex-col gap-12", variant === "classic" && "@4xl:col-span-8 @4xl:row-start-1 @6xl:col-span-7"), children: [body, _jsx(ArticleFooter, { post: post, share: share, url: url, ctx: ctx })] })] })] })] }), related?.length ? _jsx(BlogList, { variant: "compact", title: t("relatedPosts"), posts: related, postHeadingLevel: 3, theme: { rhythm: "tight" }, components: components, labels: labels, locale: locale }) : null, cta] }));
|
|
@@ -8,6 +8,7 @@ export interface PostSummary {
|
|
|
8
8
|
href: string;
|
|
9
9
|
title: string;
|
|
10
10
|
excerpt?: string;
|
|
11
|
+
/** 3:2 cover. `sizes`: featured `(min-width: 56rem) min(59vw, 45rem), 100vw`; grid `min(34vw, 26rem)` / `min(50vw, 38rem)` / 100vw. */
|
|
11
12
|
cover?: SiteImage2;
|
|
12
13
|
category?: {
|
|
13
14
|
label: string;
|
|
@@ -7,6 +7,7 @@ import { BlockImage } from "../block-image.js";
|
|
|
7
7
|
import { BlockLink } from "../block-link.js";
|
|
8
8
|
import { site2Text } from "./shared/labels-2.js";
|
|
9
9
|
import { fieldAttr, formatDate2, isoDay, useBlockIds2 } from "./shared/kit-2.js";
|
|
10
|
+
import { columnSizes } from "./shared/image-sizes.js";
|
|
10
11
|
/**
|
|
11
12
|
* Latest posts and category archives. Receives posts as data (`PostSummary`); fetching is the
|
|
12
13
|
* app's job (or the renderer's `resolvers.posts`). Each post is an `article` with one link on the
|
|
@@ -48,13 +49,13 @@ function Cover({ post, ctx, sizes, priority, className }) {
|
|
|
48
49
|
}
|
|
49
50
|
function Editorial({ posts, ctx }) {
|
|
50
51
|
const [first, ...rest] = posts;
|
|
51
|
-
return (_jsxs("div", { className: "flex flex-col gap-12", children: [_jsxs("article", { className: "vb-post group/post relative grid gap-x-(--gutter) gap-y-6 @4xl:grid-cols-12 @4xl:items-end", children: [ctx.covers && first.cover ? (_jsx("div", { className: "@4xl:col-span-7", children: _jsx(Cover, { post: first, ctx: ctx, sizes:
|
|
52
|
+
return (_jsxs("div", { className: "flex flex-col gap-12", children: [_jsxs("article", { className: "vb-post group/post relative grid gap-x-(--gutter) gap-y-6 @4xl:grid-cols-12 @4xl:items-end", children: [ctx.covers && first.cover ? (_jsx("div", { className: "@4xl:col-span-7", children: _jsx(Cover, { post: first, ctx: ctx, sizes: columnSizes(7 / 12) }) })) : null, _jsxs("div", { className: cn("flex flex-col gap-4", ctx.covers && first.cover ? "@4xl:col-span-5" : "@4xl:col-span-9"), children: [_jsx(Category, { post: first }), _jsx(PostTitle, { post: first, ctx: ctx, className: "font-display text-display-m" }), first.excerpt ? _jsx("p", { className: "max-w-[52ch] text-fg-muted text-pretty", children: first.excerpt }) : null, _jsx(Meta, { post: first, ctx: ctx })] })] }), rest.length ? (_jsx("ul", { className: "grid gap-x-(--gutter) @2xl:grid-cols-2", children: rest.map((p, i) => (_jsx("li", { className: "border-t border-line", children: _jsxs("article", { className: "vb-post group/post relative flex flex-col gap-2 py-6", children: [_jsx(PostDate, { post: p, ctx: ctx }), _jsx(PostTitle, { post: p, ctx: ctx, className: "text-h3 font-semibold tracking-[-0.012em]" }), p.category ? _jsx("p", { className: "text-small text-fg-muted", children: p.category.label }) : null] }) }, p.href + i))) })) : null] }));
|
|
52
53
|
}
|
|
53
54
|
function ListPost({ post, ctx }) {
|
|
54
55
|
return (_jsxs("article", { className: "vb-post group/post relative grid gap-x-(--gutter) gap-y-2 py-6 @2xl:grid-cols-12 @2xl:items-baseline", children: [_jsx(PostDate, { post: post, ctx: ctx, className: "@2xl:col-span-2" }), _jsxs("div", { className: "flex flex-col gap-1.5 @2xl:col-span-8", children: [_jsx(PostTitle, { post: post, ctx: ctx, className: "text-h3 font-semibold tracking-[-0.012em]" }), post.excerpt ? _jsx("p", { className: "line-clamp-1 text-small text-fg-muted", children: post.excerpt }) : null] }), _jsx("p", { className: "text-small text-fg-muted @2xl:col-span-2 @2xl:text-end", children: post.readingMinutes ? ctx.t("minRead", { count: post.readingMinutes }) : null })] }));
|
|
55
56
|
}
|
|
56
57
|
function GridPost({ post, ctx }) {
|
|
57
|
-
return (_jsxs("article", { className: "vb-post group/post relative flex flex-col gap-4", children: [ctx.covers ? post.cover ? _jsx(Cover, { post: post, ctx: ctx, sizes:
|
|
58
|
+
return (_jsxs("article", { className: "vb-post group/post relative flex flex-col gap-4", children: [ctx.covers ? post.cover ? _jsx(Cover, { post: post, ctx: ctx, sizes: `(min-width: 56rem) min(34vw, 26rem), ${columnSizes(1 / 2, { from: "42rem" })}` }) : _jsx("span", { "aria-hidden": true, className: "block aspect-[3/2] rounded-ui bg-surface-2" }) : null, _jsxs("div", { className: "flex flex-col gap-2.5", children: [_jsx(Category, { post: post }), _jsx(PostTitle, { post: post, ctx: ctx, className: "text-h3 font-semibold tracking-[-0.012em]" }), _jsx(Meta, { post: post, ctx: ctx })] })] }));
|
|
58
59
|
}
|
|
59
60
|
function CompactPost({ post, ctx }) {
|
|
60
61
|
return (_jsxs("article", { className: "vb-post group/post relative flex flex-col gap-1 py-4", children: [_jsx(PostTitle, { post: post, ctx: ctx, className: "text-body font-medium" }), _jsx(PostDate, { post: post, ctx: ctx })] }));
|
|
@@ -6,6 +6,7 @@ import { telHref } from "./shared/kit-2.js";
|
|
|
6
6
|
import { BrandIcon, BRAND_NAMES, waLink } from "./shared/brand-icons.js";
|
|
7
7
|
import { DockHours } from "./shared/dock-hours.js";
|
|
8
8
|
import { DockAvoid } from "./shared/dock-avoid.js";
|
|
9
|
+
import { nameWithLabel } from "./shared/tap.js";
|
|
9
10
|
function actionSpec(a, p, t) {
|
|
10
11
|
const iconClass = "size-5";
|
|
11
12
|
switch (a) {
|
|
@@ -49,7 +50,8 @@ export function ContactDock(props) {
|
|
|
49
50
|
const defaultActions = ["call", "whatsapp", "directions", "booking", "email"];
|
|
50
51
|
const actions = (props.actions ?? defaultActions).map((a) => [a, actionSpec(a, props, t)]).filter((x) => !!x[1]).slice(0, 4);
|
|
51
52
|
const showFab = variant !== "bar" && href;
|
|
53
|
+
const fabLabel = label ?? t("messageUs");
|
|
52
54
|
const showBar = variant !== "fab" && actions.length >= 2;
|
|
53
55
|
const hoursIsland = hours && offHours !== "show" ? _jsx(DockHours, { hours: hours, mode: offHours, text: t("offHours"), now: now, className: "vb-dock-offhours" }) : null;
|
|
54
|
-
return (_jsxs("div", { "data-contact-dock": "", "data-variant": variant, "data-position": position, "data-contained": contained ? "" : undefined, "data-block-id": editMode ? id : undefined, className: cn("vb-dock", className), children: [showFab ? (_jsxs("div", { className: cn("vb-dock-fab", variant === "both" && "vb-dock-desktop", position === "start" && "flex-row-reverse"), children: [hoursIsland ? _jsx("span", { className: "vb-dock-note surface-popover max-w-[16rem] rounded-ui px-3 py-2 text-xs text-fg empty:hidden [&:has(>span:empty)]:hidden", children: hoursIsland }) : null, _jsx("span", { "aria-hidden": true, className: "vb-dock-label surface-popover rounded-full px-3.5 py-2 text-sm font-medium text-fg", children:
|
|
56
|
+
return (_jsxs("div", { "data-contact-dock": "", "data-variant": variant, "data-position": position, "data-contained": contained ? "" : undefined, "data-block-id": editMode ? id : undefined, className: cn("vb-dock", className), children: [showFab ? (_jsxs("div", { className: cn("vb-dock-fab", variant === "both" && "vb-dock-desktop", position === "start" && "flex-row-reverse"), children: [hoursIsland ? _jsx("span", { className: "vb-dock-note surface-popover max-w-[16rem] rounded-ui px-3 py-2 text-xs text-fg empty:hidden [&:has(>span:empty)]:hidden", children: hoursIsland }) : null, _jsx("span", { "aria-hidden": true, className: "vb-dock-label surface-popover rounded-full px-3.5 py-2 text-sm font-medium text-fg", children: fabLabel }), _jsx("a", { href: href, target: "_blank", rel: "noopener noreferrer", "data-track": provider, "aria-label": nameWithLabel(fabLabel, `${t("messageUsOn", { network })}, ${t("opensInNewTab").replace(/[()]/g, "")}`), className: "vb-dock-button inline-flex size-[52px] shrink-0 items-center justify-center rounded-full bg-fg text-scene transition-opacity hover:opacity-90 focus-ring", children: _jsx(BrandIcon, { name: provider, className: "size-6" }) })] })) : null, showBar ? (_jsxs("nav", { "aria-label": t("contactActions"), className: cn("vb-dock-bar surface-bar border-t border-line", variant === "both" && "vb-dock-mobile"), children: [hoursIsland && !showFab ? _jsx("p", { className: "px-4 pt-2 text-center text-xs text-fg-muted empty:hidden", children: hoursIsland }) : null, _jsx("ul", { className: "grid", style: { gridTemplateColumns: `repeat(${actions.length}, minmax(0, 1fr))` }, children: actions.map(([a, s]) => (_jsx("li", { children: _jsxs("a", { href: s.href, target: s.external ? "_blank" : undefined, rel: s.external ? "noopener noreferrer" : undefined, "data-track": a, "aria-label": nameWithLabel(s.label, s.aria), className: "flex h-14 flex-col items-center justify-center gap-1 text-[11px] font-medium text-fg transition-colors hover:bg-hover focus-ring", children: [s.icon, _jsx("span", { "aria-hidden": true, children: s.label })] }) }, a))) })] })) : null, !contained && (showFab || showBar) ? _jsx(DockAvoid, {}) : null] }));
|
|
55
57
|
}
|
|
@@ -40,6 +40,6 @@ export function ContactForm({ variant = "split", eyebrow, title, lead, fields =
|
|
|
40
40
|
}
|
|
41
41
|
function InfoList({ info, t, locale, editMode }) {
|
|
42
42
|
const hours = typeof info.hours === "object" ? summarizeHours(info.hours, locale) : undefined;
|
|
43
|
-
const linkClass = "text-fg underline decoration-line underline-offset-[0.25em] transition-colors hover:decoration-current focus-ring rounded-[2px]";
|
|
43
|
+
const linkClass = "inline-flex min-h-6 items-center text-fg underline decoration-line underline-offset-[0.25em] transition-colors hover:decoration-current focus-ring rounded-[2px]";
|
|
44
44
|
return (_jsxs("dl", { className: "flex flex-col border-t border-line", children: [info.phone ? (_jsxs("div", { className: "flex flex-col gap-2 border-b border-line py-5", children: [_jsx("dt", { className: "font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", children: t("phone") }), _jsx("dd", { ...fieldAttr(editMode, "info.phone"), children: _jsx("a", { href: telHref(info.phone), className: "font-display text-display-m tabular text-fg transition-colors hover:text-accent-ink focus-ring rounded-[2px]", "data-track": "call", children: prettyPhone(info.phone) }) })] })) : null, info.email ? (_jsxs("div", { className: "flex flex-col gap-2 border-b border-line py-5", children: [_jsx("dt", { className: "font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", children: t("email") }), _jsx("dd", { children: _jsx("a", { href: `mailto:${info.email}`, className: cn(linkClass, "text-lead"), "data-track": "email", children: info.email }) })] })) : null, info.address ? (_jsxs("div", { className: "flex flex-col gap-2 border-b border-line py-5", children: [_jsx("dt", { className: "font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", children: t("address") }), _jsx("dd", { className: "not-italic text-fg", children: _jsx("address", { className: "not-italic", children: (typeof info.address === "string" ? info.address.split("\n") : addressLines(info.address, { locale })).map((l, i) => (_jsx("span", { className: "block", children: l }, i))) }) })] })) : null, info.hours ? (_jsxs("div", { className: "flex flex-col gap-2 border-b border-line py-5", children: [_jsx("dt", { className: "font-mono text-eyebrow uppercase tracking-[0.08em] text-fg-muted", children: t("hours") }), _jsx("dd", { className: "text-fg", children: hours ? (_jsx("ul", { className: "flex flex-col gap-1", children: hours.map((h, i) => (_jsxs("li", { className: "flex justify-between gap-6 tabular", children: [_jsx("span", { children: h.days }), _jsx("span", { className: h.closed ? "text-fg-subtle" : undefined, children: h.closed ? t("closedDay") : h.text })] }, i))) })) : info.hours })] })) : null] }));
|
|
45
45
|
}
|
|
@@ -22,7 +22,7 @@ export interface CtaBookingConfig {
|
|
|
22
22
|
timeZone?: string;
|
|
23
23
|
/** "Hi, I'd like a table for {guests} on {date} at {time}." */
|
|
24
24
|
messageTemplate?: string;
|
|
25
|
-
/** embed: preview image shown before the calendar loads. */
|
|
25
|
+
/** embed: preview image shown before the calendar loads. `sizes`: `min(100vw, 76rem)`. */
|
|
26
26
|
preview?: SiteImage;
|
|
27
27
|
}
|
|
28
28
|
export interface CtaProps extends Omit<BlockBaseProps, "variant" | "labels"> {
|
|
@@ -39,7 +39,7 @@ export interface CtaProps extends Omit<BlockBaseProps, "variant" | "labels"> {
|
|
|
39
39
|
lead?: React.ReactNode;
|
|
40
40
|
/** ≤ 2; one primary. In `minimal` only the first is used, as a large text link. */
|
|
41
41
|
actions?: SiteCta[];
|
|
42
|
-
/** split: the photo. */
|
|
42
|
+
/** split: the photo. `sizes`: `(min-width: 56rem) min(42vw, 32rem), 100vw`. */
|
|
43
43
|
media?: SiteImage;
|
|
44
44
|
/** band/split: mono contact line ("0216 000 00 00 · WhatsApp"). */
|
|
45
45
|
contact?: {
|
package/dist/blocks/site/cta.js
CHANGED
|
@@ -11,6 +11,8 @@ import { EmbedFacade } from "./embed-facade.js";
|
|
|
11
11
|
import { formatSitePhone } from "./navbar.js";
|
|
12
12
|
import { ArrowEnd, CtaList, field, siteTemplate, siteText, useBlockIds } from "./_kit-1.js";
|
|
13
13
|
import { Reveal } from "../../motion/reveal.js";
|
|
14
|
+
import { headingLines } from "./shared/heading-lines.js";
|
|
15
|
+
import { columnSizes, SITE_SIZES } from "./shared/image-sizes.js";
|
|
14
16
|
/** `https://wa.me/<digits>?text=<encoded>` from an E.164 number. */
|
|
15
17
|
export function waLink(e164, text) {
|
|
16
18
|
const base = `https://wa.me/${e164.replace(/\D/g, "")}`;
|
|
@@ -32,7 +34,7 @@ export function Cta(props) {
|
|
|
32
34
|
return (_jsx(Section, { theme: sectionTheme, anchor: anchor, headingId: headingId, blockId: base, blockType: "cta", editMode: editMode, components: components, labels: labels, "data-variant": variant, className: cn("vb-cta", className), children: _jsx(Reveal, { disabled: editMode, children: variant === "split" ? _jsx(Split, { ...inner }) : variant === "minimal" ? _jsx(Minimal, { ...inner }) : variant === "newsletter" ? _jsx(Newsletter, { ...inner }) : variant === "booking" ? _jsx(Booking, { ...inner }) : _jsx(Band, { ...inner }) }) }));
|
|
33
35
|
}
|
|
34
36
|
function Heading({ p, size = "display-l", className }) {
|
|
35
|
-
return _jsx(BlockHeading, { id: p.headingId, level: p.headingLevel ?? 2, size: size, text: p.title, editMode: p.editMode, field: "props.title", labels: p.labels, locale: p.locale, className: className });
|
|
37
|
+
return _jsx(BlockHeading, { id: p.headingId, level: p.headingLevel ?? 2, size: size, text: headingLines(p.title), editMode: p.editMode, field: "props.title", labels: p.labels, locale: p.locale, className: className });
|
|
36
38
|
}
|
|
37
39
|
function Eyebrow({ p, className }) {
|
|
38
40
|
if (!p.eyebrow)
|
|
@@ -54,7 +56,7 @@ function ContactLine({ p, className }) {
|
|
|
54
56
|
if (!c?.phone && !c?.whatsapp)
|
|
55
57
|
return null;
|
|
56
58
|
const t = siteText(p.labels, p.locale);
|
|
57
|
-
const linkCls = "rounded-[2px] underline decoration-current/30 underline-offset-4 transition-[text-decoration-color] hover:decoration-current focus-ring";
|
|
59
|
+
const linkCls = "inline-flex min-h-6 items-center pointer-coarse:min-h-11 rounded-[2px] underline decoration-current/30 underline-offset-4 transition-[text-decoration-color] hover:decoration-current focus-ring";
|
|
58
60
|
return (_jsxs("p", { className: cn("flex flex-wrap items-center gap-x-3 gap-y-1 font-mono text-small tabular text-fg-muted", className), ...field(p.editMode, "contact"), children: [c.phone ? (_jsx("a", { href: `tel:${c.phone}`, "data-track": "call", className: linkCls, children: formatSitePhone(c.phone, p.locale) })) : null, c.phone && c.whatsapp ? _jsx("span", { "aria-hidden": true, children: "\u00B7" }) : null, c.whatsapp ? (_jsxs("a", { href: waLink(c.whatsapp, c.whatsappText), target: "_blank", rel: "noopener noreferrer", "data-track": "whatsapp", className: linkCls, children: ["WhatsApp", _jsxs("span", { className: "sr-only", children: [" ", t("opensWhatsApp")] })] })) : null] }));
|
|
59
61
|
}
|
|
60
62
|
const actionsOf = (p, extra) => _jsx(CtaList, { actions: p.actions, components: p.components, labels: p.labels, locale: p.locale, editMode: p.editMode, ...extra });
|
|
@@ -64,7 +66,7 @@ function Band(p) {
|
|
|
64
66
|
}
|
|
65
67
|
/* split ----------------------------------------------------------------- */
|
|
66
68
|
function Split(p) {
|
|
67
|
-
return (_jsxs(Container, { className: "grid gap-x-(--gutter) gap-y-10 @4xl:grid-cols-12 @4xl:items-stretch", children: [_jsxs("div", { className: "flex flex-col justify-between gap-10 @4xl:col-span-6 @4xl:py-4", children: [_jsxs("div", { className: "flex flex-col gap-6", children: [_jsx(Eyebrow, { p: p }), _jsx(Heading, { p: p }), _jsx(Lead, { p: p })] }), _jsxs("div", { className: "flex flex-col gap-5", children: [actionsOf(p), _jsx(ContactLine, { p: p }), _jsx(Fineprint, { p: p })] })] }), p.media ? (_jsx("div", { className: "@4xl:col-span-5 @4xl:col-start-8", children: _jsx(BlockImage, { ...p.media, aspect: "4:5", sizes:
|
|
69
|
+
return (_jsxs(Container, { className: "grid gap-x-(--gutter) gap-y-10 @4xl:grid-cols-12 @4xl:items-stretch", children: [_jsxs("div", { className: "flex flex-col justify-between gap-10 @4xl:col-span-6 @4xl:py-4", children: [_jsxs("div", { className: "flex flex-col gap-6", children: [_jsx(Eyebrow, { p: p }), _jsx(Heading, { p: p }), _jsx(Lead, { p: p })] }), _jsxs("div", { className: "flex flex-col gap-5", children: [actionsOf(p), _jsx(ContactLine, { p: p }), _jsx(Fineprint, { p: p })] })] }), p.media ? (_jsx("div", { className: "@4xl:col-span-5 @4xl:col-start-8", children: _jsx(BlockImage, { ...p.media, aspect: "4:5", sizes: columnSizes(5 / 12), className: "h-full rounded-ui", components: p.components, labels: p.labels, editMode: p.editMode, field: "props.media" }) })) : null] }));
|
|
68
70
|
}
|
|
69
71
|
/* minimal --------------------------------------------------------------- */
|
|
70
72
|
function Minimal(p) {
|
|
@@ -88,7 +90,7 @@ function Booking(p) {
|
|
|
88
90
|
const locale = p.locale ?? "en-US";
|
|
89
91
|
let control;
|
|
90
92
|
if (b.provider === "embed" && b.url) {
|
|
91
|
-
control = (_jsx("div", { className: "relative aspect-[16/10] overflow-hidden rounded-ui border border-line @4xl:aspect-[21/9]", children: _jsxs(EmbedFacade, { kind: "iframe", src: b.url, title: typeof p.title === "string" ? p.title.replace(/\*/g, "") : t("openCalendar"), buttonLabel: t("openCalendar"), children: [b.preview ? _jsx(BlockImage, { ...b.preview, decorative: true, aspect: "21:9", className: "absolute inset-0 h-full opacity-60", components: p.components }) : null, _jsxs("span", { className: "absolute inset-0 flex flex-col items-center justify-center gap-3 bg-[color-mix(in_oklch,var(--scene)_55%,transparent)] text-center", children: [_jsxs("span", { className: "inline-flex h-12 items-center gap-2.5 rounded-ui bg-fg px-6 text-[15px] font-semibold text-scene", children: [_jsx(CalendarDays, { "aria-hidden": true, strokeWidth: 1.5, className: "size-[18px]" }), t("openCalendar")] }), _jsx("span", { className: "text-small text-fg-muted", children: t("loadsCalendar", { provider: new URL(b.url).hostname.replace(/^www\./, "") }) })] })] }) }));
|
|
93
|
+
control = (_jsx("div", { className: "relative aspect-[16/10] overflow-hidden rounded-ui border border-line @4xl:aspect-[21/9]", children: _jsxs(EmbedFacade, { kind: "iframe", src: b.url, title: typeof p.title === "string" ? p.title.replace(/\*/g, "") : t("openCalendar"), buttonLabel: t("openCalendar"), children: [b.preview ? _jsx(BlockImage, { ...b.preview, decorative: true, aspect: "21:9", sizes: SITE_SIZES.container, className: "absolute inset-0 h-full opacity-60", components: p.components }) : null, _jsxs("span", { className: "absolute inset-0 flex flex-col items-center justify-center gap-3 bg-[color-mix(in_oklch,var(--scene)_55%,transparent)] text-center", children: [_jsxs("span", { className: "inline-flex h-12 items-center gap-2.5 rounded-ui bg-fg px-6 text-[15px] font-semibold text-scene", children: [_jsx(CalendarDays, { "aria-hidden": true, strokeWidth: 1.5, className: "size-[18px]" }), t("openCalendar")] }), _jsx("span", { className: "text-small text-fg-muted", children: t("loadsCalendar", { provider: new URL(b.url).hostname.replace(/^www\./, "") }) })] })] }) }));
|
|
92
94
|
}
|
|
93
95
|
else if (b.provider === "phone") {
|
|
94
96
|
control = (_jsxs("div", { className: "flex flex-wrap items-center gap-x-6 gap-y-3", children: [b.phone ? (_jsx(BlockLink, { href: `tel:${b.phone}`, appearance: "primary", components: p.components, labels: p.labels, "data-track": "call", children: formatSitePhone(b.phone, locale) })) : null, actionsOf(p, { forceStyle: "link" })] }));
|
|
@@ -7,6 +7,7 @@ export interface FeatureItem {
|
|
|
7
7
|
text?: React.ReactNode;
|
|
8
8
|
/** Lucide name ("calendar-check") or a node. Small, before the title, never boxed. */
|
|
9
9
|
icon?: React.ReactNode;
|
|
10
|
+
/** `sizes`: tabs/sticky panel `min(59vw, 45rem)` or `min(50vw, 38rem)`, grid `min(34vw, 26rem)`, stacked (narrow) `min(100vw, 56rem)`. */
|
|
10
11
|
media?: SiteImage;
|
|
11
12
|
/** Makes the whole row clickable (one link, stretched with `::after`). */
|
|
12
13
|
link?: {
|
|
@@ -8,6 +8,7 @@ import { FeatureTabs } from "./feature-tabs.js";
|
|
|
8
8
|
import { SiteIcon } from "./_site-icon-1.js";
|
|
9
9
|
import { ArrowEnd, indexLabel, useBlockIds } from "./_kit-1.js";
|
|
10
10
|
import { Stagger } from "../../motion/reveal.js";
|
|
11
|
+
import { columnSizes, SITE_SIZES } from "./shared/image-sizes.js";
|
|
11
12
|
const plain = (n) => (typeof n === "string" ? n : undefined);
|
|
12
13
|
/**
|
|
13
14
|
* Services, treatments or product modules — without the card wall. Server component; the `tabs`
|
|
@@ -33,15 +34,15 @@ export function FeatureGrid({ variant = "list", eyebrow, title, lead, actions, i
|
|
|
33
34
|
value: `f${i}`,
|
|
34
35
|
title: it.title,
|
|
35
36
|
text: it.text,
|
|
36
|
-
panel: (_jsxs("div", { className: "flex flex-col gap-4", children: [it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes:
|
|
37
|
-
})) }), _jsx("ol", { className: "flex flex-col @4xl:hidden", children: items.map((it, i) => (_jsxs("li", { className: "flex flex-col gap-3 border-t border-line py-6", children: [itemTitle(it, i), itemText(it, i), it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes:
|
|
37
|
+
panel: (_jsxs("div", { className: "flex flex-col gap-4", children: [it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes: columnSizes(7 / 12), className: "rounded-ui border border-line", components: components, labels: labels }) : null, it.link ? (_jsxs(BlockLink, { href: it.link.href, newTab: it.link.newTab, appearance: "link", ...linkProps, children: [it.link.label ?? it.title, _jsx(ArrowEnd, {})] })) : null] })),
|
|
38
|
+
})) }), _jsx("ol", { className: "flex flex-col @4xl:hidden", children: items.map((it, i) => (_jsxs("li", { className: "flex flex-col gap-3 border-t border-line py-6", children: [itemTitle(it, i), itemText(it, i), it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes: SITE_SIZES.narrowOnly, className: "mt-2 rounded-ui border border-line", components: components, labels: labels }) : null] }, i))) })] }));
|
|
38
39
|
}
|
|
39
40
|
else if (v === "sticky-media") {
|
|
40
41
|
const names = items.map((_, i) => `--vbf-${base.replace(/[^a-zA-Z0-9_-]/g, "")}-${i}`);
|
|
41
|
-
body = (_jsxs("div", { className: "vb-sticky-features grid gap-x-(--gutter) @4xl:grid-cols-12", style: { timelineScope: names.join(", ") }, children: [_jsx("div", { className: "vb-sticky-media", "aria-hidden": true, children: _jsx("div", { className: "sticky top-[calc(var(--vb-nav-h,4.5rem)+2rem)]", children: _jsx("div", { className: "relative overflow-hidden rounded-ui", children: items.map((it, i) => it.media ? (_jsx(BlockImage, { ...it.media, decorative: true, aspect: "4:5", sizes:
|
|
42
|
+
body = (_jsxs("div", { className: "vb-sticky-features grid gap-x-(--gutter) @4xl:grid-cols-12", style: { timelineScope: names.join(", ") }, children: [_jsx("div", { className: "vb-sticky-media", "aria-hidden": true, children: _jsx("div", { className: "sticky top-[calc(var(--vb-nav-h,4.5rem)+2rem)]", children: _jsx("div", { className: "relative overflow-hidden rounded-ui", children: items.map((it, i) => it.media ? (_jsx(BlockImage, { ...it.media, decorative: true, aspect: "4:5", sizes: columnSizes(6 / 12), className: cn("vb-sticky-frame", i > 0 && "absolute! inset-0"), "data-pos": i === 0 ? "first" : i === items.length - 1 ? "last" : undefined, style: { "--vbf-timeline": names[i], zIndex: i }, components: components }, i)) : null) }) }) }), _jsx("ol", { className: "vb-sticky-list flex flex-col", children: items.map((it, i) => (_jsxs("li", { className: "vb-sticky-item flex min-h-[min(70svh,34rem)] flex-col justify-center gap-4 border-t border-line py-10", style: { viewTimelineName: names[i] }, children: [it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes: SITE_SIZES.narrowOnly, className: "vb-sticky-inline mb-2 rounded-ui", components: components, labels: labels }) : null, _jsx("span", { className: "font-mono text-eyebrow tabular text-fg-subtle", children: indexLabel(i) }), itemTitle(it, i, "text-display-m font-display font-normal"), itemText(it, i, "max-w-[46ch]"), it.link ? (_jsxs(BlockLink, { href: it.link.href, newTab: it.link.newTab, appearance: "link", ...linkProps, className: "mt-2", children: [it.link.label ?? it.title, _jsx(ArrowEnd, {})] })) : null] }, i))) })] }));
|
|
42
43
|
}
|
|
43
44
|
else if (v === "grid") {
|
|
44
|
-
body = (_jsx("ul", { className: cn("grid gap-x-(--gutter) gap-y-12 @2xl:grid-cols-2", columns >= 3 && "@4xl:grid-cols-3", columns === 4 && "@6xl:grid-cols-4", columns === 2 && "@4xl:grid-cols-2"), children: items.map((it, i) => (_jsxs("li", { className: cn("group/fi relative flex flex-col gap-3 border-t border-line pt-4", it.link && "vb-linked"), children: [it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes:
|
|
45
|
+
body = (_jsx("ul", { className: cn("grid gap-x-(--gutter) gap-y-12 @2xl:grid-cols-2", columns >= 3 && "@4xl:grid-cols-3", columns === 4 && "@6xl:grid-cols-4", columns === 2 && "@4xl:grid-cols-2"), children: items.map((it, i) => (_jsxs("li", { className: cn("group/fi relative flex flex-col gap-3 border-t border-line pt-4", it.link && "vb-linked"), children: [it.media ? _jsx(BlockImage, { ...it.media, aspect: "3:2", sizes: columnSizes(4 / 12), className: "mb-2 rounded-ui", components: components, labels: labels }) : null, _jsxs("div", { className: "flex items-start gap-3 pt-2", children: [showIndex ? _jsx("span", { className: "pt-1.5 font-mono text-eyebrow tabular text-fg-subtle", children: indexLabel(i) }) : null, it.icon ? _jsx(SiteIcon, { icon: it.icon, className: "mt-1" }) : null, itemTitle(it, i)] }), itemText(it, i), more(it)] }, i))) }));
|
|
45
46
|
}
|
|
46
47
|
else {
|
|
47
48
|
body = (_jsx("ol", { className: "grid gap-x-(--gutter) @4xl:grid-cols-2", children: items.map((it, i) => (_jsxs("li", { className: cn("group/fi relative grid grid-cols-[3rem_1fr] gap-x-4 gap-y-3 border-t border-line py-7 @2xl:grid-cols-[4.5rem_1fr]", it.link && "vb-linked", !showIndex && "grid-cols-1!"), children: [showIndex ? _jsx("span", { className: "row-span-3 pt-1.5 font-mono text-eyebrow tabular text-fg-subtle", children: indexLabel(i) }) : null, itemTitle(it, i), itemText(it, i, "max-w-[52ch]"), more(it)] }, i))) }));
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
-
import
|
|
3
|
+
import * as Tabs from "radix-ui/tabs";
|
|
4
4
|
import { cn } from "../../lib/cn.js";
|
|
5
5
|
/**
|
|
6
6
|
* Tabs island of the Feature grid `tabs` variant (WAI-ARIA Tabs via Radix, vertical arrow keys).
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
|
+
import { type JsonLdNode } from "../json-ld.js";
|
|
2
3
|
import type { BlockBaseProps } from "../types.js";
|
|
3
4
|
import type { AddressValue, SocialNetwork } from "../schema/types.js";
|
|
4
5
|
import { type Site2BlockLabels } from "./shared/labels-2.js";
|
|
@@ -13,7 +14,7 @@ export interface FooterProps extends Omit<BlockBaseProps, "variant" | "labels" |
|
|
|
13
14
|
*/
|
|
14
15
|
variant?: FooterVariant;
|
|
15
16
|
brand: {
|
|
16
|
-
name: string;
|
|
17
|
+
name: string; /** An image logo renders 2rem tall (`sizes="12rem"`). */
|
|
17
18
|
logo?: React.ReactNode | SiteImage2;
|
|
18
19
|
href?: string;
|
|
19
20
|
};
|
|
@@ -53,8 +54,16 @@ export interface FooterProps extends Omit<BlockBaseProps, "variant" | "labels" |
|
|
|
53
54
|
};
|
|
54
55
|
/** © year. Default: the render year. */
|
|
55
56
|
year?: number;
|
|
57
|
+
/**
|
|
58
|
+
* Organization JSON-LD built from brand, company, contact and social. Default true. Pass an
|
|
59
|
+
* object to refine it (`{ "@type": "Dentist", url: "https://…" }`; a LocalBusiness subtype
|
|
60
|
+
* suits a shop or clinic), or false when the page already emits its own.
|
|
61
|
+
*/
|
|
62
|
+
jsonLd?: boolean | JsonLdNode;
|
|
56
63
|
labels?: Site2BlockLabels;
|
|
57
64
|
}
|
|
65
|
+
/** `Organization` JSON-LD from the footer's details (absolute brand href becomes `url`). */
|
|
66
|
+
export declare function footerJsonLd(props: Pick<FooterProps, "brand" | "company" | "contact" | "social">): JsonLdNode;
|
|
58
67
|
/**
|
|
59
68
|
* Site footer: navigation, contact, socials, legal links and the company details e-commerce law
|
|
60
69
|
* requires in many countries. `footer` landmark with labelled inner navs; social links are named
|
|
@@ -65,4 +74,4 @@ export interface FooterProps extends Omit<BlockBaseProps, "variant" | "labels" |
|
|
|
65
74
|
* company={{ legalName: "Vamos Dijital Ajans Ltd. Şti.", registry: [{ label: "Tax no.", value: "923 145 6780" }] }} />
|
|
66
75
|
* ```
|
|
67
76
|
*/
|
|
68
|
-
export declare function Footer({ variant, brand, about, columns, contact, social, legalLinks, company, credit, cookieSettings, newsletter, year, id, anchor, theme, className, labels, locale, components, editMode, }: FooterProps): React.JSX.Element;
|
|
77
|
+
export declare function Footer({ variant, brand, about, columns, contact, social, legalLinks, company, credit, cookieSettings, newsletter, year, jsonLd, id, anchor, theme, className, labels, locale, components, editMode, }: FooterProps): React.JSX.Element;
|