beiser
This commit is contained in:
parent
e52e451d98
commit
70251093a8
|
|
@ -185,7 +185,7 @@ export default function MapView({ theme = "dark" }) {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className="map-view">
|
||||
<div className="map-page">\n <div className="map-stage">
|
||||
<div ref={containerRef} className="map-container" />
|
||||
{status && <div className="map-status">{status}</div>}
|
||||
|
||||
|
|
@ -280,7 +280,12 @@ export default function MapView({ theme = "dark" }) {
|
|||
</div>
|
||||
)}
|
||||
|
||||
<div className="map-overlay map-overlay-wall">
|
||||
|
||||
</div>
|
||||
|
||||
{/* BELOW THE MAP (page scroll) */}
|
||||
<div className="below-stage">
|
||||
<div className="below-row">
|
||||
<div className="sociowall-panel">
|
||||
<div className="sociowall-header">Sociowall</div>
|
||||
<PostList
|
||||
|
|
@ -291,6 +296,7 @@ export default function MapView({ theme = "dark" }) {
|
|||
onSelectPost={handleSelectPost}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className="chat-panel">
|
||||
<div className="chat-header">Chat</div>
|
||||
<ul className="chat-users">
|
||||
|
|
@ -301,5 +307,9 @@ export default function MapView({ theme = "dark" }) {
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div style={{ height: 24 }} />
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,17 +2,17 @@ import maplibregl from "maplibre-gl";
|
|||
import React from "react";
|
||||
import { createRoot } from "react-dom/client";
|
||||
|
||||
import TemplateMarkerCard from "./TemplateMarkerCard";
|
||||
import { getDefaultTemplateCached } from "../../api/templates";
|
||||
import CardRenderer from "../Cards/CardRenderer";
|
||||
import { cardTokens } from "../../theme/cardTokens";
|
||||
import { getTemplateSpecForPost, adaptPostToTemplateData } from "./templateSpecs";
|
||||
|
||||
export function clearAllMarkers(markersRef, expandedElRef) {
|
||||
if (markersRef.current) {
|
||||
for (const m of markersRef.current) {
|
||||
try {
|
||||
if (m?.el && m.el.__reactRoot) {
|
||||
try { m.el.__reactRoot.unmount(); } catch {}
|
||||
m.el.__reactRoot = null;
|
||||
}
|
||||
if (m?.el && m.el.__swUnmount) m.el.__swUnmount();
|
||||
} catch {}
|
||||
try {
|
||||
m.marker.remove();
|
||||
} catch {}
|
||||
}
|
||||
|
|
@ -72,29 +72,30 @@ export function clearOcclusion(markersRef) {
|
|||
}
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return String(str)
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
function mountCard(container, spec, data, theme) {
|
||||
const root = createRoot(container);
|
||||
const tokens = cardTokens?.[theme] || cardTokens.blue;
|
||||
|
||||
root.render(
|
||||
React.createElement(CardRenderer, {
|
||||
spec,
|
||||
data,
|
||||
themeTokens: tokens,
|
||||
onAction: (action, value) => {
|
||||
if (action?.type === "open_url" && value) {
|
||||
try { window.open(value, "_blank"); } catch {}
|
||||
}
|
||||
},
|
||||
className: "",
|
||||
})
|
||||
);
|
||||
|
||||
return () => {
|
||||
try { root.unmount(); } catch {}
|
||||
};
|
||||
}
|
||||
|
||||
function currentTheme() {
|
||||
try {
|
||||
const t = document?.body?.getAttribute("data-theme") || "dark";
|
||||
if (t === "dark" || t === "blue" || t === "light") return t;
|
||||
} catch {}
|
||||
return "dark";
|
||||
}
|
||||
|
||||
function ensureReactRoot(el) {
|
||||
if (el.__reactRoot) return el.__reactRoot;
|
||||
el.__reactRoot = createRoot(el);
|
||||
return el.__reactRoot;
|
||||
}
|
||||
|
||||
export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) {
|
||||
export function createMarkerForPost(post, mapRef, markersRef, expandedElRef, theme = "blue") {
|
||||
const map = mapRef.current;
|
||||
if (!map) return;
|
||||
|
||||
|
|
@ -117,65 +118,98 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) {
|
|||
return;
|
||||
}
|
||||
|
||||
const title = (post.title || "").trim() || "Untitled";
|
||||
|
||||
const root = document.createElement("div");
|
||||
root.className = "post-pin post-pin--compact";
|
||||
root.style.zIndex = "1";
|
||||
|
||||
function unmountIfAny() {
|
||||
if (root.__swUnmount) {
|
||||
try { root.__swUnmount(); } catch {}
|
||||
root.__swUnmount = null;
|
||||
}
|
||||
}
|
||||
|
||||
function renderCompact() {
|
||||
unmountIfAny();
|
||||
root.className = "post-pin post-pin--compact";
|
||||
root.style.zIndex = "1";
|
||||
|
||||
// pointer stays: .post-pin-pointer-small (UNCHANGED)
|
||||
root.innerHTML = `
|
||||
<div class="post-pin-bubble">
|
||||
<div class="post-pin-dot"></div>
|
||||
<div class="post-pin-text">${escapeHtml(title)}</div>
|
||||
</div>
|
||||
<div class="sw-template-mini-wrap"></div>
|
||||
<div class="post-pin-pointer-small"></div>
|
||||
`;
|
||||
|
||||
if (root.__reactRoot) {
|
||||
try { root.__reactRoot.unmount(); } catch {}
|
||||
root.__reactRoot = null;
|
||||
const wrap = root.querySelector(".sw-template-mini-wrap");
|
||||
if (wrap) {
|
||||
const spec = getTemplateSpecForPost(post, "mini");
|
||||
const data = adaptPostToTemplateData(post);
|
||||
root.__swUnmount = mountCard(wrap, spec, data, theme);
|
||||
}
|
||||
|
||||
clearOcclusion(markersRef);
|
||||
}
|
||||
|
||||
async function renderExpanded() {
|
||||
function renderExpanded() {
|
||||
unmountIfAny();
|
||||
root.className = "post-pin post-pin--expanded";
|
||||
root.style.zIndex = "999999";
|
||||
|
||||
// pointer stays: .post-card-pointer (UNCHANGED)
|
||||
const headline = escapeHtml(post?.title || "Untitled");
|
||||
|
||||
root.innerHTML = `
|
||||
<div class="post-card">
|
||||
<div class="post-card-body" style="font-size:11px; opacity:.9;">Loading template…</div>
|
||||
<div class="post-card sw-expanded-shell">
|
||||
<div class="sw-expanded-top">
|
||||
<div class="sw-expanded-left">
|
||||
<div class="sw-template-full-wrap"></div>
|
||||
</div>
|
||||
|
||||
<div class="sw-expanded-right">
|
||||
<div class="sw-watch-title">Watching</div>
|
||||
<div class="sw-watch-list">
|
||||
<div class="sw-watch-item">☑ Julia ★<div class="sw-watch-msg">— I see it…</div></div>
|
||||
<div class="sw-watch-item">☑ Kim / CNN<div class="sw-watch-msg">— Blah blah blah</div></div>
|
||||
<div class="sw-watch-item">☑ Yan ★<div class="sw-watch-msg">— Watch this!!</div></div>
|
||||
</div>
|
||||
<button class="sw-add-feed-btn" type="button">ADD TO FEED</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="sw-news-generated">
|
||||
<div class="sw-news-title">${headline}</div>
|
||||
<div class="sw-news-sub">The news here (generated)</div>
|
||||
</div>
|
||||
|
||||
<div class="post-card-footer sw-actions-row">
|
||||
<button class="post-card-btn">Contact</button>
|
||||
<button class="post-card-btn">Like</button>
|
||||
<button class="post-card-btn">Share</button>
|
||||
<button class="post-card-btn">Fix</button>
|
||||
</div>
|
||||
|
||||
<div class="post-card-comments sw-livechat">
|
||||
<div class="sw-livechat-title">Live chat / comments</div>
|
||||
<div class="sw-livechat-body">
|
||||
<div class="sw-chat-line">— …</div>
|
||||
<div class="sw-chat-line">— what the f***?</div>
|
||||
<div class="sw-chat-line">— nice…</div>
|
||||
<div class="sw-chat-line">— my eyes!!</div>
|
||||
</div>
|
||||
<div class="sw-livechat-inputrow">
|
||||
<input class="sw-livechat-input" placeholder="Write a comment…" />
|
||||
<button class="sw-livechat-post" type="button">POST</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="post-card-pointer"></div>
|
||||
`;
|
||||
|
||||
const cardEl = root.querySelector(".post-card");
|
||||
if (!cardEl) return;
|
||||
|
||||
const theme = currentTheme();
|
||||
|
||||
try {
|
||||
const tpl = await getDefaultTemplateCached("news");
|
||||
|
||||
const rr = ensureReactRoot(cardEl);
|
||||
rr.render(
|
||||
React.createElement(TemplateMarkerCard, {
|
||||
post,
|
||||
template: tpl,
|
||||
theme,
|
||||
mode: "full",
|
||||
})
|
||||
);
|
||||
} catch (e) {
|
||||
cardEl.innerHTML = `
|
||||
<div style="font-weight:800; font-size:13px; margin-bottom:4px;">${escapeHtml(title)}</div>
|
||||
<div style="font-size:11px; opacity:.9;">(template load failed)</div>
|
||||
`;
|
||||
const wrap = root.querySelector(".sw-template-full-wrap");
|
||||
if (wrap) {
|
||||
const spec = getTemplateSpecForPost(post, "full");
|
||||
const data = adaptPostToTemplateData(post);
|
||||
root.__swUnmount = mountCard(wrap, spec, data, theme);
|
||||
}
|
||||
|
||||
requestAnimationFrame(() => {
|
||||
|
|
@ -210,3 +244,11 @@ export function createMarkerForPost(post, mapRef, markersRef, expandedElRef) {
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(str) {
|
||||
return String(str ?? "")
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,71 @@
|
|||
/**
|
||||
* Frontend-only template specs (no DB changes).
|
||||
* Keep pointers as-is:
|
||||
* - compact triangle: .post-pin-pointer-small
|
||||
* - expanded triangle: .post-card-pointer
|
||||
*/
|
||||
|
||||
export const TEMPLATE_SPECS = {
|
||||
news: {
|
||||
mini_spec: {
|
||||
size: { w: 280, h: 110 },
|
||||
background: { type: "gradient", value: "newsBlue" },
|
||||
radius: 18,
|
||||
layers: [
|
||||
{ id: "badge", type: "chip", x: 14, y: 12, text: "NEWS", style: "chip.news" },
|
||||
{ id: "title", type: "text", x: 14, y: 40, w: 252, h: 44, bind: "data.headline", style: "text.title", maxLines: 2 },
|
||||
{ id: "meta", type: "text", x: 14, y: 88, w: 252, h: 16, bind: "data.source", style: "text.meta", maxLines: 1 }
|
||||
],
|
||||
},
|
||||
full_spec: {
|
||||
size: { w: 360, h: 520 },
|
||||
background: { type: "solid", value: "surface" },
|
||||
radius: 22,
|
||||
layers: [
|
||||
{ id: "hero", type: "image", x: 0, y: 0, w: 360, h: 220, bind: "data.image" },
|
||||
{ id: "badge", type: "chip", x: 16, y: 16, text: "NEWS", style: "chip.news" },
|
||||
{ id: "title", type: "text", x: 16, y: 240, w: 328, h: 78, bind: "data.headline", style: "text.h1", maxLines: 3 },
|
||||
{ id: "summary", type: "text", x: 16, y: 325, w: 328, h: 110, bind: "data.summary", style: "text.body", maxLines: 5 },
|
||||
{
|
||||
id: "cta",
|
||||
type: "button",
|
||||
x: 16, y: 450, w: 328, h: 48,
|
||||
text: "Open source",
|
||||
style: "button.primary",
|
||||
action: { type: "open_url", bind: "data.url" }
|
||||
}
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
function normCat(post) {
|
||||
const c = (post?.category || "").toString().toUpperCase();
|
||||
if (c === "NEWS") return "news";
|
||||
if (c === "EVENT") return "news";
|
||||
if (c === "FRIENDS") return "news";
|
||||
if (c === "MARKET") return "news";
|
||||
return "news";
|
||||
}
|
||||
|
||||
export function getTemplateKeyForPost(post) {
|
||||
// Future mapping: if (post.template_id === 101) return "news";
|
||||
return normCat(post);
|
||||
}
|
||||
|
||||
export function getTemplateSpecForPost(post, variant /* "mini"|"full" */) {
|
||||
const key = getTemplateKeyForPost(post);
|
||||
const t = TEMPLATE_SPECS[key] || TEMPLATE_SPECS.news;
|
||||
return variant === "full" ? t.full_spec : t.mini_spec;
|
||||
}
|
||||
|
||||
export function adaptPostToTemplateData(post) {
|
||||
const headline = post?.title || "Untitled";
|
||||
const author = post?.author ? `by ${post.author}` : "";
|
||||
const sub = post?.sub_category ? String(post.sub_category) : "";
|
||||
const source = author || sub || "";
|
||||
const summary = post?.snippet || post?.body || "";
|
||||
const url = post?.url || "";
|
||||
const image = post?.image_large || post?.image_small || post?.image || post?.media_url || "";
|
||||
return { headline, source, summary, url, image };
|
||||
}
|
||||
|
|
@ -1,24 +1,28 @@
|
|||
import React from "react";
|
||||
|
||||
function getKmFromPost(post) {
|
||||
return post.km ?? post.distance_km ?? post.distanceKm ?? post.dist_km ?? null;
|
||||
}
|
||||
import CardRenderer from "../Cards/CardRenderer";
|
||||
import { cardTokens } from "../../theme/cardTokens";
|
||||
import { getTemplateSpecForPost, adaptPostToTemplateData } from "../Map/templateSpecs";
|
||||
|
||||
export default function PostCard({ post, selected, onSelect }) {
|
||||
const title = post.title || post.name || "Post";
|
||||
const fullBody = post.body || post.content || post.text || "";
|
||||
const body = fullBody.length > 80 ? fullBody.slice(0, 77) + "..." : fullBody;
|
||||
const km = getKmFromPost(post);
|
||||
|
||||
function handleClick() {
|
||||
if (onSelect) onSelect(post);
|
||||
}
|
||||
const spec = getTemplateSpecForPost(post, "mini");
|
||||
const data = adaptPostToTemplateData(post);
|
||||
|
||||
return (
|
||||
<article className={"post-card" + (selected ? " selected" : "")} onClick={handleClick}>
|
||||
<h3>{title}</h3>
|
||||
<p>{body}</p>
|
||||
{km != null && <span className="post-km">{km} km</span>}
|
||||
<article
|
||||
className={"post-card" + (selected ? " selected" : "")}
|
||||
onClick={() => onSelect && onSelect(post)}
|
||||
style={{ padding: 6 }}
|
||||
>
|
||||
<div className="sw-wall-mini">
|
||||
<CardRenderer
|
||||
spec={spec}
|
||||
data={data}
|
||||
themeTokens={cardTokens.blue}
|
||||
onAction={(action, value) => {
|
||||
if (action?.type === "open_url" && value) window.open(value, "_blank");
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
</article>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,5 @@
|
|||
.app-root { height: 100vh; display: flex; flex-direction: column; }
|
||||
.main-shell { flex: 1; display: flex; padding: 0; }
|
||||
.map-shell { position: relative; flex: 1; border-radius: 0; overflow: hidden; border: none; background: #020617; }
|
||||
.app-root{ min-height:100vh; display:flex; flex-direction:column; }
|
||||
|
||||
/* IMPORTANT: allow scrolling under the map */
|
||||
.main-shell{ flex: 0 0 auto; display:block; padding:0; }
|
||||
.map-shell{ position:relative; width:100%; height:62vh; border-radius:0; overflow:hidden; border:none; background:#020617; }
|
||||
|
|
|
|||
|
|
@ -1,5 +1,19 @@
|
|||
.map-view { position: relative; width: 100%; height: 100%; }
|
||||
.map-container, .maplibre-container { position: absolute; inset: 0; width: 100%; height: 100%; }
|
||||
.map-page { display: flex; flex-direction: column; }
|
||||
|
||||
/* Map stage (visible first). Then you scroll down for the wall. */
|
||||
.map-stage {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
height: clamp(520px, 72vh, 860px);
|
||||
}
|
||||
|
||||
.map-container, .maplibre-container {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.map-status {
|
||||
position: absolute;
|
||||
left: 0.5rem;
|
||||
|
|
@ -8,4 +22,21 @@
|
|||
padding: 0.2rem 0.4rem;
|
||||
background: rgba(0, 0, 0, 0.6);
|
||||
border-radius: 0.5rem;
|
||||
z-index: 30;
|
||||
}
|
||||
|
||||
/* Below map area */
|
||||
.below-stage{
|
||||
padding: 0.25rem 0.7rem 1.0rem;
|
||||
}
|
||||
.below-row{
|
||||
display:flex;
|
||||
gap:.6rem;
|
||||
align-items:stretch;
|
||||
}
|
||||
|
||||
/* Mobile: stack */
|
||||
@media (max-width: 640px){
|
||||
.map-stage{ height: clamp(520px, 78vh, 900px); }
|
||||
.below-row{ flex-direction: column; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -88,3 +88,548 @@
|
|||
border-top:11px solid rgba(8, 20, 40, 0.97);
|
||||
filter: drop-shadow(0 2px 4px rgba(0,0,0,.8));
|
||||
}
|
||||
|
||||
/* ===== Template marker wrappers (pointers stay the same classes) ===== */
|
||||
.sw-template-mini-wrap{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transform: translate(-50%, calc(-100% - 12px));
|
||||
}
|
||||
|
||||
.sw-expanded-shell{
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.sw-expanded-top{
|
||||
display:flex;
|
||||
gap:10px;
|
||||
align-items:flex-start;
|
||||
}
|
||||
|
||||
.sw-expanded-left{
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sw-expanded-right{
|
||||
width: 150px;
|
||||
min-width: 140px;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:8px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.sw-watch-title{
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
color:#bfdbfe;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .06em;
|
||||
}
|
||||
|
||||
.sw-watch-list{
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:6px;
|
||||
}
|
||||
|
||||
.sw-watch-item{
|
||||
font-size: 11px;
|
||||
color:#e8f5ff;
|
||||
background: rgba(3, 14, 32, 0.75);
|
||||
border: 1px solid rgba(90, 190, 255, 0.35);
|
||||
border-radius: 10px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.sw-watch-msg{
|
||||
opacity:.85;
|
||||
margin-top:2px;
|
||||
font-size: 10px;
|
||||
color:#c7e3ff;
|
||||
}
|
||||
|
||||
.sw-add-feed-btn{
|
||||
margin-top: 2px;
|
||||
border: 1px solid rgba(148,163,184,.7);
|
||||
border-radius: 10px;
|
||||
padding: 8px 10px;
|
||||
background: rgba(5, 35, 70, 0.85);
|
||||
color:#e8f5ff;
|
||||
font-weight: 900;
|
||||
font-size: 11px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.sw-news-generated{
|
||||
margin-top: 10px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 12px;
|
||||
background: rgba(3, 14, 32, 0.75);
|
||||
border: 1px solid rgba(90, 190, 255, 0.35);
|
||||
}
|
||||
|
||||
.sw-news-title{
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
color:#e8f5ff;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.sw-news-sub{
|
||||
font-size: 10px;
|
||||
color:#9eb7ff;
|
||||
}
|
||||
|
||||
.sw-actions-row{
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.sw-livechat{
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.sw-livechat-title{
|
||||
font-weight: 800;
|
||||
letter-spacing:.04em;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.sw-livechat-body{
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:3px;
|
||||
margin-bottom: 8px;
|
||||
opacity:.95;
|
||||
}
|
||||
|
||||
.sw-chat-line{
|
||||
font-size: 10px;
|
||||
color:#c7e3ff;
|
||||
}
|
||||
|
||||
.sw-livechat-inputrow{
|
||||
display:flex;
|
||||
gap:8px;
|
||||
align-items:center;
|
||||
}
|
||||
|
||||
.sw-livechat-input{
|
||||
flex:1;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(90, 190, 255, 0.35);
|
||||
background: rgba(3, 14, 32, 0.75);
|
||||
color:#e8f5ff;
|
||||
padding: 8px 10px;
|
||||
font-size: 11px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.sw-livechat-post{
|
||||
border:none;
|
||||
border-radius: 10px;
|
||||
padding: 8px 12px;
|
||||
background: rgba(56,189,248,0.22);
|
||||
color:#e8f5ff;
|
||||
font-weight: 900;
|
||||
font-size: 11px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
/* ===== Template marker wrappers (pointers stay the same classes) ===== */
|
||||
.sw-template-mini-wrap{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transform: translate(-50%, calc(-100% - 12px));
|
||||
}
|
||||
|
||||
.sw-expanded-shell{
|
||||
padding: 10px 12px;
|
||||
}
|
||||
|
||||
.sw-expanded-top{
|
||||
display:flex;
|
||||
gap:10px;
|
||||
align-items:flex-start;
|
||||
}
|
||||
|
||||
.sw-expanded-left{
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.sw-expanded-right{
|
||||
width: 150px;
|
||||
min-width: 140px;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:8px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.sw-watch-title{
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
color:#bfdbfe;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .06em;
|
||||
}
|
||||
|
||||
.sw-watch-list{
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:6px;
|
||||
}
|
||||
|
||||
.sw-watch-item{
|
||||
font-size: 11px;
|
||||
color:#e8f5ff;
|
||||
background: rgba(3, 14, 32, 0.75);
|
||||
border: 1px solid rgba(90, 190, 255, 0.35);
|
||||
border-radius: 10px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.sw-watch-msg{
|
||||
opacity:.85;
|
||||
margin-top:2px;
|
||||
font-size: 10px;
|
||||
color:#c7e3ff;
|
||||
}
|
||||
|
||||
.sw-add-feed-btn{
|
||||
margin-top: 2px;
|
||||
border: 1px solid rgba(148,163,184,.7);
|
||||
border-radius: 10px;
|
||||
padding: 8px 10px;
|
||||
background: rgba(5, 35, 70, 0.85);
|
||||
color:#e8f5ff;
|
||||
font-weight: 900;
|
||||
font-size: 11px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.sw-news-generated{
|
||||
margin-top: 10px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 12px;
|
||||
background: rgba(3, 14, 32, 0.75);
|
||||
border: 1px solid rgba(90, 190, 255, 0.35);
|
||||
}
|
||||
|
||||
.sw-news-title{
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
color:#e8f5ff;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.sw-news-sub{
|
||||
font-size: 10px;
|
||||
color:#9eb7ff;
|
||||
}
|
||||
|
||||
.sw-actions-row{
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.sw-livechat{
|
||||
margin-top: 8px;
|
||||
}
|
||||
|
||||
.sw-livechat-title{
|
||||
font-weight: 800;
|
||||
letter-spacing:.04em;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.sw-livechat-body{
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:3px;
|
||||
margin-bottom: 8px;
|
||||
opacity:.95;
|
||||
}
|
||||
|
||||
.sw-chat-line{
|
||||
font-size: 10px;
|
||||
color:#c7e3ff;
|
||||
}
|
||||
|
||||
.sw-livechat-inputrow{
|
||||
display:flex;
|
||||
gap:8px;
|
||||
align-items:center;
|
||||
}
|
||||
|
||||
.sw-livechat-input{
|
||||
flex:1;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(90, 190, 255, 0.35);
|
||||
background: rgba(3, 14, 32, 0.75);
|
||||
color:#e8f5ff;
|
||||
padding: 8px 10px;
|
||||
font-size: 11px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.sw-livechat-post{
|
||||
border:none;
|
||||
border-radius: 10px;
|
||||
padding: 8px 12px;
|
||||
background: rgba(56,189,248,0.22);
|
||||
color:#e8f5ff;
|
||||
font-weight: 900;
|
||||
font-size: 11px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
/* ===== Template marker wrappers (pointers stay the same classes) ===== */
|
||||
.sw-template-mini-wrap{
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: 0;
|
||||
transform: translate(-50%, calc(-100% - 12px));
|
||||
}
|
||||
|
||||
.sw-expanded-shell{ padding: 10px 12px; }
|
||||
|
||||
.sw-expanded-top{
|
||||
display:flex;
|
||||
gap:10px;
|
||||
align-items:flex-start;
|
||||
}
|
||||
|
||||
.sw-expanded-left{ flex: 1; }
|
||||
|
||||
.sw-expanded-right{
|
||||
width: 150px;
|
||||
min-width: 140px;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:8px;
|
||||
padding-top: 4px;
|
||||
}
|
||||
|
||||
.sw-watch-title{
|
||||
font-size: 11px;
|
||||
font-weight: 800;
|
||||
color:#bfdbfe;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: .06em;
|
||||
}
|
||||
|
||||
.sw-watch-list{ display:flex; flex-direction:column; gap:6px; }
|
||||
|
||||
.sw-watch-item{
|
||||
font-size: 11px;
|
||||
color:#e8f5ff;
|
||||
background: rgba(3, 14, 32, 0.75);
|
||||
border: 1px solid rgba(90, 190, 255, 0.35);
|
||||
border-radius: 10px;
|
||||
padding: 6px 8px;
|
||||
}
|
||||
|
||||
.sw-watch-msg{
|
||||
opacity:.85;
|
||||
margin-top:2px;
|
||||
font-size: 10px;
|
||||
color:#c7e3ff;
|
||||
}
|
||||
|
||||
.sw-add-feed-btn{
|
||||
margin-top: 2px;
|
||||
border: 1px solid rgba(148,163,184,.7);
|
||||
border-radius: 10px;
|
||||
padding: 8px 10px;
|
||||
background: rgba(5, 35, 70, 0.85);
|
||||
color:#e8f5ff;
|
||||
font-weight: 900;
|
||||
font-size: 11px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
.sw-news-generated{
|
||||
margin-top: 10px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 12px;
|
||||
background: rgba(3, 14, 32, 0.75);
|
||||
border: 1px solid rgba(90, 190, 255, 0.35);
|
||||
}
|
||||
|
||||
.sw-news-title{
|
||||
font-size: 12px;
|
||||
font-weight: 900;
|
||||
color:#e8f5ff;
|
||||
margin-bottom: 2px;
|
||||
}
|
||||
|
||||
.sw-news-sub{ font-size: 10px; color:#9eb7ff; }
|
||||
|
||||
.sw-actions-row{ margin-top: 8px; }
|
||||
|
||||
.sw-livechat{ margin-top: 8px; }
|
||||
|
||||
.sw-livechat-title{
|
||||
font-weight: 800;
|
||||
letter-spacing:.04em;
|
||||
margin-bottom: 6px;
|
||||
}
|
||||
|
||||
.sw-livechat-body{
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
gap:3px;
|
||||
margin-bottom: 8px;
|
||||
opacity:.95;
|
||||
}
|
||||
|
||||
.sw-chat-line{ font-size: 10px; color:#c7e3ff; }
|
||||
|
||||
.sw-livechat-inputrow{ display:flex; gap:8px; align-items:center; }
|
||||
|
||||
.sw-livechat-input{
|
||||
flex:1;
|
||||
border-radius: 999px;
|
||||
border: 1px solid rgba(90, 190, 255, 0.35);
|
||||
background: rgba(3, 14, 32, 0.75);
|
||||
color:#e8f5ff;
|
||||
padding: 8px 10px;
|
||||
font-size: 11px;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.sw-livechat-post{
|
||||
border:none;
|
||||
border-radius: 10px;
|
||||
padding: 8px 12px;
|
||||
background: rgba(56,189,248,0.22);
|
||||
color:#e8f5ff;
|
||||
font-weight: 900;
|
||||
font-size: 11px;
|
||||
cursor:pointer;
|
||||
}
|
||||
|
||||
/* =========================================================
|
||||
RESPONSIVE SIZING FIX
|
||||
- Mini markers were too big (280x110 real size)
|
||||
- Expanded card was overflowing on mobile
|
||||
========================================================= */
|
||||
|
||||
/* Mini template wrapper: keep pointer EXACTLY as-is, only scale the card */
|
||||
.sw-template-mini-wrap{
|
||||
/* was: translate(-50%, calc(-100% - 12px)) */
|
||||
transform: translate(-50%, calc(-100% - 12px)) scale(var(--sw-mini-scale, 0.72));
|
||||
transform-origin: bottom center;
|
||||
will-change: transform;
|
||||
}
|
||||
|
||||
/* Expanded card: make it fit phone screens nicely */
|
||||
.post-pin--expanded .post-card{
|
||||
max-width: min(92vw, 380px);
|
||||
width: auto;
|
||||
max-height: min(72vh, 640px);
|
||||
overflow: auto;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
}
|
||||
|
||||
/* Layout in expanded sketch area */
|
||||
.sw-expanded-top{
|
||||
gap: 10px;
|
||||
}
|
||||
|
||||
/* Mobile tweaks */
|
||||
@media (max-width: 640px){
|
||||
/* Shrink mini markers even more on phones */
|
||||
.sw-template-mini-wrap{
|
||||
--sw-mini-scale: 0.58;
|
||||
}
|
||||
|
||||
/* Expanded: stack instead of side-by-side */
|
||||
.sw-expanded-top{
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
/* Right panel becomes horizontal strip (or you can hide it if you prefer) */
|
||||
.sw-expanded-right{
|
||||
width: 100%;
|
||||
min-width: 0;
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
overflow-x: auto;
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
.sw-watch-title{
|
||||
white-space: nowrap;
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.sw-watch-list{
|
||||
flex-direction: row;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.sw-watch-item{
|
||||
min-width: 180px;
|
||||
flex: 0 0 auto;
|
||||
}
|
||||
|
||||
.sw-add-feed-btn{
|
||||
flex: 0 0 auto;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* SW_EXPANDED_OFFSET_FIX: lower expanded cards on phones (keep pointers unchanged) */
|
||||
@media (max-width: 640px){
|
||||
.post-pin--expanded .post-card{
|
||||
transform: translate(-50%, calc(-100% - 6px));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* SW_MOBILE_EXPANDED_TIGHTEN: make expanded marker card less tall on phones (no pointer change) */
|
||||
@media (max-width: 640px){
|
||||
.post-pin--expanded .post-card{
|
||||
padding: 8px 10px;
|
||||
max-width: 300px;
|
||||
}
|
||||
.post-pin--expanded .post-card-main{ gap: 6px; margin-bottom: 6px; }
|
||||
.post-pin--expanded .post-card-media{ width: 56px; height: 56px; border-radius: 10px; }
|
||||
.post-pin--expanded .post-card-title{ font-size: 12px; line-height: 15px; }
|
||||
.post-pin--expanded .post-card-body{ font-size: 11px; max-height: 56px; }
|
||||
}
|
||||
|
||||
/* SW_TEMPLATE_FULL_TIGHTEN: make template-full card less tall on phones */
|
||||
@media (max-width: 640px){
|
||||
.sw-template-full{
|
||||
transform: translate(-50%, calc(-100% - 14px)) scale(0.88);
|
||||
transform-origin: bottom center;
|
||||
}
|
||||
}
|
||||
|
||||
/* ===== MOBILE TUNING: expanded marker popup (template OR classic) ===== */
|
||||
@media (max-width: 640px){
|
||||
/* shrink the whole expanded marker a bit */
|
||||
.post-pin--expanded{
|
||||
transform: translate3d(0,0,0) scale(0.86);
|
||||
transform-origin: bottom center;
|
||||
}
|
||||
|
||||
/* keep it inside screen */
|
||||
.post-pin--expanded .post-card{
|
||||
max-width: min(92vw, 340px) !important;
|
||||
}
|
||||
|
||||
/* title/body: prevent giant blocks */
|
||||
.post-pin--expanded .post-card-title{ font-size: 12px !important; line-height: 15px !important; }
|
||||
.post-pin--expanded .post-card-body{ font-size: 11px !important; line-height: 14px !important; max-height: 54px !important; }
|
||||
|
||||
/* buttons/footer area: tighter */
|
||||
.post-pin--expanded .post-card-footer{ gap: 5px !important; }
|
||||
.post-pin--expanded .post-card-btn{ font-size: 10px !important; padding: 3px 7px !important; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,34 +1,39 @@
|
|||
.map-overlay-wall {
|
||||
position:absolute;
|
||||
left:.7rem; right:.7rem; bottom:.6rem;
|
||||
display:flex; gap:.6rem; align-items:stretch;
|
||||
pointer-events:none; z-index:10;
|
||||
/* Sociowall + Chat BELOW the map */
|
||||
.below-row{
|
||||
display:flex;
|
||||
gap:.6rem;
|
||||
align-items:stretch;
|
||||
}
|
||||
.map-overlay-wall * { pointer-events:auto; }
|
||||
|
||||
.sociowall-panel {
|
||||
flex: 1.5;
|
||||
min-height: 80px;
|
||||
max-height: 150px;
|
||||
flex: 1.8;
|
||||
min-height: 160px;
|
||||
background: rgba(15, 23, 42, 0.96);
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.9);
|
||||
box-shadow: 0 10px 28px rgba(0,0,0,.9), 0 0 18px rgba(56,189,248,.45);
|
||||
padding: .45rem .55rem;
|
||||
display:flex; flex-direction:column;
|
||||
}
|
||||
.sociowall-header {
|
||||
font-size: .78rem; font-weight: 600;
|
||||
letter-spacing: .06em; text-transform: uppercase;
|
||||
color:#bfdbfe; margin-bottom:.25rem;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
}
|
||||
|
||||
.post-list { flex:1; overflow-y:auto; padding-right:.15rem; }
|
||||
.post-list-header { display:flex; align-items:center; gap:.5rem; margin-bottom:.2rem; font-size:.7rem; color:#cbd5f5; }
|
||||
.km-filter input[type="range"] { width:130px; }
|
||||
.sociowall-header {
|
||||
font-size: .78rem;
|
||||
font-weight: 700;
|
||||
letter-spacing: .06em;
|
||||
text-transform: uppercase;
|
||||
color:#bfdbfe;
|
||||
margin-bottom:.25rem;
|
||||
}
|
||||
|
||||
.post-list { flex: 1; overflow-y: auto; padding-right: .15rem; max-height: 44vh; }
|
||||
.post-list-header { display:flex; align-items:center; gap:.5rem; margin-bottom:.25rem; font-size:.7rem; color:#cbd5f5; }
|
||||
.km-filter input[type="range"] { width:160px; }
|
||||
|
||||
.status-text { font-size:.72rem; opacity:.85; margin:.15rem 0; }
|
||||
.status-error { color:#fecaca; }
|
||||
|
||||
/* list items compact */
|
||||
.post-card {
|
||||
border-radius:10px;
|
||||
border:1px solid rgba(55, 65, 81, 0.9);
|
||||
|
|
@ -53,28 +58,41 @@
|
|||
.post-card h3 { margin:0 0 .12rem 0; font-size:.8rem; }
|
||||
.post-card p { margin:0; font-size:.72rem; opacity:.9; }
|
||||
|
||||
.post-km {
|
||||
display:inline-block; margin-top:.12rem; font-size:.68rem;
|
||||
padding:.05rem .35rem; border-radius:999px;
|
||||
background: rgba(15, 118, 110, 0.2);
|
||||
border: 1px solid rgba(45, 212, 191, 0.7);
|
||||
color: #a5f3fc;
|
||||
}
|
||||
|
||||
.chat-panel {
|
||||
width:150px; min-width:130px;
|
||||
max-height:150px;
|
||||
width: 170px;
|
||||
min-width: 170px;
|
||||
background: rgba(15, 23, 42, 0.96);
|
||||
border-radius: 14px;
|
||||
border: 1px solid rgba(148, 163, 184, 0.9);
|
||||
box-shadow: 0 10px 28px rgba(0,0,0,.9), 0 0 18px rgba(56,189,248,.45);
|
||||
padding:.4rem .45rem;
|
||||
display:flex; flex-direction:column;
|
||||
display:flex;
|
||||
flex-direction:column;
|
||||
|
||||
/* stays visible beside wall while scrolling inside the list */
|
||||
position: sticky;
|
||||
top: 10px;
|
||||
align-self: flex-start;
|
||||
}
|
||||
|
||||
.chat-header {
|
||||
font-size:.78rem; font-weight:600; letter-spacing:.06em; text-transform:uppercase;
|
||||
color:#bfdbfe; margin-bottom:.2rem;
|
||||
font-size:.78rem;
|
||||
font-weight:700;
|
||||
letter-spacing:.06em;
|
||||
text-transform:uppercase;
|
||||
color:#bfdbfe;
|
||||
margin-bottom:.2rem;
|
||||
}
|
||||
.chat-users { list-style:none; padding:0; margin:0; font-size:.76rem; }
|
||||
.chat-users li { display:flex; align-items:center; gap:.3rem; padding:.18rem .1rem; }
|
||||
.chat-users li::before { content:"👤"; font-size:.72rem; opacity:.9; }
|
||||
|
||||
/* Mobile: stack */
|
||||
@media (max-width: 640px){
|
||||
.below-row{ flex-direction: column; }
|
||||
.chat-panel{ width: 100%; min-width: 0; position: static; }
|
||||
.post-list{ max-height: 46vh; }
|
||||
}
|
||||
|
||||
/* Guard: never render Sociowall overlay on top of the map */
|
||||
.map-overlay-wall{ display:none !important; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue