@unabridged/midwest 0.24.3 → 0.25.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/app/assets/javascript/midwest/index.ts +8 -0
- package/app/assets/javascript/midwest/map_providers.ts +61 -0
- package/app/assets/javascript/midwest.js +1009 -9
- package/app/assets/javascript/midwest.js.map +1 -1
- package/app/assets/stylesheets/midwest.css +1 -1
- package/app/assets/stylesheets/midwest.tailwind.css +13 -1
- package/dist/css/midwest.css +1 -1
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js +8 -0
- package/dist/javascript/collection/app/assets/javascript/midwest/index.js.map +1 -1
- package/dist/javascript/collection/app/assets/javascript/midwest/map_providers.js +56 -0
- package/dist/javascript/collection/app/assets/javascript/midwest/map_providers.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/form/address_component/address_component_controller.js +441 -0
- package/dist/javascript/collection/app/components/midwest/form/address_component/address_component_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/form/form_controller.js +37 -3
- package/dist/javascript/collection/app/components/midwest/form/form_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/form/live_summary_component/live_summary_component_controller.js +55 -1
- package/dist/javascript/collection/app/components/midwest/form/live_summary_component/live_summary_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/map_component/map_component_controller.js +297 -0
- package/dist/javascript/collection/app/components/midwest/map_component/map_component_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/onboarding_component/onboarding_component_controller.js +34 -5
- package/dist/javascript/collection/app/components/midwest/onboarding_component/onboarding_component_controller.js.map +1 -1
- package/dist/javascript/collection/app/components/midwest/page_transition_component/page_transition_component_controller.js +38 -0
- package/dist/javascript/collection/app/components/midwest/page_transition_component/page_transition_component_controller.js.map +1 -0
- package/dist/javascript/collection/app/components/midwest/popover_component/popover_component_controller.js +67 -0
- package/dist/javascript/collection/app/components/midwest/popover_component/popover_component_controller.js.map +1 -0
- package/dist/javascript/midwest.js +1009 -9
- package/dist/javascript/midwest.js.map +1 -1
- package/package.json +1 -1
package/dist/javascript/collection/app/components/midwest/map_component/map_component_controller.js
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus';
|
|
2
|
+
import { loadMapKitScript, loadGoogleMapsScript, loadLeafletScript } from '../../../assets/javascript/midwest/map_providers.js';
|
|
3
|
+
|
|
4
|
+
const LEAFLET_TILES = {
|
|
5
|
+
MutedStandard: {
|
|
6
|
+
url: "https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png",
|
|
7
|
+
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>'
|
|
8
|
+
},
|
|
9
|
+
Standard: {
|
|
10
|
+
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
|
|
11
|
+
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
|
|
12
|
+
maxZoom: 19
|
|
13
|
+
},
|
|
14
|
+
Hybrid: {
|
|
15
|
+
url: "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",
|
|
16
|
+
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>'
|
|
17
|
+
},
|
|
18
|
+
Satellite: {
|
|
19
|
+
url: "https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}",
|
|
20
|
+
attribution: "Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community",
|
|
21
|
+
maxZoom: 18
|
|
22
|
+
},
|
|
23
|
+
Dark: {
|
|
24
|
+
url: "https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png",
|
|
25
|
+
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors © <a href="https://carto.com/attributions">CARTO</a>'
|
|
26
|
+
}
|
|
27
|
+
};
|
|
28
|
+
const GOOGLE_MUTED_STYLES = [
|
|
29
|
+
{ elementType: "geometry", stylers: [{ color: "#f5f5f5" }] },
|
|
30
|
+
{ elementType: "labels.icon", stylers: [{ visibility: "off" }] },
|
|
31
|
+
{ elementType: "labels.text.fill", stylers: [{ color: "#616161" }] },
|
|
32
|
+
{ elementType: "labels.text.stroke", stylers: [{ color: "#f5f5f5" }] },
|
|
33
|
+
{ featureType: "administrative.land_parcel", elementType: "labels", stylers: [{ visibility: "off" }] },
|
|
34
|
+
{ featureType: "poi", elementType: "geometry", stylers: [{ color: "#eeeeee" }] },
|
|
35
|
+
{ featureType: "poi.park", elementType: "geometry", stylers: [{ color: "#e5e5e5" }] },
|
|
36
|
+
{ featureType: "road", elementType: "geometry", stylers: [{ color: "#ffffff" }] },
|
|
37
|
+
{ featureType: "road.highway", elementType: "geometry", stylers: [{ color: "#dadada" }] },
|
|
38
|
+
{ featureType: "water", elementType: "geometry", stylers: [{ color: "#c9c9c9" }] }
|
|
39
|
+
];
|
|
40
|
+
const GOOGLE_DARK_STYLES = [
|
|
41
|
+
{ elementType: "geometry", stylers: [{ color: "#242f3e" }] },
|
|
42
|
+
{ elementType: "labels.text.stroke", stylers: [{ color: "#242f3e" }] },
|
|
43
|
+
{ elementType: "labels.text.fill", stylers: [{ color: "#746855" }] },
|
|
44
|
+
{ featureType: "administrative.locality", elementType: "labels.text.fill", stylers: [{ color: "#d59563" }] },
|
|
45
|
+
{ featureType: "poi", elementType: "labels.text.fill", stylers: [{ color: "#d59563" }] },
|
|
46
|
+
{ featureType: "poi.park", elementType: "geometry", stylers: [{ color: "#263c3f" }] },
|
|
47
|
+
{ featureType: "poi.park", elementType: "labels.text.fill", stylers: [{ color: "#6b9a76" }] },
|
|
48
|
+
{ featureType: "road", elementType: "geometry", stylers: [{ color: "#38414e" }] },
|
|
49
|
+
{ featureType: "road", elementType: "geometry.stroke", stylers: [{ color: "#212a37" }] },
|
|
50
|
+
{ featureType: "road", elementType: "labels.text.fill", stylers: [{ color: "#9ca5b3" }] },
|
|
51
|
+
{ featureType: "road.highway", elementType: "geometry", stylers: [{ color: "#746855" }] },
|
|
52
|
+
{ featureType: "road.highway", elementType: "geometry.stroke", stylers: [{ color: "#1f2835" }] },
|
|
53
|
+
{ featureType: "road.highway", elementType: "labels.text.fill", stylers: [{ color: "#f3d19c" }] },
|
|
54
|
+
{ featureType: "transit", elementType: "geometry", stylers: [{ color: "#2f3948" }] },
|
|
55
|
+
{ featureType: "transit.station", elementType: "labels.text.fill", stylers: [{ color: "#d59563" }] },
|
|
56
|
+
{ featureType: "water", elementType: "geometry", stylers: [{ color: "#17263c" }] },
|
|
57
|
+
{ featureType: "water", elementType: "labels.text.fill", stylers: [{ color: "#515c6d" }] },
|
|
58
|
+
{ featureType: "water", elementType: "labels.text.stroke", stylers: [{ color: "#17263c" }] }
|
|
59
|
+
];
|
|
60
|
+
class Map extends Controller {
|
|
61
|
+
static values = {
|
|
62
|
+
latitude: { type: Number, default: 37.334886 },
|
|
63
|
+
longitude: { type: Number, default: -122.008988 },
|
|
64
|
+
span: { type: Number, default: 0.1 },
|
|
65
|
+
mapType: { type: String, default: "MutedStandard" },
|
|
66
|
+
colorScheme: { type: String, default: "Auto" },
|
|
67
|
+
provider: { type: String, default: "" },
|
|
68
|
+
token: { type: String, default: "" },
|
|
69
|
+
tokenUrl: { type: String, default: "" },
|
|
70
|
+
apiKey: { type: String, default: "" },
|
|
71
|
+
zoom: { type: Number, default: -1 },
|
|
72
|
+
markers: { type: Array, default: [] },
|
|
73
|
+
showsUserLocation: { type: Boolean, default: false },
|
|
74
|
+
showsMapTypeControl: { type: Boolean, default: false },
|
|
75
|
+
showsZoomControl: { type: Boolean, default: true },
|
|
76
|
+
showsCompass: { type: String, default: "Adaptive" },
|
|
77
|
+
rotation: { type: Number, default: 0 }
|
|
78
|
+
};
|
|
79
|
+
mapInstance = null;
|
|
80
|
+
async connect() {
|
|
81
|
+
const provider = this.providerValue;
|
|
82
|
+
if (!provider)
|
|
83
|
+
return;
|
|
84
|
+
try {
|
|
85
|
+
if (provider === "mapkit") {
|
|
86
|
+
await loadMapKitScript();
|
|
87
|
+
this.initMapKit();
|
|
88
|
+
this.initMapKitMap();
|
|
89
|
+
} else if (provider === "google") {
|
|
90
|
+
await loadGoogleMapsScript(this.apiKeyValue);
|
|
91
|
+
this.initGoogleMap();
|
|
92
|
+
} else if (provider === "leaflet") {
|
|
93
|
+
await loadLeafletScript();
|
|
94
|
+
this.initLeafletMap();
|
|
95
|
+
}
|
|
96
|
+
} catch (err) {
|
|
97
|
+
console.error(`[midwest-map] Failed to initialise ${provider} map:`, err);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
disconnect() {
|
|
101
|
+
if (this.mapInstance) {
|
|
102
|
+
if (this.providerValue === "mapkit")
|
|
103
|
+
this.mapInstance.destroy();
|
|
104
|
+
if (this.providerValue === "leaflet")
|
|
105
|
+
this.mapInstance.remove();
|
|
106
|
+
this.mapInstance = null;
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
tokenValueChanged() {
|
|
110
|
+
if (this.mapInstance && this.providerValue === "mapkit")
|
|
111
|
+
this.initMapKit();
|
|
112
|
+
}
|
|
113
|
+
// --- MapKit ---
|
|
114
|
+
initMapKit() {
|
|
115
|
+
const mk = window.mapkit;
|
|
116
|
+
const token = this.tokenValue;
|
|
117
|
+
const tokenUrl = this.tokenUrlValue;
|
|
118
|
+
mk.init({
|
|
119
|
+
authorizationCallback: (done) => {
|
|
120
|
+
if (token) {
|
|
121
|
+
done(token);
|
|
122
|
+
} else if (tokenUrl) {
|
|
123
|
+
fetch(tokenUrl).then((r) => r.text()).then(done).catch((err) => console.error("[midwest-map] Token fetch failed:", err));
|
|
124
|
+
}
|
|
125
|
+
},
|
|
126
|
+
language: document.documentElement.lang || "en"
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
initMapKitMap() {
|
|
130
|
+
const mk = window.mapkit;
|
|
131
|
+
const region = new mk.CoordinateRegion(
|
|
132
|
+
new mk.Coordinate(this.latitudeValue, this.longitudeValue),
|
|
133
|
+
new mk.CoordinateSpan(this.spanValue, this.spanValue)
|
|
134
|
+
);
|
|
135
|
+
const colorScheme = this.colorSchemeValue === "Auto" ? window.matchMedia("(prefers-color-scheme: dark)").matches ? mk.Map.ColorSchemes.Dark : mk.Map.ColorSchemes.Light : mk.Map.ColorSchemes[this.colorSchemeValue];
|
|
136
|
+
this.mapInstance = new mk.Map(this.element, {
|
|
137
|
+
region,
|
|
138
|
+
mapType: mk.Map.MapTypes[this.mapTypeValue],
|
|
139
|
+
colorScheme,
|
|
140
|
+
showsUserLocation: this.showsUserLocationValue,
|
|
141
|
+
showsMapTypeControl: this.showsMapTypeControlValue,
|
|
142
|
+
showsZoomControl: this.showsZoomControlValue,
|
|
143
|
+
showsCompass: mk.FeatureVisibility[this.showsCompassValue],
|
|
144
|
+
rotation: this.rotationValue
|
|
145
|
+
});
|
|
146
|
+
this.addMapKitMarkers();
|
|
147
|
+
this.element.dataset.mapLoaded = "true";
|
|
148
|
+
}
|
|
149
|
+
addMapKitMarkers() {
|
|
150
|
+
const mk = window.mapkit;
|
|
151
|
+
if (!this.markersValue.length)
|
|
152
|
+
return;
|
|
153
|
+
const annotations = this.markersValue.map((m) => {
|
|
154
|
+
const opts = {};
|
|
155
|
+
if (m.title)
|
|
156
|
+
opts.title = m.title;
|
|
157
|
+
if (m.subtitle)
|
|
158
|
+
opts.subtitle = m.subtitle;
|
|
159
|
+
if (m.color)
|
|
160
|
+
opts.color = m.color;
|
|
161
|
+
if (m.glyphText)
|
|
162
|
+
opts.glyphText = m.glyphText;
|
|
163
|
+
return new mk.MarkerAnnotation(
|
|
164
|
+
new mk.Coordinate(m.latitude, m.longitude),
|
|
165
|
+
opts
|
|
166
|
+
);
|
|
167
|
+
});
|
|
168
|
+
this.mapInstance.addAnnotations(annotations);
|
|
169
|
+
const openIndex = this.markersValue.findIndex((m) => m.open);
|
|
170
|
+
if (openIndex >= 0) {
|
|
171
|
+
this.mapInstance.selectedAnnotation = annotations[openIndex];
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
// --- Google Maps ---
|
|
175
|
+
initGoogleMap() {
|
|
176
|
+
const g = window.google;
|
|
177
|
+
const zoom = this.zoomValue > 0 ? this.zoomValue : this.spanToZoom(this.spanValue);
|
|
178
|
+
const isDark = this.colorSchemeValue === "Dark" || this.colorSchemeValue === "Auto" && window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
179
|
+
const mapTypeId = this.googleMapTypeId();
|
|
180
|
+
const opts = {
|
|
181
|
+
center: { lat: this.latitudeValue, lng: this.longitudeValue },
|
|
182
|
+
zoom,
|
|
183
|
+
mapTypeId,
|
|
184
|
+
disableDefaultUI: true,
|
|
185
|
+
zoomControl: this.showsZoomControlValue,
|
|
186
|
+
mapTypeControl: this.showsMapTypeControlValue,
|
|
187
|
+
fullscreenControl: false,
|
|
188
|
+
streetViewControl: false
|
|
189
|
+
};
|
|
190
|
+
const styles = this.googleMapStyles(isDark, mapTypeId);
|
|
191
|
+
if (styles)
|
|
192
|
+
opts.styles = styles;
|
|
193
|
+
this.mapInstance = new g.maps.Map(this.element, opts);
|
|
194
|
+
this.addGoogleMarkers();
|
|
195
|
+
this.element.dataset.mapLoaded = "true";
|
|
196
|
+
}
|
|
197
|
+
googleMapTypeId() {
|
|
198
|
+
const types = {
|
|
199
|
+
Standard: "roadmap",
|
|
200
|
+
Hybrid: "hybrid",
|
|
201
|
+
Satellite: "satellite",
|
|
202
|
+
MutedStandard: "roadmap"
|
|
203
|
+
};
|
|
204
|
+
return types[this.mapTypeValue] ?? "roadmap";
|
|
205
|
+
}
|
|
206
|
+
googleMapStyles(isDark, mapTypeId) {
|
|
207
|
+
if (mapTypeId !== "roadmap")
|
|
208
|
+
return null;
|
|
209
|
+
if (isDark)
|
|
210
|
+
return GOOGLE_DARK_STYLES;
|
|
211
|
+
if (this.mapTypeValue === "MutedStandard")
|
|
212
|
+
return GOOGLE_MUTED_STYLES;
|
|
213
|
+
return null;
|
|
214
|
+
}
|
|
215
|
+
addGoogleMarkers() {
|
|
216
|
+
const g = window.google;
|
|
217
|
+
if (!this.markersValue.length)
|
|
218
|
+
return;
|
|
219
|
+
this.markersValue.forEach((m) => {
|
|
220
|
+
const marker = new g.maps.Marker({
|
|
221
|
+
map: this.mapInstance,
|
|
222
|
+
position: { lat: m.latitude, lng: m.longitude },
|
|
223
|
+
title: m.title ?? ""
|
|
224
|
+
});
|
|
225
|
+
if (m.open && m.title) {
|
|
226
|
+
const content = m.subtitle ? `<strong>${m.title}</strong><br><span style="color:#777">${m.subtitle}</span>` : `<strong>${m.title}</strong>`;
|
|
227
|
+
const infoWindow = new g.maps.InfoWindow({ content });
|
|
228
|
+
infoWindow.open(this.mapInstance, marker);
|
|
229
|
+
}
|
|
230
|
+
});
|
|
231
|
+
}
|
|
232
|
+
// --- Leaflet ---
|
|
233
|
+
initLeafletMap() {
|
|
234
|
+
const L = window.L;
|
|
235
|
+
const zoom = this.zoomValue > 0 ? this.zoomValue : this.spanToZoom(this.spanValue);
|
|
236
|
+
const isDark = this.colorSchemeValue === "Dark" || this.colorSchemeValue === "Auto" && window.matchMedia("(prefers-color-scheme: dark)").matches;
|
|
237
|
+
const tileKey = isDark ? "Dark" : this.mapTypeValue;
|
|
238
|
+
const tile = LEAFLET_TILES[tileKey] ?? LEAFLET_TILES.MutedStandard;
|
|
239
|
+
this.mapInstance = L.map(this.element, {
|
|
240
|
+
center: [this.latitudeValue, this.longitudeValue],
|
|
241
|
+
zoom,
|
|
242
|
+
zoomControl: this.showsZoomControlValue,
|
|
243
|
+
attributionControl: true
|
|
244
|
+
});
|
|
245
|
+
L.tileLayer(tile.url, {
|
|
246
|
+
attribution: tile.attribution,
|
|
247
|
+
maxZoom: tile.maxZoom ?? 19
|
|
248
|
+
}).addTo(this.mapInstance);
|
|
249
|
+
this.addLeafletMarkers();
|
|
250
|
+
this.element.dataset.mapLoaded = "true";
|
|
251
|
+
}
|
|
252
|
+
addLeafletMarkers() {
|
|
253
|
+
const L = window.L;
|
|
254
|
+
if (!this.markersValue.length)
|
|
255
|
+
return;
|
|
256
|
+
this.markersValue.forEach((m) => {
|
|
257
|
+
const icon = this.leafletMarkerIcon(m.color, m.glyphText);
|
|
258
|
+
const marker = L.marker([m.latitude, m.longitude], { icon }).addTo(this.mapInstance);
|
|
259
|
+
if (m.title) {
|
|
260
|
+
const content = m.subtitle ? `<strong>${m.title}</strong><br><span style="color:#777">${m.subtitle}</span>` : `<strong>${m.title}</strong>`;
|
|
261
|
+
marker.bindPopup(content);
|
|
262
|
+
if (m.open)
|
|
263
|
+
marker.openPopup();
|
|
264
|
+
}
|
|
265
|
+
});
|
|
266
|
+
}
|
|
267
|
+
// Returns a Leaflet icon — default blue when no color/glyph, otherwise a
|
|
268
|
+
// custom SVG pin rendered via divIcon.
|
|
269
|
+
leafletMarkerIcon(color, glyphText) {
|
|
270
|
+
const L = window.L;
|
|
271
|
+
if (!color && !glyphText)
|
|
272
|
+
return new L.Icon.Default();
|
|
273
|
+
const fill = color ?? "#2563eb";
|
|
274
|
+
const inner = glyphText ? `<text x="12" y="16" text-anchor="middle" font-size="9" font-family="system-ui,sans-serif" fill="white">${glyphText}</text>` : `<circle cx="12" cy="12" r="4" fill="rgba(255,255,255,0.9)"/>`;
|
|
275
|
+
const svg = [
|
|
276
|
+
'<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 36" width="24" height="36">',
|
|
277
|
+
` <path d="M12 0C5.373 0 0 5.373 0 12c0 9 12 24 12 24s12-15 12-24C24 5.373 18.627 0 12 0z" fill="${fill}"/>`,
|
|
278
|
+
` ${inner}`,
|
|
279
|
+
"</svg>"
|
|
280
|
+
].join("");
|
|
281
|
+
return L.divIcon({
|
|
282
|
+
html: svg,
|
|
283
|
+
className: "",
|
|
284
|
+
iconSize: [24, 36],
|
|
285
|
+
iconAnchor: [12, 36],
|
|
286
|
+
popupAnchor: [0, -36]
|
|
287
|
+
});
|
|
288
|
+
}
|
|
289
|
+
// Approximate MapKit coordinate span to a Web Mercator zoom level.
|
|
290
|
+
// CoordinateSpan 360° = zoom 0; halving span adds 1 zoom level.
|
|
291
|
+
spanToZoom(span) {
|
|
292
|
+
return Math.round(Math.log2(360 / span));
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
export { Map as default };
|
|
297
|
+
//# sourceMappingURL=map_component_controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"map_component_controller.js","sources":["../../../../../../../app/components/midwest/map_component/map_component_controller.ts"],"sourcesContent":["import { Controller } from '@hotwired/stimulus'\nimport { loadMapKitScript, loadGoogleMapsScript, loadLeafletScript } from '../../../assets/javascript/midwest/map_providers'\n\n// Leaflet tile layers keyed by Midwest map type (MapKit-style strings) and a\n// special 'Dark' entry for dark color schemes. CartoDB and ESRI tiles are free\n// for open-source / low-traffic use; no API key required.\nconst LEAFLET_TILES: Record<string, { url: string; attribution: string; maxZoom?: number }> = {\n MutedStandard: {\n url: 'https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}{r}.png',\n attribution: '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors © <a href=\"https://carto.com/attributions\">CARTO</a>'\n },\n Standard: {\n url: 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',\n attribution: '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors',\n maxZoom: 19\n },\n Hybrid: {\n url: 'https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png',\n attribution: '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors © <a href=\"https://carto.com/attributions\">CARTO</a>'\n },\n Satellite: {\n url: 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',\n attribution: 'Tiles © Esri — Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community',\n maxZoom: 18\n },\n Dark: {\n url: 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png',\n attribution: '© <a href=\"https://www.openstreetmap.org/copyright\">OpenStreetMap</a> contributors © <a href=\"https://carto.com/attributions\">CARTO</a>'\n }\n}\n\n// Muted (light) style for Google Maps — mirrors MapKit's MutedStandard.\nconst GOOGLE_MUTED_STYLES = [\n { elementType: 'geometry', stylers: [{ color: '#f5f5f5' }] },\n { elementType: 'labels.icon', stylers: [{ visibility: 'off' }] },\n { elementType: 'labels.text.fill', stylers: [{ color: '#616161' }] },\n { elementType: 'labels.text.stroke', stylers: [{ color: '#f5f5f5' }] },\n { featureType: 'administrative.land_parcel', elementType: 'labels', stylers: [{ visibility: 'off' }] },\n { featureType: 'poi', elementType: 'geometry', stylers: [{ color: '#eeeeee' }] },\n { featureType: 'poi.park', elementType: 'geometry', stylers: [{ color: '#e5e5e5' }] },\n { featureType: 'road', elementType: 'geometry', stylers: [{ color: '#ffffff' }] },\n { featureType: 'road.highway', elementType: 'geometry', stylers: [{ color: '#dadada' }] },\n { featureType: 'water', elementType: 'geometry', stylers: [{ color: '#c9c9c9' }] }\n]\n\n// Google's standard dark theme.\nconst GOOGLE_DARK_STYLES = [\n { elementType: 'geometry', stylers: [{ color: '#242f3e' }] },\n { elementType: 'labels.text.stroke', stylers: [{ color: '#242f3e' }] },\n { elementType: 'labels.text.fill', stylers: [{ color: '#746855' }] },\n { featureType: 'administrative.locality', elementType: 'labels.text.fill', stylers: [{ color: '#d59563' }] },\n { featureType: 'poi', elementType: 'labels.text.fill', stylers: [{ color: '#d59563' }] },\n { featureType: 'poi.park', elementType: 'geometry', stylers: [{ color: '#263c3f' }] },\n { featureType: 'poi.park', elementType: 'labels.text.fill', stylers: [{ color: '#6b9a76' }] },\n { featureType: 'road', elementType: 'geometry', stylers: [{ color: '#38414e' }] },\n { featureType: 'road', elementType: 'geometry.stroke', stylers: [{ color: '#212a37' }] },\n { featureType: 'road', elementType: 'labels.text.fill', stylers: [{ color: '#9ca5b3' }] },\n { featureType: 'road.highway', elementType: 'geometry', stylers: [{ color: '#746855' }] },\n { featureType: 'road.highway', elementType: 'geometry.stroke', stylers: [{ color: '#1f2835' }] },\n { featureType: 'road.highway', elementType: 'labels.text.fill', stylers: [{ color: '#f3d19c' }] },\n { featureType: 'transit', elementType: 'geometry', stylers: [{ color: '#2f3948' }] },\n { featureType: 'transit.station', elementType: 'labels.text.fill', stylers: [{ color: '#d59563' }] },\n { featureType: 'water', elementType: 'geometry', stylers: [{ color: '#17263c' }] },\n { featureType: 'water', elementType: 'labels.text.fill', stylers: [{ color: '#515c6d' }] },\n { featureType: 'water', elementType: 'labels.text.stroke', stylers: [{ color: '#17263c' }] }\n]\n\n\nexport default class Map extends Controller<HTMLElement> {\n static values = {\n latitude: { type: Number, default: 37.334886 },\n longitude: { type: Number, default: -122.008988 },\n span: { type: Number, default: 0.1 },\n mapType: { type: String, default: 'MutedStandard' },\n colorScheme: { type: String, default: 'Auto' },\n provider: { type: String, default: '' },\n token: { type: String, default: '' },\n tokenUrl: { type: String, default: '' },\n apiKey: { type: String, default: '' },\n zoom: { type: Number, default: -1 },\n markers: { type: Array, default: [] },\n showsUserLocation: { type: Boolean, default: false },\n showsMapTypeControl: { type: Boolean, default: false },\n showsZoomControl: { type: Boolean, default: true },\n showsCompass: { type: String, default: 'Adaptive' },\n rotation: { type: Number, default: 0 }\n }\n\n declare latitudeValue: number\n declare longitudeValue: number\n declare spanValue: number\n declare mapTypeValue: string\n declare colorSchemeValue: string\n declare providerValue: string\n declare tokenValue: string\n declare tokenUrlValue: string\n declare apiKeyValue: string\n declare zoomValue: number\n declare markersValue: {\n latitude: number\n longitude: number\n title?: string\n subtitle?: string\n color?: string\n glyphText?: string\n /** When true, the callout / info window is shown on first load. */\n open?: boolean\n }[]\n\n declare showsUserLocationValue: boolean\n declare showsMapTypeControlValue: boolean\n declare showsZoomControlValue: boolean\n declare showsCompassValue: string\n declare rotationValue: number\n\n private mapInstance: any = null\n\n async connect (): Promise<void> {\n const provider = this.providerValue\n if (!provider) return\n\n try {\n if (provider === 'mapkit') {\n await loadMapKitScript()\n this.initMapKit()\n this.initMapKitMap()\n } else if (provider === 'google') {\n await loadGoogleMapsScript(this.apiKeyValue)\n this.initGoogleMap()\n } else if (provider === 'leaflet') {\n await loadLeafletScript()\n this.initLeafletMap()\n }\n } catch (err) {\n console.error(`[midwest-map] Failed to initialise ${provider} map:`, err)\n }\n }\n\n disconnect (): void {\n if (this.mapInstance) {\n if (this.providerValue === 'mapkit') this.mapInstance.destroy()\n if (this.providerValue === 'leaflet') this.mapInstance.remove()\n this.mapInstance = null\n }\n }\n\n tokenValueChanged (): void {\n if (this.mapInstance && this.providerValue === 'mapkit') this.initMapKit()\n }\n\n // --- MapKit ---\n\n private initMapKit (): void {\n const mk = (window as any).mapkit\n const token = this.tokenValue\n const tokenUrl = this.tokenUrlValue\n\n mk.init({\n authorizationCallback: (done: (t: string) => void) => {\n if (token) {\n done(token)\n } else if (tokenUrl) {\n fetch(tokenUrl)\n .then(r => r.text())\n .then(done)\n .catch(err => console.error('[midwest-map] Token fetch failed:', err))\n }\n },\n language: document.documentElement.lang || 'en'\n })\n }\n\n private initMapKitMap (): void {\n const mk = (window as any).mapkit\n\n const region = new mk.CoordinateRegion(\n new mk.Coordinate(this.latitudeValue, this.longitudeValue),\n new mk.CoordinateSpan(this.spanValue, this.spanValue)\n )\n\n const colorScheme = this.colorSchemeValue === 'Auto'\n ? (window.matchMedia('(prefers-color-scheme: dark)').matches\n ? mk.Map.ColorSchemes.Dark\n : mk.Map.ColorSchemes.Light)\n : mk.Map.ColorSchemes[this.colorSchemeValue]\n\n this.mapInstance = new mk.Map(this.element, {\n region,\n mapType: mk.Map.MapTypes[this.mapTypeValue],\n colorScheme,\n showsUserLocation: this.showsUserLocationValue,\n showsMapTypeControl: this.showsMapTypeControlValue,\n showsZoomControl: this.showsZoomControlValue,\n showsCompass: mk.FeatureVisibility[this.showsCompassValue],\n rotation: this.rotationValue\n })\n\n this.addMapKitMarkers()\n this.element.dataset.mapLoaded = 'true'\n }\n\n private addMapKitMarkers (): void {\n const mk = (window as any).mapkit\n if (!this.markersValue.length) return\n\n const annotations = this.markersValue.map(m => {\n const opts: Record<string, any> = {}\n if (m.title) opts.title = m.title\n if (m.subtitle) opts.subtitle = m.subtitle\n if (m.color) opts.color = m.color\n if (m.glyphText) opts.glyphText = m.glyphText\n\n return new mk.MarkerAnnotation(\n new mk.Coordinate(m.latitude, m.longitude),\n opts\n )\n })\n\n this.mapInstance.addAnnotations(annotations)\n\n // Select the first marker flagged as open so its callout shows on load.\n const openIndex = this.markersValue.findIndex(m => m.open)\n if (openIndex >= 0) {\n this.mapInstance.selectedAnnotation = annotations[openIndex]\n }\n }\n\n // --- Google Maps ---\n\n private initGoogleMap (): void {\n const g = (window as any).google\n const zoom = this.zoomValue > 0 ? this.zoomValue : this.spanToZoom(this.spanValue)\n\n const isDark = this.colorSchemeValue === 'Dark' ||\n (this.colorSchemeValue === 'Auto' && window.matchMedia('(prefers-color-scheme: dark)').matches)\n\n const mapTypeId = this.googleMapTypeId()\n\n const opts: Record<string, any> = {\n center: { lat: this.latitudeValue, lng: this.longitudeValue },\n zoom,\n mapTypeId,\n disableDefaultUI: true,\n zoomControl: this.showsZoomControlValue,\n mapTypeControl: this.showsMapTypeControlValue,\n fullscreenControl: false,\n streetViewControl: false\n }\n\n const styles = this.googleMapStyles(isDark, mapTypeId)\n if (styles) opts.styles = styles\n\n this.mapInstance = new g.maps.Map(this.element, opts)\n this.addGoogleMarkers()\n this.element.dataset.mapLoaded = 'true'\n }\n\n private googleMapTypeId (): string {\n const types: Record<string, string> = {\n Standard: 'roadmap',\n Hybrid: 'hybrid',\n Satellite: 'satellite',\n MutedStandard: 'roadmap'\n }\n return types[this.mapTypeValue] ?? 'roadmap'\n }\n\n private googleMapStyles (isDark: boolean, mapTypeId: string): object[] | null {\n if (mapTypeId !== 'roadmap') return null\n if (isDark) return GOOGLE_DARK_STYLES\n if (this.mapTypeValue === 'MutedStandard') return GOOGLE_MUTED_STYLES\n return null\n }\n\n private addGoogleMarkers (): void {\n const g = (window as any).google\n if (!this.markersValue.length) return\n\n this.markersValue.forEach(m => {\n const marker = new g.maps.Marker({\n map: this.mapInstance,\n position: { lat: m.latitude, lng: m.longitude },\n title: m.title ?? ''\n })\n\n if (m.open && m.title) {\n const content = m.subtitle\n ? `<strong>${m.title}</strong><br><span style=\"color:#777\">${m.subtitle}</span>`\n : `<strong>${m.title}</strong>`\n const infoWindow = new g.maps.InfoWindow({ content })\n infoWindow.open(this.mapInstance, marker)\n }\n })\n }\n\n // --- Leaflet ---\n\n private initLeafletMap (): void {\n const L = (window as any).L\n const zoom = this.zoomValue > 0 ? this.zoomValue : this.spanToZoom(this.spanValue)\n\n const isDark = this.colorSchemeValue === 'Dark' ||\n (this.colorSchemeValue === 'Auto' && window.matchMedia('(prefers-color-scheme: dark)').matches)\n\n const tileKey = isDark ? 'Dark' : this.mapTypeValue\n const tile = LEAFLET_TILES[tileKey] ?? LEAFLET_TILES.MutedStandard\n\n this.mapInstance = L.map(this.element, {\n center: [this.latitudeValue, this.longitudeValue],\n zoom,\n zoomControl: this.showsZoomControlValue,\n attributionControl: true\n })\n\n L.tileLayer(tile.url, {\n attribution: tile.attribution,\n maxZoom: tile.maxZoom ?? 19\n }).addTo(this.mapInstance)\n\n this.addLeafletMarkers()\n this.element.dataset.mapLoaded = 'true'\n }\n\n private addLeafletMarkers (): void {\n const L = (window as any).L\n if (!this.markersValue.length) return\n\n this.markersValue.forEach(m => {\n const icon = this.leafletMarkerIcon(m.color, m.glyphText)\n const marker = L.marker([m.latitude, m.longitude], { icon })\n .addTo(this.mapInstance)\n\n if (m.title) {\n const content = m.subtitle\n ? `<strong>${m.title}</strong><br><span style=\"color:#777\">${m.subtitle}</span>`\n : `<strong>${m.title}</strong>`\n marker.bindPopup(content)\n if (m.open) marker.openPopup()\n }\n })\n }\n\n // Returns a Leaflet icon — default blue when no color/glyph, otherwise a\n // custom SVG pin rendered via divIcon.\n private leafletMarkerIcon (color?: string, glyphText?: string): any {\n const L = (window as any).L\n if (!color && !glyphText) return new L.Icon.Default()\n\n const fill = color ?? '#2563eb'\n const inner = glyphText\n ? `<text x=\"12\" y=\"16\" text-anchor=\"middle\" font-size=\"9\" font-family=\"system-ui,sans-serif\" fill=\"white\">${glyphText}</text>`\n : `<circle cx=\"12\" cy=\"12\" r=\"4\" fill=\"rgba(255,255,255,0.9)\"/>`\n\n const svg = [\n '<svg xmlns=\"http://www.w3.org/2000/svg\" viewBox=\"0 0 24 36\" width=\"24\" height=\"36\">',\n ` <path d=\"M12 0C5.373 0 0 5.373 0 12c0 9 12 24 12 24s12-15 12-24C24 5.373 18.627 0 12 0z\" fill=\"${fill}\"/>`,\n ` ${inner}`,\n '</svg>'\n ].join('')\n\n return L.divIcon({\n html: svg,\n className: '',\n iconSize: [24, 36] as [number, number],\n iconAnchor: [12, 36] as [number, number],\n popupAnchor: [0, -36] as [number, number]\n })\n }\n\n // Approximate MapKit coordinate span to a Web Mercator zoom level.\n // CoordinateSpan 360° = zoom 0; halving span adds 1 zoom level.\n private spanToZoom (span: number): number {\n return Math.round(Math.log2(360 / span))\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -10,14 +10,14 @@ const FOCUSABLE = [
|
|
|
10
10
|
].join(", ");
|
|
11
11
|
const GAP = 12;
|
|
12
12
|
class Onboarding extends Controller {
|
|
13
|
-
static targets = ["step", "overlay", "card", "title", "body", "back", "next", "done", "arrow"];
|
|
13
|
+
static targets = ["step", "overlay", "card", "title", "body", "back", "next", "done", "arrow", "motion"];
|
|
14
14
|
static values = {
|
|
15
|
-
autoOpen: { type: Boolean, default: false }
|
|
16
|
-
confirm: { type: String, default: "" }
|
|
15
|
+
autoOpen: { type: Boolean, default: false }
|
|
17
16
|
};
|
|
18
17
|
steps = [];
|
|
19
18
|
index = 0;
|
|
20
19
|
active = false;
|
|
20
|
+
justOpened = false;
|
|
21
21
|
previousFocus = null;
|
|
22
22
|
highlighted = null;
|
|
23
23
|
connect() {
|
|
@@ -47,6 +47,7 @@ class Onboarding extends Controller {
|
|
|
47
47
|
this.previousFocus = document.activeElement;
|
|
48
48
|
this.overlayTarget.hidden = false;
|
|
49
49
|
this.cardTarget.hidden = false;
|
|
50
|
+
this.justOpened = true;
|
|
50
51
|
document.addEventListener("keydown", this.handleKeydown, true);
|
|
51
52
|
window.addEventListener("resize", this.reposition);
|
|
52
53
|
window.addEventListener("scroll", this.reposition, true);
|
|
@@ -81,14 +82,14 @@ class Onboarding extends Controller {
|
|
|
81
82
|
cancel() {
|
|
82
83
|
if (!this.active)
|
|
83
84
|
return;
|
|
84
|
-
if (this.confirmValue && !window.confirm(this.confirmValue))
|
|
85
|
-
return;
|
|
86
85
|
this.teardown();
|
|
87
86
|
this.dispatch("cancel", { bubbles: true });
|
|
88
87
|
}
|
|
89
88
|
render(index) {
|
|
90
89
|
this.index = index;
|
|
91
90
|
const step = this.steps[index];
|
|
91
|
+
const replay = !this.justOpened;
|
|
92
|
+
this.justOpened = false;
|
|
92
93
|
this.clearHighlight();
|
|
93
94
|
this.titleTarget.textContent = step.title;
|
|
94
95
|
this.renderBody(step);
|
|
@@ -100,6 +101,9 @@ class Onboarding extends Controller {
|
|
|
100
101
|
} else {
|
|
101
102
|
this.center();
|
|
102
103
|
}
|
|
104
|
+
if (replay && this.hasMotionTarget) {
|
|
105
|
+
this.replayMotion(this.cardTarget.dataset.placement ?? "center");
|
|
106
|
+
}
|
|
103
107
|
this.dispatch("show", { detail: { index, name: step.name }, bubbles: true });
|
|
104
108
|
this.focusCard();
|
|
105
109
|
}
|
|
@@ -201,9 +205,34 @@ class Onboarding extends Controller {
|
|
|
201
205
|
this.cardTarget.focus();
|
|
202
206
|
});
|
|
203
207
|
}
|
|
208
|
+
// Re-triggers the midwest_motion @starting-style entry on step transitions.
|
|
209
|
+
// The initial show is handled by @starting-style firing when the card's
|
|
210
|
+
// `hidden` is removed; subsequent steps need an explicit re-trigger since the
|
|
211
|
+
// card stays in the DOM. Toggling `hidden` on the motion element triggers
|
|
212
|
+
// @starting-style again; the rAF ensures both DOM writes land in the same
|
|
213
|
+
// paint so there is no blank-card flash.
|
|
214
|
+
replayMotion(placement) {
|
|
215
|
+
const el = this.motionTarget;
|
|
216
|
+
const dir = { bottom: "up", top: "down", right: "left", left: "right" };
|
|
217
|
+
el.classList.remove("from-up", "from-down", "from-left", "from-right");
|
|
218
|
+
const from = dir[placement];
|
|
219
|
+
if (from)
|
|
220
|
+
el.classList.add(`from-${from}`);
|
|
221
|
+
el.hidden = true;
|
|
222
|
+
requestAnimationFrame(() => {
|
|
223
|
+
if (this.active)
|
|
224
|
+
el.hidden = false;
|
|
225
|
+
});
|
|
226
|
+
}
|
|
204
227
|
teardown() {
|
|
205
228
|
this.active = false;
|
|
206
229
|
this.clearHighlight();
|
|
230
|
+
this.element.querySelectorAll("[popover]").forEach((el) => {
|
|
231
|
+
try {
|
|
232
|
+
el.hidePopover();
|
|
233
|
+
} catch {
|
|
234
|
+
}
|
|
235
|
+
});
|
|
207
236
|
this.overlayTarget.hidden = true;
|
|
208
237
|
this.cardTarget.hidden = true;
|
|
209
238
|
document.removeEventListener("keydown", this.handleKeydown, true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"onboarding_component_controller.js","sources":["../../../../../../../app/components/midwest/onboarding_component/onboarding_component_controller.ts"],"sourcesContent":["import { Controller } from '@hotwired/stimulus'\n\nconst FOCUSABLE = [\n 'a[href]',\n 'button:not([disabled])',\n 'textarea:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])'\n].join(', ')\n\nconst GAP = 12 // px between the target and the card\n\ninterface StepConfig {\n el: HTMLElement\n name: string\n selector: string\n position: string\n title: string\n nextText: string\n backText: string\n completeText: string\n canClickTarget: boolean\n}\n\n// Native, dependency-free guided-tour engine. Reads the nested step config\n// carriers, then walks the user through them with a dimmed overlay, a card\n// positioned against each step's target element, and Back/Next/Done buttons.\nexport default class Onboarding extends Controller<HTMLElement> {\n static targets = ['step', 'overlay', 'card', 'title', 'body', 'back', 'next', 'done', 'arrow']\n\n static values = {\n autoOpen: { type: Boolean, default: false },\n confirm: { type: String, default: '' }\n }\n\n declare readonly stepTargets: HTMLElement[]\n declare readonly overlayTarget: HTMLElement\n declare readonly cardTarget: HTMLElement\n declare readonly titleTarget: HTMLElement\n declare readonly bodyTarget: HTMLElement\n declare readonly backTarget: HTMLButtonElement\n declare readonly nextTarget: HTMLButtonElement\n declare readonly doneTarget: HTMLButtonElement\n declare readonly arrowTarget: HTMLElement\n declare autoOpenValue: boolean\n declare confirmValue: string\n\n private steps: StepConfig[] = []\n private index = 0\n private active = false\n private previousFocus: HTMLElement | null = null\n private highlighted: HTMLElement | null = null\n\n connect () {\n this.steps = this.stepTargets.map((el) => ({\n el,\n name: el.dataset.name ?? '',\n selector: el.dataset.selector ?? '',\n position: el.dataset.position ?? 'auto',\n title: el.dataset.title ?? '',\n nextText: el.dataset.nextText ?? 'Next',\n backText: el.dataset.backText ?? 'Back',\n completeText: el.dataset.completeText ?? 'Done',\n canClickTarget: el.dataset.canClickTarget === 'true'\n }))\n\n window.addEventListener('midwest-onboarding:open', this.handleOpen)\n\n if (this.autoOpenValue) this.start()\n }\n\n disconnect () {\n window.removeEventListener('midwest-onboarding:open', this.handleOpen)\n this.teardown()\n }\n\n start () {\n if (this.active || this.steps.length === 0) return\n this.active = true\n this.previousFocus = document.activeElement as HTMLElement | null\n this.overlayTarget.hidden = false\n this.cardTarget.hidden = false\n document.addEventListener('keydown', this.handleKeydown, true)\n window.addEventListener('resize', this.reposition)\n window.addEventListener('scroll', this.reposition, true)\n this.dispatch('start', { bubbles: true })\n this.render(0)\n }\n\n next () {\n if (this.index < this.steps.length - 1) this.render(this.index + 1)\n else this.complete()\n }\n\n back () {\n if (this.index > 0) this.render(this.index - 1)\n }\n\n // Jump to a step by its `name`.\n show (name: string) {\n const target = this.steps.findIndex((step) => step.name === name)\n if (target >= 0) {\n if (!this.active) this.start()\n this.render(target)\n }\n }\n\n complete () {\n if (!this.active) return\n this.teardown()\n this.dispatch('complete', { bubbles: true })\n }\n\n cancel () {\n if (!this.active) return\n if (this.confirmValue && !window.confirm(this.confirmValue)) return\n this.teardown()\n this.dispatch('cancel', { bubbles: true })\n }\n\n private render (index: number) {\n this.index = index\n const step = this.steps[index]\n\n this.clearHighlight()\n\n this.titleTarget.textContent = step.title\n this.renderBody(step)\n this.renderButtons(step, index)\n\n const target = step.selector\n ? document.querySelector<HTMLElement>(step.selector)\n : null\n\n if (target) {\n this.highlight(target, step.canClickTarget)\n this.position(target, step.position)\n } else {\n this.center()\n }\n\n this.dispatch('show', { detail: { index, name: step.name }, bubbles: true })\n this.focusCard()\n }\n\n private renderBody (step: StepConfig) {\n this.bodyTarget.replaceChildren()\n const template = step.el.querySelector('template')\n if (template) this.bodyTarget.appendChild(template.content.cloneNode(true))\n }\n\n private renderButtons (step: StepConfig, index: number) {\n const isFirst = index === 0\n const isLast = index === this.steps.length - 1\n\n // Back keeps its layout box (visibility) so the forward button never shifts;\n // Next and Done are mutually exclusive, so exactly one fills the right slot.\n this.backTarget.textContent = step.backText\n this.backTarget.style.visibility = isFirst ? 'hidden' : ''\n\n // Inline display beats the button's own `display: inline-flex` rule.\n this.nextTarget.textContent = step.nextText\n this.nextTarget.style.display = isLast ? 'none' : ''\n\n this.doneTarget.textContent = step.completeText\n this.doneTarget.style.display = isLast ? '' : 'none'\n }\n\n private highlight (target: HTMLElement, canClickTarget: boolean) {\n target.classList.add('midwest-onboarding-highlight')\n target.classList.toggle('is-locked', !canClickTarget)\n this.highlighted = target\n target.scrollIntoView({ behavior: 'smooth', block: 'center' })\n }\n\n private clearHighlight () {\n this.highlighted?.classList.remove('midwest-onboarding-highlight', 'is-locked')\n this.highlighted = null\n }\n\n // Places the card against the target, choosing a side when `position` is auto.\n private position (target: HTMLElement, position: string) {\n const rect = target.getBoundingClientRect()\n const card = this.cardTarget.getBoundingClientRect()\n const vw = window.innerWidth\n const vh = window.innerHeight\n\n let placement = position\n if (placement === 'auto') {\n if (rect.bottom + card.height + GAP <= vh) placement = 'bottom'\n else if (rect.top - card.height - GAP >= 0) placement = 'top'\n else if (rect.right + card.width + GAP <= vw) placement = 'right'\n else placement = 'left'\n }\n\n let top: number\n let left: number\n\n switch (placement) {\n case 'top':\n top = rect.top - card.height - GAP\n left = rect.left + rect.width / 2 - card.width / 2\n break\n case 'left':\n top = rect.top + rect.height / 2 - card.height / 2\n left = rect.left - card.width - GAP\n break\n case 'right':\n top = rect.top + rect.height / 2 - card.height / 2\n left = rect.right + GAP\n break\n default: // bottom\n top = rect.bottom + GAP\n left = rect.left + rect.width / 2 - card.width / 2\n break\n }\n\n // Keep the card fully on screen.\n left = Math.max(8, Math.min(left, vw - card.width - 8))\n top = Math.max(8, Math.min(top, vh - card.height - 8))\n\n this.cardTarget.style.top = `${top}px`\n this.cardTarget.style.left = `${left}px`\n this.cardTarget.dataset.placement = placement\n\n this.positionArrow(placement, rect, top, left)\n }\n\n // Nudges the arrow so it points at the target's center, not the card's.\n private positionArrow (placement: string, rect: DOMRect, top: number, left: number) {\n const card = this.cardTarget.getBoundingClientRect()\n const arrow = this.arrowTarget.style\n\n if (placement === 'top' || placement === 'bottom') {\n const targetCenter = rect.left + rect.width / 2\n const offset = Math.max(12, Math.min(targetCenter - left, card.width - 12))\n arrow.left = `${offset}px`\n arrow.top = ''\n } else {\n const targetCenter = rect.top + rect.height / 2\n const offset = Math.max(12, Math.min(targetCenter - top, card.height - 12))\n arrow.top = `${offset}px`\n arrow.left = ''\n }\n }\n\n private center () {\n const card = this.cardTarget.getBoundingClientRect()\n this.cardTarget.style.top = `${Math.max(8, window.innerHeight / 2 - card.height / 2)}px`\n this.cardTarget.style.left = `${Math.max(8, window.innerWidth / 2 - card.width / 2)}px`\n this.cardTarget.dataset.placement = 'center'\n }\n\n private focusCard () {\n requestAnimationFrame(() => {\n if (this.active) this.cardTarget.focus()\n })\n }\n\n private teardown () {\n this.active = false\n this.clearHighlight()\n this.overlayTarget.hidden = true\n this.cardTarget.hidden = true\n document.removeEventListener('keydown', this.handleKeydown, true)\n window.removeEventListener('resize', this.reposition)\n window.removeEventListener('scroll', this.reposition, true)\n this.previousFocus?.focus()\n this.previousFocus = null\n }\n\n private handleOpen = (event: Event) => {\n const detail = (event as CustomEvent).detail ?? {}\n const id = String(detail.id ?? '').replace('#', '')\n if (id === this.element.id) this.start()\n }\n\n private reposition = () => {\n if (!this.active) return\n const step = this.steps[this.index]\n const target = step.selector ? document.querySelector<HTMLElement>(step.selector) : null\n if (target) this.position(target, step.position)\n else this.center()\n }\n\n private handleKeydown = (event: KeyboardEvent) => {\n if (!this.active) return\n\n switch (event.key) {\n case 'Escape':\n event.preventDefault()\n this.cancel()\n break\n case 'ArrowRight':\n event.preventDefault()\n this.next()\n break\n case 'ArrowLeft':\n event.preventDefault()\n this.back()\n break\n case 'Tab':\n this.trapTab(event)\n break\n }\n }\n\n // Keep Tab focus within the card while the tour is active.\n private trapTab (event: KeyboardEvent) {\n const focusable = Array.from(this.cardTarget.querySelectorAll<HTMLElement>(FOCUSABLE))\n .filter((el) => !el.hidden && el.offsetParent !== null && getComputedStyle(el).visibility !== 'hidden')\n if (focusable.length === 0) return\n\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n const active = document.activeElement\n\n if (event.shiftKey) {\n if (active === first || active === this.cardTarget || !this.cardTarget.contains(active)) {\n event.preventDefault()\n last.focus()\n }\n } else if (active === last || !this.cardTarget.contains(active)) {\n event.preventDefault()\n first.focus()\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"onboarding_component_controller.js","sources":["../../../../../../../app/components/midwest/onboarding_component/onboarding_component_controller.ts"],"sourcesContent":["import { Controller } from '@hotwired/stimulus'\n\nconst FOCUSABLE = [\n 'a[href]',\n 'button:not([disabled])',\n 'textarea:not([disabled])',\n 'input:not([disabled])',\n 'select:not([disabled])',\n '[tabindex]:not([tabindex=\"-1\"])'\n].join(', ')\n\nconst GAP = 12 // px between the target and the card\n\ninterface StepConfig {\n el: HTMLElement\n name: string\n selector: string\n position: string\n title: string\n nextText: string\n backText: string\n completeText: string\n canClickTarget: boolean\n}\n\n// Native, dependency-free guided-tour engine. Reads the nested step config\n// carriers, then walks the user through them with a dimmed overlay, a card\n// positioned against each step's target element, and Back/Next/Done buttons.\nexport default class Onboarding extends Controller<HTMLElement> {\n static targets = ['step', 'overlay', 'card', 'title', 'body', 'back', 'next', 'done', 'arrow', 'motion']\n\n static values = {\n autoOpen: { type: Boolean, default: false }\n }\n\n declare readonly stepTargets: HTMLElement[]\n declare readonly overlayTarget: HTMLElement\n declare readonly cardTarget: HTMLElement\n declare readonly titleTarget: HTMLElement\n declare readonly bodyTarget: HTMLElement\n declare readonly backTarget: HTMLButtonElement\n declare readonly nextTarget: HTMLButtonElement\n declare readonly doneTarget: HTMLButtonElement\n declare readonly arrowTarget: HTMLElement\n declare readonly motionTarget: HTMLElement\n declare readonly hasMotionTarget: boolean\n declare autoOpenValue: boolean\n\n private steps: StepConfig[] = []\n private index = 0\n private active = false\n private justOpened = false\n private previousFocus: HTMLElement | null = null\n private highlighted: HTMLElement | null = null\n\n connect () {\n this.steps = this.stepTargets.map((el) => ({\n el,\n name: el.dataset.name ?? '',\n selector: el.dataset.selector ?? '',\n position: el.dataset.position ?? 'auto',\n title: el.dataset.title ?? '',\n nextText: el.dataset.nextText ?? 'Next',\n backText: el.dataset.backText ?? 'Back',\n completeText: el.dataset.completeText ?? 'Done',\n canClickTarget: el.dataset.canClickTarget === 'true'\n }))\n\n window.addEventListener('midwest-onboarding:open', this.handleOpen)\n\n if (this.autoOpenValue) this.start()\n }\n\n disconnect () {\n window.removeEventListener('midwest-onboarding:open', this.handleOpen)\n this.teardown()\n }\n\n start () {\n if (this.active || this.steps.length === 0) return\n this.active = true\n this.previousFocus = document.activeElement as HTMLElement | null\n this.overlayTarget.hidden = false\n this.cardTarget.hidden = false\n this.justOpened = true\n document.addEventListener('keydown', this.handleKeydown, true)\n window.addEventListener('resize', this.reposition)\n window.addEventListener('scroll', this.reposition, true)\n this.dispatch('start', { bubbles: true })\n this.render(0)\n }\n\n next () {\n if (this.index < this.steps.length - 1) this.render(this.index + 1)\n else this.complete()\n }\n\n back () {\n if (this.index > 0) this.render(this.index - 1)\n }\n\n // Jump to a step by its `name`.\n show (name: string) {\n const target = this.steps.findIndex((step) => step.name === name)\n if (target >= 0) {\n if (!this.active) this.start()\n this.render(target)\n }\n }\n\n complete () {\n if (!this.active) return\n this.teardown()\n this.dispatch('complete', { bubbles: true })\n }\n\n cancel () {\n if (!this.active) return\n this.teardown()\n this.dispatch('cancel', { bubbles: true })\n }\n\n private render (index: number) {\n this.index = index\n const step = this.steps[index]\n const replay = !this.justOpened\n this.justOpened = false\n\n this.clearHighlight()\n\n this.titleTarget.textContent = step.title\n this.renderBody(step)\n this.renderButtons(step, index)\n\n const target = step.selector\n ? document.querySelector<HTMLElement>(step.selector)\n : null\n\n if (target) {\n this.highlight(target, step.canClickTarget)\n this.position(target, step.position)\n } else {\n this.center()\n }\n\n if (replay && this.hasMotionTarget) {\n this.replayMotion(this.cardTarget.dataset.placement ?? 'center')\n }\n\n this.dispatch('show', { detail: { index, name: step.name }, bubbles: true })\n this.focusCard()\n }\n\n private renderBody (step: StepConfig) {\n this.bodyTarget.replaceChildren()\n const template = step.el.querySelector('template')\n if (template) this.bodyTarget.appendChild(template.content.cloneNode(true))\n }\n\n private renderButtons (step: StepConfig, index: number) {\n const isFirst = index === 0\n const isLast = index === this.steps.length - 1\n\n // Back keeps its layout box (visibility) so the forward button never shifts;\n // Next and Done are mutually exclusive, so exactly one fills the right slot.\n this.backTarget.textContent = step.backText\n this.backTarget.style.visibility = isFirst ? 'hidden' : ''\n\n // Inline display beats the button's own `display: inline-flex` rule.\n this.nextTarget.textContent = step.nextText\n this.nextTarget.style.display = isLast ? 'none' : ''\n\n this.doneTarget.textContent = step.completeText\n this.doneTarget.style.display = isLast ? '' : 'none'\n }\n\n private highlight (target: HTMLElement, canClickTarget: boolean) {\n target.classList.add('midwest-onboarding-highlight')\n target.classList.toggle('is-locked', !canClickTarget)\n this.highlighted = target\n target.scrollIntoView({ behavior: 'smooth', block: 'center' })\n }\n\n private clearHighlight () {\n this.highlighted?.classList.remove('midwest-onboarding-highlight', 'is-locked')\n this.highlighted = null\n }\n\n // Places the card against the target, choosing a side when `position` is auto.\n private position (target: HTMLElement, position: string) {\n const rect = target.getBoundingClientRect()\n const card = this.cardTarget.getBoundingClientRect()\n const vw = window.innerWidth\n const vh = window.innerHeight\n\n let placement = position\n if (placement === 'auto') {\n if (rect.bottom + card.height + GAP <= vh) placement = 'bottom'\n else if (rect.top - card.height - GAP >= 0) placement = 'top'\n else if (rect.right + card.width + GAP <= vw) placement = 'right'\n else placement = 'left'\n }\n\n let top: number\n let left: number\n\n switch (placement) {\n case 'top':\n top = rect.top - card.height - GAP\n left = rect.left + rect.width / 2 - card.width / 2\n break\n case 'left':\n top = rect.top + rect.height / 2 - card.height / 2\n left = rect.left - card.width - GAP\n break\n case 'right':\n top = rect.top + rect.height / 2 - card.height / 2\n left = rect.right + GAP\n break\n default: // bottom\n top = rect.bottom + GAP\n left = rect.left + rect.width / 2 - card.width / 2\n break\n }\n\n // Keep the card fully on screen.\n left = Math.max(8, Math.min(left, vw - card.width - 8))\n top = Math.max(8, Math.min(top, vh - card.height - 8))\n\n this.cardTarget.style.top = `${top}px`\n this.cardTarget.style.left = `${left}px`\n this.cardTarget.dataset.placement = placement\n\n this.positionArrow(placement, rect, top, left)\n }\n\n // Nudges the arrow so it points at the target's center, not the card's.\n private positionArrow (placement: string, rect: DOMRect, top: number, left: number) {\n const card = this.cardTarget.getBoundingClientRect()\n const arrow = this.arrowTarget.style\n\n if (placement === 'top' || placement === 'bottom') {\n const targetCenter = rect.left + rect.width / 2\n const offset = Math.max(12, Math.min(targetCenter - left, card.width - 12))\n arrow.left = `${offset}px`\n arrow.top = ''\n } else {\n const targetCenter = rect.top + rect.height / 2\n const offset = Math.max(12, Math.min(targetCenter - top, card.height - 12))\n arrow.top = `${offset}px`\n arrow.left = ''\n }\n }\n\n private center () {\n const card = this.cardTarget.getBoundingClientRect()\n this.cardTarget.style.top = `${Math.max(8, window.innerHeight / 2 - card.height / 2)}px`\n this.cardTarget.style.left = `${Math.max(8, window.innerWidth / 2 - card.width / 2)}px`\n this.cardTarget.dataset.placement = 'center'\n }\n\n private focusCard () {\n requestAnimationFrame(() => {\n if (this.active) this.cardTarget.focus()\n })\n }\n\n // Re-triggers the midwest_motion @starting-style entry on step transitions.\n // The initial show is handled by @starting-style firing when the card's\n // `hidden` is removed; subsequent steps need an explicit re-trigger since the\n // card stays in the DOM. Toggling `hidden` on the motion element triggers\n // @starting-style again; the rAF ensures both DOM writes land in the same\n // paint so there is no blank-card flash.\n private replayMotion (placement: string) {\n const el = this.motionTarget\n const dir: Record<string, string> = { bottom: 'up', top: 'down', right: 'left', left: 'right' }\n\n el.classList.remove('from-up', 'from-down', 'from-left', 'from-right')\n const from = dir[placement]\n if (from) el.classList.add(`from-${from}`)\n\n el.hidden = true\n requestAnimationFrame(() => { if (this.active) el.hidden = false })\n }\n\n private teardown () {\n this.active = false\n this.clearHighlight()\n // Close any open confirmation popovers before hiding the card.\n this.element.querySelectorAll<HTMLElement>('[popover]').forEach((el) => {\n try { el.hidePopover() } catch { /* already hidden */ }\n })\n this.overlayTarget.hidden = true\n this.cardTarget.hidden = true\n document.removeEventListener('keydown', this.handleKeydown, true)\n window.removeEventListener('resize', this.reposition)\n window.removeEventListener('scroll', this.reposition, true)\n this.previousFocus?.focus()\n this.previousFocus = null\n }\n\n private handleOpen = (event: Event) => {\n const detail = (event as CustomEvent).detail ?? {}\n const id = String(detail.id ?? '').replace('#', '')\n if (id === this.element.id) this.start()\n }\n\n private reposition = () => {\n if (!this.active) return\n const step = this.steps[this.index]\n const target = step.selector ? document.querySelector<HTMLElement>(step.selector) : null\n if (target) this.position(target, step.position)\n else this.center()\n }\n\n private handleKeydown = (event: KeyboardEvent) => {\n if (!this.active) return\n\n switch (event.key) {\n case 'Escape':\n event.preventDefault()\n this.cancel()\n break\n case 'ArrowRight':\n event.preventDefault()\n this.next()\n break\n case 'ArrowLeft':\n event.preventDefault()\n this.back()\n break\n case 'Tab':\n this.trapTab(event)\n break\n }\n }\n\n // Keep Tab focus within the card while the tour is active.\n private trapTab (event: KeyboardEvent) {\n const focusable = Array.from(this.cardTarget.querySelectorAll<HTMLElement>(FOCUSABLE))\n .filter((el) => !el.hidden && el.offsetParent !== null && getComputedStyle(el).visibility !== 'hidden')\n if (focusable.length === 0) return\n\n const first = focusable[0]\n const last = focusable[focusable.length - 1]\n const active = document.activeElement\n\n if (event.shiftKey) {\n if (active === first || active === this.cardTarget || !this.cardTarget.contains(active)) {\n event.preventDefault()\n last.focus()\n }\n } else if (active === last || !this.cardTarget.contains(active)) {\n event.preventDefault()\n first.focus()\n }\n }\n}\n"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { Controller } from '@hotwired/stimulus';
|
|
2
|
+
|
|
3
|
+
class PageTransition extends Controller {
|
|
4
|
+
static values = {
|
|
5
|
+
style: { type: String, default: "fade" },
|
|
6
|
+
duration: { type: String, default: "" },
|
|
7
|
+
easing: { type: String, default: "" }
|
|
8
|
+
};
|
|
9
|
+
injectedMeta = null;
|
|
10
|
+
connect() {
|
|
11
|
+
if (this.styleValue === "none")
|
|
12
|
+
return;
|
|
13
|
+
if (!document.querySelector('meta[name="view-transition"]')) {
|
|
14
|
+
const meta = document.createElement("meta");
|
|
15
|
+
meta.name = "view-transition";
|
|
16
|
+
meta.content = "same-origin";
|
|
17
|
+
document.head.appendChild(meta);
|
|
18
|
+
this.injectedMeta = meta;
|
|
19
|
+
}
|
|
20
|
+
document.documentElement.dataset.midwestPageTransition = this.styleValue;
|
|
21
|
+
if (this.durationValue) {
|
|
22
|
+
document.documentElement.style.setProperty("--page-transition-duration", this.durationValue);
|
|
23
|
+
}
|
|
24
|
+
if (this.easingValue) {
|
|
25
|
+
document.documentElement.style.setProperty("--page-transition-easing", this.easingValue);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
disconnect() {
|
|
29
|
+
this.injectedMeta?.remove();
|
|
30
|
+
this.injectedMeta = null;
|
|
31
|
+
delete document.documentElement.dataset.midwestPageTransition;
|
|
32
|
+
document.documentElement.style.removeProperty("--page-transition-duration");
|
|
33
|
+
document.documentElement.style.removeProperty("--page-transition-easing");
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { PageTransition as default };
|
|
38
|
+
//# sourceMappingURL=page_transition_component_controller.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"page_transition_component_controller.js","sources":["../../../../../../../app/components/midwest/page_transition_component/page_transition_component_controller.ts"],"sourcesContent":["import { Controller } from '@hotwired/stimulus'\n\n// Enables Turbo View Transitions by injecting the <meta name=\"view-transition\">\n// tag that Turbo 8 requires to wrap navigations in document.startViewTransition(),\n// and by writing `data-midwest-page-transition` onto <html> so the CSS keyframe\n// rules scope to the chosen style. Custom duration/easing are pushed as CSS custom\n// properties on <html> so they win over the layer defaults without !important.\n//\n// Values\n// style — transition key, one of:\n// \"fade\" | \"slide-left\" | \"slide-right\" | \"slide-up\" |\n// \"slide-down\" | \"scale\" | \"none\"\n// duration — optional resolved CSS value (e.g. \"var(--duration-fast)\", \"200ms\")\n// easing — optional resolved CSS value (e.g. \"var(--ease-emphasized)\")\nexport default class PageTransition extends Controller<HTMLElement> {\n static values = {\n style: { type: String, default: 'fade' },\n duration: { type: String, default: '' },\n easing: { type: String, default: '' }\n }\n\n declare styleValue: string\n declare durationValue: string\n declare easingValue: string\n\n private injectedMeta: HTMLMetaElement | null = null\n\n connect () {\n // `style: :none` means \"install no transitions at all.\"\n if (this.styleValue === 'none') return\n\n // Inject the Turbo View Transitions meta tag if not already present. We\n // track what we injected so we can remove it precisely on disconnect.\n if (!document.querySelector('meta[name=\"view-transition\"]')) {\n const meta = document.createElement('meta')\n meta.name = 'view-transition'\n meta.content = 'same-origin'\n document.head.appendChild(meta)\n this.injectedMeta = meta\n }\n\n // The CSS uses this attribute to scope ::view-transition-* animations.\n document.documentElement.dataset.midwestPageTransition = this.styleValue\n\n // Per-call timing overrides — win over the layer defaults without !important.\n if (this.durationValue) {\n document.documentElement.style.setProperty('--page-transition-duration', this.durationValue)\n }\n if (this.easingValue) {\n document.documentElement.style.setProperty('--page-transition-easing', this.easingValue)\n }\n }\n\n disconnect () {\n this.injectedMeta?.remove()\n this.injectedMeta = null\n delete document.documentElement.dataset.midwestPageTransition\n document.documentElement.style.removeProperty('--page-transition-duration')\n document.documentElement.style.removeProperty('--page-transition-easing')\n }\n}\n"],"names":[],"mappings":";;AAcA,MAAqB,uBAAuB,UAAwB,CAAA;AAAA,EAClE,OAAO,MAAS,GAAA;AAAA,IACd,KAAO,EAAA,EAAE,IAAM,EAAA,MAAA,EAAQ,SAAS,MAAO,EAAA;AAAA,IACvC,QAAU,EAAA,EAAE,IAAM,EAAA,MAAA,EAAQ,SAAS,EAAG,EAAA;AAAA,IACtC,MAAQ,EAAA,EAAE,IAAM,EAAA,MAAA,EAAQ,SAAS,EAAG,EAAA;AAAA,GACtC,CAAA;AAAA,EAMQ,YAAuC,GAAA,IAAA,CAAA;AAAA,EAE/C,OAAW,GAAA;AAET,IAAA,IAAI,KAAK,UAAe,KAAA,MAAA;AAAQ,MAAA,OAAA;AAIhC,IAAA,IAAI,CAAC,QAAA,CAAS,aAAc,CAAA,8BAA8B,CAAG,EAAA;AAC3D,MAAM,MAAA,IAAA,GAAO,QAAS,CAAA,aAAA,CAAc,MAAM,CAAA,CAAA;AAC1C,MAAA,IAAA,CAAK,IAAO,GAAA,iBAAA,CAAA;AACZ,MAAA,IAAA,CAAK,OAAU,GAAA,aAAA,CAAA;AACf,MAAS,QAAA,CAAA,IAAA,CAAK,YAAY,IAAI,CAAA,CAAA;AAC9B,MAAA,IAAA,CAAK,YAAe,GAAA,IAAA,CAAA;AAAA,KACtB;AAGA,IAAS,QAAA,CAAA,eAAA,CAAgB,OAAQ,CAAA,qBAAA,GAAwB,IAAK,CAAA,UAAA,CAAA;AAG9D,IAAA,IAAI,KAAK,aAAe,EAAA;AACtB,MAAA,QAAA,CAAS,eAAgB,CAAA,KAAA,CAAM,WAAY,CAAA,4BAAA,EAA8B,KAAK,aAAa,CAAA,CAAA;AAAA,KAC7F;AACA,IAAA,IAAI,KAAK,WAAa,EAAA;AACpB,MAAA,QAAA,CAAS,eAAgB,CAAA,KAAA,CAAM,WAAY,CAAA,0BAAA,EAA4B,KAAK,WAAW,CAAA,CAAA;AAAA,KACzF;AAAA,GACF;AAAA,EAEA,UAAc,GAAA;AACZ,IAAA,IAAA,CAAK,cAAc,MAAO,EAAA,CAAA;AAC1B,IAAA,IAAA,CAAK,YAAe,GAAA,IAAA,CAAA;AACpB,IAAO,OAAA,QAAA,CAAS,gBAAgB,OAAQ,CAAA,qBAAA,CAAA;AACxC,IAAS,QAAA,CAAA,eAAA,CAAgB,KAAM,CAAA,cAAA,CAAe,4BAA4B,CAAA,CAAA;AAC1E,IAAS,QAAA,CAAA,eAAA,CAAgB,KAAM,CAAA,cAAA,CAAe,0BAA0B,CAAA,CAAA;AAAA,GAC1E;AACF;;;;"}
|