efficient-web-builder 19.1.0 → 22.0.0
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/CHANGELOG.md +301 -0
- package/dist/components/AnimatedStat.js +1 -1
- package/dist/components/Banner.js +1 -1
- package/dist/components/Cards.js +1 -1
- package/dist/components/CategoryBar.js +1 -1
- package/dist/components/ChartSection.js +1 -1
- package/dist/components/ChatSettingsPanel.js +1 -1
- package/dist/components/ContactLocations.js +3 -3
- package/dist/components/Countdown.js +1 -1
- package/dist/components/DealerFinder.d.ts +12 -0
- package/dist/components/DealerFinder.js +1 -0
- package/dist/components/DocMasthead.js +1 -1
- package/dist/components/DocMetaCards.js +1 -1
- package/dist/components/DocTabs.js +1 -1
- package/dist/components/FAQ.js +1 -1
- package/dist/components/FeaturedCover.js +1 -1
- package/dist/components/Footprint.d.ts +8 -1
- package/dist/components/Footprint.js +5 -5
- package/dist/components/HeroPartPanel.d.ts +7 -0
- package/dist/components/HeroPartPanel.js +1 -0
- package/dist/components/HeroPoster.d.ts +2 -1
- package/dist/components/HeroPoster.js +1 -1
- package/dist/components/HeroSingle.d.ts +12 -1
- package/dist/components/HeroSingle.js +11 -1
- package/dist/components/HeroSlide.d.ts +2 -1
- package/dist/components/HeroSlide.js +1 -1
- package/dist/components/HeroVideoPlay.d.ts +13 -0
- package/dist/components/HeroVideoPlay.js +1 -0
- package/dist/components/ImageBlock.d.ts +2 -1
- package/dist/components/ImageBlock.js +1 -1
- package/dist/components/KeyFeatures.js +1 -1
- package/dist/components/LocationGroupHeader.js +1 -1
- package/dist/components/Logos.js +1 -1
- package/dist/components/LongRead.js +1 -1
- package/dist/components/Manifesto.js +1 -1
- package/dist/components/Masthead.js +1 -1
- package/dist/components/MediaUploadField.d.ts +3 -1
- package/dist/components/MediaUploadField.js +1 -1
- package/dist/components/MockupSection.js +1 -1
- package/dist/components/OrderSummary.js +1 -1
- package/dist/components/PagesPanel.js +1 -1
- package/dist/components/ParagraphsA.js +1 -1
- package/dist/components/PostCard.js +1 -1
- package/dist/components/PreviewChatContext.d.ts +8 -0
- package/dist/components/PreviewChatContext.js +1 -1
- package/dist/components/PreviewHeroPartContext.d.ts +3 -0
- package/dist/components/PreviewHeroPartContext.js +1 -0
- package/dist/components/ProcessSteps.js +1 -1
- package/dist/components/Prose.js +1 -1
- package/dist/components/PublishedPartStylesContext.js +1 -1
- package/dist/components/Reel.js +1 -1
- package/dist/components/SectionEditor.d.ts +10 -0
- package/dist/components/SectionEditor.js +1 -1
- package/dist/components/SectionEyebrow.js +1 -1
- package/dist/components/SectionHeader.js +1 -1
- package/dist/components/SectionIcon.js +1 -1
- package/dist/components/Series.js +1 -1
- package/dist/components/SiteFields.d.ts +3 -1
- package/dist/components/SiteFields.js +1 -1
- package/dist/components/SiteTextStylesField.js +1 -1
- package/dist/components/Split.js +1 -1
- package/dist/components/StandaloneSectionEditor.js +1 -1
- package/dist/components/Stats.js +1 -1
- package/dist/components/StatsParagraph.js +1 -1
- package/dist/components/StorefrontEditor.js +1 -1
- package/dist/components/Testimonials.js +1 -1
- package/dist/components/Text.js +1 -1
- package/dist/components/ThemeField.js +1 -1
- package/dist/components/Timeline.js +1 -1
- package/dist/components/TimelineTrack.js +1 -1
- package/dist/components/WebsiteScreenParts.js +1 -1
- package/dist/components/storefront-editor.css +103 -302
- package/dist/components/useLocationsTree.js +1 -1
- package/dist/lib/content/chat-launcher.d.ts +9 -0
- package/dist/lib/content/chat-launcher.js +1 -0
- package/dist/lib/content/page-width.d.ts +1 -0
- package/dist/lib/content/page-width.js +1 -1
- package/dist/lib/content/phone-focus.d.ts +7 -0
- package/dist/lib/content/phone-focus.js +1 -0
- package/dist/lib/dealer-source.d.ts +34 -0
- package/dist/lib/dealer-source.js +1 -0
- package/dist/lib/editor-words-catalogue.d.ts +23 -7
- package/dist/lib/editor-words-catalogue.js +4 -4
- package/dist/lib/hero-arrows.d.ts +1 -1
- package/dist/lib/hero-media-layout.d.ts +11 -0
- package/dist/lib/hero-media-layout.js +1 -0
- package/dist/lib/hero-video.d.ts +15 -0
- package/dist/lib/hero-video.js +1 -0
- package/dist/lib/links/phone-href.d.ts +1 -0
- package/dist/lib/links/phone-href.js +1 -0
- package/dist/lib/markup.d.ts +5 -3
- package/dist/lib/markup.js +2 -2
- package/dist/lib/preview-header-settings.d.ts +1 -0
- package/dist/lib/puck/MarkupToolbar.d.ts +1 -1
- package/dist/lib/puck/MarkupToolbar.js +1 -1
- package/dist/lib/puck/blocks/general/Footprint/fields.js +1 -1
- package/dist/lib/puck/blocks/general/Footprint/render.js +2 -2
- package/dist/lib/puck/blocks/general/Hero/fields.js +1 -1
- package/dist/lib/puck/blocks/general/HeroSingle/fields.js +1 -1
- package/dist/lib/puck/blocks/general/HeroSingle/render.js +1 -1
- package/dist/lib/puck/blocks/general/HeroSlide/fields.js +1 -1
- package/dist/lib/puck/blocks/general/HeroSlide/render.js +1 -1
- package/dist/lib/puck/blocks/general/ImageBlock/fields.js +1 -1
- package/dist/lib/puck/blocks/general/ImageBlock/render.js +1 -1
- package/dist/lib/puck/canvas-typing.d.ts +1 -2
- package/dist/lib/puck/controls.d.ts +49 -2
- package/dist/lib/puck/controls.js +1 -1
- package/dist/lib/puck/field-groups.js +1 -1
- package/dist/lib/puck/hero-media-parts.d.ts +42 -0
- package/dist/lib/puck/hero-media-parts.js +1 -0
- package/dist/lib/puck/media-manifest.json +3 -1
- package/dist/lib/puck/part-styles.js +2 -2
- package/dist/lib/puck/passes/settings-groups.js +1 -1
- package/dist/lib/reel.d.ts +3 -3
- package/dist/lib/reel.js +1 -1
- package/dist/lib/sections/collection-card-text-regions.d.ts +9 -1
- package/dist/lib/sections/collection-card-text-regions.js +1 -1
- package/dist/lib/sections/text-regions.d.ts +1 -0
- package/dist/lib/sections/text-regions.js +1 -1
- package/dist/lib/site-text-styles.d.ts +1 -0
- package/dist/lib/site-text-styles.js +2 -2
- package/dist/lib/source-rules/heading-ceiling-sweep.js +3 -3
- package/dist/lib/source-rules/one-markup-grammar.d.ts +5 -0
- package/dist/lib/source-rules/one-markup-grammar.js +4 -0
- package/dist/lib/source-rules/one-site-column.d.ts +6 -0
- package/dist/lib/source-rules/one-site-column.js +3 -0
- package/dist/lib/source-rules/part-style-naming.js +2 -2
- package/dist/lib/source-rules/rule.d.ts +1 -0
- package/dist/lib/source-rules/rule.js +3 -1
- package/dist/lib/source-rules/size-token-paints.js +4 -4
- package/dist/lib/source-rules/style-face-matches-size.js +3 -3
- package/dist/lib/web-backend.d.ts +5 -0
- package/package.json +7 -5
- package/dist/components/ImageFocusRow.d.ts +0 -7
- package/dist/components/ImageFocusRow.js +0 -1
- package/dist/components/TemplateGlyph.d.ts +0 -6
- package/dist/components/TemplateGlyph.js +0 -1
- package/dist/components/TextStyleRow.d.ts +0 -13
- package/dist/components/TextStyleRow.js +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,305 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 22.0.0 - 2026-10-19
|
|
4
|
+
|
|
5
|
+
**A site text style's phone size** (Lewis 2026-10-19: the Hartes hero's Heading 1 draws 106px on a 390px phone; the site's style gets a phone size first, one place for every page; a single heading keeps its own phone Font size in the part text panel, which already exists).
|
|
6
|
+
|
|
7
|
+
### Changed for merchants
|
|
8
|
+
|
|
9
|
+
- **Site styles › Text, in the phone view:** each style's Size row edits its PHONE size (efficient-easel 12's `device`). The desktop size shows grey behind it; an emptied box follows the desktop size again.
|
|
10
|
+
- On the page, below the phone breakpoint, the style's face takes its phone size, and so does the size token it feeds (`--size-headingLarge` for Heading 1, `headingMedium`, `headingSmall`, `body`, `label`), so a section that reads the token (a Hero's Heading 1) follows too.
|
|
11
|
+
|
|
12
|
+
### Added for hosts
|
|
13
|
+
|
|
14
|
+
- `StoredTextStyleValues.sizePhone` (px; efficient-backend #3388 stores it). `textStylesCss` writes it under `PART_PHONE_MAX_CSS`, the token `!important` because it sits inline on `.sf-themed-content`. No phone size, no phone rule: every saved list draws what it drew.
|
|
15
|
+
|
|
16
|
+
### Guards
|
|
17
|
+
|
|
18
|
+
- New `scripts/test-text-style-phone-size.tsx`: stored and passed through in px, drawn under the one phone breakpoint (face and token), absent draws nothing, and Site styles hands easel the view.
|
|
19
|
+
|
|
20
|
+
**A picture's phone focus, set by the dot on the picture** (Lewis 2026-10-19, the Hartes hero: on a 390px phone the Ranger II truck was cut on the right; "one picture, one dot"). Published pages with no phone point draw the bytes they drew.
|
|
21
|
+
|
|
22
|
+
### Changed for merchants
|
|
23
|
+
|
|
24
|
+
- The single Hero (both pictures), the Hero slide and the Image block set their focus with the dot ON the picture row, not in an **Image focus** row of its own with a second copy of the photograph. The view you drag in sets that view's point: the desktop's, or in the **phone** view the phone's, with ↺ back to the desktop point (empty = the phone draws the desktop point). The Image block shows the dot only while its picture is cropped, as the row did.
|
|
25
|
+
- Fixed on the way: Image 2's focus row previewed the FIRST picture; its dot is on its own picture now.
|
|
26
|
+
|
|
27
|
+
### Added for hosts
|
|
28
|
+
|
|
29
|
+
- `upload(label, accept, placeholder, { focus, focusWhen })`: the picture row's focus key and when it can move anything. `focusKeptField()`: a focus key declared with no control, so it is saved.
|
|
30
|
+
- New keys, each `<key>Phone` (`phoneKey`): `imageFocalPhone`, `imageFocal2Phone` (single Hero), `imageFocalPhone` (Hero slide), `focalPhone` (Image block).
|
|
31
|
+
- `lib/content/phone-focus`: `phoneFocus(point)`, `PHONE_FOCUS_CLASS` (`sf-focus-phone`), `PHONE_FOCUS_VAR` (`--sf-focus-phone`). A phone point reaches the picture as that class and variable beside the inline desktop point; **the host's site stylesheet must draw it under the phone breakpoint** (storefront-blocks' `globals.css`, beside `.sf-pad-top-phone`). `HeroPoster`, `HeroSingle`, `HeroSlide` take `objectPositionPhone` (`objectPosition2Phone`); `ImageBlock` takes `focalPhone`.
|
|
32
|
+
|
|
33
|
+
### Removed for hosts
|
|
34
|
+
|
|
35
|
+
- `focalPicker` (no caller: the dot is on the picture) and the editor CSS that hid the second photograph (`.es-insp-rows:has([data-image-focus])`, `.sf-focal-row`). `components/ImageFocusRow.tsx` (no caller: the dot is on the picture). `components/TextStyleRow.tsx` (its last caller was `ImageFocusRow`).
|
|
36
|
+
|
|
37
|
+
### Guards
|
|
38
|
+
|
|
39
|
+
- New `scripts/test-phone-focus.tsx`: the helper, the render (with and without a phone point, the Image block only while cropped) and the fields (every focus key declared with its phone twin; each picture row names its own key).
|
|
40
|
+
- `scripts/test-panel-parts-from-the-shell.ts` §2-3: one picture, one dot; no focus row and no crop-hiding rule. `scripts/test-hero-media-show-when.tsx`: the phone keys hide with their picture for a video.
|
|
41
|
+
|
|
42
|
+
**Every picture row is the shell's media field, with a Change image bar** (Lewis 2026-10-19: "how do I change the picture?"; one picture, one dot). A MAJOR: the efficient-shell peer narrows to `^7.69.1` and the efficient-easel peer moves to `^12.0.0`.
|
|
43
|
+
|
|
44
|
+
### Breaking for hosts
|
|
45
|
+
|
|
46
|
+
- **efficient-easel 12.0.0 or later and efficient-shell 7.69.1 or later are required.** Every `upload()` row draws easel's `ImageBox`, which since easel 12 IS the shell's `MediaUploadField` (efficient-shell #528). A host moves easel, the shell and this package in one pull request; its Tailwind must scan the shell for the bar's classes (admin-portal does).
|
|
47
|
+
|
|
48
|
+
### Changed for merchants
|
|
49
|
+
|
|
50
|
+
- Every picture and video row (the 31 `upload()` rows and the chrome media panels) shows the shell's field: the picture with its file name on it, and along its bottom a **Change image** bar and a bin (on hover and keyboard focus; always on a screen with no hover). Empty, the shell's drop zone.
|
|
51
|
+
|
|
52
|
+
### Changed for hosts
|
|
53
|
+
|
|
54
|
+
- `components/MediaUploadField` no longer runs a drop intake of its own: the shell field's intake holds a dropped file to the slot's kind and to `backendOf(session).media.maxUploadBytes` and announces a refused one, and a file that passes goes to the admin through `openLibrary(file)` → `requestMediaPick(host, { file })`, as before. Its own busy and error lines and their CSS (`.sf-image-drop`, `.sf-image-status`) are gone.
|
|
55
|
+
|
|
56
|
+
### Guards
|
|
57
|
+
|
|
58
|
+
- `scripts/test-shell-peer-range.ts` records the 7.69.1 floor; `scripts/test-two-ways-to-add.ts` the easel `^12.0.0` range.
|
|
59
|
+
- `scripts/test-upload-intake-guard.ts`: no migrated surface of its own any more (the media slot's intake is the shell field's); the markers still hold that no file here reads a drop itself.
|
|
60
|
+
|
|
61
|
+
- **Every margin, padding and gap in the editor's stylesheet sits on the
|
|
62
|
+
shell's spacing scale** (SSOT cleanup batch 4, Lewis approved 2026-10-17).
|
|
63
|
+
On main of 2026-10-19, 83 declarations in `storefront-editor.css` were off the
|
|
64
|
+
scale; 75 now read `var(--space-*)`, each snapped UP (3 -> 4, 5/6/7 -> 8, 9/10/11
|
|
65
|
+
-> 12, 13/14 -> 16, 18 -> 20, 28/30 -> 32, the Pages pin's 1px line gap -> 2).
|
|
66
|
+
Eight stay as named geometry in the new guard
|
|
67
|
+
`scripts/test-editor-css-spacing-on-scale.ts` (hairline and baseline
|
|
68
|
+
nudges, the slider notch, the visually-hidden box, and the empty page's 64px
|
|
69
|
+
space, which is empty canvas space, not a panel gap). Editor chrome only;
|
|
70
|
+
no live-site section changes.
|
|
71
|
+
|
|
72
|
+
- **Each section's own text sizes are written once** (SSOT cleanup batch 4,
|
|
73
|
+
GG ruling B, 2026-10-17). While a site has not set a text style, a part keeps
|
|
74
|
+
its section's own size ("unset = the section keeping its own size",
|
|
75
|
+
`DEFAULT_SIZES`). Those sizes now live in one `TEXT_SIZE` table per section,
|
|
76
|
+
and the parts read it: 161 uses are 128 entries in 33 sections. Nothing on
|
|
77
|
+
screen changes. Every section render (through the storefront-blocks sweep,
|
|
78
|
+
with this build and main's) and the compiled CSS are byte-identical, and each
|
|
79
|
+
file's table, put back inline, gives the old source exactly.
|
|
80
|
+
- **`builtInOf()`** (`lib/sections/text-regions`): a text region reads its
|
|
81
|
+
built-in size from its section's table instead of typing it again. The
|
|
82
|
+
Collections card does this first: `COLLECTION_CARD_TEXT_SIZE` holds the five
|
|
83
|
+
runs' classes, and each `builtIn` is read from it. storefront-blocks'
|
|
84
|
+
`CollectionCard` imports the table in a follow-up after this release.
|
|
85
|
+
- **`inlineTextSizes()`** (`lib/source-rules/rule`): the source rules that
|
|
86
|
+
read class lists (style face beside its size, heading ceiling, part-style
|
|
87
|
+
naming, size-token paints) put a file's table back in place first, line for
|
|
88
|
+
line, so they check exactly what the page draws.
|
|
89
|
+
- New guard `scripts/test-text-size-tables.ts`: a file with a table writes no
|
|
90
|
+
size fallback outside it, and no entry twice.
|
|
91
|
+
- **The Pages panel's last own numbers sit on the shell's spacing scale**
|
|
92
|
+
(Lewis's scale of 2026-10-12, snapped up). The type pills (Dynamic,
|
|
93
|
+
Lightbox, Preview) pad `var(--space-2) var(--space-8)` (was 2px 7px). The
|
|
94
|
+
note that stands where the tree would (Loading, nothing found, an error) pads
|
|
95
|
+
`var(--space-12)` (was 10px 12px). The "draft" tag has no spacing of its own,
|
|
96
|
+
so it does not move. The new guard `scripts/test-pages-pills-on-scale.ts`
|
|
97
|
+
holds both. A patch.
|
|
98
|
+
|
|
99
|
+
**The copy field's formatting bar is the shell's text toolbar** (efficient-easel ^11.8.0). A minor: no stored value moves.
|
|
100
|
+
|
|
101
|
+
**The Locations Map can show the website's dealers: Source = Typed in · Dealers** (Lewis approved 2026-10-17, card [ADD] Dealer Finder, Panthera "Where to buy"). A minor: a section saved before this setting draws exactly what it drew.
|
|
102
|
+
|
|
103
|
+
### Added for merchants
|
|
104
|
+
|
|
105
|
+
- **Source** (in the Offices band): Typed in (the default, today's section) or Dealers. Dealers lists the dealers ticked "Show" in the website's Dealers list (admin), read from the ERP; the section stores nothing about a dealer.
|
|
106
|
+
- In Dealers mode the map is efficient-shell's `EntityMap` in its site look: pins in the site's own colours, light or dark by the section's Map theme, crowded pins grouped into numbered circles that zoom open, two fingers to move it on a phone. A list sits left, right or below it (one column on a phone). A list row flies the map to its pin; a pin brings its row into view.
|
|
107
|
+
- **Search** band (Dealers only): **Show** switch, **Hint** and **No match**. A search puts the nearest dealers first, with km, and frames the five nearest on the map.
|
|
108
|
+
- **List** band (Dealers only): **Position** (Left · Right · Below) and **Distance**.
|
|
109
|
+
- The rows hide by the shell's one show-when rule (`withShowWhen`).
|
|
110
|
+
|
|
111
|
+
### Added for hosts
|
|
112
|
+
|
|
113
|
+
- `lib/dealer-source`: the host hands the dealer read over through `DealerSourceProvider` (`GET /api/v1/dealers`). Without it the editor says so and a live page draws nothing.
|
|
114
|
+
- `efficient-shell` peer: see above (`^7.69.1` in this release). `maplibre-gl` and `@tanstack/react-query` are devDependencies only; the shell's map declares them as its own peers, and the host serves the MapLibre worker (`efficient-shell/map/worker-route`) and loads `efficient-shell/map-site.css`.
|
|
115
|
+
|
|
116
|
+
### Changed for merchants
|
|
117
|
+
|
|
118
|
+
- **The bar above a copy field is the shell `TextToolbar`**, the one text toolbar every editor draws (efficient-easel 11.5.0 made `MarkBar` a wrapper over it). Bold, Italic and Strikethrough wear the shell's own glyphs, names and 32px-high buttons where the 14px hand-drawn glyphs and the 22px row were; Clear formatting sits in the toolbar's last cell, right after the marks, with no divider of the inspector's. The bar is still inside the copy box's frame and still hidden until the caret is in the words.
|
|
119
|
+
|
|
120
|
+
### Changed for hosts
|
|
121
|
+
|
|
122
|
+
- **`efficient-easel` peer and dev range `^11.8.0`** (was `^11.2.0`; `^12.0.0` in this release, above).
|
|
123
|
+
- `MarkupToolbar` draws `efficient-shell/ui`'s `TextToolbar` directly, not easel's deprecated `MarkBar` (removed in easel 12.0.0); it puts `sf-markup-bar` on the toolbar's wrapper. `storefront-editor.css` places the bar by that class only and dresses no shell toolbar class: the old `.es-copy-bar` / `.es-copy-mark` / `.es-copy-bar-split` rules are gone with the bar that drew them.
|
|
124
|
+
|
|
125
|
+
### Guards
|
|
126
|
+
|
|
127
|
+
- `test-markup-toolbar`, `test-clear-formatting`, `test-shop-text-box` read the shell toolbar: its `rosh-tb-btn` marks, its words from `DEFAULT_SHELL_STRINGS.textToolbar` (Bold, Italic, Strikethrough), Clear in the toolbar's own row, the bar hidden at rest by `.sf-markup-bar`, and the marks' mousedown guard in the shell's toolbar. `test-two-ways-to-add` checks `^11.8.0`. All four failed on the bump alone.
|
|
128
|
+
|
|
129
|
+
**Seven editor glyphs come from the shell's one icon set** (SSOT cleanup, Lewis approved 2026-10-17; harness UI-24). A patch: the drawings change a little, nothing else.
|
|
130
|
+
|
|
131
|
+
### Changed
|
|
132
|
+
|
|
133
|
+
- Site styles > Theme > Add new theme: the shell's `add` (was a local plus).
|
|
134
|
+
- Pages panel, each page row: the shell's `page` (was a local document).
|
|
135
|
+
- Text styles, the saved tick: the shell's `check` (was a local tick, stroke 3; the shell's is lighter).
|
|
136
|
+
- Contact locations tree: a region group is `globe`, a city `location`, a city's map `map` (were three local drawings; the note that the shell had no `map` is out of date).
|
|
137
|
+
- The toolbar's Save as Template button: the shell's `template` glyph (`components/TemplateGlyph` was a local copy of the same bookmark; deleted). Lewis approved 2026-10-19.
|
|
138
|
+
|
|
139
|
+
Before/after sheet: GG's notes folder, `ssot-batch2-wb-icons-before-after.png`.
|
|
140
|
+
|
|
141
|
+
## 21.0.0 - 2026-10-19
|
|
142
|
+
|
|
143
|
+
**The picture strip is efficient-shell's `PictureTiles`** (Lewis 2026-10-17 chose picture tiles for the header country picker's "Show as"; GG: one copy, in the shell). The Photo Grid's "Layout" and the Box's "Block widths" drew one local strip; the header picker is its third row, so the strip moved into efficient-shell 7.53.0 (Efficient-Pty-Ltd/efficient-shell#518) and both rows draw their pictures into it. Release it as a **major**: the shell peer range narrows to `^7.53.0`.
|
|
144
|
+
|
|
145
|
+
### Changed for merchants
|
|
146
|
+
|
|
147
|
+
- **The two rows look a little different, on purpose** (GG approved 2026-10-17): the tiles are 2px smaller, have the control corner radius, a lighter edge and a ring when chosen, and the gap between tiles is 8px (it was 6px, off the spacing scale). Same layout, same drawings, same choices.
|
|
148
|
+
|
|
149
|
+
### For hosts
|
|
150
|
+
|
|
151
|
+
- `efficient-shell` peer: `^7.53.0` (was `^7.52.0`).
|
|
152
|
+
- `CountrySwitchBlock` is in `GROUPED_BLOCKS` (beside `SearchBlock` and `CartBlock`): it takes its own `Show as` in efficient-storefront-blocks. `CountrySwitchBlock._gStyle` is retired as the Style bucket header's echo.
|
|
153
|
+
- `PreviewHeaderSettings.countrySelectorShowAs` (optional): what the closed country picker shows. Absent reads as the country name.
|
|
154
|
+
- Deleted from `components/storefront-editor.css`: the strip's own tile look and the `.sf-tile-tip` wrapper rules. `.sf-gallery-layouts` keeps only its 7px under the row's name; `.sf-gallery-pic` now fills its tile (`width: 100%`).
|
|
155
|
+
|
|
156
|
+
### Security
|
|
157
|
+
|
|
158
|
+
- **A stored text style can no longer carry code onto the live page.** A part's size, phone size, weight, line and letter spacing, paragraph spacing, padding and width are drawn only when the document holds a finite number, and its alignment only when it is one of left / center / right / justify; anything else draws nothing, the rule colour, role, case and font already followed. Before, those values were copied into the published `<style>` as saved, so a page saved outside the editor (the page-saving API, a pasted layout) with a size of `1px}</style><script>…` ran script for every shopper. Shape only, never range: every number a document holds draws the bytes it drew before (`scripts/test-part-style-injection.tsx` compares against output captured before the change).
|
|
159
|
+
- The published part stylesheet escapes `<` as `\3c ` before it is written into its `<style>` element. No rule writes one, so no valid byte changes.
|
|
160
|
+
|
|
161
|
+
## 20.3.0 - 2026-10-17
|
|
162
|
+
|
|
163
|
+
**A Hero's picture can be two pictures: Layout One · Side by side · Stacked** (Lewis approved 2026-10-17; step 1 of the pantherawheels.com rebuild, whose "FUSION FORGED" banners are two pictures side by side). A minor: every stored page draws what it drew.
|
|
164
|
+
|
|
165
|
+
**The site's content column is one export, `SITE_COLUMN`, and the phone link one helper, `phoneHref`** (SSOT cleanup, Lewis approved 2026-10-17). The column was typed out four times (the FAQ here; Events, GalleryGrid and DynamicGalleryGrid in efficient-storefront-blocks), the `tel:` link twice. A minor: pages render the same bytes (one edge case below).
|
|
166
|
+
|
|
167
|
+
**The Live chat panel gets a Look group: the chat button's colour, icon, corner, label and phones** (Lewis approved 2026-10-17). A minor: five new optional chat-settings fields; a site that sets none draws what it draws today. The site's launcher reads them in efficient-storefront-blocks, after the backend fields ship.
|
|
168
|
+
|
|
169
|
+
### Changed for merchants
|
|
170
|
+
|
|
171
|
+
- **`Layout`** on the Hero's Media part, for an image: One · Side by side · Stacked, each shown as its picture. **The phone has its own value on the same row, in phone view** (with the house ↺ reset); unset, a side-by-side pair stacks on a phone (GG), and a phone value of One hides the second picture there.
|
|
172
|
+
- **Image 2** — the second picture is a part of its own: a Layers row under Media and its own panel (image, Image focus, Tint color, Tint opacity). With two pictures the rows read `Image 1` and `Image 2`, and so do their panels' heads. Each picture is its own click target on the canvas.
|
|
173
|
+
- One (absent) is today's single picture, drawn byte for byte as before.
|
|
174
|
+
- Every layout grid class names its screen (`max-[719px]:` / `min-[720px]:`): a bare `grid-cols-1` is outranked in the editor canvas by the editor's own copy of Tailwind, which drew the pair stacked on the desktop canvas.
|
|
175
|
+
- Inset and One big, two small are approved and come next, in the same row.
|
|
176
|
+
|
|
177
|
+
### Added for hosts
|
|
178
|
+
|
|
179
|
+
- `lib/hero-media-layout`: the layouts, `resolvePhoneLayout`, `heroPictureCount`.
|
|
180
|
+
- `src/lib/puck/media-manifest.json` regenerated (the storefront's `gen-media-manifest.ts --out`): `mediaImage2` is a media field. **The backend's vendored copy must be re-vendored when the storefront moves to this release**, or template saves are refused on the hash.
|
|
181
|
+
- `PhoneChoiceStrip` (pure) and `PhoneChoiceRow` (reads the desktop value) are exported, so a guard that has no live store can draw the strip, as it does `WidthFollowField`.
|
|
182
|
+
- `phoneChoiceField` (`lib/puck/controls`): a choice setting's phone row, the twin of `phoneOverrideField` for px rows. The desktop row and the phone row show through the one show-when rule's device context.
|
|
183
|
+
- The `image2` part (`heroImage2` mode), `heroPartLabel`; `describeNode` hands a panel the node's props, so a part's head says what its Layers row says.
|
|
184
|
+
- `SITE_COLUMN` from `lib/content/page-width`: the centred box capped at `--sf-site-width`, with the site gutter and its 20px step under 800px. Import it; never retype it. The blocks' three copies move onto it in their own pull request.
|
|
185
|
+
- The rule `oneSiteColumn` (`lib/source-rules/one-site-column`): no TS/TSX file spells the string; a repository names the file that defines it, or none.
|
|
186
|
+
- `phoneHref(value)` from `lib/links/phone-href`: `tel:` + the number's `+` and digits, or `undefined` when nothing is left. It was written twice (ContactLocations here, ContactRouting in the blocks); the blocks keep their own "is this a phone number" test and call this for the href.
|
|
187
|
+
- `ChatSettings` (lib/web-backend) gains optional `launcherColor`, `launcherIcon`, `launcherPosition` (`"right" | "left"`), `launcherLabel` and `showOnPhones`; the backend's `launcher_color`, `launcher_icon`, `launcher_position`, `launcher_label`, `show_on_phones`. A host that sends none reads as the defaults, and the editor writes them through the same `chat.update` patch.
|
|
188
|
+
- `lib/content/chat-launcher`: `CHAT_LAUNCHER_ICONS`, `DEFAULT_CHAT_LAUNCHER_ICON`, `CHAT_LAUNCHER_POSITIONS`, `DEFAULT_CHAT_LAUNCHER_POSITION`, and the readers `chatLauncherIcon`, `chatLauncherPosition`, `chatLauncherColor` — one definition for the panel and the site's launcher.
|
|
189
|
+
- `ChatPreview` gains the five fields; `chatPreviewFrom(settings)` builds it from a stored row (the editor seeds through it).
|
|
190
|
+
|
|
191
|
+
### Changed
|
|
192
|
+
|
|
193
|
+
- The FAQ imports `SITE_COLUMN` instead of its own copy. Same string, same bytes.
|
|
194
|
+
- Contact locations build the phone link with `phoneHref`. Same href for every number with a digit in it; a phone with no digit at all (only words) now has no link instead of a bare `tel:`.
|
|
195
|
+
|
|
196
|
+
### Added for merchants
|
|
197
|
+
|
|
198
|
+
- **Live chat > Look**, between "Show the chat bubble" and Wording:
|
|
199
|
+
- **Button color**: follows the site's button color (the caption says "Site button color"; nothing stored), or a color of its own through the editor's one color control.
|
|
200
|
+
- **Icon**: the shell's IconPicker over six glyphs of the one icon set; default `chat-bubble-left-right`.
|
|
201
|
+
- **Position**: Right / Left, the panel's two-way choice; default Right.
|
|
202
|
+
- **Label**: words beside the button; blank is icon only. Hint: "Shows when a visitor points at the button" (on the site it shows on hover, never on phones).
|
|
203
|
+
- **Show on phones**: on by default.
|
|
204
|
+
- **A reset slot on every row of the panel**, switches and wording included, so the switches, the color and the ↺ line up (Lewis 2026-10-17).
|
|
205
|
+
|
|
206
|
+
### Guards
|
|
207
|
+
|
|
208
|
+
- New `scripts/test-hero-media-layout.tsx` (28): the layouts and the phone's answer, the Layout rows per view, the Image 2 part, the Layers rows, the published grid and the phone hide, and one click target per picture. Failed before the change (module not found).
|
|
209
|
+
- `test-hero-media-show-when`, `test-hero-media-parts`, `test-hero-video-play` know the new rows and part. `row-icons.baseline.json`: HeroSingle 3 (the second Tint color row is a colour row, drawn without a glyph like the first).
|
|
210
|
+
- `scripts/test-band-row-names.tsx` §4 reads a panel the way it is drawn: the rows the one show-when reader shows for that view (so a desktop row and its phone twin are one row), compared within ONE panel (a part's rows are drawn in the part's own panel, so Image 1 and Image 2 may both say `Tint color`).
|
|
211
|
+
- New `scripts/test-site-column.ts`: `SITE_COLUMN` is the width cap, the gutter and its 800px step; no file in `src/` spells it. Red with a copy put back (checked by hand).
|
|
212
|
+
- New `scripts/test-phone-href.ts`: the normaliser on spaced, bracketed, dotted and `+` numbers; null, blank and words give no link; ContactLocations uses it and spells no `tel:` itself.
|
|
213
|
+
- New `scripts/test-chat-launcher-look.tsx` (6): the rows and their order and glyphs, a reset slot on every row, the defaults and what they say, changed rows light ↺, an old row reads as the defaults, every icon option is in the shell set.
|
|
214
|
+
|
|
215
|
+
## 20.2.0 - 2026-10-17
|
|
216
|
+
|
|
217
|
+
**A Hero's video: one Video, two sources, two ways to play** (Lewis approved 2026-10-17; pantherawheels.com rebuild). A minor: every stored page draws what it drew.
|
|
218
|
+
|
|
219
|
+
### Changed for merchants
|
|
220
|
+
|
|
221
|
+
- **`Source`: Upload · YouTube** on the Hero's Media part, for a video. Upload shows `Video file`; YouTube shows `YouTube link` (a share, watch, embed or Shorts link, or the bare id).
|
|
222
|
+
- **`Playback`: Background · Click to play.** Background is today's muted loop behind the words (a YouTube background loops muted too). Click to play shows the poster and a round Play button in the theme's button colour (the accent until a theme sets one); a press plays the video **with sound** over the Hero, and the words, the tint and the gradient step aside while it plays.
|
|
223
|
+
- **`Video poster`** is the cover in every case; a YouTube video with no poster shows YouTube's own cover.
|
|
224
|
+
- In the editor nothing plays: the Play button is drawn, and a click on the picture opens the Media part.
|
|
225
|
+
- Absent Source and Playback are an upload as a background: every published Hero draws the bytes it drew.
|
|
226
|
+
|
|
227
|
+
### Added for hosts
|
|
228
|
+
|
|
229
|
+
- `lib/hero-video`: the sources, the playbacks, `parseYouTubeId`, and the only two YouTube addresses the Hero builds — the player `https://www.youtube-nocookie.com/embed/<id>` and the cover `https://i.ytimg.com/vi/<id>/…`, the two hosts the storefront CSP allows (efficient-ops #1195).
|
|
230
|
+
- `components/HeroVideoPlay` (shopper output): the cover / background player and the Play button / player.
|
|
231
|
+
- `heroMediaFields` takes `rules` for a row whose show-when reads more than the Type.
|
|
232
|
+
|
|
233
|
+
### Guards
|
|
234
|
+
|
|
235
|
+
- New `scripts/test-hero-video-play.tsx` (47): link shapes in and out, the two hosts and no other in any Hero file, the Media rows for each Type × Source, the published bytes of five Heroes against a fixture taken before the change (`scripts/fixtures/hero-single-bytes.json`), Click to play and a YouTube background as drawn, and nothing playing in the editor. Failed before the change (module not found).
|
|
236
|
+
- `scripts/test-every-button-follows-the-button-colour.ts`: the Play button is a button, so `bg-btn` fills and sites are 3. `scripts/test-hero-media-show-when.tsx`: the Hero's image rows also hide the three video rows, and an uploaded video hides the YouTube link.
|
|
237
|
+
|
|
238
|
+
## 20.1.0 - 2026-10-17
|
|
239
|
+
|
|
240
|
+
**The Hero's picture has its own panels: Hero › Media › Image** (Lewis approved the Wix-style split, 2026-10-17, via GG; pantherawheels.com rebuild). A minor: no stored key moves, and every stored page draws what it drew.
|
|
241
|
+
|
|
242
|
+
**The website reads the one text grammar, `efficient-shell/markup`** (Lewis, 2026-10-12, decision D1). Every reader in `src/` (`lib/markup.ts`, and `lib/puck/canvas-typing.ts` through it) now imports the grammar from `efficient-shell/markup`, not `efficient-easel/markup`. The two copies are the same file except one rule, and the builder takes that rule too.
|
|
243
|
+
|
|
244
|
+
### Changed for merchants
|
|
245
|
+
|
|
246
|
+
- **Clicking a Hero shows the Hero's own settings only.** Width, Overlay, Corners, Border, Theme, Height, Anchor name, Animation. The picture's rows moved to two parts below it.
|
|
247
|
+
- **Media** — a row under the Hero in Layers (after its Boxes) with its own panel: `Type`, and for a video its `Video file` and `Video poster`.
|
|
248
|
+
- **Image** — under Media, for an image Hero: its own panel with the image, `Image focus`, `Tint color` and `Tint opacity`. A video Hero has no Image row.
|
|
249
|
+
- **Clicking the picture on the canvas opens its part** (the Image, or the Media for a video), outlined like the ticker strip. Each part's panel has the house "up one level" link (Image → Media → Hero).
|
|
250
|
+
- **"Background video" is now "Video file"** on the Hero and the Hero Slide (label only; the key `mediaVideo` is kept). It is not always a background now.
|
|
251
|
+
- **`++` after a letter or a digit is ordinary text.** The `++` Strong word style now opens only at a word start, like the shell's own `++`: "A++ rated tyres, A++ service" draws as typed, and "Save ++20%++ today" is still Strong. Saved copy with `++` right after a letter or digit (any script) draws differently; nothing else moves.
|
|
252
|
+
|
|
253
|
+
### Added for hosts
|
|
254
|
+
|
|
255
|
+
- `lib/puck/hero-media-parts`: the parts, their labels, `inPart` (a field names its part), `fieldsOfPart` (the ONE filter: the node's own panel draws no part's row, a part's panel only its own), the Layers rows, and `VIDEO_FILE_LABEL`.
|
|
256
|
+
- `SiteFields` takes `part`; inspector modes `heroMedia` / `heroImage` (`components/HeroPartPanel`); `PreviewHeroPartProvider` beside the ticker's in both editors. A part opens with the Hero selected, in one step (easel `select`).
|
|
257
|
+
- Only the single Hero for now: easel draws a section's own Layers rows for top-level sections only, so a Hero Slide's parts wait for that.
|
|
258
|
+
- **The two heroes' hand-written `resolveFields` hides are gone** (GG 2026-10-17): their media rows carry the one show-when rule instead (`forMediaType`, read by the rows panel only). Same rows for every Type as before — the Hero counts an absent Type as an image, a Hero Slide as a video, as the old hides did — and a hidden row keeps its value.
|
|
259
|
+
|
|
260
|
+
### Changed for hosts
|
|
261
|
+
|
|
262
|
+
- New tree-wide rule `lib/source-rules/one-markup-grammar.ts` (`oneMarkupGrammar`): no file imports `efficient-easel/markup`. A host can run it over its own files.
|
|
263
|
+
- No pin change: `efficient-shell` `^7.52.0` already ships `efficient-shell/markup` with every name the builder imports (the shell holds the grammar since 6.0.0).
|
|
264
|
+
|
|
265
|
+
### Guards
|
|
266
|
+
|
|
267
|
+
- New `scripts/test-hero-media-parts.tsx` (31): which part draws each row, the one filter, "Video file" on both heroes, the two panels, the Layers rows for an image and a video Hero, and that a published Hero carries no part marker. Failed before the change (module not found).
|
|
268
|
+
- New `scripts/test-one-grammar-from-shell.tsx` (21): the `A++` copy draws and strips as typed; a run at a word start is Strong; a run right after a letter, digit, accented letter or CJK ideograph is plain; no `src/` file imports `efficient-easel/markup`. Red before the change (9 of 21).
|
|
269
|
+
- `scripts/test-shop-text-box.tsx`, `scripts/test-storefront-links-bullets.tsx` and `scripts/test-storefront-new-text-marks.tsx` now pin `efficient-shell/markup` in place of `efficient-easel/markup`.
|
|
270
|
+
- New `scripts/test-hero-media-show-when.tsx` (10): neither hero has a `resolveFields`; for an absent, an image and a video Type the rows shown are exactly the ones the old hide left; a hidden row keeps its value. Failed before the change (8 of 10).
|
|
271
|
+
|
|
272
|
+
|
|
273
|
+
## 20.0.0 - 2026-10-17
|
|
274
|
+
|
|
275
|
+
**The Hero's slide picker has a fifth shape: Squares** (pantherawheels.com rebuild; Lewis approved 2026-10-17). Every stored page draws what it drew.
|
|
276
|
+
|
|
277
|
+
### Breaking for hosts
|
|
278
|
+
|
|
279
|
+
- **efficient-shell 7.52.0 or later is required.** The peer range narrows from `^7.50.0` to `^7.52.0`: the panel draws the shell's `slide-picker-squares` glyph (efficient-shell #519), first published in 7.52.0. A narrowed range is a major (scripts/test-shell-peer-range). A host's bump to web-builder 20 must raise its own efficient-shell to 7.52.0 or later in the same pull request: the storefront is on `^7.48.0` and the admin portal on `^7.49.0` today.
|
|
280
|
+
|
|
281
|
+
### Changed for merchants
|
|
282
|
+
|
|
283
|
+
- **`Slide picker shape` offers Squares**, after Cards, Dots, Numbers and Bar: a column of numbered boxes that touch (32px, 1 2 3, not zero-padded), the shown one filled with the site accent and its ink, the others on the over-media scrim. It sits on the same nine anchors (Panthera's look is Right) and hides below 800px like every other shape (Lewis: hidden on a phone).
|
|
284
|
+
- **Numbers is unchanged**: published heroes draw its mono `01 02` figures as before.
|
|
285
|
+
|
|
286
|
+
### Added for hosts
|
|
287
|
+
|
|
288
|
+
- `squares` in `REEL_SHAPES` and `HERO_REEL_SHAPES` (`lib/reel`), its label and its rail (`flex-col gap-0`); `components/Reel` draws it. The panel's picture is the shell's `slide-picker-squares` glyph (efficient-shell 7.52.0).
|
|
289
|
+
- **`cardShowFields` takes each row's own default** (`defaults: CardShowDefaults`
|
|
290
|
+
— `showCardSizes`, `showCardBuy`, `showCardPage`). The wheel card redesign
|
|
291
|
+
(2026-10-17) rests the Product Grid's `Sizes` ON while its `Buy button` and
|
|
292
|
+
`Page button` stay OFF, which one `bottomDefault` cannot say. A section now
|
|
293
|
+
declares its defaults once and hands the SAME object to the panel and to its
|
|
294
|
+
render, so the switch and the card cannot disagree. `bottomDefault` still
|
|
295
|
+
works and draws what it drew. Guard: `scripts/test-card-show-row-defaults.tsx`.
|
|
296
|
+
|
|
297
|
+
### Guards
|
|
298
|
+
|
|
299
|
+
- `scripts/test-shell-peer-range.ts` records the 7.52.0 floor.
|
|
300
|
+
- New `scripts/test-hero-picker-squares.tsx` (27): the shape, its label, its picture in the panel, one numbered button per slide with `data-idx`, the accent marker, the 32px box, the gap-less column on the shared rail and gate, and that Cards stays the default and Numbers is untouched. Failed before the change (11 of 27).
|
|
301
|
+
- `scripts/test-shape-choices-are-pictures.tsx` expects five picker pictures; `scripts/test-every-button-follows-the-button-colour.ts` counts 17 accent markers (the shown square is a marker like the active dot).
|
|
302
|
+
|
|
3
303
|
## 19.1.0 - 2026-10-17
|
|
4
304
|
|
|
5
305
|
**A Customer Photo Grid card's words stand on any of the nine anchors, and a repeated card has a template** (owner 2026-10-17, Panthera rebuild; approved mockup v3 https://claude.ai/artifact/4nGAU5Ge8uR4LaWbDbYrgc). A minor: every stored card draws exactly what it drew.
|
|
@@ -43,6 +343,7 @@
|
|
|
43
343
|
### Needs
|
|
44
344
|
|
|
45
345
|
- efficient-shell 7.50.0 (`isShown`).
|
|
346
|
+
|
|
46
347
|
## 18.4.0 - 2026-10-17
|
|
47
348
|
|
|
48
349
|
**A site can show a Share button on its blog posts, and Assistant joins the fonts** (Panthera rebuild; Lewis approved the design 2026-10-17). A minor: no stored key changes, and every site draws what it drew.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as n,jsxs as
|
|
1
|
+
"use client";import{jsx as n,jsxs as o}from"react/jsx-runtime";import{useCountUp as r}from"./useCountUp.js";const t={headingMedium:"text-[length:var(--size-headingMedium,40px)]",label:"text-[length:var(--size-label,13px)]"};function u({target:a,label:i}){const e=a.match(/^(\d+)(.*)$/),s=e?parseInt(e[1],10):0,m=e?e[2]:"",{ref:l,value:d}=r(s),c=e?d+m:a;return o("div",{ref:l,className:"min-w-0",children:[n("div",{className:`font-heading2 ${t.headingMedium} font-medium leading-none tracking-[-0.02em] tabular-nums`,"aria-label":a,children:c}),n("div",{className:`mt-1 font-mono ${t.label} uppercase tracking-[0.2em] text-fg-dim`,children:i})]})}export{u as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,jsxs as s}from"react/jsx-runtime";import f from"./BlockFrame.js";import
|
|
1
|
+
import{jsx as e,jsxs as s}from"react/jsx-runtime";import f from"./BlockFrame.js";import g from"./Btn.js";import{media as u}from"../lib/media.js";import{CONTENT_SIZES as v,sizedImage as N}from"../lib/sized-image.js";import b from"./SectionEyebrow.js";import{renderGold as m,renderHeadline as y,splitParagraphs as M}from"./goldPhrases.js";import{ON_MEDIA_INK_STYLE as z}from"../lib/content/ink.js";const n={headingMediumUpTo800:"max-[800px]:text-[length:min(var(--size-headingMedium,32px),var(--sf-heading-max,9999px))]",headingMedium:"text-[length:min(var(--size-headingMedium,44px),var(--sf-heading-max,9999px))]",body:"text-[length:var(--size-body,16px)]"};function E({eyebrow:r,title:a,body:d,ctaText:t,ctaHref:l,imageSrc:i,contained:o,overlay:c=60}){const p=Math.min(Math.max(c,0),100)/100;return e(f,{contained:o,className:"py-6",children:s("div",{className:"sf-card relative overflow-hidden border border-line bg-surface px-10 py-16 max-[800px]:px-6 max-[800px]:py-10",children:[i&&s("div",{className:"absolute inset-0","aria-hidden":!0,children:[e(_,{src:u(i)}),e("div",{className:"absolute inset-0",style:{background:`rgba(0,0,0,${p})`}})]}),s("div",{className:"relative z-[1] flex max-w-[680px] flex-col gap-5",style:i?z:void 0,children:[r&&e(b,{text:r}),a&&e("h2",{className:`font-heading2 ${n.headingMedium} font-medium leading-[1.05] tracking-[-0.03em] ${n.headingMediumUpTo800}`,children:y(a,!1)}),d&&M(d).map((h,x)=>e("p",{className:`${n.body} leading-[1.6] opacity-85`,children:m(h)},x)),t&&e("div",{className:"mt-2 self-start",children:e(g,{href:l||"#",variant:"primary",children:m(t)})})]})]})})}function _({src:r}){const a=N(r);return e("img",{src:a.src,...a.srcSet?{srcSet:a.srcSet,sizes:v}:{},alt:"",className:"h-full w-full object-cover"})}export{E as default};
|
package/dist/components/Cards.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as e,Fragment as
|
|
1
|
+
import{jsx as e,Fragment as f,jsxs as n}from"react/jsx-runtime";import u from"./SectionEyebrow.js";import{renderGold as l,renderHeadline as v}from"./goldPhrases.js";const t={headingMediumUpTo800:"max-[800px]:text-[length:min(var(--size-headingMedium,32px),var(--sf-heading-max,9999px))]",headingMedium:"text-[length:min(var(--size-headingMedium,44px),var(--sf-heading-max,9999px))]",body:"text-[length:var(--size-body,15px)]",headingSmall:"text-[length:var(--size-headingSmall,20px)]"},b="mx-auto max-w-[var(--sf-site-width,1600px)] px-[var(--sf-site-gutter,40px)] py-[72px] max-[960px]:px-[min(24px,var(--sf-site-gutter,40px))]",N="mx-auto max-w-[var(--sf-site-width,1600px)] px-[var(--sf-site-gutter,40px)] pt-0 pb-[72px] max-[960px]:px-[min(24px,var(--sf-site-gutter,40px))]";function M({look:x,eyebrow:i,title:r,items:p,columns:o,omitHead:m=!1,editing:d=!1}={}){const g=Math.min(4,Math.max(2,o??3)),s=(p??[]).filter(a=>d||(a.title??"").trim()||(a.body??"").trim()),c=!!(i??"").trim()||!!(r??"").trim();return!d&&!c&&s.length===0?null:e("section",{className:"relative z-[2] bg-surface",children:n("div",{className:m?N:b,children:[m?null:n(f,{children:[(i??"").trim()&&e(u,{text:i??"",look:x,className:"mb-4 block"}),(r??"").trim()&&e("h2",{className:`mb-9 font-heading2 ${t.headingMedium} font-medium uppercase leading-none tracking-[-0.02em] text-fg max-[800px]:mb-7 ${t.headingMediumUpTo800}`,children:v(r??"",!1)})]}),s.length>0&&e("div",{className:"sf-card grid grid-cols-1 gap-px border border-line bg-line min-[700px]:[grid-template-columns:repeat(var(--cd-cols),minmax(0,1fr))]",style:{"--cd-cols":String(g)},children:s.map((a,h)=>n("div",{className:"bg-bg p-8 max-[800px]:p-7",children:[e("div",{className:`mb-3 font-heading3 ${t.headingSmall} font-medium uppercase tracking-[0.02em] text-fg`,children:l((a.title??"").trim()||"Card title")}),e("p",{className:`${t.body} leading-[1.6] text-fg-dim`,children:l((a.body??"").trim()||"A short line about this card.")})]},h))})]})})}export{M as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as r,jsxs as
|
|
1
|
+
import{jsx as r,jsxs as s}from"react/jsx-runtime";import{SiteLink as u}from"../lib/site-runtime.js";import{renderChrome as l}from"./goldPhrases.js";import{STICKY_UNDERHEADER_CLASS as v}from"../lib/puck/header-clearance.js";import{say as k}from"../lib/editor-words.js";const n={body:"text-[length:var(--size-body,12px)]",label:"text-[length:var(--size-label,13px)]"};function w({tabs:d,activeTab:i,links:b,sticky:f=!0,editing:h=!1}){const c=(d??[]).filter(e=>e?.label?.trim()),a=c.length>0?c:h?[{label:k("Tab")}]:[],o=(b??[]).filter(e=>e?.label?.trim());if(a.length===0&&o.length===0)return null;const p=i?a.findIndex(e=>e.label.toLowerCase()===i.toLowerCase()):-1,g=p===-1?0:p;return r("nav",{className:`${f?`${v} sticky top-[var(--sf-header-h,88px)] z-40 `:""}border-b border-line px-[var(--sf-site-gutter,40px)] backdrop-blur-md max-[1100px]:px-[min(16px,var(--sf-site-gutter,40px))]`,style:{background:"color-mix(in oklab, var(--bg) 92%, transparent)"},children:s("div",{className:"mx-auto flex max-w-[calc(var(--sf-site-width,1600px)_-_var(--sf-site-gutter,40px)*2)] items-stretch gap-0 overflow-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden",children:[a.map((e,t)=>{const m=t===g,x=e.count?.trim()?e.count:"";return s("span",{className:[`relative whitespace-nowrap border-r border-line px-[22px] py-[18px] font-mono ${n.label} uppercase tracking-[0.22em] transition-colors`,t===0?"border-l border-line":"",m?"text-fg":"text-fg-dim hover:text-fg"].join(" "),children:[l(e.label),x&&r("span",{className:`ml-2 ${n.body} text-accent opacity-70`,children:l(x)}),m&&r("span",{className:"absolute inset-x-0 -bottom-px h-[2px] bg-accent"})]},t)}),o.length>0&&r("div",{className:"ml-auto flex border-l border-line",children:o.map((e,t)=>s(u,{href:e.href||"#",className:`flex items-center gap-2.5 px-[22px] py-[18px] font-mono ${n.label} uppercase tracking-[0.22em] text-fg-dim hover:text-fg`,children:[l(e.label)," ",r("span",{className:"text-accent",children:"↗"})]},t))})]})})}export{w as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{jsx as
|
|
1
|
+
import{jsx as t,jsxs as o}from"react/jsx-runtime";import{renderGold as g,renderHeadline as N}from"./goldPhrases.js";const d={headingMediumUpTo800:"max-[800px]:text-[length:min(var(--size-headingMedium,32px),var(--sf-heading-max,9999px))]",headingMedium:"text-[length:min(var(--size-headingMedium,44px),var(--sf-heading-max,9999px))]",bodySmall:"text-[length:var(--size-body,15px)]",bodyLarge:"text-[length:var(--size-body,16px)]",labelSmall:"text-[length:var(--size-label,10px)]",label:"text-[length:var(--size-label,11px)]",labelLarge:"text-[length:var(--size-label,12px)]"};function v(e){return Number.isInteger(e)?String(e):String(Math.round(e*10)/10)}const b=[100,70,46,30,19,12];function y(e){return`color-mix(in srgb, var(--accent) ${b[e%b.length]}%, var(--bg))`}function $({data:e,suffix:x}){const h=Math.max(...e.map(m=>m.value),0)||1;return t("div",{className:"flex w-full flex-col gap-6",children:e.map((m,a)=>o("div",{children:[o("div",{className:"mb-2 flex items-baseline justify-between gap-4",children:[t("span",{className:`font-mono ${d.label} uppercase tracking-[0.14em] text-fg/60`,children:g(m.label)}),o("span",{className:`font-display ${d.bodyLarge} font-medium text-fg`,children:[v(m.value),x]})]}),t("div",{className:"h-[10px] border border-line bg-bg",children:t("div",{className:"h-full bg-accent",style:{width:`${Math.max(m.value/h*100,m.value===0?0:2)}%`}})})]},a))})}function k({data:e,suffix:x}){const a={l:42,r:42,t:36,b:38},f=640-a.l-a.r,c=300-a.t-a.b,n=Math.max(...e.map(l=>l.value),0)||1,s=e.length>1?f/(e.length-1):0,i=e.map((l,r)=>({x:a.l+(e.length>1?r*s:f/2),y:a.t+c-l.value/n*c})),p=i.map((l,r)=>`${r===0?"M":"L"}${l.x},${l.y}`).join(" "),u=`${p} L${i[i.length-1].x},${a.t+c} L${i[0].x},${a.t+c} Z`;return o("svg",{viewBox:"0 0 640 300",className:"w-full",role:"img","aria-hidden":!0,children:[[.25,.5,.75,1].map(l=>t("line",{x1:a.l,x2:640-a.r,y1:a.t+c*(1-l),y2:a.t+c*(1-l),stroke:"var(--site-line)",strokeWidth:"1"},l)),t("line",{x1:a.l,x2:640-a.r,y1:a.t+c,y2:a.t+c,stroke:"var(--site-line)",strokeWidth:"1.5"}),t("path",{d:u,fill:"var(--accent)",opacity:"0.13"}),t("path",{d:p,fill:"none",stroke:"var(--accent)",strokeWidth:"2.5"}),i.map((l,r)=>o("g",{children:[t("circle",{cx:l.x,cy:l.y,r:"4",fill:"var(--accent)"}),o("text",{x:l.x,y:l.y-12,textAnchor:"middle",className:"font-display",fontSize:"14",fill:"var(--fg)",children:[v(e[r].value),x]}),t("text",{x:l.x,y:288,textAnchor:"middle",className:"font-mono",fontSize:"10",letterSpacing:"0.1em",fill:"var(--fg)",opacity:"0.55",children:e[r].label.toUpperCase()})]},r))]})}function M({data:e,suffix:x}){const h=e.reduce((n,s)=>n+s.value,0)||1,m=80,a=2*Math.PI*m;let f=0;const c=e.map((n,s)=>{const i=n.value/h,p={frac:i,dash:Math.max(i*a-3,0),off:f,ink:y(s)};return f+=i*a,p});return o("div",{className:"flex w-full flex-wrap items-center justify-center gap-10",children:[t("svg",{viewBox:"0 0 200 200",className:"w-[220px] max-w-full -rotate-90",role:"img","aria-hidden":!0,children:c.map((n,s)=>t("circle",{cx:"100",cy:"100",r:m,fill:"none",stroke:n.ink,strokeWidth:"34",strokeDasharray:`${n.dash} ${a-n.dash}`,strokeDashoffset:-n.off},s))}),t("ul",{className:"flex min-w-[200px] flex-1 flex-col gap-3",children:e.map((n,s)=>o("li",{className:"flex items-baseline gap-3",children:[t("span",{"aria-hidden":!0,className:"h-[10px] w-[10px] flex-shrink-0 rotate-45",style:{background:y(s)}}),t("span",{className:`flex-1 font-mono ${d.label} uppercase tracking-[0.12em] text-fg/60`,children:g(n.label)}),t("span",{className:`font-display ${d.bodySmall} font-medium text-fg`,children:x?`${v(n.value)}${x}`:`${Math.round(n.value/h*100)}%`})]},s))})]})}function w({eyebrow:e,title:x,body:h,chartStyle:m,items:a,valueSuffix:f,caption:c,editing:n=!1}={}){const s=(f??"").trim()===""?"":f??"",i=(a??[]).map(r=>({label:(r.label??"").trim(),value:parseFloat(r.value??"")})).filter(r=>n?!0:r.label&&Number.isFinite(r.value)).map(r=>({label:r.label||"—",value:Number.isFinite(r.value)?r.value:0})),p=!!(e??"").trim()||!!(x??"").trim();if(!n&&!p&&i.length===0)return null;const u=m??"bars",l=i.length===0?null:u==="line"&&i.length>1?t(k,{data:i,suffix:s}):u==="donut"?t(M,{data:i,suffix:s}):t($,{data:i,suffix:s});return t("section",{className:"relative z-[2] border-y border-line bg-surface",children:o("div",{className:"mx-auto grid max-w-[var(--sf-site-width,1600px)] items-center gap-14 px-[var(--sf-site-gutter,40px)] py-[76px] min-[960px]:[grid-template-columns:minmax(0,5fr)_minmax(0,7fr)] max-[960px]:px-[min(24px,var(--sf-site-gutter,40px))]",children:[o("div",{children:[(e??"").trim()&&t("span",{className:`inline-block bg-accent px-3.5 py-1.5 font-mono ${d.labelLarge} font-semibold uppercase tracking-[0.16em] text-bg`,children:g(e??"")}),(x??"").trim()&&t("h2",{className:`mt-5 font-heading2 ${d.headingMedium} font-medium uppercase leading-[1.04] tracking-[-0.02em] text-fg ${d.headingMediumUpTo800}`,children:N(x??"",!1)}),(h??"").trim()&&t("p",{className:`mt-5 max-w-[46ch] ${d.bodySmall} leading-relaxed text-fg/65`,children:h})]}),o("div",{children:[l,(c??"").trim()&&t("p",{className:`mt-4 font-mono ${d.labelSmall} uppercase tracking-[0.14em] text-fg/40`,children:c})]})]})})}export{w as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as
|
|
1
|
+
"use client";import{jsx as n,jsxs as g,Fragment as w}from"react/jsx-runtime";import{useEffect as P,useId as C,useState as E}from"react";import{usePreviewChat as T,DEFAULT_CHAT_PREVIEW as S}from"./PreviewChatContext.js";import{usePreviewHeader as _}from"./PreviewHeaderContext.js";import{IconPicker as R}from"efficient-shell/ui";import{SettingsDefaultReset as y,SettingsField as h,SettingsSegmented as A,SettingsSwitch as I}from"efficient-easel/panel";import*as O from"efficient-easel/icons";import{ButtonIcon as B,ButtonIconIcon as H,EyeIcon as N,PhoneIcon as v,PlacementIcon as x,TypeIcon as D}from"efficient-easel/icons";import{ColourControl as U}from"../lib/puck/ColourControl.js";import{GroupLabel as L}from"../lib/puck/controls.js";import{say as a,editorEaselWords as F}from"../lib/editor-words.js";import{normalizeTheme as k,themeButtonColour as G}from"../lib/theme.js";import{CHAT_LAUNCHER_ICONS as j,DEFAULT_CHAT_LAUNCHER_ICON as d,DEFAULT_CHAT_LAUNCHER_POSITION as p,chatLauncherIcon as W}from"../lib/content/chat-launcher.js";const z="Chat with us",K="We usually reply within a few minutes",V="Hi! Ask us anything — about a wheel, an order, or fitment. Leave your details and we’ll reply.";function b({label:e,value:o,placeholder:l,onCommit:i,multiline:r=!1,hint:t}){const[c,m]=E(o);P(()=>m(o),[o]);const f=()=>{c!==o&&i(c)};return n(h,{label:e,icon:n(D,{}),changed:o!=="",action:n(u,{label:e,changed:o!=="",onReset:()=>i("")}),hint:t,children:r?n("textarea",{value:c,placeholder:l,rows:3,onChange:s=>m(s.target.value),onBlur:f,"aria-label":e}):n("input",{type:"text",value:c,placeholder:l,onChange:s=>m(s.target.value),onBlur:f,onKeyDown:s=>{s.key==="Enter"&&s.currentTarget.blur()},"aria-label":e,autoComplete:"off"})})}function u({label:e,changed:o,onReset:l}){return n(y,{words:F()?.panel,label:e,changed:o,onReset:l})}function $({label:e,icon:o,checked:l,fallback:i,onChange:r,hint:t}){const c=C();return n(h,{label:e,labelId:c,icon:o,changed:l!==i,action:g("span",{className:"inline-flex items-center gap-2",children:[n(I,{checked:l,onChange:r,ariaLabelledBy:c}),n(u,{label:e,changed:l!==i,onReset:()=>r(i)})]}),hint:t})}function q(e){const o=e.replace(/(^|-)([a-z])/g,(i,r,t)=>t.toUpperCase()),l=O[`${o}Icon`];return l?n(l,{size:16}):null}const J=j.map(e=>({value:e,icon:q(e)}));function M(){const{theme:e}=_(),o=k(e).themes[0];return G(o)??o?.accent??""}function Q(){const{chat:e,setChat:o}=T(),l=C(),i=M(),r=e.launcherIcon?W(e.launcherIcon):d;return g(w,{children:[n(h,{label:a("Show the chat bubble"),labelId:l,icon:n(N,{}),changed:!e.enabled,action:g("span",{className:"inline-flex items-center gap-2",children:[n(I,{checked:e.enabled,onChange:t=>o({enabled:t}),ariaLabelledBy:l}),n(u,{label:a("Show the chat bubble"),changed:!e.enabled,onReset:()=>o({enabled:!0})})]}),hint:e.enabled?a("The floating chat bubble shows in the corner of every page."):a("The chat bubble is hidden across the whole site.")}),n(L,{label:"Look"}),n(h,{label:a("Button color"),icon:n(B,{}),changed:e.launcherColor!=="",action:n(U,{label:a("Button color"),value:e.launcherColor,inherited:i,caption:e.launcherColor?void 0:a("Site button color"),onChange:t=>o({launcherColor:t}),after:n(u,{label:a("Button color"),changed:e.launcherColor!=="",onReset:()=>o({launcherColor:""})})})}),n(h,{label:a("Icon"),icon:n(H,{}),changed:r!==d,action:n(u,{label:a("Icon"),changed:r!==d,onReset:()=>o({launcherIcon:""})}),children:n(R,{label:a("Icon"),value:r,options:J,onChange:t=>o({launcherIcon:t===d?"":t})})}),n(h,{label:a("Position"),icon:n(x,{}),changed:e.launcherPosition!==p,action:n(u,{label:a("Position"),changed:e.launcherPosition!==p,onReset:()=>o({launcherPosition:p})}),children:n(A,{options:[{label:a("Right"),id:"right"},{label:a("Left"),id:"left"}],value:e.launcherPosition,ariaLabel:a("Position"),onChange:t=>o({launcherPosition:t})})}),n(b,{label:a("Label"),value:e.launcherLabel,placeholder:a("Icon only"),onCommit:t=>o({launcherLabel:t}),hint:a("Shows when a visitor points at the button")}),n($,{label:a("Show on phones"),icon:n(v,{}),checked:e.showOnPhones,fallback:S.showOnPhones,onChange:t=>o({showOnPhones:t})}),n(L,{label:"Wording"}),n(b,{label:a("Title"),value:e.title,placeholder:z,onCommit:t=>o({title:t})}),n(b,{label:a("Subtitle"),value:e.subtitle,placeholder:K,onCommit:t=>o({subtitle:t})}),n(b,{label:a("Greeting message"),value:e.greeting,placeholder:V,onCommit:t=>o({greeting:t}),multiline:!0}),n("p",{className:"es-field-hint sf-hint-keep",children:a("Leave a field blank to use the built-in wording.")})]})}export{Q as default};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"use client";import{jsx as t,jsxs as i,Fragment as W}from"react/jsx-runtime";import{sectionRunsFullWidth as
|
|
1
|
+
"use client";import{jsx as t,jsxs as i,Fragment as W}from"react/jsx-runtime";import{sectionRunsFullWidth as ne}from"../lib/content/section-width.js";import{SiteImage as re}from"../lib/site-runtime.js";import{markOverlayPortal as ie}from"../lib/puck/overlay-portal.js";import{useEffect as P,useRef as K,useState as T}from"react";import{stripMarkup as se}from"../lib/markup.js";import ce from"./EditorialSectionHead.js";import le from"./SectionEyebrow.js";import{scrollBehavior as V}from"./useReducedMotion.js";import{renderGold as X,renderHeadline as de}from"./goldPhrases.js";import{CONTACT_LOCATIONS_CARDS_PLACEHOLDER as pe,CONTACT_LOCATIONS_DEFAULTS as R,parseLocationTileShape as ue,splitLegacyRegion as me}from"../lib/content/contact.js";import{galleryCardRatioClass as ge}from"../lib/content/gallery-card-layout.js";import{MAP_PALETTE as fe,mapBackgroundFor as B,mapMarkerColorFor as Y,mapPaletteFor as he,mapStylesFor as J,watchMapTheme as be}from"../lib/map-styles.js";import{hasMapCoords as xe,loadGoogleMaps as ve,watchMapLoad as ye}from"../lib/maps-loader.js";import{resolveMapId as ke,resolveMapsKey as we,useSiteMapId as Ne,useSiteMapsKey as Se}from"../lib/maps-config.js";import{bucketByGroup as Ce,groupColsClass as Z}from"../lib/locations-grid.js";import{encodeLocationPartId as Q,usePreviewLocationPart as Ae}from"./PreviewLocationPartContext.js";import{LocationGroupHeader as Ie}from"./LocationGroupHeader.js";import{tk as ee}from"./floating-editor/tokens.js";import{phoneHref as Le}from"../lib/links/phone-href.js";const Me=`
|
|
2
2
|
.studio-map .gmap-canvas img[src*="google"] {
|
|
3
3
|
filter: grayscale(1) brightness(0.55) contrast(0.9);
|
|
4
4
|
opacity: 0.7;
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
.contact-loc[data-active="true"] > div:first-child { background-color: var(--accent); }
|
|
31
31
|
.contact-loc[data-active="true"] > div:first-child,
|
|
32
32
|
.contact-loc[data-active="true"] > div:first-child * { color: var(--bg) !important; }
|
|
33
|
-
`,
|
|
33
|
+
`,Oe=`
|
|
34
34
|
.loc-part-pick { cursor: pointer; position: relative; }
|
|
35
35
|
/* ⚠️ AN OVERLAY, NOT AN outline, AND THAT IS THE WHOLE FIX.
|
|
36
36
|
An inset outline is painted in the element's own box decoration and a
|
|
@@ -70,4 +70,4 @@
|
|
|
70
70
|
.loc-part-pick::after { transition: none; }
|
|
71
71
|
}
|
|
72
72
|
`;function te({value:p}){if(typeof p=="string"){const a=p.split(`
|
|
73
|
-
`);return t(W,{children:a.map((y,s)=>i("span",{children:[y,s<a.length-1?t("br",{}):null]},s))})}return t(W,{children:p})}function Oe(){return t("div",{className:"absolute inset-0 flex items-center justify-center",style:{background:"repeating-linear-gradient(45deg, rgba(201,164,97,.05) 0 1px, transparent 1px 14px), linear-gradient(135deg, var(--site-surface-2), var(--site-surface))"},children:i("svg",{width:"34",height:"34",viewBox:"0 0 24 24",fill:"none",stroke:"var(--fg-dim)",strokeWidth:1.4,"aria-hidden":"true",style:{opacity:.4},children:[t("rect",{x:"3",y:"4",width:"18",height:"16",rx:"1.5"}),t("circle",{cx:"8.5",cy:"9.5",r:"1.6"}),t("path",{d:"M21 16l-5-5-4 4-2-2-7 7"})]})})}function Ee({sectionNumber:p=R.sectionNumber,heading:a=R.heading,tagline:y=R.tagline,galleryKicker:s=R.galleryKicker,galleryHint:f=R.galleryHint,locations:w=pe,apiKey:O="",mapId:E="",mapTheme:n="auto",imageShape:N,editing:c,nodeId:l,...b}){const[x,F]=T(!1);P(()=>{const e=()=>F(document.documentElement.getAttribute("data-theme")==="light");e();const r=new MutationObserver(e);return r.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme"]}),()=>r.disconnect()},[]);const[_,$]=T(null),A=e=>`loc-${e}`,j=e=>{$(e),typeof document<"u"&&document.getElementById(e)?.scrollIntoView({behavior:V(),block:"start"}),!c&&typeof window<"u"&&window.history?.replaceState&&window.history.replaceState(null,"",`#${e}`)};P(()=>{if(typeof window>"u")return;const e=window.location.hash.replace(/^#/,"");!e||!document.getElementById(e)||($(e),requestAnimationFrame(()=>{document.getElementById(e)?.scrollIntoView({behavior:V(),block:"start"})}))},[]);const H=ge(ue(N)),{select:D,selectedLocationPartId:I}=Ae(),U=(e,r)=>l?Q(l,e,r):"",z=(e,r)=>!!(c&&l&&I===U(e,r)),L=(e,r)=>c&&l?`${e}:${r}`:void 0,d=K(null);P(()=>{if(!c||!l)return;const e=d.current;if(!e)return;const k=Array.from(e.querySelectorAll("[data-loc-pick]")).map(o=>{const m=ie(o),g=S=>{const h=o.getAttribute("data-loc-pick")??"",[M,C]=h.split(":");C!=="image"&&C!=="map"||(S.stopPropagation(),S.preventDefault(),D(Q(l,Number(M),C)))};return o.addEventListener("click",g,{capture:!0}),()=>{m?.(),o.removeEventListener("click",g,{capture:!0})}});return()=>k.forEach(o=>o())},[c,l,D,w.length]);const u=Ce(w,e=>String(e.group??""),e=>String(e.groupName??""));return i("section",{id:"studios",ref:d,className:oe(b)?"scroll-mt-24 border-b border-line px-[var(--sf-site-gutter,40px)] py-[100px] max-[1100px]:px-[min(24px,var(--sf-site-gutter,40px))] max-[1100px]:py-[60px]":"mx-auto max-w-[var(--sf-site-width,1600px)] scroll-mt-24 border-b border-line px-[var(--sf-site-gutter,40px)] py-[100px] max-[1100px]:px-[min(24px,var(--sf-site-gutter,40px))] max-[1100px]:py-[60px]",style:x?void 0:{background:"var(--section-bg)"},children:[t("style",{dangerouslySetInnerHTML:{__html:Le}}),c&&l&&t("style",{dangerouslySetInnerHTML:{__html:Me}}),(p||a||y)&&t(ce,{num:p,title:a?de(a,!0):void 0,desc:y?X(y):void 0,spaceBelow:"mb-10"}),w.length>0&&i("div",{className:"mb-12",children:[(s||f)&&i("div",{className:"mb-5 flex items-baseline justify-between gap-6 border-b border-line pb-4",children:[s&&t(le,{text:s,size:12}),f&&t("span",{className:"font-mono text-[length:var(--size-label,11px)] uppercase tracking-[0.22em] text-fg-dim",children:X(f)})]}),t("div",{className:"flex flex-col gap-6",children:u.map((e,r)=>{const k=e.items.length===1;return i("div",{children:[e.key&&t("div",{className:"mb-2.5 font-mono text-[length:var(--size-label,11px)] uppercase tracking-[0.3em] text-accent",children:e.key}),t("div",{className:["grid gap-[2px] border border-line bg-line",Z(e.items.length),e.items.length>1?"max-[700px]:grid-cols-2":""].join(" "),children:e.items.map(({item:o,index:m})=>{const g=String(o?.name??""),S=String(o?.photo??""),h=A(m);return i("a",{href:`#${h}`,"aria-label":`Jump to ${g||"location"} map`,onClick:M=>{M.preventDefault(),j(h)},"data-loc-pick":L(m,"image"),className:["group relative block",H,"overflow-hidden bg-bg outline-none focus-visible:ring-2 focus-visible:ring-accent",k?"max-h-[440px]":"",...L(m,"image")?["loc-part-pick"]:[]].join(" "),"data-selected":z(m,"image")?"":void 0,children:[S?t(re,{src:S,alt:se(g||"Location"),fill:!0,sizes:k?"100vw":"(max-width: 700px) 50vw, 33vw",className:"object-cover grayscale transition-[filter] duration-500 group-hover:grayscale-0"}):t(Oe,{}),t("div",{className:"pointer-events-none absolute inset-0",style:{background:"linear-gradient(180deg, transparent 50%, rgba(10,10,10,0.85) 100%)"}}),t("figcaption",{className:"absolute inset-x-0 bottom-0 z-[1] flex items-center justify-start gap-2 px-3 py-3 font-mono text-[length:var(--size-label,11px)] uppercase tracking-[0.22em] text-overlay-fg/85",children:t("span",{children:g})})]},m)})})]},e.key?`g:${e.key}`:`ungrouped:${r}`)})})]}),u.map((e,r)=>i("div",{className:r===0?"":"mt-10",children:[e.key&&t(Ie,{groupKey:e.key,name:e.name,count:e.items.length}),t("div",{className:["grid gap-[2px] border border-line bg-line",Z(e.items.length),e.items.length>1?"max-[1100px]:grid-cols-1":""].join(" "),children:e.items.map(({item:k,index:o})=>t(_e,{id:A(o),card:k,active:_===A(o),apiKey:O,mapId:E,mapTheme:n,mapPick:L(o,"map"),mapSelected:z(o,"map"),editing:c},o))})]},e.key?`g:${e.key}`:`ungrouped:${r}`)),w.length>0&&i("p",{className:"mt-8 text-right font-mono text-[10px] uppercase tracking-[0.25em] text-fg-dim/50",children:["Location coordinates ©"," ",t("a",{href:"https://www.openstreetmap.org/copyright",target:"_blank",rel:"noopener noreferrer",className:"hover:text-accent",children:"OpenStreetMap contributors"})]})]})}function _e({id:p,card:a,active:y,apiKey:s,mapId:f,mapTheme:w,mapPick:O,mapSelected:E,editing:n}){const N=a.phone?`tel:${String(a.phone).replace(/[^+\d]/g,"")}`:void 0,c=me(a.region),l=String(a.state??"").trim()||c.state,b=String(a.country??"").trim()||c.country,x=[l,b].filter(Boolean).join(" · ");return i("article",{id:p,"data-active":y?"true":void 0,className:"contact-loc grid min-h-[840px] scroll-mt-[120px] grid-rows-[auto_1fr] bg-bg max-[1100px]:min-h-0",children:[i("div",{className:"flex min-h-[280px] flex-col gap-3 border-b border-line px-7 py-7",children:[i("div",{className:"flex items-start justify-between gap-3.5",children:[i("div",{children:[a.name&&t("div",{className:"font-heading3 text-[length:var(--size-headingSmall,30px)] font-medium uppercase leading-none tracking-[-0.02em]",children:a.name}),x&&t("div",{className:"mt-1.5 font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.24em] text-accent",children:x})]}),a.timezone&&t("span",{className:"whitespace-nowrap border border-line px-2.5 py-1.5 font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.22em] text-fg-dim",children:a.timezone})]}),a.address!=null&&a.address!==""&&t("div",{className:"mt-1.5 font-display text-[length:var(--size-body,14px)] leading-[1.5] text-fg-dim",children:t(te,{value:a.address})}),a.phone&&t("div",{children:t("a",{href:N,className:"font-mono text-[length:var(--size-label,13px)] uppercase tracking-[0.18em] text-fg hover:text-accent",children:a.phone})}),a.hours!=null&&a.hours!==""&&t("div",{className:"font-mono text-[length:var(--size-label,12px)] uppercase leading-[1.7] tracking-[0.18em] text-fg-dim",children:t(te,{value:a.hours})})]}),t(je,{name:String(a.name??""),state:l,country:b,lat:Number(a.lat),lng:Number(a.lng),mapsUrl:String(a.mapsUrl??""),apiKey:s,mapId:f,mapTheme:w,pick:O,selected:E,editing:n})]})}const $e=13;function je({name:p,state:a,country:y,lat:s,lng:f,mapsUrl:w,apiKey:O,mapId:E,mapTheme:n="auto",pick:N,selected:c,editing:l}){const b=we(O,Se()),x=ke(E,Ne()),F=xe(s,f),_=!!b&&F,$=a,A=K(null),j=K(null),[H,D]=T(!1),I=`${b}|${s}|${f}|${x}|${n}`,[U,z]=T(null),L=U===I,d=n!=="auto",u=he(n),[e,r]=T(n==="auto"?fe.dark.background:B(n));P(()=>{r(B(n))},[n]),P(()=>{if(!_)return;const m=A.current;if(!m)return;let g=null,S=null,h=!1,M=null,C=null;const q=v=>({path:v.SymbolPath.CIRCLE,scale:7,fillColor:Y(n),fillOpacity:1,strokeColor:Y(n),strokeOpacity:.25,strokeWeight:10}),ae=async()=>{C=ye(()=>{h||z(I)});try{const v=await ve(b);if(h||!j.current)return;g=new v.Map(j.current,{center:{lat:s,lng:f},zoom:$e,disableDefaultUI:!0,gestureHandling:"cooperative",keyboardShortcuts:!1,clickableIcons:!1,backgroundColor:B(n),...x?{mapId:x}:{styles:J(n)}}),S=new v.Marker({map:g,position:{lat:s,lng:f},icon:q(v)}),g.addListener("tilesloaded",()=>{C?.ready(),h||D(!0)}),n==="auto"&&!x&&(M=be(()=>{h||!g||(g.setOptions({styles:J(n),backgroundColor:B(n)}),S?.setIcon(q(v)),r(B(n)))}))}catch(v){console.error("[StudioMap] Maps SDK failed:",v),C?.stop(),h||z(I)}},G=new IntersectionObserver(v=>{for(const ne of v)if(ne.isIntersecting){G.disconnect(),ae();break}},{rootMargin:"200px"});return G.observe(m),()=>{h=!0,G.disconnect(),C?.stop(),M?.()}},[_,b,s,f,x,n,I]);const k=w||(p?`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent([p,a,y].filter(Boolean).join(" "))}`:""),o=i("div",{className:["absolute left-5 top-5 z-10 flex max-w-[64%] flex-col gap-1.5 border p-3.5",d?"":"border-line bg-bg shadow-[0_8px_24px_rgba(0,0,0,.4)]"].join(" "),style:d?{backgroundColor:u.info.bg,borderColor:`color-mix(in oklab, ${u.info.fgDim} 35%, transparent)`,boxShadow:u.info.shadow}:void 0,children:[i("span",{className:"font-display text-[length:var(--size-body,14px)] font-medium tracking-[-0.01em]",style:d?{color:u.info.fg}:void 0,children:[p||"Location",$?`, ${$}`:""]}),k&&t("a",{href:k,target:"_blank",rel:"noopener noreferrer",className:["font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.24em] transition-colors",d?"":"text-accent hover:text-fg"].join(" "),style:d?{color:u.info.accent}:void 0,children:"Get Directions ↗"})]});if(!_||L){const m=d?`linear-gradient(135deg, ${u.background}, ${u.info.bg})`:"linear-gradient(135deg, var(--site-surface-2), var(--site-surface))";return i("div",{"data-loc-pick":N,className:["relative overflow-hidden max-[1100px]:aspect-square",d?"":"bg-bg",...N?["loc-part-pick"]:[]].join(" "),"data-selected":c?"":void 0,style:{...d?{backgroundColor:u.background}:null,backgroundImage:`repeating-linear-gradient(45deg, rgba(201,164,97,.04) 0 1px, transparent 1px 14px), repeating-linear-gradient(-45deg, rgba(201,164,97,.04) 0 1px, transparent 1px 14px), ${m}`},children:[o,l&&t("div",{className:["absolute inset-0 flex items-center justify-center px-6 text-center font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.3em]",d?"":"text-fg-dim/40"].join(" "),style:d?{color:u.info.fgDim,opacity:.4}:void 0,children:b?L?"The map could not load. Check the key":"Add latitude & longitude":"Add a Google Maps API key"})]})}return i("div",{ref:A,"data-loc-pick":N,className:["studio-map relative overflow-hidden max-[1100px]:aspect-square",...N?["loc-part-pick"]:[]].join(" "),"data-selected":c?"":void 0,style:{backgroundColor:e},children:[t("div",{ref:j,className:"gmap-canvas absolute inset-0"}),t("div",{className:["pointer-events-none absolute inset-0 flex items-center justify-center font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.3em] text-fg-dim transition-opacity duration-500",H?"opacity-0":"opacity-100"].join(" "),children:"Loading map…"}),o]})}export{Ee as default};
|
|
73
|
+
`);return t(W,{children:a.map((y,s)=>i("span",{children:[y,s<a.length-1?t("br",{}):null]},s))})}return t(W,{children:p})}function Ee(){return t("div",{className:"absolute inset-0 flex items-center justify-center",style:{background:"repeating-linear-gradient(45deg, rgba(201,164,97,.05) 0 1px, transparent 1px 14px), linear-gradient(135deg, var(--site-surface-2), var(--site-surface))"},children:i("svg",{width:"34",height:"34",viewBox:"0 0 24 24",fill:"none",stroke:"var(--fg-dim)",strokeWidth:1.4,"aria-hidden":"true",style:{opacity:.4},children:[t("rect",{x:"3",y:"4",width:"18",height:"16",rx:"1.5"}),t("circle",{cx:"8.5",cy:"9.5",r:"1.6"}),t("path",{d:"M21 16l-5-5-4 4-2-2-7 7"})]})})}function _e({sectionNumber:p=R.sectionNumber,heading:a=R.heading,tagline:y=R.tagline,galleryKicker:s=R.galleryKicker,galleryHint:f=R.galleryHint,locations:w=pe,apiKey:O="",mapId:E="",mapTheme:o="auto",imageShape:N,editing:c,nodeId:l,...b}){const[x,F]=T(!1);P(()=>{const e=()=>F(document.documentElement.getAttribute("data-theme")==="light");e();const r=new MutationObserver(e);return r.observe(document.documentElement,{attributes:!0,attributeFilter:["data-theme"]}),()=>r.disconnect()},[]);const[_,j]=T(null),A=e=>`loc-${e}`,z=e=>{j(e),typeof document<"u"&&document.getElementById(e)?.scrollIntoView({behavior:V(),block:"start"}),!c&&typeof window<"u"&&window.history?.replaceState&&window.history.replaceState(null,"",`#${e}`)};P(()=>{if(typeof window>"u")return;const e=window.location.hash.replace(/^#/,"");!e||!document.getElementById(e)||(j(e),requestAnimationFrame(()=>{document.getElementById(e)?.scrollIntoView({behavior:V(),block:"start"})}))},[]);const H=ge(ue(N)),{select:D,selectedLocationPartId:I}=Ae(),U=(e,r)=>l?Q(l,e,r):"",$=(e,r)=>!!(c&&l&&I===U(e,r)),L=(e,r)=>c&&l?`${e}:${r}`:void 0,d=K(null);P(()=>{if(!c||!l)return;const e=d.current;if(!e)return;const k=Array.from(e.querySelectorAll("[data-loc-pick]")).map(n=>{const m=ie(n),g=S=>{const h=n.getAttribute("data-loc-pick")??"",[M,C]=h.split(":");C!=="image"&&C!=="map"||(S.stopPropagation(),S.preventDefault(),D(Q(l,Number(M),C)))};return n.addEventListener("click",g,{capture:!0}),()=>{m?.(),n.removeEventListener("click",g,{capture:!0})}});return()=>k.forEach(n=>n())},[c,l,D,w.length]);const u=Ce(w,e=>String(e.group??""),e=>String(e.groupName??""));return i("section",{id:"studios",ref:d,className:ne(b)?"scroll-mt-24 border-b border-line px-[var(--sf-site-gutter,40px)] py-[100px] max-[1100px]:px-[min(24px,var(--sf-site-gutter,40px))] max-[1100px]:py-[60px]":"mx-auto max-w-[var(--sf-site-width,1600px)] scroll-mt-24 border-b border-line px-[var(--sf-site-gutter,40px)] py-[100px] max-[1100px]:px-[min(24px,var(--sf-site-gutter,40px))] max-[1100px]:py-[60px]",style:x?void 0:{background:"var(--section-bg)"},children:[t("style",{dangerouslySetInnerHTML:{__html:Me}}),c&&l&&t("style",{dangerouslySetInnerHTML:{__html:Oe}}),(p||a||y)&&t(ce,{num:p,title:a?de(a,!0):void 0,desc:y?X(y):void 0,spaceBelow:"mb-10"}),w.length>0&&i("div",{className:"mb-12",children:[(s||f)&&i("div",{className:"mb-5 flex items-baseline justify-between gap-6 border-b border-line pb-4",children:[s&&t(le,{text:s,size:12}),f&&t("span",{className:"font-mono text-[length:var(--size-label,11px)] uppercase tracking-[0.22em] text-fg-dim",children:X(f)})]}),t("div",{className:"flex flex-col gap-6",children:u.map((e,r)=>{const k=e.items.length===1;return i("div",{children:[e.key&&t("div",{className:"mb-2.5 font-mono text-[length:var(--size-label,11px)] uppercase tracking-[0.3em] text-accent",children:e.key}),t("div",{className:["grid gap-[2px] border border-line bg-line",Z(e.items.length),e.items.length>1?"max-[700px]:grid-cols-2":""].join(" "),children:e.items.map(({item:n,index:m})=>{const g=String(n?.name??""),S=String(n?.photo??""),h=A(m);return i("a",{href:`#${h}`,"aria-label":`Jump to ${g||"location"} map`,onClick:M=>{M.preventDefault(),z(h)},"data-loc-pick":L(m,"image"),className:["group relative block",H,"overflow-hidden bg-bg outline-none focus-visible:ring-2 focus-visible:ring-accent",k?"max-h-[440px]":"",...L(m,"image")?["loc-part-pick"]:[]].join(" "),"data-selected":$(m,"image")?"":void 0,children:[S?t(re,{src:S,alt:se(g||"Location"),fill:!0,sizes:k?"100vw":"(max-width: 700px) 50vw, 33vw",className:"object-cover grayscale transition-[filter] duration-500 group-hover:grayscale-0"}):t(Ee,{}),t("div",{className:"pointer-events-none absolute inset-0",style:{background:"linear-gradient(180deg, transparent 50%, rgba(10,10,10,0.85) 100%)"}}),t("figcaption",{className:"absolute inset-x-0 bottom-0 z-[1] flex items-center justify-start gap-2 px-3 py-3 font-mono text-[length:var(--size-label,11px)] uppercase tracking-[0.22em] text-overlay-fg/85",children:t("span",{children:g})})]},m)})})]},e.key?`g:${e.key}`:`ungrouped:${r}`)})})]}),u.map((e,r)=>i("div",{className:r===0?"":"mt-10",children:[e.key&&t(Ie,{groupKey:e.key,name:e.name,count:e.items.length}),t("div",{className:["grid gap-[2px] border border-line bg-line",Z(e.items.length),e.items.length>1?"max-[1100px]:grid-cols-1":""].join(" "),children:e.items.map(({item:k,index:n})=>t(je,{id:A(n),card:k,active:_===A(n),apiKey:O,mapId:E,mapTheme:o,mapPick:L(n,"map"),mapSelected:$(n,"map"),editing:c},n))})]},e.key?`g:${e.key}`:`ungrouped:${r}`)),w.length>0&&i("p",{className:"mt-8 text-right font-mono text-[10px] uppercase tracking-[0.25em] text-fg-dim/50",children:["Location coordinates ©"," ",t("a",{href:"https://www.openstreetmap.org/copyright",target:"_blank",rel:"noopener noreferrer",className:"hover:text-accent",children:"OpenStreetMap contributors"})]})]})}function je({id:p,card:a,active:y,apiKey:s,mapId:f,mapTheme:w,mapPick:O,mapSelected:E,editing:o}){const N=Le(a.phone),c=me(a.region),l=String(a.state??"").trim()||c.state,b=String(a.country??"").trim()||c.country,x=[l,b].filter(Boolean).join(" · ");return i("article",{id:p,"data-active":y?"true":void 0,className:"contact-loc grid min-h-[840px] scroll-mt-[120px] grid-rows-[auto_1fr] bg-bg max-[1100px]:min-h-0",children:[i("div",{className:"flex min-h-[280px] flex-col gap-3 border-b border-line px-7 py-7",children:[i("div",{className:"flex items-start justify-between gap-3.5",children:[i("div",{children:[a.name&&t("div",{className:"font-heading3 text-[length:var(--size-headingSmall,30px)] font-medium uppercase leading-none tracking-[-0.02em]",children:a.name}),x&&t("div",{className:"mt-1.5 font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.24em] text-accent",children:x})]}),a.timezone&&t("span",{className:"whitespace-nowrap border border-line px-2.5 py-1.5 font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.22em] text-fg-dim",children:a.timezone})]}),a.address!=null&&a.address!==""&&t("div",{className:"mt-1.5 font-display text-[length:var(--size-body,14px)] leading-[1.5] text-fg-dim",children:t(te,{value:a.address})}),a.phone&&t("div",{children:t("a",{href:N,className:"font-mono text-[length:var(--size-label,13px)] uppercase tracking-[0.18em] text-fg hover:text-accent",children:a.phone})}),a.hours!=null&&a.hours!==""&&t("div",{className:"font-mono text-[length:var(--size-label,12px)] uppercase leading-[1.7] tracking-[0.18em] text-fg-dim",children:t(te,{value:a.hours})})]}),t($e,{name:String(a.name??""),state:l,country:b,lat:Number(a.lat),lng:Number(a.lng),mapsUrl:String(a.mapsUrl??""),apiKey:s,mapId:f,mapTheme:w,pick:O,selected:E,editing:o})]})}const ze=13;function $e({name:p,state:a,country:y,lat:s,lng:f,mapsUrl:w,apiKey:O,mapId:E,mapTheme:o="auto",pick:N,selected:c,editing:l}){const b=we(O,Se()),x=ke(E,Ne()),F=xe(s,f),_=!!b&&F,j=a,A=K(null),z=K(null),[H,D]=T(!1),I=`${b}|${s}|${f}|${x}|${o}`,[U,$]=T(null),L=U===I,d=o!=="auto",u=he(o),[e,r]=T(o==="auto"?fe.dark.background:B(o));P(()=>{r(B(o))},[o]),P(()=>{if(!_)return;const m=A.current;if(!m)return;let g=null,S=null,h=!1,M=null,C=null;const q=v=>({path:v.SymbolPath.CIRCLE,scale:7,fillColor:Y(o),fillOpacity:1,strokeColor:Y(o),strokeOpacity:.25,strokeWeight:10}),ae=async()=>{C=ye(()=>{h||$(I)});try{const v=await ve(b);if(h||!z.current)return;g=new v.Map(z.current,{center:{lat:s,lng:f},zoom:ze,disableDefaultUI:!0,gestureHandling:"cooperative",keyboardShortcuts:!1,clickableIcons:!1,backgroundColor:B(o),...x?{mapId:x}:{styles:J(o)}}),S=new v.Marker({map:g,position:{lat:s,lng:f},icon:q(v)}),g.addListener("tilesloaded",()=>{C?.ready(),h||D(!0)}),o==="auto"&&!x&&(M=be(()=>{h||!g||(g.setOptions({styles:J(o),backgroundColor:B(o)}),S?.setIcon(q(v)),r(B(o)))}))}catch(v){console.error("[StudioMap] Maps SDK failed:",v),C?.stop(),h||$(I)}},G=new IntersectionObserver(v=>{for(const oe of v)if(oe.isIntersecting){G.disconnect(),ae();break}},{rootMargin:"200px"});return G.observe(m),()=>{h=!0,G.disconnect(),C?.stop(),M?.()}},[_,b,s,f,x,o,I]);const k=w||(p?`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent([p,a,y].filter(Boolean).join(" "))}`:""),n=i("div",{className:["absolute left-5 top-5 z-10 flex max-w-[64%] flex-col gap-1.5 border p-3.5",d?"":"border-line bg-bg shadow-[0_8px_24px_rgba(0,0,0,.4)]"].join(" "),style:d?{backgroundColor:u.info.bg,borderColor:`color-mix(in oklab, ${u.info.fgDim} 35%, transparent)`,boxShadow:u.info.shadow}:void 0,children:[i("span",{className:"font-display text-[length:var(--size-body,14px)] font-medium tracking-[-0.01em]",style:d?{color:u.info.fg}:void 0,children:[p||"Location",j?`, ${j}`:""]}),k&&t("a",{href:k,target:"_blank",rel:"noopener noreferrer",className:["font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.24em] transition-colors",d?"":"text-accent hover:text-fg"].join(" "),style:d?{color:u.info.accent}:void 0,children:"Get Directions ↗"})]});if(!_||L){const m=d?`linear-gradient(135deg, ${u.background}, ${u.info.bg})`:"linear-gradient(135deg, var(--site-surface-2), var(--site-surface))";return i("div",{"data-loc-pick":N,className:["relative overflow-hidden max-[1100px]:aspect-square",d?"":"bg-bg",...N?["loc-part-pick"]:[]].join(" "),"data-selected":c?"":void 0,style:{...d?{backgroundColor:u.background}:null,backgroundImage:`repeating-linear-gradient(45deg, rgba(201,164,97,.04) 0 1px, transparent 1px 14px), repeating-linear-gradient(-45deg, rgba(201,164,97,.04) 0 1px, transparent 1px 14px), ${m}`},children:[n,l&&t("div",{className:["absolute inset-0 flex items-center justify-center px-6 text-center font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.3em]",d?"":"text-fg-dim/40"].join(" "),style:d?{color:u.info.fgDim,opacity:.4}:void 0,children:b?L?"The map could not load. Check the key":"Add latitude & longitude":"Add a Google Maps API key"})]})}return i("div",{ref:A,"data-loc-pick":N,className:["studio-map relative overflow-hidden max-[1100px]:aspect-square",...N?["loc-part-pick"]:[]].join(" "),"data-selected":c?"":void 0,style:{backgroundColor:e},children:[t("div",{ref:z,className:"gmap-canvas absolute inset-0"}),t("div",{className:["pointer-events-none absolute inset-0 flex items-center justify-center font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.3em] text-fg-dim transition-opacity duration-500",H?"opacity-0":"opacity-100"].join(" "),children:"Loading map…"}),n]})}export{_e as default};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
"use client";import{jsx as e,jsxs as o,Fragment as
|
|
1
|
+
"use client";import{jsx as e,jsxs as o,Fragment as E}from"react/jsx-runtime";import{useEffect as T,useState as f}from"react";import u from"./PartStyledKind.js";import{renderGold as y,renderHeadline as M}from"./goldPhrases.js";import{PART_KIND_ATTR as h}from"../lib/puck/text-parts.js";import{TEXT_KIND_NAME as g,TEXT_KIND_VALUE as v}from"../lib/sections/section-text-kinds.js";const c={headingSmall:"text-[length:min(var(--size-headingSmall,32px),var(--sf-heading-max,9999px))]",labelSmall:"text-[length:var(--size-label,11px)]",labelLarge:"text-[length:var(--size-label,12px)]"},D=24*3600*1e3,I=[{key:"hours",label:"Hours"},{key:"minutes",label:"Minutes"},{key:"seconds",label:"Seconds"}];function L(t,n){return Math.max(0,Math.ceil((t-n)/1e3))}function $(t){const n=Date.parse((t??"").trim());return Number.isFinite(n)?n:null}const z=t=>String(t).padStart(2,"0");function A({title:t,zeroText:n,endsAt:w,showUnits:N=!0,editing:i=!1}={}){const b=$(w),[S]=f(()=>Date.now()+D),l=b??(i?S:null),[_,m]=f(()=>Date.now());if(T(()=>{if(l===null)return;m(Date.now());const a=window.setInterval(()=>m(Date.now()),1e3);return()=>window.clearInterval(a)},[l]),l===null)return null;const s=L(l,_),r=[Math.floor(s/3600),Math.floor(s%3600/60),s%60],d=(t??"").trim(),p=(n??"").trim(),k=`${r[0]} hours, ${r[1]} minutes and ${r[2]} seconds left`;return e("section",{className:"relative z-[2] bg-bg",role:"timer","aria-live":"off",children:o("div",{className:"mx-auto flex max-w-[var(--sf-site-width,1600px)] flex-col items-center gap-4 px-[var(--sf-site-gutter,40px)] py-12 text-center max-[960px]:px-[min(24px,var(--sf-site-gutter,40px))]",children:[d&&e("p",{className:`font-mono ${c.labelLarge} uppercase tracking-[0.2em] text-fg-dim`,children:y(d)}),s===0?p&&e("p",{className:`font-heading3 ${c.headingSmall} font-medium leading-tight text-fg`,children:M(p,!1)}):o(E,{children:[e("span",{className:"sr-only",children:k}),e("div",{"aria-hidden":!0,className:"flex items-start gap-[clamp(8px,2vw,24px)]",children:I.map((a,x)=>o("div",{className:"contents",children:[x>0&&e("span",{className:"font-mono text-[length:clamp(32px,7vw,80px)] leading-none text-accent",children:":"}),o("div",{className:"flex min-w-0 flex-col items-center gap-2",children:[e(u,{kind:v,children:e("span",{"data-countdown-unit":a.key,...i?{[h]:v}:{},suppressHydrationWarning:!0,className:"font-mono text-[length:clamp(40px,8vw,96px)] font-medium leading-none tabular-nums text-fg",children:z(r[x])})}),N&&e(u,{kind:g,children:e("span",{...i?{[h]:g}:{},className:`font-mono ${c.labelSmall} uppercase tracking-[0.2em] text-fg-dim`,children:a.label})})]})]},a.key))})]})]})})}export{A as default,L as secondsLeft};
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { type MapThemeMode } from "../lib/map-styles.js";
|
|
2
|
+
import { type DealerListPosition } from "../lib/dealer-source.js";
|
|
3
|
+
export default function DealerFinder({ mapLabel, mapTheme, search, searchHint, noMatchText, listPosition, showDistance, editing, }: {
|
|
4
|
+
mapLabel: string;
|
|
5
|
+
mapTheme: MapThemeMode;
|
|
6
|
+
search: boolean;
|
|
7
|
+
searchHint: string;
|
|
8
|
+
noMatchText: string;
|
|
9
|
+
listPosition: DealerListPosition;
|
|
10
|
+
showDistance: boolean;
|
|
11
|
+
editing?: boolean;
|
|
12
|
+
}): import("react").JSX.Element | null;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"use client";import{jsx as n,jsxs as a}from"react/jsx-runtime";import{useEffect as C,useMemo as y,useRef as V,useState as c}from"react";import{EntityMap as W}from"efficient-shell/map";import{mapModeIsLight as N,watchMapTheme as G}from"../lib/map-styles.js";import{dealerDistanceLabel as Q,dealerPins as Y,parseDealerListPosition as J,useDealerSource as U}from"../lib/dealer-source.js";import{groupColsClass as X}from"../lib/locations-grid.js";import{say as Z}from"../lib/editor-words.js";const D={near:null,dealers:[]},H="max-h-[480px] overflow-y-auto overscroll-contain",ee=5,te="h-[480px] max-[800px]:h-[320px]",ne={"--rosh-map-pin":"var(--accent)","--rosh-map-pin-text":"var(--bg)","--rosh-map-pin-edge":"var(--bg)","--rosh-map-ring":"var(--fg)","--rosh-map-radius":"0px","--rosh-map-font":"var(--font-role-label)"};function ae(t){const[s,o]=c(()=>N(t));return C(()=>(o(N(t)),G(()=>o(N(t)))),[t]),s?"light":"dark"}function re({mapLabel:t,mapTheme:s,search:o,searchHint:l,noMatchText:f,listPosition:u,showDistance:E,editing:I}){const d=U(),[r,x]=c(D),[w,z]=c(""),[T,S]=c(!1),[k,g]=c(null),[A,B]=c(null),b=V(null),P=ae(s);C(()=>{if(!d)return;let e=!0;return d({}).then(i=>e&&x(i)).catch(()=>e&&x(D)),()=>{e=!1}},[d]);const p=y(()=>Y(r.dealers),[r.dealers]),R=y(()=>new Set(p.map(e=>e.key)),[p]),K=y(()=>r.near==="found"?p.slice(0,ee).map(e=>e.key):null,[r.near,p]),O=e=>{g(e),R.has(e)&&B(i=>({key:e,n:(i?.n??0)+1}))},$=e=>{g(e),b.current?.querySelector(`[data-dealer="${CSS.escape(e)}"]`)?.scrollIntoView({block:"nearest"})};if(!d)return I?n("p",{"data-dealers-unavailable":!0,className:"font-mono text-[length:var(--size-label,12px)] uppercase tracking-[0.24em] text-fg-dim",children:Z("Dealers are not available on this website")}):null;const q=e=>{e.preventDefault();const i=w.trim();S(!0),g(null),d(i?{near:i}:{}).then(x).catch(()=>x(F=>({...F,near:"try_again"}))).finally(()=>S(!1))},m=J(u),_=m!=="below",v=r.near==="not_found"||r.near==="try_again",j=o?a("form",{role:"search",onSubmit:q,className:"mb-4 flex gap-2",children:[n("input",{type:"search",value:w,onChange:e=>z(e.target.value),placeholder:l,"aria-label":l,"aria-invalid":v||void 0,className:["min-w-0 flex-1 border bg-bg px-3 py-2 text-[length:var(--size-body,14px)] text-fg outline-none",v?"border-accent":"border-line focus:border-fg"].join(" ")}),n("button",{type:"submit","aria-label":l,disabled:T,className:"grid w-10 shrink-0 place-items-center bg-fg text-bg disabled:opacity-50",children:a("svg",{"aria-hidden":!0,width:"16",height:"16",viewBox:"0 0 24 24",fill:"none",stroke:"currentColor",strokeWidth:"2",children:[n("circle",{cx:"11",cy:"11",r:"7"}),n("path",{d:"M20 20l-3.5-3.5"})]})})]}):null,L=v&&f?n("p",{role:"status",className:"mb-3 text-[length:var(--size-body,14px)] text-fg-dim",children:f}):null,h=r.dealers.map(e=>n(se,{dealer:e,active:k===e.id,onSelect:()=>O(e.id),distance:E?Q(e.distanceKm):"",tile:!_},e.id)),M=n("div",{style:ne,children:n(W,{pins:p,selectedKey:k,focus:A,onSelect:$,ariaLabel:t,className:te,groupCrowded:!0,fitTo:K,look:"site",theme:P,cooperativeGestures:"coarse"})});return _?a("div",{"data-dealer-finder":m,className:["grid items-start gap-6 max-[800px]:grid-cols-1",m==="left"?"grid-cols-[minmax(260px,360px)_1fr]":"grid-cols-[1fr_minmax(260px,360px)]"].join(" "),children:[a("div",{className:["min-w-0",m==="right"?"order-2":"","max-[800px]:order-2"].join(" "),children:[j,L,n("div",{ref:b,className:[H,"max-[800px]:max-h-none"].join(" "),children:h})]}),n("div",{className:["min-w-0",m==="right"?"order-1":"","max-[800px]:order-1"].join(" "),children:M})]}):a("div",{"data-dealer-finder":"below",children:[j,L,M,h.length>0&&n("div",{ref:b,className:["mt-12 grid gap-[2px] bg-bg",X(h.length),h.length>2?"max-[1100px]:grid-cols-2 max-[700px]:grid-cols-1":"max-[700px]:grid-cols-1"].join(" "),children:h})]})}function se({dealer:t,active:s,onSelect:o,distance:l,tile:f}){const u=[t.city,[t.state,t.postcode].filter(Boolean).join(" ")].filter(Boolean).join(" ");return a("div",{"data-dealer":t.id,className:["flex gap-3 bg-bg text-left outline outline-1 -outline-offset-1",f?"px-6 py-5":"border-b border-line px-1 py-4",s?"outline-accent":"outline-transparent hover:outline-line"].join(" "),children:[a("button",{type:"button",onClick:o,"aria-pressed":s,className:"min-w-0 flex-1 text-left",children:[n("span",{className:"block font-display text-[length:var(--size-body,16px)] font-medium leading-[1.25] text-fg",children:t.name}),t.address&&n("span",{className:"mt-1 block text-[length:var(--size-body,14px)] text-fg-dim",children:t.address}),u&&n("span",{className:"block text-[length:var(--size-body,14px)] text-fg-dim",children:u})]}),a("span",{className:"flex shrink-0 flex-col items-end gap-1 text-[length:var(--size-body,14px)]",children:[l&&n("span",{className:"text-fg-dim",children:l}),t.phone&&n("a",{href:`tel:${t.phone.replace(/[^\d+]/g,"")}`,className:"text-fg underline underline-offset-2",children:t.phone}),t.website&&n("a",{href:t.website,target:"_blank",rel:"noopener noreferrer",className:"text-fg underline underline-offset-2",children:t.website.replace(/^https?:\/\//,"").replace(/\/$/,"")})]})]})}export{re as default};
|