mirror of
https://github.com/tiennm99/HugoBlox-kit.git
synced 2026-10-11 03:12:56 +00:00
feat(map): add open-source map block (MapLibre GL + OpenFreeMap)
Single-location + multi-marker, side-by-side and map-only layouts. Zero-config — no API key. MapLibre is vendored via vite and loaded conditionally when a page contains a map block. Provider resolution is a single switchable function so other tile providers can be added later without changing the block contract.
This commit is contained in:
1 parent
f107371dc2
commit
4de0ad0af9
12 files changed
+518
-2
No files matched your search
@@ -0,0 +1 @@
|
||||
.maplibregl-map{font:12px/20px Helvetica Neue,Arial,Helvetica,sans-serif;overflow:hidden;position:relative;-webkit-tap-highlight-color:rgb(0 0 0/0)}.maplibregl-canvas{left:0;position:absolute;top:0}.maplibregl-map:fullscreen{height:100%;width:100%}.maplibregl-ctrl-group button.maplibregl-ctrl-compass{touch-action:none}.maplibregl-canvas-container.maplibregl-interactive,.maplibregl-ctrl-group button.maplibregl-ctrl-compass{cursor:grab;-webkit-user-select:none;-moz-user-select:none;user-select:none}.maplibregl-canvas-container.maplibregl-interactive.maplibregl-track-pointer{cursor:pointer}.maplibregl-canvas-container.maplibregl-interactive:active,.maplibregl-ctrl-group button.maplibregl-ctrl-compass:active{cursor:grabbing}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-canvas-container.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:pinch-zoom}.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan,.maplibregl-canvas-container.maplibregl-touch-zoom-rotate.maplibregl-touch-drag-pan .maplibregl-canvas{touch-action:none}.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures,.maplibregl-canvas-container.maplibregl-touch-drag-pan.maplibregl-cooperative-gestures .maplibregl-canvas{touch-action:pan-x pan-y}.maplibregl-ctrl-bottom-left,.maplibregl-ctrl-bottom-right,.maplibregl-ctrl-top-left,.maplibregl-ctrl-top-right{pointer-events:none;position:absolute;z-index:2}.maplibregl-ctrl-top-left{left:0;top:0}.maplibregl-ctrl-top-right{right:0;top:0}.maplibregl-ctrl-bottom-left{bottom:0;left:0}.maplibregl-ctrl-bottom-right{bottom:0;right:0}.maplibregl-ctrl{clear:both;pointer-events:auto;transform:translate(0)}.maplibregl-ctrl-top-left .maplibregl-ctrl{float:left;margin:10px 0 0 10px}.maplibregl-ctrl-top-right .maplibregl-ctrl{float:right;margin:10px 10px 0 0}.maplibregl-ctrl-bottom-left .maplibregl-ctrl{float:left;margin:0 0 10px 10px}.maplibregl-ctrl-bottom-right .maplibregl-ctrl{float:right;margin:0 10px 10px 0}.maplibregl-ctrl-group{background:#fff;border-radius:4px}.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px rgba(0,0,0,.1)}@media (forced-colors:active){.maplibregl-ctrl-group:not(:empty){box-shadow:0 0 0 2px ButtonText}}.maplibregl-ctrl-group button{background-color:transparent;border:0;box-sizing:border-box;cursor:pointer;display:block;height:29px;outline:none;padding:0;width:29px}.maplibregl-ctrl-group button+button{border-top:1px solid #ddd}.maplibregl-ctrl button .maplibregl-ctrl-icon{background-position:50%;background-repeat:no-repeat;display:block;height:100%;width:100%}@media (forced-colors:active){.maplibregl-ctrl-icon{background-color:transparent}.maplibregl-ctrl-group button+button{border-top:1px solid ButtonText}}.maplibregl-ctrl button::-moz-focus-inner{border:0;padding:0}.maplibregl-ctrl-attrib-button:focus,.maplibregl-ctrl-group button:focus{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl button:disabled{cursor:not-allowed}.maplibregl-ctrl button:disabled .maplibregl-ctrl-icon{opacity:.25}.maplibregl-ctrl button:not(:disabled):hover{background-color:rgb(0 0 0/5%)}.maplibregl-ctrl-group button:focus:focus-visible{box-shadow:0 0 2px 2px #0096ff}.maplibregl-ctrl-group button:focus:not(:focus-visible){box-shadow:none}.maplibregl-ctrl-group button:focus:first-child{border-radius:4px 4px 0 0}.maplibregl-ctrl-group button:focus:last-child{border-radius:0 0 4px 4px}.maplibregl-ctrl-group button:focus:only-child{border-radius:inherit}.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23333' viewBox='0 0 29 29'%3E%3Cpath d='M14.5 8.5c-.75 0-1.5.75-1.5 1.5v3h-3c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h3v3c0 .75.75 1.5 1.5 1.5S16 19.75 16 19v-3h3c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13h-3v-3c0-.75-.75-1.5-1.5-1.5'/%3E%3C/svg%3E")}@media (forced-colors:active){.maplibregl-ctrl button.maplibregl-ctrl-zoom-out .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewBox='0 0 29 29'%3E%3Cpath d='M10 13c-.75 0-1.5.75-1.5 1.5S9.25 16 10 16h9c.75 0 1.5-.75 1.5-1.5S19.75 13 19 13z'/%3E%3C/svg%3E")}.maplibregl-ctrl button.maplibregl-ctrl-zoom-in .maplibregl-ctrl-icon{background-image:url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='29' height='29' fill='%23fff' viewLine truncated
|
||||
@@ -0,0 +1,59 @@
|
||||
/**
|
||||
* MapLibre GL JS
|
||||
* @license 3-Clause BSD. Full text of license: https://github.com/maplibre/maplibre-gl-js/blob/v4.7.1/LICENSE.txt
|
||||
*/
|
||||
(function (global, factory) {
|
||||
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
|
||||
typeof define === 'function' && define.amd ? define(factory) :
|
||||
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.maplibregl = factory());
|
||||
})(this, (function () { 'use strict';
|
||||
|
||||
/* eslint-disable */
|
||||
|
||||
var maplibregl = {};
|
||||
var modules = {};
|
||||
function define(moduleName, _dependencies, moduleFactory) {
|
||||
modules[moduleName] = moduleFactory;
|
||||
|
||||
// to get the list of modules see generated dist/maplibre-gl-dev.js file (look for `define(` calls)
|
||||
if (moduleName !== 'index') {
|
||||
return;
|
||||
}
|
||||
|
||||
// we assume that when an index module is initializing then other modules are loaded already
|
||||
var workerBundleString = 'var sharedModule = {}; (' + modules.shared + ')(sharedModule); (' + modules.worker + ')(sharedModule);'
|
||||
|
||||
var sharedModule = {};
|
||||
// the order of arguments of a module factory depends on rollup (it decides who is whose dependency)
|
||||
// to check the correct order, see dist/maplibre-gl-dev.js file (look for `define(` calls)
|
||||
// we assume that for our 3 chunks it will generate 3 modules and their order is predefined like the following
|
||||
modules.shared(sharedModule);
|
||||
modules.index(maplibregl, sharedModule);
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
maplibregl.setWorkerUrl(window.URL.createObjectURL(new Blob([workerBundleString], { type: 'text/javascript' })));
|
||||
}
|
||||
|
||||
return maplibregl;
|
||||
};
|
||||
|
||||
|
||||
|
||||
define("shared",["exports"],(function(t){"use strict";function e(t,e,r,n){return new(r||(r=Promise))((function(i,s){function a(t){try{l(n.next(t));}catch(t){s(t);}}function o(t){try{l(n.throw(t));}catch(t){s(t);}}function l(t){var e;t.done?i(t.value):(e=t.value,e instanceof r?e:new r((function(t){t(e);}))).then(a,o);}l((n=n.apply(t,e||[])).next());}))}function r(t){return t&&t.__esModule&&Object.prototype.hasOwnProperty.call(t,"default")?t.default:t}"function"==typeof SuppressedError&&SuppressedError;var n=i;function i(t,e){this.x=t,this.y=e;}i.prototype={clone:function(){return new i(this.x,this.y)},add:function(t){return this.clone()._add(t)},sub:function(t){return this.clone()._sub(t)},multByPoint:function(t){return this.clone()._multByPoint(t)},divByPoint:function(t){return this.clone()._divByPoint(t)},mult:function(t){return this.clone()._mult(t)},div:function(t){return this.clone()._div(t)},rotate:function(t){return this.clone()._rotate(t)},rotateAround:function(t,e){return this.clone()._rotateAround(t,e)},matMult:function(t){return this.clone()._matMult(t)},unit:function(){return this.clone()._unit()},perp:function(){return this.clone()._perp()},round:function(){return this.clone()._round()},mag:function(){return Math.sqrt(this.x*this.x+this.y*this.y)},equals:function(t){return this.x===t.x&&this.y===t.y},dist:function(t){return Math.sqrt(this.distSqr(t))},distSqr:function(t){var e=t.x-this.x,r=t.y-this.y;return e*e+r*r},angle:function(){return Math.atan2(this.y,this.x)},angleTo:function(t){return Math.atan2(this.y-t.y,this.x-t.x)},angleWith:function(t){return this.angleWithSep(t.x,t.y)},angleWithSep:function(t,e){return Math.atan2(this.x*e-this.y*t,this.x*t+this.y*e)},_matMult:function(t){var e=t[2]*this.x+t[3]*this.y;return this.x=t[0]*this.x+t[1]*this.y,this.y=e,this},_add:function(t){return this.x+=t.x,this.y+=t.y,this},_sub:function(t){return this.x-=t.x,this.y-=t.y,this},_mult:function(t){return this.x*=t,this.y*=t,this},_div:function(t){return this.x/=t,this.y/=t,this},_multByPoint:function(t){return this.x*=t.x,this.y*=t.y,this},_divByPoint:function(t){return this.x/=t.x,this.y/=t.y,this},_unit:function(){return this._div(this.mag()),this},_perp:function(){var t=this.y;return this.y=this.x,this.x=-t,this},_rotate:function(t){var e=Math.cos(t),r=Math.sin(t),n=r*this.x+e*this.y;return this.x=e*this.x-r*this.y,this.y=n,this},_rotateAround:function(t,e){var r=Math.cos(t),n=Math.sin(t),i=e.y+n*(this.x-e.x)+r*(this.y-e.y);return this.x=e.x+r*(this.x-e.x)-n*(this.y-e.y),this.y=i,this},_round:function(){return this.x=Math.round(this.x),this.y=Math.round(this.y),this}},i.convert=function(t){return t instanceof i?t:Array.isArray(t)?new i(t[0],t[1]):t};var s=r(n),a=o;function o(t,e,r,n){this.cx=3*t,this.bx=3*(r-t)-this.cx,this.ax=1-this.cx-this.bx,this.cy=3*e,this.by=3*(n-e)-this.cy,this.ay=1-this.cy-this.by,this.p1x=t,this.p1y=e,this.p2x=r,this.p2y=n;}o.prototype={sampleCurveX:function(t){return ((this.ax*t+this.bx)*t+this.cx)*t},sampleCurveY:function(t){return ((this.ay*t+this.by)*t+this.cy)*t},sampleCurveDerivativeX:function(t){return (3*this.ax*t+2*this.bx)*t+this.cx},solveCurveX:function(t,e){if(void 0===e&&(e=1e-6),t<0)return 0;if(t>1)return 1;for(var r=t,n=0;n<8;n++){var i=this.sampleCurveX(r)-t;if(Math.abs(i)<e)return r;var s=this.sampleCurveDerivativeX(r);if(Math.abs(s)<1e-6)break;r-=i/s;}var a=0,o=1;for(r=t,n=0;n<20&&(i=this.sampleCurveX(r),!(Math.abs(i-t)<e));n++)t>i?a=r:o=r,r=.5*(o-a)+a;return r},solve:function(t,e){return this.sampleCurveY(this.solveCurveX(t,e))}};var l=r(a);let u,c;function h(){return null==u&&(u="undefined"!=typeof OffscreenCanvas&&new OffscreenCanvas(1,1).getContext("2d")&&"function"==typeof createImageBitmap),u}function p(){if(null==c&&(c=!1,h())){const t=5,e=new OffscreenCanvas(t,t).getContext("2d",{willReadFrequently:!0});if(e){for(let r=0;r<t*t;r++){const n=4*r;e.fillStyle=`rgb(${n},${n+1},${n+2})`,e.fillRect(r%t,Math.floor(r/t),1,1);}const r=e.getImageData(0,0,t,t).data;for(let e=0;e<t*t*4;e++)if(e%4!=3&&r[e]!==e){c=!0;break}}}return c||!1}function f(t,e,r,n){const i=new l(t,e,r,n);return t=>i.solve(t)}const d=f(.25,.1,.25,1);function y(t,e,r){return Math.min(r,Math.max(e,t))}function m(t,e,r){const n=r-e,i=((t-e)%n+n)%n+e;return i===e?r:i}function g(t,...e){for(const r of e)for(const e in r)t[e]=r[e];return t}let x=1;function v(t,e,r){const n={};for(const r in t)n[r]=e.call(this,t[r],r,t);return n}function b(t,e,r){const n={};for(const r in t)e.call(this,t[r],r,t)&&(n[r]=t[r]);return n}function w(t){return Array.isArray(t)?t.map(w):"object"==typeof t&&t?v(t,w):t}const _={};function A(t){_[t]||("undefined"!=typeof console&&console.warn(t),_[t]=!0);}function S(t,e,r){return (r.y-t.y)*(e.x-t.x)>(e.y-t.y)*(r.x-t.x)}function k(t){return "undefined"!=typeof WorkerGlobalScope&&void 0!==t&&t instanceof WorkerGlobalScope}let M=null;function I(t){return "undefined"!=typeof ImageBitmap&&t instanceof ImageBitmap}const z="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCLine truncated
|
||||
|
||||
define("worker",["./shared"],(function(e){"use strict";class t{constructor(e){this.keyCache={},e&&this.replace(e);}replace(e){this._layerConfigs={},this._layers={},this.update(e,[]);}update(t,o){for(const o of t){this._layerConfigs[o.id]=o;const t=this._layers[o.id]=e.aA(o);t._featureFilter=e.a7(t.filter),this.keyCache[o.id]&&delete this.keyCache[o.id];}for(const e of o)delete this.keyCache[e],delete this._layerConfigs[e],delete this._layers[e];this.familiesBySource={};const i=e.bk(Object.values(this._layerConfigs),this.keyCache);for(const e of i){const t=e.map((e=>this._layers[e.id])),o=t[0];if("none"===o.visibility)continue;const i=o.source||"";let s=this.familiesBySource[i];s||(s=this.familiesBySource[i]={});const r=o.sourceLayer||"_geojsonTileLayer";let n=s[r];n||(n=s[r]=[]),n.push(t);}}}class o{constructor(t){const o={},i=[];for(const e in t){const s=t[e],r=o[e]={};for(const e in s){const t=s[+e];if(!t||0===t.bitmap.width||0===t.bitmap.height)continue;const o={x:0,y:0,w:t.bitmap.width+2,h:t.bitmap.height+2};i.push(o),r[e]={rect:o,metrics:t.metrics};}}const{w:s,h:r}=e.p(i),n=new e.o({width:s||1,height:r||1});for(const i in t){const s=t[i];for(const t in s){const r=s[+t];if(!r||0===r.bitmap.width||0===r.bitmap.height)continue;const a=o[i][t].rect;e.o.copy(r.bitmap,n,{x:0,y:0},{x:a.x+1,y:a.y+1},r.bitmap);}}this.image=n,this.positions=o;}}e.bl("GlyphAtlas",o);class i{constructor(t){this.tileID=new e.S(t.tileID.overscaledZ,t.tileID.wrap,t.tileID.canonical.z,t.tileID.canonical.x,t.tileID.canonical.y),this.uid=t.uid,this.zoom=t.zoom,this.pixelRatio=t.pixelRatio,this.tileSize=t.tileSize,this.source=t.source,this.overscaling=this.tileID.overscaleFactor(),this.showCollisionBoxes=t.showCollisionBoxes,this.collectResourceTiming=!!t.collectResourceTiming,this.returnDependencies=!!t.returnDependencies,this.promoteId=t.promoteId,this.inFlightDependencies=[];}parse(t,i,r,n){return e._(this,void 0,void 0,(function*(){this.status="parsing",this.data=t,this.collisionBoxArray=new e.a5;const a=new e.bm(Object.keys(t.layers).sort()),l=new e.bn(this.tileID,this.promoteId);l.bucketLayerIDs=[];const c={},u={featureIndex:l,iconDependencies:{},patternDependencies:{},glyphDependencies:{},availableImages:r},h=i.familiesBySource[this.source];for(const o in h){const i=t.layers[o];if(!i)continue;1===i.version&&e.w(`Vector tile source "${this.source}" layer "${o}" does not use vector tile spec v2 and therefore may have some rendering errors.`);const n=a.encode(o),d=[];for(let e=0;e<i.length;e++){const t=i.feature(e),s=l.getId(t,o);d.push({feature:t,id:s,index:e,sourceLayerIndex:n});}for(const t of h[o]){const o=t[0];o.source!==this.source&&e.w(`layer.source = ${o.source} does not equal this.source = ${this.source}`),o.minzoom&&this.zoom<Math.floor(o.minzoom)||o.maxzoom&&this.zoom>=o.maxzoom||"none"!==o.visibility&&(s(t,this.zoom,r),(c[o.id]=o.createBucket({index:l.bucketLayerIDs.length,layers:t,zoom:this.zoom,pixelRatio:this.pixelRatio,overscaling:this.overscaling,collisionBoxArray:this.collisionBoxArray,sourceLayerIndex:n,sourceID:this.source})).populate(d,u,this.tileID.canonical),l.bucketLayerIDs.push(t.map((e=>e.id))));}}const d=e.aF(u.glyphDependencies,(e=>Object.keys(e).map(Number)));this.inFlightDependencies.forEach((e=>null==e?void 0:e.abort())),this.inFlightDependencies=[];let f=Promise.resolve({});if(Object.keys(d).length){const e=new AbortController;this.inFlightDependencies.push(e),f=n.sendAsync({type:"GG",data:{stacks:d,source:this.source,tileID:this.tileID,type:"glyphs"}},e);}const g=Object.keys(u.iconDependencies);let p=Promise.resolve({});if(g.length){const e=new AbortController;this.inFlightDependencies.push(e),p=n.sendAsync({type:"GI",data:{icons:g,source:this.source,tileID:this.tileID,type:"icons"}},e);}const m=Object.keys(u.patternDependencies);let y=Promise.resolve({});if(m.length){const e=new AbortController;this.inFlightDependencies.push(e),y=n.sendAsync({type:"GI",data:{icons:m,source:this.source,tileID:this.tileID,type:"patterns"}},e);}const[v,w,x]=yield Promise.all([f,p,y]),b=new o(v),S=new e.bo(w,x);for(const t in c){const o=c[t];o instanceof e.a6?(s(o.layers,this.zoom,r),e.bp({bucket:o,glyphMap:v,glyphPositions:b.positions,imageMap:w,imagePositions:S.iconPositions,showCollisionBoxes:this.showCollisionBoxes,canonical:this.tileID.canonical})):o.hasPattern&&(o instanceof e.bq||o instanceof e.br||o instanceof e.bs)&&(s(o.layers,this.zoom,r),o.addFeatures(u,this.tileID.canonical,S.patternPositions));}return this.status="done",{buckets:Object.values(c).filter((e=>!e.isEmpty())),featureIndex:l,collisionBoxArray:this.collisionBoxArray,glyphAtlasImage:b.image,imageAtlas:S,glyphMap:this.returnDependencies?v:null,iconMap:this.returnDependencies?w:null,glyphPositions:this.returnDependencies?b.positions:null}}))}}function s(t,o,i){const s=new e.z(o);for(const e of t)e.recalculate(s,i);}class r{constructor(e,t,o){this.actor=e,this.layerIndex=t,this.availableImages=o,this.fetching={},this.loading={},this.loaded={};}loadVeLine truncated
|
||||
|
||||
define("index",["exports","./shared"],(function(t,e){"use strict";var i="4.7.1";let a,s;const o={now:"undefined"!=typeof performance&&performance&&performance.now?performance.now.bind(performance):Date.now.bind(Date),frameAsync:t=>new Promise(((i,a)=>{const s=requestAnimationFrame(i);t.signal.addEventListener("abort",(()=>{cancelAnimationFrame(s),a(e.c());}));})),getImageData(t,e=0){return this.getImageCanvasContext(t).getImageData(-e,-e,t.width+2*e,t.height+2*e)},getImageCanvasContext(t){const e=window.document.createElement("canvas"),i=e.getContext("2d",{willReadFrequently:!0});if(!i)throw new Error("failed to create canvas 2d context");return e.width=t.width,e.height=t.height,i.drawImage(t,0,0,t.width,t.height),i},resolveURL:t=>(a||(a=document.createElement("a")),a.href=t,a.href),hardwareConcurrency:"undefined"!=typeof navigator&&navigator.hardwareConcurrency||4,get prefersReducedMotion(){return !!matchMedia&&(null==s&&(s=matchMedia("(prefers-reduced-motion: reduce)")),s.matches)}};class r{static testProp(t){if(!r.docStyle)return t[0];for(let e=0;e<t.length;e++)if(t[e]in r.docStyle)return t[e];return t[0]}static create(t,e,i){const a=window.document.createElement(t);return void 0!==e&&(a.className=e),i&&i.appendChild(a),a}static createNS(t,e){return window.document.createElementNS(t,e)}static disableDrag(){r.docStyle&&r.selectProp&&(r.userSelect=r.docStyle[r.selectProp],r.docStyle[r.selectProp]="none");}static enableDrag(){r.docStyle&&r.selectProp&&(r.docStyle[r.selectProp]=r.userSelect);}static setTransform(t,e){t.style[r.transformProp]=e;}static addEventListener(t,e,i,a={}){t.addEventListener(e,i,"passive"in a?a:a.capture);}static removeEventListener(t,e,i,a={}){t.removeEventListener(e,i,"passive"in a?a:a.capture);}static suppressClickInternal(t){t.preventDefault(),t.stopPropagation(),window.removeEventListener("click",r.suppressClickInternal,!0);}static suppressClick(){window.addEventListener("click",r.suppressClickInternal,!0),window.setTimeout((()=>{window.removeEventListener("click",r.suppressClickInternal,!0);}),0);}static getScale(t){const e=t.getBoundingClientRect();return {x:e.width/t.offsetWidth||1,y:e.height/t.offsetHeight||1,boundingClientRect:e}}static getPoint(t,i,a){const s=i.boundingClientRect;return new e.P((a.clientX-s.left)/i.x-t.clientLeft,(a.clientY-s.top)/i.y-t.clientTop)}static mousePos(t,e){const i=r.getScale(t);return r.getPoint(t,i,e)}static touchPos(t,e){const i=[],a=r.getScale(t);for(let s=0;s<e.length;s++)i.push(r.getPoint(t,a,e[s]));return i}static mouseButton(t){return t.button}static remove(t){t.parentNode&&t.parentNode.removeChild(t);}}r.docStyle="undefined"!=typeof window&&window.document&&window.document.documentElement.style,r.selectProp=r.testProp(["userSelect","MozUserSelect","WebkitUserSelect","msUserSelect"]),r.transformProp=r.testProp(["transform","WebkitTransform"]);const n={supported:!1,testSupport:function(t){!c&&h&&(u?d(t):l=t);}};let l,h,c=!1,u=!1;function d(t){const e=t.createTexture();t.bindTexture(t.TEXTURE_2D,e);try{if(t.texImage2D(t.TEXTURE_2D,0,t.RGBA,t.RGBA,t.UNSIGNED_BYTE,h),t.isContextLost())return;n.supported=!0;}catch(t){}t.deleteTexture(e),c=!0;}var _;"undefined"!=typeof document&&(h=document.createElement("img"),h.onload=()=>{l&&d(l),l=null,u=!0;},h.onerror=()=>{c=!0,l=null;},h.src="data:image/webp;base64,UklGRh4AAABXRUJQVlA4TBEAAAAvAQAAAAfQ//73v/+BiOh/AAA="),function(t){let i,a,s,o;t.resetRequestQueue=()=>{i=[],a=0,s=0,o={};},t.addThrottleControl=t=>{const e=s++;return o[e]=t,e},t.removeThrottleControl=t=>{delete o[t],l();},t.getImage=(t,a,s=!0)=>new Promise(((o,r)=>{n.supported&&(t.headers||(t.headers={}),t.headers.accept="image/webp,*/*"),e.e(t,{type:"image"}),i.push({abortController:a,requestParameters:t,supportImageRefresh:s,state:"queued",onError:t=>{r(t);},onSuccess:t=>{o(t);}}),l();}));const r=t=>e._(this,void 0,void 0,(function*(){t.state="running";const{requestParameters:i,supportImageRefresh:s,onError:o,onSuccess:r,abortController:n}=t,c=!1===s&&!e.i(self)&&!e.g(i.url)&&(!i.headers||Object.keys(i.headers).reduce(((t,e)=>t&&"accept"===e),!0));a++;const u=c?h(i,n):e.m(i,n);try{const i=yield u;delete t.abortController,t.state="completed",i.data instanceof HTMLImageElement||e.b(i.data)?r(i):i.data&&r({data:yield(d=i.data,"function"==typeof createImageBitmap?e.d(d):e.f(d)),cacheControl:i.cacheControl,expires:i.expires});}catch(e){delete t.abortController,o(e);}finally{a--,l();}var d;})),l=()=>{const t=(()=>{for(const t of Object.keys(o))if(o[t]())return !0;return !1})()?e.a.MAX_PARALLEL_IMAGE_REQUESTS_PER_FRAME:e.a.MAX_PARALLEL_IMAGE_REQUESTS;for(let e=a;e<t&&i.length>0;e++){const t=i.shift();t.abortController.signal.aborted?e--:r(t);}},h=(t,i)=>new Promise(((a,s)=>{const o=new Image,r=t.url,n=t.credentials;n&&"include"===n?o.crossOrigin="use-credentials":(n&&"same-origin"===n||!e.s(r))&&(o.crossOrigin="anonymous"),i.signal.addEventListener("abort",(()=>{o.src="",s(e.c());})),o.fetchPriority="high",o.onload=()=>{o.onerror=o.onload=null,a(Line truncated
|
||||
|
||||
//
|
||||
// Our custom intro provides a specialized "define()" function, called by the
|
||||
// AMD modules below, that sets up the worker blob URL and then executes the
|
||||
// main module, storing its exported value as 'maplibregl'
|
||||
|
||||
|
||||
var maplibregl$1 = maplibregl;
|
||||
|
||||
return maplibregl$1;
|
||||
|
||||
}));
|
||||
//# sourceMappingURL=maplibre-gl.js.map
|
||||
@@ -0,0 +1,15 @@
|
||||
import {render} from "preact";
|
||||
import {MapBlock} from "./component.jsx";
|
||||
|
||||
function renderMapBlocks() {
|
||||
const blocks = document.querySelectorAll('[data-block-type="map"]');
|
||||
blocks.forEach((block) => {
|
||||
const propsData = block.dataset.props;
|
||||
if (propsData) {
|
||||
const props = JSON.parse(propsData);
|
||||
render(<MapBlock {...props} />, block);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
renderMapBlocks();
|
||||
@@ -0,0 +1,313 @@
|
||||
import {useEffect, useRef} from "preact/hooks";
|
||||
|
||||
/*
|
||||
* Map block — open-source interactive maps for HugoBlox.
|
||||
*
|
||||
* Default provider stack: MapLibre GL (BSD) + OpenFreeMap (free OSM-based vector
|
||||
* tiles, no API key, no rate limit). Provider resolution is intentionally a
|
||||
* single switchable function so additional providers (MapTiler, Mapbox, Google,
|
||||
* Stadia) can be added later without changing the component contract.
|
||||
*/
|
||||
|
||||
const OPENFREEMAP_STYLE_URLS = {
|
||||
streets: "https://tiles.openfreemap.org/styles/liberty",
|
||||
light: "https://tiles.openfreemap.org/styles/positron",
|
||||
dark: "https://tiles.openfreemap.org/styles/dark",
|
||||
bright: "https://tiles.openfreemap.org/styles/bright",
|
||||
};
|
||||
|
||||
function resolveStyleUrl(provider, styleName) {
|
||||
const name = (provider && provider.name) || "openfreemap";
|
||||
const style = styleName || (provider && provider.style) || "streets";
|
||||
switch (name) {
|
||||
case "openfreemap":
|
||||
return OPENFREEMAP_STYLE_URLS[style] || OPENFREEMAP_STYLE_URLS.streets;
|
||||
default:
|
||||
return OPENFREEMAP_STYLE_URLS.streets;
|
||||
}
|
||||
}
|
||||
|
||||
const HEIGHT_CLASS = {
|
||||
sm: "h-72 md:h-80",
|
||||
md: "h-96 md:h-[28rem]",
|
||||
lg: "h-[28rem] md:h-[36rem]",
|
||||
full: "h-[calc(100vh-4rem)]",
|
||||
};
|
||||
|
||||
function heightClass(h) {
|
||||
return HEIGHT_CLASS[h] || HEIGHT_CLASS.md;
|
||||
}
|
||||
|
||||
function renderInline(text) {
|
||||
if (!text) return "";
|
||||
return String(text)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/\*\*(.*?)\*\*/g, "<strong>$1</strong>")
|
||||
.replace(/\*(.*?)\*/g, "<em>$1</em>");
|
||||
}
|
||||
|
||||
function escapeHtml(text) {
|
||||
return String(text ?? "")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
}
|
||||
|
||||
function directionsUrl(location, override) {
|
||||
if (override) return override;
|
||||
if (location?.lat == null || location?.lng == null) return null;
|
||||
return `https://www.openstreetmap.org/directions?to=${location.lat}%2C${location.lng}`;
|
||||
}
|
||||
|
||||
function MapCanvas({location, zoom, markers, provider, style, interactive, attribution, design}) {
|
||||
const ref = useRef(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!ref.current) return;
|
||||
if (typeof window === "undefined") return;
|
||||
|
||||
let map;
|
||||
let resizeObserver;
|
||||
let cancelled = false;
|
||||
let attempts = 0;
|
||||
|
||||
function init() {
|
||||
if (cancelled) return;
|
||||
const lib = window.maplibregl;
|
||||
if (!lib) {
|
||||
if (attempts++ < 60) {
|
||||
setTimeout(init, 50);
|
||||
} else {
|
||||
console.warn("[hugoblox/map] MapLibre GL did not load in time");
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const pins =
|
||||
markers && markers.length > 0
|
||||
? markers.filter((m) => m.lat != null && m.lng != null)
|
||||
: location?.lat != null && location?.lng != null
|
||||
? [{lat: location.lat, lng: location.lng, title: location.address}]
|
||||
: [];
|
||||
|
||||
const center =
|
||||
pins.length > 0
|
||||
? [pins[0].lng, pins[0].lat]
|
||||
: location?.lat != null && location?.lng != null
|
||||
? [location.lng, location.lat]
|
||||
: [0, 0];
|
||||
|
||||
map = new lib.Map({
|
||||
container: ref.current,
|
||||
style: resolveStyleUrl(provider, style),
|
||||
center,
|
||||
zoom: zoom || 14,
|
||||
attributionControl: attribution === false ? false : {compact: true},
|
||||
interactive: interactive !== false,
|
||||
cooperativeGestures: design?.cooperative_gestures === true,
|
||||
});
|
||||
|
||||
if (interactive !== false) {
|
||||
map.addControl(new lib.NavigationControl({showCompass: false}), "top-right");
|
||||
}
|
||||
|
||||
if (pins.length > 1) {
|
||||
const bounds = new lib.LngLatBounds();
|
||||
pins.forEach((m) => bounds.extend([m.lng, m.lat]));
|
||||
map.once("load", () => map.fitBounds(bounds, {padding: 60, maxZoom: 15, duration: 0}));
|
||||
}
|
||||
|
||||
pins.forEach((m) => {
|
||||
const marker = new lib.Marker({color: "var(--map-pin-color, #6366f1)"}).setLngLat([m.lng, m.lat]);
|
||||
const popupHtml = [
|
||||
m.title ? `<strong>${escapeHtml(m.title)}</strong>` : "",
|
||||
m.description ? `<div class="mt-1 text-sm">${escapeHtml(m.description)}</div>` : "",
|
||||
m.url ? `<div class="mt-2"><a class="text-primary-600 underline" href="${escapeHtml(m.url)}">View details →</a></div>` : "",
|
||||
]
|
||||
.filter(Boolean)
|
||||
.join("");
|
||||
if (popupHtml) {
|
||||
marker.setPopup(new lib.Popup({offset: 24, closeButton: true}).setHTML(popupHtml));
|
||||
}
|
||||
marker.addTo(map);
|
||||
});
|
||||
|
||||
// MapLibre measures the container at construction. In grid/flex layouts the
|
||||
// resolved size may not be final on the first frame, so the GL canvas ends
|
||||
// up smaller than the visible container and tiles paint into nothing.
|
||||
// Observe size changes and forward to map.resize().
|
||||
if (typeof ResizeObserver !== "undefined") {
|
||||
resizeObserver = new ResizeObserver(() => {
|
||||
if (map) map.resize();
|
||||
});
|
||||
resizeObserver.observe(ref.current);
|
||||
}
|
||||
}
|
||||
|
||||
init();
|
||||
|
||||
return () => {
|
||||
cancelled = true;
|
||||
if (resizeObserver) resizeObserver.disconnect();
|
||||
if (map) map.remove();
|
||||
};
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [location?.lat, location?.lng, zoom, JSON.stringify(markers), style, interactive, attribution]);
|
||||
|
||||
return <div ref={ref} class="w-full h-full bg-gray-100 dark:bg-gray-800" aria-label="Map" role="img" />;
|
||||
}
|
||||
|
||||
function AddressCard({content, location, ctaUrl, dark}) {
|
||||
const {title, subtitle, cta = {}} = content;
|
||||
const addressLines = (location?.address || "")
|
||||
.split("\n")
|
||||
.map((line) => line.trim())
|
||||
.filter(Boolean);
|
||||
const hasItems = addressLines.length > 0 || cta.phone || cta.email;
|
||||
|
||||
return (
|
||||
<div class={`flex flex-col justify-center p-6 sm:p-8 ${dark ? "bg-gray-900 text-white" : "bg-white dark:bg-gray-800"}`}>
|
||||
{title && (
|
||||
<h2
|
||||
class="text-2xl sm:text-3xl font-bold text-gray-900 dark:text-white tracking-tight mb-2"
|
||||
dangerouslySetInnerHTML={{__html: renderInline(title)}}
|
||||
/>
|
||||
)}
|
||||
{subtitle && (
|
||||
<p class="text-base text-gray-600 dark:text-gray-400 mb-6" dangerouslySetInnerHTML={{__html: renderInline(subtitle)}} />
|
||||
)}
|
||||
|
||||
{hasItems && (
|
||||
<ul class="space-y-3 mb-6">
|
||||
{addressLines.length > 0 && (
|
||||
<li class="flex items-start gap-3 text-sm leading-6 text-gray-700 dark:text-gray-300">
|
||||
<svg class="mt-0.5 h-5 w-5 flex-shrink-0 text-primary-600 dark:text-primary-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M9.69 18.933l.003.001C9.89 19.02 10 19 10 19s.11.02.308-.066l.002-.001.006-.003.018-.008a5.741 5.741 0 00.281-.14c.186-.096.446-.24.757-.433.62-.384 1.445-.966 2.274-1.765C15.302 14.988 17 12.493 17 9A7 7 0 103 9c0 3.492 1.698 5.988 3.355 7.584a13.731 13.731 0 002.273 1.765 11.842 11.842 0 00.976.544l.062.029.018.008.006.003zM10 11.25a2.25 2.25 0 100-4.5 2.25 2.25 0 000 4.5z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
<span>
|
||||
{addressLines.map((line, i) => (
|
||||
<span key={i} class="block">
|
||||
{line}
|
||||
</span>
|
||||
))}
|
||||
</span>
|
||||
</li>
|
||||
)}
|
||||
{cta.phone && (
|
||||
<li class="flex items-start gap-3 text-sm leading-6 text-gray-700 dark:text-gray-300">
|
||||
<svg class="mt-0.5 h-5 w-5 flex-shrink-0 text-primary-600 dark:text-primary-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path d="M2 3a1 1 0 011-1h2.153a1 1 0 01.986.836l.74 4.435a1 1 0 01-.54 1.06l-1.548.773a11.037 11.037 0 006.105 6.105l.774-1.548a1 1 0 011.059-.54l4.435.74a1 1 0 01.836.986V17a1 1 0 01-1 1h-2C7.82 18 2 12.18 2 5V3z" />
|
||||
</svg>
|
||||
<a href={`tel:${cta.phone}`} class="hover:text-primary-600 dark:hover:text-primary-400">
|
||||
{cta.phone}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
{cta.email && (
|
||||
<li class="flex items-start gap-3 text-sm leading-6 text-gray-700 dark:text-gray-300">
|
||||
<svg class="mt-0.5 h-5 w-5 flex-shrink-0 text-primary-600 dark:text-primary-400" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path d="M2.003 5.884L10 9.882l7.997-3.998A2 2 0 0016 4H4a2 2 0 00-1.997 1.884z" />
|
||||
<path d="M18 8.118l-8 4-8-4V14a2 2 0 002 2h12a2 2 0 002-2V8.118z" />
|
||||
</svg>
|
||||
<a href={`mailto:${cta.email}`} class="hover:text-primary-600 dark:hover:text-primary-400 break-all">
|
||||
{cta.email}
|
||||
</a>
|
||||
</li>
|
||||
)}
|
||||
</ul>
|
||||
)}
|
||||
|
||||
{ctaUrl && (
|
||||
<a
|
||||
href={ctaUrl}
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
class="inline-flex items-center justify-center gap-2 self-start rounded-xl bg-gradient-to-r from-primary-600 to-secondary-600 px-5 py-3 text-sm font-semibold text-white shadow-sm hover:from-primary-700 hover:to-secondary-700 hover:shadow-md transition-all duration-200 focus:outline-none focus:ring-2 focus:ring-primary-500 focus:ring-offset-2 dark:focus:ring-offset-gray-900"
|
||||
>
|
||||
{(content.cta && content.cta.directions && content.cta.directions.text) || "Get directions"}
|
||||
<svg class="h-4 w-4" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true">
|
||||
<path
|
||||
fill-rule="evenodd"
|
||||
d="M3 10a.75.75 0 01.75-.75h10.638L10.23 5.29a.75.75 0 111.04-1.08l5.5 5.25a.75.75 0 010 1.08l-5.5 5.25a.75.75 0 11-1.04-1.08l4.158-3.96H3.75A.75.75 0 013 10z"
|
||||
clip-rule="evenodd"
|
||||
/>
|
||||
</svg>
|
||||
</a>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export const MapBlock = ({content = {}, design = {}, provider}) => {
|
||||
const {location = {}, zoom, markers, cta = {}} = content;
|
||||
const layout = design.layout || "side-by-side";
|
||||
const heightCls = heightClass(design.height);
|
||||
const radius = design.border === "none" ? "" : "rounded-3xl";
|
||||
const borderShadow = design.border === "none" ? "" : "ring-1 ring-gray-200 dark:ring-gray-700 shadow-lg";
|
||||
|
||||
const ctaUrl = directionsUrl(location, cta.directions?.url);
|
||||
|
||||
if (layout === "map-only") {
|
||||
return (
|
||||
<div class="py-12 sm:py-16 px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-7xl mx-auto">
|
||||
{(content.title || content.subtitle) && (
|
||||
<div class="text-center max-w-3xl mx-auto mb-8">
|
||||
{content.title && (
|
||||
<h2
|
||||
class="text-3xl sm:text-4xl font-bold text-gray-900 dark:text-white tracking-tight mb-3"
|
||||
dangerouslySetInnerHTML={{__html: renderInline(content.title)}}
|
||||
/>
|
||||
)}
|
||||
{content.subtitle && (
|
||||
<p class="text-lg text-gray-600 dark:text-gray-400" dangerouslySetInnerHTML={{__html: renderInline(content.subtitle)}} />
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
<div class={`relative overflow-hidden ${heightCls} ${radius} ${borderShadow}`}>
|
||||
<MapCanvas
|
||||
location={location}
|
||||
zoom={zoom}
|
||||
markers={markers}
|
||||
provider={provider}
|
||||
style={design.style}
|
||||
interactive={design.interactive}
|
||||
attribution={design.attribution}
|
||||
design={design}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
// side-by-side (default)
|
||||
return (
|
||||
<div class="py-12 sm:py-16 px-4 sm:px-6 lg:px-8">
|
||||
<div class="max-w-7xl mx-auto">
|
||||
<div class={`grid grid-cols-1 lg:grid-cols-2 overflow-hidden ${radius} ${borderShadow}`}>
|
||||
<AddressCard content={content} location={location} ctaUrl={ctaUrl} />
|
||||
<div class={`relative ${heightCls} order-first lg:order-last`}>
|
||||
<MapCanvas
|
||||
location={location}
|
||||
zoom={zoom}
|
||||
markers={markers}
|
||||
provider={provider}
|
||||
style={design.style}
|
||||
interactive={design.interactive}
|
||||
attribution={design.attribution}
|
||||
design={design}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"id": "map",
|
||||
"name": "Map",
|
||||
"version": "1.0.0",
|
||||
"license": "MIT",
|
||||
"category": "location",
|
||||
"intent": ["inform", "navigate"],
|
||||
"verticals": ["academic-cv", "agency", "event", "startup", "local-business", "podcaster"],
|
||||
"tags": ["map", "location", "address", "directions", "openstreetmap", "openfreemap", "maplibre"],
|
||||
"description": "Interactive open-source map showing a single location or a set of markers. Powered by MapLibre GL and OpenFreeMap — zero-config, no API key, fully open. Renders an address card alongside the map by default, with optional CTAs for directions, phone, and email.",
|
||||
"author": "Hugo Blox",
|
||||
"homepage": "https://hugoblox.com/blocks/",
|
||||
"repository": "https://github.com/HugoBlox/kit",
|
||||
"keywords": ["hugo", "map", "maplibre", "openfreemap", "openstreetmap", "location", "directions"],
|
||||
"dependencies": {
|
||||
"maplibre-gl": "^4.7.1"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
/* Map block — MapLibre popup theming */
|
||||
|
||||
.maplibregl-popup-content {
|
||||
border-radius: 0.75rem;
|
||||
padding: 0.75rem 1rem;
|
||||
box-shadow: 0 4px 20px rgb(0 0 0 / 0.12);
|
||||
color: #111827;
|
||||
font-size: 0.875rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.maplibregl-popup-content a {
|
||||
color: var(--color-primary-600, #4f46e5);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.maplibregl-popup-close-button {
|
||||
padding: 0 0.375rem;
|
||||
font-size: 1rem;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.dark .maplibregl-popup-content {
|
||||
background: #1f2937;
|
||||
color: #f3f4f6;
|
||||
}
|
||||
|
||||
.dark .maplibregl-popup-close-button {
|
||||
color: #d1d5db;
|
||||
}
|
||||
|
||||
/* The popup tip is rendered as a triangle via a single non-transparent border
|
||||
side; MapLibre sets each anchor variant individually. Overriding all four
|
||||
sides is safe — the other three remain border:none. */
|
||||
.dark .maplibregl-popup-tip {
|
||||
border-top-color: #1f2937 !important;
|
||||
border-bottom-color: #1f2937 !important;
|
||||
border-left-color: #1f2937 !important;
|
||||
border-right-color: #1f2937 !important;
|
||||
}
|
||||
@@ -107,6 +107,11 @@
|
||||
{{ end }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Detect map blocks so libraries.html can load MapLibre GL */}}
|
||||
{{ if eq $block_type "map" }}
|
||||
{{ $page.Store.Set "has_map" true }}
|
||||
{{ end }}
|
||||
|
||||
{{/* Detect FAQ blocks for JSON-LD structured data (avoid race conditions) */}}
|
||||
{{ if eq $block_type "faq" }}
|
||||
{{ if $block.content.items }}
|
||||
|
||||
@@ -1,3 +1,11 @@
|
||||
{{/* MapLibre GL — load before Preact block scripts so window.maplibregl is available when the map block hydrates */}}
|
||||
{{ if .Page.Store.Get "has_map" }}
|
||||
{{ $maplibre_css := resources.Get "dist/lib/maplibre/maplibre-gl.css" | resources.Fingerprint "sha256" }}
|
||||
<link rel="stylesheet" href="{{ $maplibre_css.RelPermalink }}" integrity="{{ $maplibre_css.Data.Integrity }}" />
|
||||
{{ $maplibre_js := resources.Get "dist/lib/maplibre/maplibre-gl.js" | resources.Fingerprint "sha256" }}
|
||||
<script defer src="{{ $maplibre_js.RelPermalink }}" integrity="{{ $maplibre_js.Data.Integrity }}"></script>
|
||||
{{ end }}
|
||||
|
||||
{{/* Load Preact for component-based blocks */}}
|
||||
{{/* Check if any blocks on this page use Preact (set by init.html) */}}
|
||||
{{ if .Page.Store.Get "needs_preact" }}
|
||||
|
||||
+2
-1
@@ -16,6 +16,7 @@
|
||||
"dependencies": {
|
||||
"alpinejs": "^3.15.0",
|
||||
"katex": "^0.16.22",
|
||||
"maplibre-gl": "^4.7.1",
|
||||
"markmap-autoloader": "^0.18.12",
|
||||
"medium-zoom": "^1.0.8",
|
||||
"mermaid": "^11.12.0",
|
||||
@@ -40,7 +41,7 @@
|
||||
"update:starters": "poetry run python ./scripts/update.py",
|
||||
"vendor:libs": "vite build --config vite.config.js",
|
||||
"vendor:libs:watch": "vite build --config vite.config.js --watch",
|
||||
"vendor:update-and-build": "pnpm up katex mermaid markmap-autoloader alpinejs plotly.js preact --latest && pnpm vendor:libs",
|
||||
"vendor:update-and-build": "pnpm up katex mermaid markmap-autoloader alpinejs plotly.js preact maplibre-gl --latest && pnpm vendor:libs",
|
||||
"test:releaser": "poetry run python scripts/test_release_modules.py",
|
||||
"check:template-deps": "node scripts/check-template-deps.mjs"
|
||||
},
|
||||
|
||||
Generated
+3
@@ -14,6 +14,9 @@ importers:
|
||||
katex:
|
||||
specifier: ^0.16.22
|
||||
version: 0.16.22
|
||||
maplibre-gl:
|
||||
specifier: ^4.7.1
|
||||
version: 4.7.1
|
||||
markmap-autoloader:
|
||||
specifier: ^0.18.12
|
||||
version: 0.18.12(markmap-common@0.18.9)
|
||||
|
||||
@@ -47,6 +47,49 @@ sections:
|
||||
design:
|
||||
columns: "3"
|
||||
|
||||
- block: map
|
||||
content:
|
||||
title: "Visit the Lab"
|
||||
subtitle: "We're based on the Stanford campus — drop in any time during office hours."
|
||||
location:
|
||||
address: |
|
||||
353 Jane Stanford Way
|
||||
Stanford, CA 94305
|
||||
United States
|
||||
lat: 37.4275
|
||||
lng: -122.1697
|
||||
zoom: 15
|
||||
cta:
|
||||
directions:
|
||||
text: "Get directions"
|
||||
phone: "+1-650-555-0142"
|
||||
email: "hello@example.com"
|
||||
design:
|
||||
layout: side-by-side
|
||||
height: md
|
||||
style: streets
|
||||
|
||||
- block: map
|
||||
content:
|
||||
title: "Where to find us"
|
||||
markers:
|
||||
- lat: 37.4275
|
||||
lng: -122.1697
|
||||
title: "Stanford AI Lab"
|
||||
description: "Main campus building"
|
||||
- lat: 37.4419
|
||||
lng: -122.1430
|
||||
title: "Palo Alto Office"
|
||||
description: "Downtown Palo Alto"
|
||||
- lat: 37.7749
|
||||
lng: -122.4194
|
||||
title: "San Francisco HQ"
|
||||
description: "Mission District"
|
||||
design:
|
||||
layout: map-only
|
||||
height: lg
|
||||
style: light
|
||||
|
||||
- block: cta-card
|
||||
content:
|
||||
title: "Ready to get started?"
|
||||
|
||||
+11
-1
@@ -26,7 +26,7 @@ export default defineConfig({
|
||||
},
|
||||
rollupOptions: {
|
||||
// Don't bundle these libraries, just copy them
|
||||
external: ["mermaid", "plotly.js", "katex", "markmap-autoloader", "alpinejs"],
|
||||
external: ["mermaid", "plotly.js", "katex", "markmap-autoloader", "alpinejs", "maplibre-gl"],
|
||||
},
|
||||
},
|
||||
|
||||
@@ -75,6 +75,16 @@ export default defineConfig({
|
||||
dest: "alpinejs/",
|
||||
},
|
||||
|
||||
// MapLibre GL - Open-source interactive vector maps (map block)
|
||||
{
|
||||
src: "node_modules/maplibre-gl/dist/maplibre-gl.js",
|
||||
dest: "maplibre/",
|
||||
},
|
||||
{
|
||||
src: "node_modules/maplibre-gl/dist/maplibre-gl.css",
|
||||
dest: "maplibre/",
|
||||
},
|
||||
|
||||
// Preact for interactive components
|
||||
{
|
||||
src: "node_modules/preact/dist/preact.min.js",
|
||||
|
||||
Reference in new issue
Block a user