Skip to content
Merged
34 changes: 34 additions & 0 deletions components/field-showcase-card.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import GlimmerComponent from '@glimmer/component';
import { CardContainer } from '@cardstack/boxel-ui/components';

// Created to avoid duplicating the field-example card's styles across every
// field Spec (each Spec previously redefined this block twice -- once in
// Isolated, once in Edit -- with its own border/background/radius). Reuses
// CardContainer for those (per pret-ui-theming Rule 4: don't restate
// CardContainer's defaults) and only adds the layout this card needs.
export interface FieldShowcaseCardSignature {
Blocks: {
default: [];
};
Element: HTMLElement;
}

export default class FieldShowcaseCard extends GlimmerComponent<FieldShowcaseCardSignature> {
<template>
<CardContainer
@displayBoundaries={{true}}
class='field-showcase-card'
...attributes
>
{{yield}}
</CardContainer>
<style scoped>
.field-showcase-card {
padding: var(--boxel-sp-xs);
display: flex;
flex-direction: column;
gap: var(--boxel-sp-xs);
}
</style>
</template>
}
31 changes: 31 additions & 0 deletions components/field-showcase.gts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import GlimmerComponent from '@glimmer/component';

// Created to avoid duplicating this page-level container's styles across
// every field Spec (each Spec previously redefined this block twice --
// once in its Isolated component, once in Edit). Centralizing it here
// makes future theming/token updates a single-file change instead of a
// 6+ file find-and-replace.
export interface FieldShowcaseSignature {
Blocks: {
default: [];
};
Element: HTMLElement;
}

export default class FieldShowcase extends GlimmerComponent<FieldShowcaseSignature> {
<template>
<article class='field-showcase' ...attributes>
{{yield}}
</article>
<style scoped>
.field-showcase {
--field-showcase-background-color: var(--boxel-200);

height: 100%;
min-height: max-content;
padding: var(--boxel-sp);
background-color: var(--field-showcase-background-color);
}
</style>
</template>
}
94 changes: 50 additions & 44 deletions components/map-render.gts
Original file line number Diff line number Diff line change
Expand Up @@ -210,70 +210,76 @@ function injectPopupStyles() {
popupStylesInjected = true;
let style = document.createElement('style');
style.setAttribute('data-bx-map-popup', '');
// The ~20 hex colors below (popup text grays, status green/red, link blue,
// marker category colors) are special-use: they're data-driven distinct
// hues (e.g. food/hotel/attraction marker colors, open/closed status) with
// no matching semantic token in the current theme, not UI chrome. Our
// theming tokens/variables don't yet cover this case, so they stay as
// literal hex until a token exists for it.
style.textContent = `
.leaflet-popup-content-wrapper {
border-radius: 12px;
box-shadow: 0 8px 24px rgba(0,0,0,0.16);
padding: 2px;
border-radius: 0.75rem;
box-shadow: 0 0.5rem 1.5rem rgba(0,0,0,0.16);
padding: 0.125rem;
}
.leaflet-popup-content { margin: 12px 14px; }
.leaflet-popup-content { margin: 0.75rem 0.875rem; }
.bx-rich-popup {
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Helvetica, Arial, sans-serif;
color: #1f2937;
line-height: 1.4;
}
.bx-rich-popup strong { font-size: 14px; font-weight: 700; color: #111827; }
.bx-rich-popup strong { font-size: 0.875rem; font-weight: 700; color: #111827; }
.bx-rp-img {
display: block; width: 100%; height: 124px; object-fit: cover;
border-radius: 8px; margin: 8px 0; background: #f1f5f9;
display: block; width: 100%; height: 7.75rem; object-fit: cover;
border-radius: 0.5rem; margin: 0.5rem 0; background-color: #f1f5f9;
}
.bx-rp-group { margin-top: 8px; }
.bx-rp-group { margin-top: 0.5rem; }
.bx-rp-group-label {
display: flex; align-items: center; gap: 5px;
font-weight: 700; font-size: 11px; letter-spacing: 0.02em;
text-transform: uppercase; color: #6b7280; margin-bottom: 2px;
display: flex; align-items: center; gap: 0.3125rem;
font-weight: 700; font-size: 0.6875rem; letter-spacing: 0.02em;
text-transform: uppercase; color: #6b7280; margin-bottom: 0.125rem;
}
.bx-rp-item {
font-size: 12.5px; line-height: 1.6; color: #374151;
padding-left: 14px; position: relative;
font-size: 0.78125rem; line-height: 1.6; color: #374151;
padding-left: 0.875rem; position: relative;
}
.bx-rp-item::before {
content: ''; position: absolute; left: 4px; top: 8px;
width: 4px; height: 4px; border-radius: 50%; background: currentColor; opacity: 0.5;
content: ''; position: absolute; left: 0.25rem; top: 0.5rem;
width: 0.25rem; height: 0.25rem; border-radius: 50%; background-color: currentColor; opacity: 0.5;
}
.bx-rp-empty { margin-top: 8px; font-size: 12px; color: #9ca3af; }
.bx-rp-kind { margin-top: 2px; font-size: 11px; color: #6b7280; }
.bx-rp-empty { margin-top: 0.5rem; font-size: 0.75rem; color: #9ca3af; }
.bx-rp-kind { margin-top: 0.125rem; font-size: 0.6875rem; color: #6b7280; }
.bx-rp-hours {
display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
margin-top: 8px; font-size: 12px; color: #374151;
display: flex; flex-wrap: wrap; align-items: baseline; gap: 0.375rem;
margin-top: 0.5rem; font-size: 0.75rem; color: #374151;
}
.bx-rp-hours-text { color: #6b7280; }
.bx-rp-badge { font-size: 11px; font-weight: 700; }
.bx-rp-badge { font-size: 0.6875rem; font-weight: 700; }
.bx-rp-open { color: #15803d; }
.bx-rp-closed { color: #b91c1c; }
.bx-rp-meta {
display: block; margin-top: 6px; font-size: 12px;
display: block; margin-top: 0.375rem; font-size: 0.75rem;
color: #374151; text-decoration: none;
}
a.bx-rp-meta { color: #1a73e8; }
a.bx-rp-meta:hover { text-decoration: underline; }
.bx-rp-link {
display: inline-flex; align-items: center; gap: 5px;
margin-top: 10px; padding: 5px 10px; border-radius: 7px;
font-size: 12px; font-weight: 600; color: #1a73e8;
background: #eef4fe; text-decoration: none;
display: inline-flex; align-items: center; gap: 0.3125rem;
margin-top: 0.625rem; padding: 0.3125rem 0.625rem; border-radius: 0.4375rem;
font-size: 0.75rem; font-weight: 600; color: #1a73e8;
background-color: #eef4fe; text-decoration: none;
transition: background 0.12s ease;
}
.bx-rp-link:hover { background: #dbe8fd; }
.bx-rp-link:hover { background-color: #dbe8fd; }
/* skeleton shimmer */
.bx-sk-img, .bx-sk-line {
border-radius: 6px;
background: linear-gradient(100deg, #eceff3 30%, #f6f8fa 50%, #eceff3 70%);
border-radius: 0.375rem;
background-image: linear-gradient(100deg, #eceff3 30%, #f6f8fa 50%, #eceff3 70%);
background-size: 200% 100%;
animation: bx-sk-shimmer 1.2s ease-in-out infinite;
}
.bx-sk-img { width: 100%; height: 124px; margin: 8px 0; }
.bx-sk-line { height: 11px; margin-top: 8px; }
.bx-sk-img { width: 100%; height: 7.75rem; margin: 0.5rem 0; }
.bx-sk-line { height: 0.6875rem; margin-top: 0.5rem; }
.bx-sk-line.w-40 { width: 40%; }
.bx-sk-line.w-70 { width: 70%; }
.bx-sk-line.w-90 { width: 90%; }
Expand All @@ -285,14 +291,14 @@ function injectPopupStyles() {
stacks beneath (same 30px square, white, bordered "leaflet-bar" look). */
.bx-fit-btn {
box-sizing: border-box;
width: 34px; height: 34px; padding: 0;
width: 2.125rem; height: 2.125rem; padding: 0;
display: flex; align-items: center; justify-content: center;
background: #fff; color: #374151;
border: 2px solid rgba(0,0,0,0.2); border-radius: 4px;
background-color: var(--boxel-light); color: #374151;
border: 0.125rem solid rgba(0,0,0,0.2); border-radius: 0.25rem;
cursor: pointer;
transition: color 0.12s ease, background 0.12s ease;
}
.bx-fit-btn:hover { background: #f4f4f4; color: #111827; }
.bx-fit-btn:hover { background-color: var(--boxel-50); color: #111827; }
`;
document.head.appendChild(style);
}
Expand All @@ -319,9 +325,9 @@ export class MapRender extends GlimmerComponent<MapRenderSignature> {
display: flex;
align-items: center;
justify-content: center;
background: var(--boxel-50);
color: #666;
font-size: 14px;
background-color: var(--boxel-50);
color: var(--boxel-500);
font-size: 0.875rem;
text-align: center;
flex: 1;
overflow: hidden; /* Prevent scrollbars from affecting tile calculations */
Expand All @@ -330,7 +336,7 @@ export class MapRender extends GlimmerComponent<MapRenderSignature> {
figure.map :deep(.leaflet-container) {
width: 100% !important;
height: 100% !important;
min-height: 300px;
min-height: 18.75rem;
}
</style>
<link
Expand Down Expand Up @@ -739,10 +745,10 @@ export default class LeafletModifier extends Modifier<LeafletModifierSignature>
// footprint until the real layout kicks in.
if (element && (element.clientWidth === 0 || element.clientHeight === 0)) {
if (!element.style.minWidth) {
element.style.minWidth = '320px';
element.style.minWidth = '20rem';
}
if (!element.style.minHeight) {
element.style.minHeight = '320px';
element.style.minHeight = '20rem';
}
}

Expand Down Expand Up @@ -940,10 +946,10 @@ function escapeHtml(s: string): string {
function createNearbyMarker(place: NearbyPlace): LeafletMarker {
const meta = NEARBY_META[place.kind];
const html = `<div style="
width:24px;height:24px;border-radius:50%;
background:${meta.color};color:#fff;
border:2.5px solid #fff;
box-shadow:0 1px 4px rgba(0,0,0,0.4);
width:1.5rem;height:1.5rem;border-radius:50%;
background-color:${meta.color};color:var(--boxel-light);
border:0.15625rem solid var(--boxel-light);
box-shadow:0 0.0625rem 0.25rem rgba(0,0,0,0.4);
display:flex;align-items:center;justify-content:center;
line-height:0;">${NEARBY_ICON[place.kind]}</div>`;
return L.marker([place.lat, place.lng], {
Expand Down
Loading
Loading