@v-office/website-headless 0.2.0 → 1.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/README.md +2177 -55
- package/dist/enhance/index.d.mts +501 -180
- package/dist/enhance/index.mjs +1388 -472
- package/dist/index-CPMXjfFH.d.mts +2724 -0
- package/dist/index.d.mts +2614 -1181
- package/dist/index.mjs +8568 -1739
- package/dist/parts-CS1VBrPt.mjs +648 -0
- package/dist/sdk-claim-CsYFzvEc.mjs +72 -0
- package/dist/sdk-create-Bsd9lqj_.mjs +9 -0
- package/instructions/CHANGELOG.md +1 -0
- package/instructions/MIGRATION.md +1 -0
- package/instructions/versions/1.0.0/CHANGELOG.md +170 -0
- package/instructions/versions/1.0.0/MIGRATION.md +252 -0
- package/instructions/versions/1.0.0/release.json +10 -0
- package/package.json +6 -4
package/dist/enhance/index.d.mts
CHANGED
|
@@ -1,184 +1,505 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
* with scroll-snap on the track on touch devices, or scroll-snap
|
|
6
|
-
* none + JS-driven scrollTo on hover-capable devices).
|
|
7
|
-
*
|
|
8
|
-
* Native scroll matters for two reasons:
|
|
9
|
-
* 1. Lazy-loaded `<img loading="lazy">` only triggers load when
|
|
10
|
-
* the image enters the visible viewport via real scroll. With
|
|
11
|
-
* transform-based shifting, the browser doesn't recognize the
|
|
12
|
-
* image as "in view", so subsequent images stay un-loaded
|
|
13
|
-
* (white squares) when the user clicks next.
|
|
14
|
-
* 2. The scroll-snap behavior native scroll provides on touch is
|
|
15
|
-
* what the iOS swipe momentum/snap relies on; transform-based
|
|
16
|
-
* shifting can't reproduce it.
|
|
17
|
-
*
|
|
18
|
-
* Dots are optional and only updated when present. Their accessible label
|
|
19
|
-
* follows `data-label-template` on the dots list (`{count}` images,
|
|
20
|
-
* `{current}` shown) — the component renders the template from its copy, so
|
|
21
|
-
* the enhancer carries no language of its own.
|
|
22
|
-
*/
|
|
23
|
-
export declare function initImageCarousels(selector?: string): void;
|
|
24
|
-
export declare function initRentalCarousel(viewport: HTMLElement): void;
|
|
1
|
+
import { An as LngLat, Mn as MapPoint, _a as EnhancePart, a as PlaceMarker, ba as GateMarkers, ga as ENHANCE_PARTS, jn as MapBounds, s as ClusterOptions, va as EnhanceParts, wn as SameSpotLayout, ya as GATE_MARKERS } from "../index-CPMXjfFH.mjs";
|
|
2
|
+
//#region src/enhance/lifecycle.d.ts
|
|
3
|
+
/** Removes everything a binder added. Safe to call more than once. */
|
|
4
|
+
type Dispose = () => void;
|
|
25
5
|
//#endregion
|
|
26
|
-
//#region src/enhance/
|
|
27
|
-
/**
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
*
|
|
34
|
-
*
|
|
35
|
-
*
|
|
36
|
-
*
|
|
37
|
-
*
|
|
38
|
-
*
|
|
39
|
-
*
|
|
40
|
-
*
|
|
41
|
-
* `data-
|
|
42
|
-
*
|
|
43
|
-
*
|
|
44
|
-
*
|
|
45
|
-
*
|
|
46
|
-
* the
|
|
47
|
-
*
|
|
48
|
-
*
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
*
|
|
64
|
-
*
|
|
65
|
-
*
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
*
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
6
|
+
//#region src/enhance/favorites.d.ts
|
|
7
|
+
/** Which Merkliste the hearts write to. */
|
|
8
|
+
interface FavoritesBinding {
|
|
9
|
+
/** `favoritesStorageKey(namespace)`: the site's one key (no default, so no guest loses a list). */
|
|
10
|
+
storageKey: string;
|
|
11
|
+
}
|
|
12
|
+
/** Bind every heart and badge under `root` (enhance() passes its root; late-rendered hearts are painted as
|
|
13
|
+
* they appear):
|
|
14
|
+
* - a heart is `[data-fav]` or `[data-part="favorite"]`; its rental id is `data-fav-id`, else the enclosing
|
|
15
|
+
* `[data-result-card]`'s value (a heart without an id does nothing). It gets `aria-pressed` and
|
|
16
|
+
* `data-saved`; every heart of one rental repaints together (a gallery's mobile and desktop copies);
|
|
17
|
+
* - a click toggles the rental, unless another handler already took the click (`defaultPrevented`: a React
|
|
18
|
+
* heart toggles the same store itself). Either way the pressed heart's glyph (its `svg`, else the button)
|
|
19
|
+
* gets `data-fav-burst="save|remove"` until its animation ends: a press is an event, so this is not CSS on
|
|
20
|
+
* `aria-pressed` (that would burst every saved heart on page load);
|
|
21
|
+
* - a badge is `[data-fav-badge]` or `[data-part="favorite-badge"]`: its text is the saved count, it
|
|
22
|
+
* carries `data-active="true"` while the count is above 0 and no `data-active` at 0 (hidden at 0: a
|
|
23
|
+
* "0" badge reads as broken; the kits wrote the value, so both `[data-active]` and
|
|
24
|
+
* `[data-active="true"]` match) and `data-fav-bump="on"` after each change; its `aria-label` is
|
|
25
|
+
* `data-template` ({count}), or `data-template-one` for one;
|
|
26
|
+
* - every change in the store, whoever made it (another tab included, through the `storage` event),
|
|
27
|
+
* dispatches FAVORITES_EVENT (`merkliste:changed`, `{ id, saved }`) on window for site scripts.
|
|
28
|
+
* Binding a root twice is a no-op. */
|
|
29
|
+
type BindFavorites = (root: ParentNode, options: FavoritesBinding) => Dispose;
|
|
30
|
+
/** See {@link BindFavorites}. */
|
|
31
|
+
export declare const bindFavorites: BindFavorites;
|
|
32
|
+
//#endregion
|
|
33
|
+
//#region src/enhance/share.d.ts
|
|
34
|
+
/** Where the shared link points. */
|
|
35
|
+
interface ShareOptions {
|
|
36
|
+
/** Canonical origin. Default: the button's `data-share-origin` (the site's configured origin), else the
|
|
37
|
+
* page's own origin. */
|
|
38
|
+
origin?: string;
|
|
39
|
+
/** Query parameters to drop besides the tracking parameters. */
|
|
40
|
+
strip?: readonly string[];
|
|
41
|
+
}
|
|
42
|
+
/** Bind one `[data-part="share"]` (or legacy `[data-share]`) button. The native share sheet opens on
|
|
43
|
+
* touch-primary devices only (a desktop visitor clicking "share" wants the link, not a system dialog);
|
|
44
|
+
* elsewhere the link goes to the clipboard, and where that is blocked a prompt shows it to copy by hand.
|
|
45
|
+
* A dismissed share sheet is a decision, not a failure: nothing else happens. After a copy the button
|
|
46
|
+
* carries `data-state="done"` for 2 s, its `[data-share-idle]` / `[data-share-done]` children swap
|
|
47
|
+
* (`hidden`), and the `[data-part="share-status"]` (or `[data-share-live]`) live region announces its
|
|
48
|
+
* `data-message` (else the done label's text). The title is `data-share-title`, else the document's.
|
|
49
|
+
* Binding twice is a no-op. */
|
|
50
|
+
type BindShare = (button: HTMLElement, options?: ShareOptions) => Dispose;
|
|
51
|
+
/** See {@link BindShare}. */
|
|
52
|
+
export declare const bindShare: BindShare;
|
|
53
|
+
//#endregion
|
|
54
|
+
//#region src/enhance/enhance.d.ts
|
|
55
|
+
/** Which engine families enhance() binds. Each is on by default; pass false where the site binds one
|
|
56
|
+
* itself. Favorites need the site's storage key and are off without it. */
|
|
57
|
+
interface EnhanceOptions {
|
|
58
|
+
/** `[data-part="strip"]` and the legacy `.card-images` strips (bound when the browser is idle: binding
|
|
59
|
+
* every row and card at once was a long task in front of the first photo's paint). */
|
|
60
|
+
strips?: boolean;
|
|
61
|
+
/** `[data-part="rental-gallery"]` galleries with their strips and layers. */
|
|
62
|
+
galleries?: boolean;
|
|
63
|
+
/** `<dialog data-part="layer">` layers (description, amenities, any site dialog on the contract). */
|
|
64
|
+
layers?: boolean;
|
|
65
|
+
/** `[data-part="reveal"]` "show more" lists. */
|
|
66
|
+
reveals?: boolean;
|
|
67
|
+
/** `[data-part="clamp"]` line-clamped texts. */
|
|
68
|
+
clamps?: boolean;
|
|
69
|
+
/** `[data-part="share"]` / `[data-share]` buttons; an object sets their options. */
|
|
70
|
+
share?: boolean | ShareOptions;
|
|
71
|
+
/** The lazy-photo fade inside every gallery. */
|
|
72
|
+
imageFade?: boolean;
|
|
73
|
+
/** `{ storageKey }` binds the `[data-fav]` hearts and badges below the root; false or absent skips. */
|
|
74
|
+
favorites?: false | FavoritesBinding;
|
|
75
|
+
}
|
|
76
|
+
/** Bind everything below `root` (default `document`). Idempotent: an element already bound (by an earlier
|
|
77
|
+
* call or a hydrated component's useEnhance) is left alone and stays its first binder's, so a second call
|
|
78
|
+
* binds only what is new and its Dispose unbinds only that. */
|
|
79
|
+
type Enhance = (root?: ParentNode, options?: EnhanceOptions) => Dispose;
|
|
80
|
+
/** See {@link Enhance}. */
|
|
81
|
+
export declare const enhance: Enhance;
|
|
82
|
+
//#endregion
|
|
83
|
+
//#region src/enhance/layers.d.ts
|
|
84
|
+
/** How one dialog behaves. */
|
|
85
|
+
interface LayerOptions {
|
|
86
|
+
/** The URL hash while open. Default: the dialog's id (one of the two is required; binding throws without). */
|
|
87
|
+
name?: string;
|
|
88
|
+
/** The inner scroller, reset to the top before the dialog closes (after closing it has no box, so a reset
|
|
89
|
+
* then is a silent no-op and the layer reopens where it was left). Default: its `[data-part="layer-body"]`. */
|
|
90
|
+
scroller?: HTMLElement | null;
|
|
91
|
+
/** Close on a click that lands on the dialog's own box, outside its card. Default true. */
|
|
92
|
+
closeOnBackdrop?: boolean;
|
|
93
|
+
/** After opening; receives the element that opened it (the gallery reads its photo index). */
|
|
94
|
+
onOpen?: (opener: HTMLElement | null) => void;
|
|
95
|
+
/** Before closing, while layout can still be read (the viewer's current photo). */
|
|
96
|
+
onClose?: () => void;
|
|
97
|
+
}
|
|
98
|
+
/** A bound dialog. */
|
|
99
|
+
interface LayerHandle {
|
|
100
|
+
/** Open (no-op when open), pushing the layer's history entry. */
|
|
101
|
+
open(opener?: HTMLElement | null): void;
|
|
102
|
+
/** Close through history.back() when the layer's entry is on top, else directly. */
|
|
103
|
+
close(): void;
|
|
104
|
+
/** Whether the layer is open. */
|
|
105
|
+
readonly isOpen: boolean;
|
|
106
|
+
/** Remove every listener; an open layer closes without touching history (the page is being left). */
|
|
107
|
+
dispose: Dispose;
|
|
108
|
+
}
|
|
109
|
+
/** Bind one dialog. Openers are `[data-part="layer-open"][aria-controls=<dialog id>]` anywhere in the
|
|
110
|
+
* document; closers are `[data-part="layer-close"]` inside it. Body scroll is locked while any layer is
|
|
111
|
+
* open; focus moves in (to the first closer) and back only after keyboard input. Binding a dialog twice
|
|
112
|
+
* returns the first handle. */
|
|
113
|
+
type BindLayer = (dialog: HTMLDialogElement, options?: LayerOptions) => LayerHandle;
|
|
114
|
+
/** See {@link BindLayer}. */
|
|
115
|
+
export declare const bindLayer: BindLayer;
|
|
116
|
+
//#endregion
|
|
117
|
+
//#region src/enhance/strip.d.ts
|
|
118
|
+
/** How a strip steps and ends. Each option may also come from the markup: `data-paging`, `data-edges`
|
|
119
|
+
* on the strip root; an option passed here wins over the markup. */
|
|
120
|
+
interface StripOptions {
|
|
121
|
+
/** "page": every item is the track's width (card photos, the mobile gallery, the viewer).
|
|
122
|
+
* "snap": items of any width, stepped to the nearest item start (rental rows, review cards). Default "page". */
|
|
123
|
+
paging?: "page" | "snap";
|
|
124
|
+
/** "stop": the arrows disable at the ends (the strip root carries `data-engaged` once first scrolled, so
|
|
125
|
+
* CSS can hide a disabled arrow until then and grey it in place after; a click cannot fall through to
|
|
126
|
+
* the card link where an arrow was). "wrap": next after the last returns to the first. Default "stop". */
|
|
127
|
+
edges?: "stop" | "wrap";
|
|
128
|
+
/** Mouse drag (2 px threshold) with a suppressor for the click that ends it. Default: paging === "snap". */
|
|
129
|
+
drag?: boolean;
|
|
130
|
+
/** Hand the next item its `data-src` / `data-srcset` on pointer, touch, focus or scroll intent, so photos
|
|
131
|
+
* 2–5 of a card are not fetched at page start (the browser's lazy-load distance applies sideways too).
|
|
132
|
+
* Default true. */
|
|
133
|
+
deferSlides?: boolean;
|
|
134
|
+
}
|
|
135
|
+
/** A bound strip. */
|
|
136
|
+
interface StripHandle {
|
|
137
|
+
/** The current item among the items that have a box. */
|
|
138
|
+
readonly index: number;
|
|
139
|
+
/** The number of items that have a box (a hidden tile does not count). */
|
|
140
|
+
readonly total: number;
|
|
141
|
+
/** Scroll to an item (clamped). "instant" overrides a CSS `scroll-behavior: smooth` (an assignment under
|
|
142
|
+
* smooth scrolling silently never moved a strip); "smooth" becomes instant under reduced motion.
|
|
143
|
+
* Default: "instant" for page strips, "smooth" for snap strips. A strip without a box ignores it. */
|
|
144
|
+
goTo(index: number, how?: "instant" | "smooth"): void;
|
|
145
|
+
/** One item back or forward, wrapping when `edges` is "wrap". */
|
|
146
|
+
step(delta: 1 | -1): void;
|
|
147
|
+
/** Back to the start (a tab switch swapped the items). */
|
|
148
|
+
reset(): void;
|
|
149
|
+
/** Remove every listener and observer. */
|
|
150
|
+
dispose: Dispose;
|
|
151
|
+
}
|
|
152
|
+
/** Bind one strip: `root` is a `[data-part="strip"]` (or the legacy `.card-images`) holding a
|
|
153
|
+
* `[data-part="strip-track"]` (legacy `[data-img-track]`), the horizontal scroller, and optional
|
|
154
|
+
* `strip-prev` / `strip-next` (`data-img-prev` / `data-img-next`), `strip-dots` (`data-dots`, one child
|
|
155
|
+
* per item) and `strip-counter`. Items are the track's `[data-part="strip-item"]`s, else its children.
|
|
156
|
+
* The engine writes state attributes only, never a class: `data-current` on the current item,
|
|
157
|
+
* `data-active` on its dot, `disabled` on the arrows, `data-engaged` / `data-dragging` on the root, the
|
|
158
|
+
* dots' `aria-label` and the counter's text from their `data-template` ({current}, {count}). Binding
|
|
159
|
+
* waits until the track has children; binding a strip twice returns the first handle. */
|
|
160
|
+
type BindStrip = (root: HTMLElement, options?: StripOptions) => StripHandle;
|
|
161
|
+
/** See {@link BindStrip}. */
|
|
162
|
+
export declare const bindStrip: BindStrip;
|
|
97
163
|
//#endregion
|
|
98
164
|
//#region src/enhance/gallery.d.ts
|
|
99
|
-
/**
|
|
100
|
-
*
|
|
101
|
-
*
|
|
102
|
-
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
110
|
-
*
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
*
|
|
126
|
-
*
|
|
127
|
-
*
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
|
|
165
|
+
/** Bind one `[data-part="rental-gallery"]`:
|
|
166
|
+
* - every strip inside it (the mobile strip, the viewer, paged floor plans) as a page strip;
|
|
167
|
+
* - its `<dialog>`s `rental-gallery-grid`, `rental-gallery-viewer`, `rental-floorplan` as layers;
|
|
168
|
+
* any element inside the gallery whose `aria-controls` names one of them opens it (mosaic tiles,
|
|
169
|
+
* "all photos", grid photos, strip photos, the floor-plan button); an opener of the viewer carries
|
|
170
|
+
* `data-index` (0-based photo), else the viewer opens on the photo the mobile strip shows;
|
|
171
|
+
* - the viewer lands on that photo once it has a width (up to 5 frames, never on photo 1 by accident),
|
|
172
|
+
* steps with the arrow keys, and on close the mobile strip returns to the photo last viewed;
|
|
173
|
+
* - the floor-plan images publish their natural ratio as `--fp-ar`, and the floor plan is warmed on the
|
|
174
|
+
* first hover or focus of its opener (srcset before src, so the warm-up fetches what the dialog shows).
|
|
175
|
+
* The per-photo caption and counter follow the photo, not the scroll event: the strip marks the photo
|
|
176
|
+
* more than half on screen with `data-current`. Binding twice is a no-op. */
|
|
177
|
+
type BindGallery = (root: HTMLElement) => Dispose;
|
|
178
|
+
/** See {@link BindGallery}. */
|
|
179
|
+
export declare const bindGallery: BindGallery;
|
|
180
|
+
//#endregion
|
|
181
|
+
//#region src/enhance/reveal.d.ts
|
|
182
|
+
/** How a reveal list grows. */
|
|
183
|
+
interface RevealOptions {
|
|
184
|
+
/** Items each "more" press reveals. Default: all that remain. The markup may say `data-step` on the root. */
|
|
185
|
+
step?: number;
|
|
186
|
+
/** A `reveal-less` control hides the revealed items again. Default: true when the markup renders a
|
|
187
|
+
* `[data-part="reveal-less"]` (a rendered control that could never work would be a dead button). */
|
|
188
|
+
collapsible?: boolean;
|
|
189
|
+
}
|
|
190
|
+
/** Bind one `[data-part="reveal"]`: its `[data-part="reveal-item"][hidden]` items are revealed by
|
|
191
|
+
* `[data-part="reveal-more"]`, which hides itself when nothing remains; `reveal-less` (shown once something
|
|
192
|
+
* is revealed) hides them again. The root carries `data-open` while revealed items show. Focus lands on the
|
|
193
|
+
* first revealed item for keyboard users. Binding twice is a no-op. */
|
|
194
|
+
type BindReveal = (root: HTMLElement, options?: RevealOptions) => Dispose;
|
|
195
|
+
/** Bind one `[data-part="clamp"]`: `[data-part="clamp-toggle"]` shows only while
|
|
196
|
+
* `[data-part="clamp-text"]` overflows its line clamp (re-measured on resize) and toggles `data-expanded`
|
|
197
|
+
* on the root and `aria-expanded` on itself; the clamp itself is CSS on the root's state. Binding twice is
|
|
198
|
+
* a no-op. */
|
|
199
|
+
type BindClamp = (root: HTMLElement) => Dispose;
|
|
200
|
+
/** See {@link BindReveal}. */
|
|
201
|
+
export declare const bindReveal: BindReveal;
|
|
202
|
+
/** See {@link BindClamp}. */
|
|
203
|
+
export declare const bindClamp: BindClamp;
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/enhance/image-fade.d.ts
|
|
206
|
+
/** Bind one root (enhance() binds every `[data-part="rental-gallery"]`). The root carries `data-image-fade`
|
|
207
|
+
* while bound: that attribute arms the CSS that keeps an unmarked lazy image transparent, so without the
|
|
208
|
+
* engine every photo simply shows. A lazy image gets `data-loaded="fade"` when it lands, `"cache"` when it
|
|
209
|
+
* was already decoded at bind (nothing arrived, so nothing animates) or when it failed (a 404 must not stay
|
|
210
|
+
* invisible and take its alt text with it). One capture listener covers the photos inside closed layers,
|
|
211
|
+
* which only start fetching once their layer opens. Binding twice is a no-op. */
|
|
212
|
+
type BindImageFade = (root: HTMLElement) => Dispose;
|
|
213
|
+
/** See {@link BindImageFade}. */
|
|
214
|
+
export declare const bindImageFade: BindImageFade;
|
|
215
|
+
//#endregion
|
|
216
|
+
//#region src/enhance/motion.d.ts
|
|
217
|
+
/** Run `mutate` inside a view transition where the browser has one and the visitor has not asked for
|
|
218
|
+
* less motion; run it directly otherwise. The look (named groups, timing) is the site's stylesheet's.
|
|
219
|
+
* An aborted transition is expected (a hidden tab, a second transition) and swallowed: it says the
|
|
220
|
+
* fade did not happen, never that `mutate` did not run. */
|
|
221
|
+
type WithViewTransition = (mutate: () => void) => void;
|
|
222
|
+
/** Install the page's one pair of input-modality listeners (pointer vs keyboard). Reference-counted:
|
|
223
|
+
* every binder that moves focus claims it and releases it through the Dispose; the listeners go with
|
|
224
|
+
* the last release. */
|
|
225
|
+
type TrackInputModality = () => Dispose;
|
|
226
|
+
/** Focus `element` only when the last real input was the keyboard. Moving focus into a layer is right
|
|
227
|
+
* for a keyboard user and wrong for a thumb: iOS paints a ring on a tapped button after a programmatic
|
|
228
|
+
* focus, which `:focus-visible` cannot prevent. Needs `trackInputModality()` to have been claimed. */
|
|
229
|
+
type FocusForKeyboard = (element: HTMLElement) => void;
|
|
230
|
+
/** See {@link WithViewTransition}. */
|
|
231
|
+
export declare const withViewTransition: WithViewTransition;
|
|
232
|
+
/** See {@link TrackInputModality}. */
|
|
233
|
+
export declare const trackInputModality: TrackInputModality;
|
|
234
|
+
/** See {@link FocusForKeyboard}. */
|
|
235
|
+
export declare const focusForKeyboard: FocusForKeyboard;
|
|
236
|
+
//#endregion
|
|
237
|
+
//#region src/enhance/map/card-hover.d.ts
|
|
238
|
+
/** Delegated on the document, so it works with any grid (a site's own included) and survives every
|
|
239
|
+
* re-render: `onHover(id)` when the pointer or focus enters a `[data-result-card="<id>"]`, `onHover(null)`
|
|
240
|
+
* when it leaves every card or the window; called once per change. Focus counts like hover, so keyboard
|
|
241
|
+
* users get the same highlight. */
|
|
242
|
+
type ObserveResultCardHover = (onHover: (id: string | null) => void) => Dispose;
|
|
243
|
+
/** See {@link ObserveResultCardHover}. */
|
|
244
|
+
export declare const observeResultCardHover: ObserveResultCardHover;
|
|
245
|
+
//#endregion
|
|
246
|
+
//#region src/enhance/map/maplibre.d.ts
|
|
247
|
+
/** The library: its Map, Marker and Popup constructors. */
|
|
248
|
+
interface MapLibreLike {
|
|
249
|
+
/** `new maplibregl.Map(options)`. */
|
|
250
|
+
Map: new (options: Record<string, unknown> & {
|
|
251
|
+
container: HTMLElement;
|
|
252
|
+
}) => MapLike;
|
|
253
|
+
/** `new maplibregl.Marker(options)`. */
|
|
254
|
+
Marker: new (options: {
|
|
255
|
+
element: HTMLElement;
|
|
256
|
+
anchor?: "center" | "bottom";
|
|
257
|
+
offset?: readonly [number, number];
|
|
258
|
+
}) => MarkerLike;
|
|
259
|
+
/** `new maplibregl.Popup(options)`. */
|
|
260
|
+
Popup: new (options: Record<string, unknown>) => PopupLike;
|
|
261
|
+
}
|
|
262
|
+
/** A map instance. */
|
|
263
|
+
interface MapLike {
|
|
264
|
+
/** Subscribe to a map event ("load", "moveend", "render", "click", "zoomstart"). */
|
|
265
|
+
on(type: string, listener: (event?: unknown) => void): void;
|
|
266
|
+
/** Unsubscribe a listener `on` registered (the binders do on dispose: the caller owns the map and may
|
|
267
|
+
* keep it across rebinds). */
|
|
268
|
+
off(type: string, listener: (event?: unknown) => void): void;
|
|
269
|
+
/** Subscribe to the next occurrence of a map event. */
|
|
270
|
+
once(type: string, listener: () => void): void;
|
|
271
|
+
/** The visible area. */
|
|
272
|
+
getBounds(): {
|
|
273
|
+
getWest(): number;
|
|
274
|
+
getSouth(): number;
|
|
275
|
+
getEast(): number;
|
|
276
|
+
getNorth(): number;
|
|
277
|
+
};
|
|
278
|
+
/** The element the map renders into. */
|
|
279
|
+
getContainer(): HTMLElement;
|
|
280
|
+
/** Frame bounds (`[[w, s], [e, n]]`). */
|
|
281
|
+
fitBounds(bounds: unknown, options: Record<string, unknown>): void;
|
|
282
|
+
/** Animate the camera. */
|
|
283
|
+
easeTo(options: Record<string, unknown>): void;
|
|
284
|
+
/** One zoom step in. */
|
|
285
|
+
zoomIn(): void;
|
|
286
|
+
/** One zoom step out. */
|
|
287
|
+
zoomOut(): void;
|
|
288
|
+
/** Re-measure the container. */
|
|
289
|
+
resize(): void;
|
|
290
|
+
/** Destroy the map. */
|
|
291
|
+
remove(): void;
|
|
292
|
+
/** Add a source. */
|
|
293
|
+
addSource(id: string, source: Record<string, unknown>): void;
|
|
294
|
+
/** A source by id. */
|
|
295
|
+
getSource(id: string): GeoJsonSourceLike | undefined;
|
|
296
|
+
/** Add a style layer. */
|
|
297
|
+
addLayer(layer: Record<string, unknown>): void;
|
|
298
|
+
/** The source's features in the loaded tiles (a feature repeats across tiles). */
|
|
299
|
+
querySourceFeatures(id: string): ReadonlyArray<{
|
|
300
|
+
properties: Record<string, unknown>;
|
|
301
|
+
geometry: {
|
|
302
|
+
coordinates: unknown;
|
|
303
|
+
};
|
|
304
|
+
}>;
|
|
305
|
+
/** Whether the source's tiles are loaded. */
|
|
306
|
+
isSourceLoaded(id: string): boolean;
|
|
307
|
+
}
|
|
308
|
+
/** A GeoJSON source. */
|
|
309
|
+
interface GeoJsonSourceLike {
|
|
310
|
+
/** Replace the data. */
|
|
311
|
+
setData(data: unknown): void;
|
|
312
|
+
/** The zoom at which a cluster breaks apart. */
|
|
313
|
+
getClusterExpansionZoom(clusterId: number): Promise<number>;
|
|
314
|
+
/** The points inside a cluster. */
|
|
315
|
+
getClusterLeaves(clusterId: number, limit: number, offset: number): Promise<ReadonlyArray<{
|
|
316
|
+
properties: Record<string, unknown>;
|
|
317
|
+
}>>;
|
|
318
|
+
}
|
|
319
|
+
/** An HTML marker. */
|
|
320
|
+
interface MarkerLike {
|
|
321
|
+
/** Position it. */
|
|
322
|
+
setLngLat(at: readonly [number, number]): MarkerLike;
|
|
323
|
+
/** Pixel offset (folded into MapLibre's own transform). */
|
|
324
|
+
setOffset(offset: readonly [number, number]): MarkerLike;
|
|
325
|
+
/** Show it on a map. */
|
|
326
|
+
addTo(map: MapLike): MarkerLike;
|
|
327
|
+
/** Take it off the map. */
|
|
328
|
+
remove(): void;
|
|
329
|
+
}
|
|
330
|
+
/** A popup. */
|
|
331
|
+
interface PopupLike {
|
|
332
|
+
/** Position it. */
|
|
333
|
+
setLngLat(at: readonly [number, number]): PopupLike;
|
|
334
|
+
/** Its content. */
|
|
335
|
+
setDOMContent(node: Node): PopupLike;
|
|
336
|
+
/** Show it on a map. */
|
|
337
|
+
addTo(map: MapLike): PopupLike;
|
|
338
|
+
/** Close it. */
|
|
339
|
+
remove(): void;
|
|
340
|
+
}
|
|
341
|
+
/** Supplied by the caller: `() => import("maplibre-gl")`. Called at most once per page, however many maps
|
|
342
|
+
* build (the first loader handed in is the one used). */
|
|
343
|
+
type MapLibreLoader = () => Promise<MapLibreLike | {
|
|
344
|
+
default: MapLibreLike;
|
|
345
|
+
}>;
|
|
346
|
+
//#endregion
|
|
347
|
+
//#region src/enhance/map/place-markers.d.ts
|
|
348
|
+
/** The markers and how they look and speak. */
|
|
349
|
+
interface PlaceMarkersOptions {
|
|
350
|
+
/** One per spot (`mergePlaces` already joined places on one spot). */
|
|
351
|
+
markers: readonly PlaceMarker[];
|
|
352
|
+
/** "bottom" for a teardrop pin (its tip on the spot), "center" for a dot. */
|
|
353
|
+
anchor: "bottom" | "center";
|
|
354
|
+
/** Render each marker's body (`data-part="map-marker"`). */
|
|
355
|
+
render: (body: HTMLSpanElement, marker: PlaceMarker) => void;
|
|
356
|
+
/** Popup text per marker (the caller joins the names), shown on activation; null = a silent marker
|
|
357
|
+
* (a `div`, not a button). One popup at a time; a map click closes it. */
|
|
358
|
+
popup: ((marker: PlaceMarker) => string) | null;
|
|
359
|
+
}
|
|
360
|
+
/** Put the markers on the map; the Dispose takes them and the popup off. */
|
|
361
|
+
type BindPlaceMarkers = (map: MapLike, lib: MapLibreLike, options: PlaceMarkersOptions) => Dispose;
|
|
362
|
+
/** See {@link BindPlaceMarkers}. */
|
|
363
|
+
export declare const bindPlaceMarkers: BindPlaceMarkers;
|
|
364
|
+
//#endregion
|
|
365
|
+
//#region src/enhance/map/result-markers.d.ts
|
|
366
|
+
/** A marker on screen. The body carries `data-part` "map-pin" (with `data-id`), "map-cluster" or "map-group"
|
|
367
|
+
* (with `data-leaf-ids`, "|"-joined, for the list → map hover; a group also `data-expanded` while open). */
|
|
368
|
+
type ResultMarker = {
|
|
369
|
+
/** One rental. */
|
|
370
|
+
kind: "pin";
|
|
371
|
+
/** `p<id>`. */
|
|
372
|
+
key: string;
|
|
373
|
+
/** The rental id. */
|
|
374
|
+
id: string;
|
|
375
|
+
/** Render the pin here. */
|
|
376
|
+
body: HTMLSpanElement;
|
|
377
|
+
/** The element MapLibre positions (the button; put the accessible name here). */
|
|
378
|
+
shell: HTMLElement;
|
|
379
|
+
} | {
|
|
380
|
+
/** A cluster of the source (`c<cluster_id>`) or a same-spot group (`g<key>`). */
|
|
381
|
+
kind: "cluster" | "group";
|
|
382
|
+
/** `c<cluster_id>` / `g<key>`. */
|
|
383
|
+
key: string;
|
|
384
|
+
/** Rentals inside (a cluster sums its groups' sizes). */
|
|
385
|
+
count: number;
|
|
386
|
+
/** The rentals inside; a cluster's resolve asynchronously (empty until then, then onMarkers again). */
|
|
387
|
+
leafIds: readonly string[];
|
|
388
|
+
/** Render the bubble here. */
|
|
389
|
+
body: HTMLSpanElement;
|
|
390
|
+
/** The element MapLibre positions. */
|
|
391
|
+
shell: HTMLElement;
|
|
392
|
+
};
|
|
393
|
+
/** What the markers show and whom they tell. */
|
|
394
|
+
interface ResultMarkersOptions {
|
|
395
|
+
/** The pins (ResultsMapState.points). */
|
|
396
|
+
points: readonly MapPoint[];
|
|
397
|
+
/** Same-spot offsets and groups (ResultsMapState.layout). */
|
|
398
|
+
layout: SameSpotLayout;
|
|
399
|
+
/** false = no clustering. With `whenPriced`, a priced set clusters with those settings. */
|
|
400
|
+
cluster: false | ClusterOptions;
|
|
401
|
+
/** The pins carry prices (ResultsMapState.priced). */
|
|
402
|
+
priced: boolean;
|
|
403
|
+
/** The on-screen markers changed (a marker came or went, a cluster's leaves resolved); not called per frame. */
|
|
404
|
+
onMarkers: (markers: readonly ResultMarker[]) => void;
|
|
405
|
+
/** A pin was activated: a release within CLICK_TOLERANCE_PX of the press, or Enter/Space. */
|
|
406
|
+
onPin: (id: string) => void;
|
|
407
|
+
/** A group bubble was activated (the caller toggles it, then calls `expand`). */
|
|
408
|
+
onGroup: (key: string) => void;
|
|
409
|
+
/** A map click outside every marker, or a zoom start (a fanned group no longer sits on its spot). */
|
|
410
|
+
onBackground: () => void;
|
|
411
|
+
}
|
|
412
|
+
/** The bound markers. */
|
|
413
|
+
interface ResultMarkers {
|
|
414
|
+
/** New points or layout (favourites, campaign chips, a measured pin width). */
|
|
415
|
+
update(points: readonly MapPoint[], layout: SameSpotLayout, priced: boolean): void;
|
|
416
|
+
/** Fan one group out on its circle around its spot (and centre the map on it), or collapse (null). */
|
|
417
|
+
expand(groupKey: string | null, pinWidthPx: number): void;
|
|
418
|
+
/** Take every marker off the map and stop listening (the map itself stays the caller's). */
|
|
419
|
+
dispose(): void;
|
|
420
|
+
}
|
|
421
|
+
/** A clustered GeoJSON source (cluster counts sum group sizes through `clusterProperties`), an invisible
|
|
422
|
+
* circle layer so the source loads, and the HTML-marker diff on every `render`, keyed `c<cluster_id>` /
|
|
423
|
+
* `p<id>` / `g<key>` and deduped across tiles. A cluster activation eases to its expansion zoom. */
|
|
424
|
+
type BindResultMarkers = (map: MapLike, lib: MapLibreLike, options: ResultMarkersOptions) => ResultMarkers;
|
|
425
|
+
/** See {@link BindResultMarkers}. */
|
|
426
|
+
export declare const bindResultMarkers: BindResultMarkers;
|
|
427
|
+
//#endregion
|
|
428
|
+
//#region src/enhance/map/surface.d.ts
|
|
429
|
+
/** Moves shorter than this between press and release are a click, on the map (`clickTolerance`) AND on a
|
|
430
|
+
* pin: one number in two places, because two thresholds deciding the same thing disagreed (MapLibre's
|
|
431
|
+
* default 3 px started a drag that carried the pin away from under the pointer). Half a pin's width:
|
|
432
|
+
* the blur that aiming itself has. */
|
|
433
|
+
export declare const CLICK_TOLERANCE_PX: 12;
|
|
434
|
+
/** How a surface frames itself when it is built. */
|
|
435
|
+
type MapCamera = {
|
|
436
|
+
center: LngLat;
|
|
437
|
+
zoom: number;
|
|
438
|
+
} |
|
|
439
|
+
/** A thunk: a surface built later frames what exists by then. */
|
|
440
|
+
{
|
|
441
|
+
bounds: () => MapBounds;
|
|
442
|
+
padding: number;
|
|
443
|
+
maxZoom: number;
|
|
444
|
+
};
|
|
445
|
+
/** One surface. */
|
|
446
|
+
interface MapSurfaceOptions {
|
|
447
|
+
/** The element MapLibre renders into. */
|
|
448
|
+
container: HTMLElement;
|
|
449
|
+
/** `() => import("maplibre-gl")`. */
|
|
450
|
+
load: MapLibreLoader;
|
|
451
|
+
/** Vector-tile style URL. */
|
|
452
|
+
styleUrl: string;
|
|
453
|
+
/** The first frame. */
|
|
454
|
+
camera: MapCamera;
|
|
455
|
+
/** Wheel over the map zooms it instead of scrolling the page. */
|
|
456
|
+
scrollZoom: boolean;
|
|
457
|
+
/** "near": build once the container is within `nearMargin` of the viewport AND has a real width.
|
|
458
|
+
* "demand": build on `build()` only (layers, click-to-load). */
|
|
459
|
+
start: "near" | "demand";
|
|
460
|
+
/** IntersectionObserver rootMargin. Default "100% 0px" (one screen of lead). */
|
|
461
|
+
nearMargin?: string;
|
|
462
|
+
/** The map has loaded its style: put contents on it. Runs once per surface. */
|
|
463
|
+
onReady?: (map: MapLike, lib: MapLibreLike) => void;
|
|
464
|
+
/** After every moveend and every size change; a 0×0 container reports `bounds: null` (a hidden map must
|
|
465
|
+
* not filter the list). `byGuest`: pointer, wheel, touch or key input on the container, or a zoom
|
|
466
|
+
* button (`zoomIn`/`zoomOut`), since the previous report; a programmatic `fit()` never is. */
|
|
467
|
+
onView?: (bounds: MapBounds | null, byGuest: boolean) => void;
|
|
468
|
+
}
|
|
469
|
+
/** A surface. */
|
|
470
|
+
interface MapSurface {
|
|
471
|
+
/** Build now if not built (no-op when built or building); resolves once the map exists. */
|
|
472
|
+
build(): Promise<void>;
|
|
473
|
+
/** The map, or null while not built. */
|
|
474
|
+
map(): MapLike | null;
|
|
475
|
+
/** One zoom step in; counts as the guest's move. */
|
|
476
|
+
zoomIn(): void;
|
|
477
|
+
/** One zoom step out; counts as the guest's move. */
|
|
478
|
+
zoomOut(): void;
|
|
479
|
+
/** Programmatic, no animation; never counts as the guest's move. */
|
|
480
|
+
fit(bounds: MapBounds, padding: number, maxZoom: number): void;
|
|
481
|
+
/** Re-measure after a layer reopens (its container was resized while hidden). */
|
|
482
|
+
resize(): void;
|
|
483
|
+
/** Remove the map and every observer and listener. */
|
|
484
|
+
destroy(): void;
|
|
485
|
+
}
|
|
486
|
+
/** One MapLibre map on one container, with `attributionControl: { compact: false }` always (the compact
|
|
487
|
+
* credit collapses on the first touch and the OSM credit disappears) and `clickTolerance` 12. */
|
|
488
|
+
type CreateMapSurface = (options: MapSurfaceOptions) => MapSurface;
|
|
489
|
+
/** The marker element in two nodes: a shell MapLibre rewrites every frame (its inline transform positions
|
|
490
|
+
* the marker; anything CSS puts there is overwritten, and `scale` scales the translation with it) and a
|
|
491
|
+
* body for every visual (rotation, scale, the hover pop), which carries `data-part`. Never style the shell.
|
|
492
|
+
* A "button" shell is `type="button"`. */
|
|
493
|
+
type MarkerElement = (spec: {
|
|
494
|
+
tag: "button" | "div";
|
|
495
|
+
part: string;
|
|
496
|
+
}) => {
|
|
497
|
+
el: HTMLElement;
|
|
498
|
+
body: HTMLSpanElement;
|
|
499
|
+
};
|
|
500
|
+
/** See {@link CreateMapSurface}. */
|
|
501
|
+
export declare const createMapSurface: CreateMapSurface;
|
|
502
|
+
/** See {@link MarkerElement}. */
|
|
503
|
+
export declare const markerElement: MarkerElement;
|
|
183
504
|
//#endregion
|
|
184
|
-
export type
|
|
505
|
+
export { type Dispose, ENHANCE_PARTS, type EnhanceOptions, type EnhancePart, type EnhanceParts, type FavoritesBinding, GATE_MARKERS, type GateMarkers, type GeoJsonSourceLike, type LayerHandle, type LayerOptions, type MapCamera, type MapLibreLike, type MapLibreLoader, type MapLike, type MapSurface, type MapSurfaceOptions, type MarkerLike, type PlaceMarkersOptions, type PopupLike, type ResultMarker, type ResultMarkers, type ResultMarkersOptions, type RevealOptions, type ShareOptions, type StripHandle, type StripOptions };
|