@getvitops/astro 7.0.0 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +142 -34
- package/dist/index.mjs +10 -3
- package/dist/index.mjs.map +1 -1
- package/dist/routes/conversion.d.mts +87 -4
- package/dist/routes/conversion.d.mts.map +1 -1
- package/dist/routes/conversion.mjs +142 -34
- package/dist/routes/conversion.mjs.map +1 -1
- package/package.json +22 -7
- package/src/components/Gallery.astro +77 -69
- package/src/components/Marquee.astro +63 -0
- package/src/components/ReviewCard.astro +60 -0
- package/src/components/ReviewCardBody.astro +67 -0
- package/src/schemas/Article.astro +2 -2
- package/src/schemas/Breadcrumb.astro +2 -2
- package/src/schemas/Carousel.astro +2 -2
- package/src/schemas/FAQ.astro +2 -2
- package/src/schemas/LocalBusiness.astro +2 -2
- package/src/schemas/Organization.astro +2 -2
- package/src/scripts/tracking.ts +4 -2
- package/virtual.d.ts +0 -11
|
@@ -1,32 +1,35 @@
|
|
|
1
1
|
---
|
|
2
2
|
/**
|
|
3
|
-
* A `.gallery` thumbnail grid whose triggers open
|
|
4
|
-
* a fully working modal lightbox with
|
|
3
|
+
* A `.gallery` thumbnail grid whose triggers open a single `.lightbox-dialog`
|
|
4
|
+
* wrapping a `<Carousel />` of every image — a fully working modal lightbox with
|
|
5
|
+
* zero JavaScript.
|
|
5
6
|
*
|
|
6
7
|
* Tier-3 in the framework's terms — an authoring convenience that emits the right
|
|
7
8
|
* `.gallery` / `.lightbox-dialog` markup and needs no runtime JS to be usable:
|
|
8
9
|
*
|
|
9
|
-
* - each thumbnail is a real
|
|
10
|
-
*
|
|
11
|
-
* Invoker Commands
|
|
12
|
-
* -
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
*
|
|
18
|
-
*
|
|
10
|
+
* - each thumbnail is a real `<a href="#slide-id">` into the carousel's own
|
|
11
|
+
* `<li id="slide-id">`, so following it is ordinary fragment navigation —
|
|
12
|
+
* no Invoker Commands, no polyfill, in every engine;
|
|
13
|
+
* - `:has(:target)` in `patterns/lightbox.css` is what reveals the dialog once
|
|
14
|
+
* that navigation lands, and the browser's own fragment scroll puts the
|
|
15
|
+
* carousel on the right slide;
|
|
16
|
+
* - prev/next, dots, arrow keys and swipe are the carousel's — reused, not
|
|
17
|
+
* re-implemented, and already dual-path (native `::scroll-button()` /
|
|
18
|
+
* `::scroll-marker` where the engine has them, real fallback buttons where
|
|
19
|
+
* it doesn't).
|
|
19
20
|
*
|
|
20
|
-
* **This component emits its own `<wc-gallery
|
|
21
|
-
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
21
|
+
* **This component emits its own `<wc-gallery>`, wrapping its own `<Carousel />`
|
|
22
|
+
* (which emits its own `<wc-carousel>`)** — do NOT wrap either in one yourself.
|
|
23
|
+
* That is the tier-3 rule ("the wrapper emits its `<wc-*>` tag with the
|
|
24
|
+
* accessible fallback inside"). `<wc-gallery>` only intercepts a trigger click to
|
|
25
|
+
* `showModal()` the dialog and run the thumbnail → full-image view-transition
|
|
26
|
+
* morph; it does not step between slides — the nested `<wc-carousel>` does that,
|
|
27
|
+
* and if it never loads, the dialog is still a plain scroll-snap strip.
|
|
25
28
|
*
|
|
26
|
-
* `<
|
|
27
|
-
*
|
|
28
|
-
*
|
|
29
|
-
*
|
|
29
|
+
* `enhance={false}` on `<Gallery />` still emits a real, working carousel inside
|
|
30
|
+
* the dialog (`enhance` is passed through) — the fallback here is not a stripped
|
|
31
|
+
* lightbox, it is a genuine one, because a scroll-snap carousel needs no
|
|
32
|
+
* `<wc-gallery>` at all to already have prev/next.
|
|
30
33
|
*
|
|
31
34
|
* Images take `string | ImageMetadata` via `<ResponsiveImage />`: a plain string
|
|
32
35
|
* renders a raw `<img>` (unchanged from before `ImageMetadata` support existed);
|
|
@@ -36,6 +39,7 @@
|
|
|
36
39
|
* why (no hard-coded closed format union).
|
|
37
40
|
*/
|
|
38
41
|
import type { HTMLAttributes } from 'astro/types';
|
|
42
|
+
import Carousel from './Carousel.astro';
|
|
39
43
|
import ResponsiveImage from './ResponsiveImage.astro';
|
|
40
44
|
import type { ImageFormat } from './ResponsiveImage.astro';
|
|
41
45
|
|
|
@@ -56,7 +60,7 @@ export interface GalleryImage {
|
|
|
56
60
|
}
|
|
57
61
|
|
|
58
62
|
export interface Props extends Omit<HTMLAttributes<'div'>, 'slot'> {
|
|
59
|
-
/** Accessible name for the grid
|
|
63
|
+
/** Accessible name for the grid, and for the dialog it opens. Required. */
|
|
60
64
|
label: string;
|
|
61
65
|
images?: GalleryImage[];
|
|
62
66
|
/** Passed to every `<Picture formats>`. Default `['avif', 'webp']`. */
|
|
@@ -67,21 +71,21 @@ export interface Props extends Omit<HTMLAttributes<'div'>, 'slot'> {
|
|
|
67
71
|
gap?: string;
|
|
68
72
|
/** `--gallery-aspect` — every thumbnail's box ratio. Default `1`. */
|
|
69
73
|
aspect?: string;
|
|
70
|
-
/** Wrap prev/next past the ends instead of stopping there. */
|
|
74
|
+
/** Wrap prev/next past the ends instead of stopping there. Passed to `<Carousel loop>`. */
|
|
71
75
|
loop?: boolean;
|
|
72
76
|
/**
|
|
73
|
-
* Emit the `<wc-gallery>` wrapper (default `true`).
|
|
74
|
-
*
|
|
75
|
-
*
|
|
76
|
-
*
|
|
77
|
-
*
|
|
78
|
-
* view-transition morph.
|
|
77
|
+
* Emit the `<wc-gallery>` wrapper (default `true`). Also controls `<Carousel
|
|
78
|
+
* enhance>` — set false where no runtime JS will ever load at all (an email, a
|
|
79
|
+
* PDF); the dialog still opens via `:target` and the carousel is still a real,
|
|
80
|
+
* scroll-snapping strip, just without the morph, autoplay-adjacent polish, or
|
|
81
|
+
* (outside Chromium today) prev/next buttons.
|
|
79
82
|
*/
|
|
80
83
|
enhance?: boolean;
|
|
81
84
|
/**
|
|
82
|
-
* Id prefix for the dialog ids. Defaults to a slug of `label`. Astro
|
|
83
|
-
* `useId`, and a random one would change on every build — so this is
|
|
84
|
-
* deterministic and overridable when two galleries on a page share a
|
|
85
|
+
* Id prefix for the dialog and slide ids. Defaults to a slug of `label`. Astro
|
|
86
|
+
* has no `useId`, and a random one would change on every build — so this is
|
|
87
|
+
* derived, deterministic and overridable when two galleries on a page share a
|
|
88
|
+
* label.
|
|
85
89
|
*/
|
|
86
90
|
id?: string;
|
|
87
91
|
}
|
|
@@ -127,7 +131,6 @@ const Shell = enhance ? 'wc-gallery' : 'div';
|
|
|
127
131
|
class:list={['gallery-shell', className]}
|
|
128
132
|
role={role ?? 'group'}
|
|
129
133
|
aria-label={label}
|
|
130
|
-
loop={enhance && loop ? '' : undefined}
|
|
131
134
|
style={vars}
|
|
132
135
|
{...rest}
|
|
133
136
|
>
|
|
@@ -137,12 +140,7 @@ const Shell = enhance ? 'wc-gallery' : 'div';
|
|
|
137
140
|
const thumb = img.thumb ?? img.src;
|
|
138
141
|
return (
|
|
139
142
|
<li class="gallery__item">
|
|
140
|
-
<
|
|
141
|
-
class="gallery__trigger"
|
|
142
|
-
type="button"
|
|
143
|
-
command="show-modal"
|
|
144
|
-
commandfor={`${uid}-img-${i}`}
|
|
145
|
-
>
|
|
143
|
+
<a class="gallery__trigger" href={`#${uid}-img-${i}`}>
|
|
146
144
|
<ResponsiveImage
|
|
147
145
|
src={thumb}
|
|
148
146
|
alt={img.alt}
|
|
@@ -154,42 +152,52 @@ const Shell = enhance ? 'wc-gallery' : 'div';
|
|
|
154
152
|
decoding={i === 0 ? undefined : 'async'}
|
|
155
153
|
/>
|
|
156
154
|
<span class="visually-hidden">View full size</span>
|
|
157
|
-
</
|
|
155
|
+
</a>
|
|
158
156
|
</li>
|
|
159
157
|
);
|
|
160
158
|
})
|
|
161
159
|
}
|
|
162
160
|
<slot />
|
|
163
161
|
</ul>
|
|
164
|
-
{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
162
|
+
<dialog id={uid} class="lightbox-dialog" closedby="any" aria-label={label}>
|
|
163
|
+
<Carousel
|
|
164
|
+
id={`${uid}-track`}
|
|
165
|
+
label={label}
|
|
166
|
+
class="lightbox-dialog__track"
|
|
167
|
+
enhance={enhance}
|
|
168
|
+
loop={loop}
|
|
169
|
+
hint={false}
|
|
170
|
+
>
|
|
171
|
+
{
|
|
172
|
+
images.map((img, i) => {
|
|
173
|
+
const thumb = img.thumb ?? img.src;
|
|
174
|
+
// A per-slide placeholder: whatever's already on screen (the
|
|
175
|
+
// thumbnail) stays painted in `.carousel__media`'s background until
|
|
176
|
+
// the full image finishes loading, so the view-transition morph
|
|
177
|
+
// never has an empty box as its target — see lightbox.css.
|
|
178
|
+
const placeholder = typeof thumb === 'string' ? `--lightbox-placeholder:url(${JSON.stringify(thumb)})` : '';
|
|
179
|
+
return (
|
|
180
|
+
<li class="carousel__slide" id={`${uid}-img-${i}`}>
|
|
181
|
+
<figure class="carousel__media" style={placeholder}>
|
|
182
|
+
<ResponsiveImage
|
|
183
|
+
src={img.src}
|
|
184
|
+
alt={img.alt}
|
|
185
|
+
formats={formats}
|
|
186
|
+
width={img.width}
|
|
187
|
+
height={img.height}
|
|
188
|
+
srcset={img.srcset}
|
|
189
|
+
sizes={img.sizes}
|
|
190
|
+
loading="lazy"
|
|
191
|
+
/>
|
|
192
|
+
{img.caption && <figcaption class="carousel__caption font-caption">{img.caption}</figcaption>}
|
|
193
|
+
</figure>
|
|
194
|
+
</li>
|
|
195
|
+
);
|
|
196
|
+
})
|
|
197
|
+
}
|
|
198
|
+
</Carousel>
|
|
199
|
+
<a class="lightbox-dialog__close" href="#" aria-label="Close">×</a>
|
|
200
|
+
</dialog>
|
|
193
201
|
</Shell>
|
|
194
202
|
|
|
195
203
|
<style>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* A `.marquee` — content scrolling seamlessly along a track (a strip of
|
|
4
|
+
* testimonials, logos, a ticker).
|
|
5
|
+
*
|
|
6
|
+
* Tier-3 like `Counter.astro`: the pattern is pure CSS
|
|
7
|
+
* (`@getvitops/core/css/patterns/marquee.css`'s `@keyframes marquee-scroll`), and
|
|
8
|
+
* `<wc-marquee>` only fixes the seam gap by cloning `.marquee__content` enough
|
|
9
|
+
* times to cover the track — "how many copies cover this container" is a
|
|
10
|
+
* measurement CSS cannot make. With no JS (or `enhance={false}`), this still
|
|
11
|
+
* scrolls exactly as authored; it just isn't seamless with a short content run.
|
|
12
|
+
*
|
|
13
|
+
* **This component emits its own `<wc-marquee>`** — do NOT wrap it in one, same
|
|
14
|
+
* rule as `Counter.astro`/`Carousel.astro`.
|
|
15
|
+
*
|
|
16
|
+
* Put the items in the default slot — a list of `<ReviewCard>`s, logos,
|
|
17
|
+
* whatever scrolls. They become the one `.marquee__content` copy the element
|
|
18
|
+
* clones from.
|
|
19
|
+
*/
|
|
20
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
21
|
+
|
|
22
|
+
export interface Props extends Omit<HTMLAttributes<'div'>, 'slot'> {
|
|
23
|
+
/** Pause the scroll on hover (default false). */
|
|
24
|
+
pauseOnHover?: boolean;
|
|
25
|
+
/** Scroll in the opposite direction (default false). */
|
|
26
|
+
reverse?: boolean;
|
|
27
|
+
/** Scroll along the block axis instead of inline (default false). */
|
|
28
|
+
vertical?: boolean;
|
|
29
|
+
/**
|
|
30
|
+
* Emit the `<wc-marquee>` wrapper (default `true`).
|
|
31
|
+
*
|
|
32
|
+
* Set false where the element cannot run. The scroll animation still works —
|
|
33
|
+
* only the seamless-loop fixup goes.
|
|
34
|
+
*/
|
|
35
|
+
enhance?: boolean;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const {
|
|
39
|
+
pauseOnHover = false,
|
|
40
|
+
reverse = false,
|
|
41
|
+
vertical = false,
|
|
42
|
+
enhance = true,
|
|
43
|
+
class: className,
|
|
44
|
+
...rest
|
|
45
|
+
} = Astro.props;
|
|
46
|
+
|
|
47
|
+
const Shell = enhance ? 'wc-marquee' : 'div';
|
|
48
|
+
---
|
|
49
|
+
|
|
50
|
+
<Shell
|
|
51
|
+
class:list={[
|
|
52
|
+
'marquee',
|
|
53
|
+
pauseOnHover && 'marquee--pause-hover',
|
|
54
|
+
reverse && 'marquee--reverse',
|
|
55
|
+
vertical && 'marquee--vertical',
|
|
56
|
+
className,
|
|
57
|
+
]}
|
|
58
|
+
{...rest}
|
|
59
|
+
>
|
|
60
|
+
<div class="marquee__content">
|
|
61
|
+
<slot />
|
|
62
|
+
</div>
|
|
63
|
+
</Shell>
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* One `.review` card, from a `vitops reviews` cache entry
|
|
4
|
+
* (`@getvitops/utils/reviews`'s `Review`).
|
|
5
|
+
*
|
|
6
|
+
* Pure tier-3: `.review` is a config-generated pattern
|
|
7
|
+
* (`patterns.items.review`), so this needs no runtime JS, no `<wc-*>` tag, and
|
|
8
|
+
* therefore no `enhance` prop. Put several inside `<Carousel>` slides or
|
|
9
|
+
* `<Marquee>` items — there is no group component.
|
|
10
|
+
*
|
|
11
|
+
* **Named `ReviewCard`, not `Review`.** `../schemas/Review.astro` already owns
|
|
12
|
+
* that name for the JSON-LD builder this component composes when `schema` is
|
|
13
|
+
* true; two same-named components differing only by directory is a needless
|
|
14
|
+
* import trap.
|
|
15
|
+
*
|
|
16
|
+
* **`schema` defaults to false.** Google's structured-data policy treats a
|
|
17
|
+
* business marking up reviews of itself as ineligible for review rich results,
|
|
18
|
+
* and risks a manual action — see AGENTS.md's Reviews section. Pass
|
|
19
|
+
* `itemReviewed` (the business the review is of) only when you have deliberately
|
|
20
|
+
* decided to emit it anyway; without it, `schema` is a no-op.
|
|
21
|
+
*
|
|
22
|
+
* **`avatar` defaults to false.** A reviewer's photo is a Google-hosted URL —
|
|
23
|
+
* rendering it by default would put a third-party request on every page and
|
|
24
|
+
* depend on a link that isn't guaranteed to stay live. With it off,
|
|
25
|
+
* `.review__initials` carries the author's initials instead.
|
|
26
|
+
*/
|
|
27
|
+
import type { ComponentProps, HTMLAttributes } from 'astro/types';
|
|
28
|
+
import type { Review } from '@getvitops/utils/reviews';
|
|
29
|
+
import ReviewCardBody from './ReviewCardBody.astro';
|
|
30
|
+
import SchemaReview from '../schemas/Review.astro';
|
|
31
|
+
|
|
32
|
+
export interface Props extends Omit<HTMLAttributes<'article'>, 'slot'> {
|
|
33
|
+
review: Review;
|
|
34
|
+
/** Emit `Review` JSON-LD via `../schemas/Review.astro` (default false). */
|
|
35
|
+
schema?: boolean;
|
|
36
|
+
/** Required for `schema` to take effect — the business this review is of. */
|
|
37
|
+
itemReviewed?: ComponentProps<typeof SchemaReview>['itemReviewed'];
|
|
38
|
+
/** Render the reviewer's Google-hosted avatar photo (default false). */
|
|
39
|
+
avatar?: boolean;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const { review, schema = false, itemReviewed, avatar = false, ...rest } = Astro.props;
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
{
|
|
46
|
+
schema && itemReviewed ? (
|
|
47
|
+
<SchemaReview
|
|
48
|
+
itemReviewed={itemReviewed}
|
|
49
|
+
reviewRating={{ ratingValue: review.rating }}
|
|
50
|
+
author={{ name: review.author }}
|
|
51
|
+
datePublished={review.publishedAt}
|
|
52
|
+
reviewBody={review.text}
|
|
53
|
+
publisher={{ name: 'Google', type: 'Organization' }}
|
|
54
|
+
>
|
|
55
|
+
<ReviewCardBody review={review} avatar={avatar} {...rest} />
|
|
56
|
+
</SchemaReview>
|
|
57
|
+
) : (
|
|
58
|
+
<ReviewCardBody review={review} avatar={avatar} {...rest} />
|
|
59
|
+
)
|
|
60
|
+
}
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
---
|
|
2
|
+
/**
|
|
3
|
+
* The `.review` markup itself — split out of `ReviewCard.astro` purely so it can
|
|
4
|
+
* be rendered twice (once bare, once wrapped in `../schemas/Review.astro`'s
|
|
5
|
+
* JSON-LD `<script>`) without duplicating the template. Not a public export;
|
|
6
|
+
* `ReviewCard.astro` is the tier-3 entry point.
|
|
7
|
+
*/
|
|
8
|
+
import type { HTMLAttributes } from 'astro/types';
|
|
9
|
+
import type { Review } from '@getvitops/utils/reviews';
|
|
10
|
+
import Icon from './Icon.astro';
|
|
11
|
+
|
|
12
|
+
export interface Props extends Omit<HTMLAttributes<'article'>, 'slot'> {
|
|
13
|
+
review: Review;
|
|
14
|
+
avatar: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const { review, avatar, class: className, ...rest } = Astro.props;
|
|
18
|
+
|
|
19
|
+
const initials = review.author
|
|
20
|
+
.split(/\s+/)
|
|
21
|
+
.filter(Boolean)
|
|
22
|
+
.slice(0, 2)
|
|
23
|
+
.map((w) => w[0]?.toUpperCase())
|
|
24
|
+
.join('');
|
|
25
|
+
|
|
26
|
+
const full = Math.floor(review.rating);
|
|
27
|
+
const hasHalf = review.rating - full >= 0.5;
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
<article class:list={['review', className]} {...rest}>
|
|
31
|
+
<header class="review__header">
|
|
32
|
+
{
|
|
33
|
+
avatar && review.authorPhoto ? (
|
|
34
|
+
<img class="review__avatar" src={review.authorPhoto} alt="" loading="lazy" />
|
|
35
|
+
) : (
|
|
36
|
+
<span class="review__initials" aria-hidden="true">{initials}</span>
|
|
37
|
+
)
|
|
38
|
+
}
|
|
39
|
+
<div class="review__identity">
|
|
40
|
+
<cite class="review__author">{review.author}</cite>
|
|
41
|
+
<time class="review__date" datetime={review.publishedAt}>
|
|
42
|
+
{new Date(review.publishedAt).toLocaleDateString()}
|
|
43
|
+
</time>
|
|
44
|
+
</div>
|
|
45
|
+
<span class="review__stars" role="img" aria-label={`${review.rating} out of 5 stars`}>
|
|
46
|
+
{Array.from({ length: 5 }, (_, i) => {
|
|
47
|
+
const half = i === full && hasHalf;
|
|
48
|
+
const empty = i >= full && !half;
|
|
49
|
+
return (
|
|
50
|
+
<Icon
|
|
51
|
+
name={half ? 'star-half' : 'star'}
|
|
52
|
+
class:list={['review__star', empty && 'review__star--empty']}
|
|
53
|
+
/>
|
|
54
|
+
);
|
|
55
|
+
})}
|
|
56
|
+
</span>
|
|
57
|
+
</header>
|
|
58
|
+
{review.text && <blockquote class="review__body">{review.text}</blockquote>}
|
|
59
|
+
{
|
|
60
|
+
review.reply && (
|
|
61
|
+
<footer class="review__reply">
|
|
62
|
+
<p class="review__reply-author">Response from the owner</p>
|
|
63
|
+
<p>{review.reply.text}</p>
|
|
64
|
+
</footer>
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
</article>
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { articleGraph } from '@getvitops/utils';
|
|
3
|
-
import type { ArticleGraphOptions } from '@getvitops/utils';
|
|
2
|
+
import { articleGraph } from '@getvitops/utils/schema';
|
|
3
|
+
import type { ArticleGraphOptions } from '@getvitops/utils/schema';
|
|
4
4
|
|
|
5
5
|
export type Props = ArticleGraphOptions;
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { breadcrumbGraph } from '@getvitops/utils';
|
|
3
|
-
import type { BreadcrumbGraphOptions, BreadcrumbItem } from '@getvitops/utils';
|
|
2
|
+
import { breadcrumbGraph } from '@getvitops/utils/schema';
|
|
3
|
+
import type { BreadcrumbGraphOptions, BreadcrumbItem } from '@getvitops/utils/schema';
|
|
4
4
|
|
|
5
5
|
export type { BreadcrumbItem };
|
|
6
6
|
export type Props = BreadcrumbGraphOptions;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { itemListGraph } from '@getvitops/utils';
|
|
3
|
-
import type { ItemListEntry, ItemListGraphOptions } from '@getvitops/utils';
|
|
2
|
+
import { itemListGraph } from '@getvitops/utils/schema';
|
|
3
|
+
import type { ItemListEntry, ItemListGraphOptions } from '@getvitops/utils/schema';
|
|
4
4
|
|
|
5
5
|
export type { ItemListEntry };
|
|
6
6
|
export type CarouselItem = ItemListEntry;
|
package/src/schemas/FAQ.astro
CHANGED
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* NOTE: Google recommends FAQ markup only for authoritative government
|
|
6
6
|
* and health sites. For other sites, consider using QAPage schema instead.
|
|
7
7
|
*/
|
|
8
|
-
import { faqGraph } from '@getvitops/utils';
|
|
9
|
-
import type { FAQGraphOptions, FAQItem } from '@getvitops/utils';
|
|
8
|
+
import { faqGraph } from '@getvitops/utils/schema';
|
|
9
|
+
import type { FAQGraphOptions, FAQItem } from '@getvitops/utils/schema';
|
|
10
10
|
|
|
11
11
|
export type { FAQItem };
|
|
12
12
|
export type Props = FAQGraphOptions;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { localBusinessGraph } from '@getvitops/utils';
|
|
3
|
-
import type { LocalBusinessGraphOptions } from '@getvitops/utils';
|
|
2
|
+
import { localBusinessGraph } from '@getvitops/utils/schema';
|
|
3
|
+
import type { LocalBusinessGraphOptions } from '@getvitops/utils/schema';
|
|
4
4
|
|
|
5
5
|
export type Props = LocalBusinessGraphOptions;
|
|
6
6
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
|
-
import { organizationGraph } from '@getvitops/utils';
|
|
3
|
-
import type { OrganizationGraphOptions } from '@getvitops/utils';
|
|
2
|
+
import { organizationGraph } from '@getvitops/utils/schema';
|
|
3
|
+
import type { OrganizationGraphOptions } from '@getvitops/utils/schema';
|
|
4
4
|
|
|
5
5
|
export type Props = OrganizationGraphOptions;
|
|
6
6
|
|
package/src/scripts/tracking.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
// Ad conversion tracking: capture click IDs from the landing URL, track tel:
|
|
2
|
-
// clicks. Form
|
|
2
|
+
// clicks. Form submission itself goes through `createConversionRoute()`
|
|
3
|
+
// (`@getvitops/astro/routes`), which handles both the no-JS `<form
|
|
4
|
+
// method="post">` path and a fetch-based one — there is no separate
|
|
5
|
+
// form-enhancement script here.
|
|
3
6
|
//
|
|
4
7
|
// Runs early and ungated on purpose. Reading the query string is not storage and
|
|
5
8
|
// needs nobody's permission; *keeping* it does. So the capture is synchronous —
|
|
@@ -100,7 +103,6 @@ function persist(): void {
|
|
|
100
103
|
ts: existing?.['ts'] ?? Date.now(),
|
|
101
104
|
};
|
|
102
105
|
|
|
103
|
-
// Remove undefined values before serializing
|
|
104
106
|
const clean = Object.fromEntries(Object.entries(merged).filter(([, v]) => v !== undefined));
|
|
105
107
|
|
|
106
108
|
const expires = new Date(Date.now() + COOKIE_DAYS * 864e5).toUTCString();
|
package/virtual.d.ts
CHANGED
|
@@ -1,14 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Astro generates the real types for `astro:env/*` into a consumer's
|
|
3
|
-
* `.astro/` on `astro sync`, which never runs for this package — so the
|
|
4
|
-
* import typechecks nowhere but a consumer project. Declared here at the
|
|
5
|
-
* shape `plan-features.ts` actually uses; the runtime value comes from
|
|
6
|
-
* Astro's env schema, and this file cannot import it.
|
|
7
|
-
*/
|
|
8
|
-
declare module 'astro:env/server' {
|
|
9
|
-
export const SITE_PLAN: string | undefined;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
1
|
/** Types for the virtual module the getvitops() integration provides to <Head />. */
|
|
13
2
|
declare module 'virtual:getvitops/head' {
|
|
14
3
|
interface FaviconLink {
|