@styleflow.app/flowpress-astro-core 0.2.0-beta.12 → 0.2.0-beta.14
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 +12 -0
- package/README.md +29 -2
- package/dist/astro/GutenbergRenderer.astro +63 -10
- package/dist/block-renderers.d.ts +10 -0
- package/dist/block-renderers.d.ts.map +1 -0
- package/dist/block-renderers.js +2 -0
- package/dist/block-renderers.js.map +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/number-spinner.d.ts.map +1 -1
- package/dist/number-spinner.js +5 -1
- package/dist/number-spinner.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
## 0.2.0-beta.14
|
|
6
|
+
|
|
7
|
+
- Added a recursive project block-renderer registry to
|
|
8
|
+
`GutenbergRenderer.astro`, including imported `sf/template` and resolved
|
|
9
|
+
query-item descendants.
|
|
10
|
+
|
|
11
|
+
## 0.2.0-beta.13
|
|
12
|
+
|
|
13
|
+
- Kept number spinners static on their final values when
|
|
14
|
+
`IntersectionObserver` or motion preference detection is unavailable,
|
|
15
|
+
rather than starting outside a verifiable viewport intersection.
|
|
16
|
+
|
|
5
17
|
## 0.2.0-beta.12
|
|
6
18
|
|
|
7
19
|
- Added the reusable `sf/number-spinner` Astro renderer with independently
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@ The package must not import from an application or project plugin.
|
|
|
9
9
|
## Install
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
pnpm add @styleflow.app/flowpress-astro-core@0.2.0-beta.
|
|
12
|
+
pnpm add @styleflow.app/flowpress-astro-core@0.2.0-beta.14
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
Prereleases are pinned exactly. The package expects Astro 7 or newer and the
|
|
@@ -44,11 +44,38 @@ visible characters. The exported
|
|
|
44
44
|
from rendered CMS blocks so consumers can generate Tailwind
|
|
45
45
|
`@source inline()` directives before compiling their frontend CSS.
|
|
46
46
|
|
|
47
|
+
Projects can add renderers for project-owned Gutenberg blocks without copying
|
|
48
|
+
or patching the core renderer. Pass a `FlowpressBlockRenderers` map keyed by the
|
|
49
|
+
full block name; the registry is propagated through every recursive rendering
|
|
50
|
+
path, including imported `sf/template` content and resolved query-item
|
|
51
|
+
templates.
|
|
52
|
+
|
|
53
|
+
```astro
|
|
54
|
+
---
|
|
55
|
+
import GutenbergRenderer from "@styleflow.app/flowpress-astro-core/GutenbergRenderer.astro";
|
|
56
|
+
import type {
|
|
57
|
+
FlowpressBlockRenderers
|
|
58
|
+
} from "@styleflow.app/flowpress-astro-core";
|
|
59
|
+
import ProjectTimelineBlock from "./ProjectTimelineBlock.astro";
|
|
60
|
+
|
|
61
|
+
const blockRenderers = {
|
|
62
|
+
"sf/timeline": ProjectTimelineBlock
|
|
63
|
+
} satisfies FlowpressBlockRenderers;
|
|
64
|
+
---
|
|
65
|
+
|
|
66
|
+
<GutenbergRenderer blocks={blocks} blockRenderers={blockRenderers} />
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Each custom renderer receives the original `block` and optional StyleFlow
|
|
70
|
+
`contract`. Its default slot contains recursively rendered child blocks, so a
|
|
71
|
+
container override can place `<slot />` where its children belong.
|
|
72
|
+
|
|
47
73
|
`sf/number-spinner` renders prefix, target number and suffix with independent
|
|
48
74
|
semantic tags and StyleFlow text coordinates. Its numeric value counts from
|
|
49
75
|
zero only when it enters the viewport. The final target remains available to
|
|
50
76
|
assistive technology and without JavaScript; `prefers-reduced-motion: reduce`
|
|
51
|
-
skips the animation entirely.
|
|
77
|
+
skips the animation entirely. Browsers without viewport observation keep the
|
|
78
|
+
server-rendered final value instead of guessing when to start.
|
|
52
79
|
|
|
53
80
|
## Release
|
|
54
81
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
import S from "@styleflow.app/astro/S.astro";
|
|
3
|
+
import type { FlowpressBlockRenderers } from "../block-renderers.js";
|
|
3
4
|
import type { StyleflowContract } from "../styleflow.js";
|
|
4
5
|
import type { FlowpressBlock } from "../types.js";
|
|
5
6
|
import ColumnBlock from "./blocks/ColumnBlock.astro";
|
|
@@ -20,9 +21,10 @@ import {
|
|
|
20
21
|
interface Props {
|
|
21
22
|
blocks: FlowpressBlock[];
|
|
22
23
|
contract?: StyleflowContract;
|
|
24
|
+
blockRenderers?: FlowpressBlockRenderers;
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
const { blocks, contract } = Astro.props;
|
|
27
|
+
const { blocks, contract, blockRenderers = {} } = Astro.props;
|
|
26
28
|
|
|
27
29
|
function youtubeEmbedUrl(value: unknown): string | null {
|
|
28
30
|
try {
|
|
@@ -47,11 +49,28 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
47
49
|
const children = block.innerBlocks ?? [];
|
|
48
50
|
const semantic = semanticProps(block.attributes, contract);
|
|
49
51
|
const htmlAttributes = blockHtmlAttributes(block.attributes);
|
|
52
|
+
const CustomBlockRenderer = blockRenderers[block.name];
|
|
53
|
+
|
|
54
|
+
if (CustomBlockRenderer) {
|
|
55
|
+
return (
|
|
56
|
+
<CustomBlockRenderer block={block} contract={contract}>
|
|
57
|
+
<Astro.self
|
|
58
|
+
blocks={children}
|
|
59
|
+
contract={contract}
|
|
60
|
+
blockRenderers={blockRenderers}
|
|
61
|
+
/>
|
|
62
|
+
</CustomBlockRenderer>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
50
65
|
|
|
51
66
|
if (name === "hero") {
|
|
52
67
|
return (
|
|
53
68
|
<HeroBlock block={block} contract={contract}>
|
|
54
|
-
<Astro.self
|
|
69
|
+
<Astro.self
|
|
70
|
+
blocks={children}
|
|
71
|
+
contract={contract}
|
|
72
|
+
blockRenderers={blockRenderers}
|
|
73
|
+
/>
|
|
55
74
|
</HeroBlock>
|
|
56
75
|
);
|
|
57
76
|
}
|
|
@@ -59,7 +78,11 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
59
78
|
if (name === "grid") {
|
|
60
79
|
return (
|
|
61
80
|
<GridBlock block={block} contract={contract}>
|
|
62
|
-
<Astro.self
|
|
81
|
+
<Astro.self
|
|
82
|
+
blocks={children}
|
|
83
|
+
contract={contract}
|
|
84
|
+
blockRenderers={blockRenderers}
|
|
85
|
+
/>
|
|
63
86
|
</GridBlock>
|
|
64
87
|
);
|
|
65
88
|
}
|
|
@@ -67,7 +90,11 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
67
90
|
if (name === "column") {
|
|
68
91
|
return (
|
|
69
92
|
<ColumnBlock block={block} contract={contract}>
|
|
70
|
-
<Astro.self
|
|
93
|
+
<Astro.self
|
|
94
|
+
blocks={children}
|
|
95
|
+
contract={contract}
|
|
96
|
+
blockRenderers={blockRenderers}
|
|
97
|
+
/>
|
|
71
98
|
</ColumnBlock>
|
|
72
99
|
);
|
|
73
100
|
}
|
|
@@ -88,7 +115,11 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
88
115
|
].includes(name)) {
|
|
89
116
|
return (
|
|
90
117
|
<TextBlock block={block} contract={contract} hasChildren={children.length > 0}>
|
|
91
|
-
<Astro.self
|
|
118
|
+
<Astro.self
|
|
119
|
+
blocks={children}
|
|
120
|
+
contract={contract}
|
|
121
|
+
blockRenderers={blockRenderers}
|
|
122
|
+
/>
|
|
92
123
|
</TextBlock>
|
|
93
124
|
);
|
|
94
125
|
}
|
|
@@ -216,7 +247,11 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
216
247
|
{items.map((item, index) => (
|
|
217
248
|
<S as="li">
|
|
218
249
|
{resolvedBlocks[index]?.length ? (
|
|
219
|
-
<Astro.self
|
|
250
|
+
<Astro.self
|
|
251
|
+
blocks={resolvedBlocks[index]}
|
|
252
|
+
contract={contract}
|
|
253
|
+
blockRenderers={blockRenderers}
|
|
254
|
+
/>
|
|
220
255
|
) : (
|
|
221
256
|
<ListItemFallback item={item} />
|
|
222
257
|
)}
|
|
@@ -229,10 +264,18 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
229
264
|
if (name === "template") {
|
|
230
265
|
return Object.keys(htmlAttributes).length > 0 ? (
|
|
231
266
|
<div {...htmlAttributes}>
|
|
232
|
-
<Astro.self
|
|
267
|
+
<Astro.self
|
|
268
|
+
blocks={children}
|
|
269
|
+
contract={contract}
|
|
270
|
+
blockRenderers={blockRenderers}
|
|
271
|
+
/>
|
|
233
272
|
</div>
|
|
234
273
|
) : (
|
|
235
|
-
<Astro.self
|
|
274
|
+
<Astro.self
|
|
275
|
+
blocks={children}
|
|
276
|
+
contract={contract}
|
|
277
|
+
blockRenderers={blockRenderers}
|
|
278
|
+
/>
|
|
236
279
|
);
|
|
237
280
|
}
|
|
238
281
|
|
|
@@ -245,7 +288,11 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
245
288
|
<S as="div" {...semantic} {...htmlAttributes}>
|
|
246
289
|
<details>
|
|
247
290
|
<summary>{String(block.attributes.title ?? "")}</summary>
|
|
248
|
-
<Astro.self
|
|
291
|
+
<Astro.self
|
|
292
|
+
blocks={children}
|
|
293
|
+
contract={contract}
|
|
294
|
+
blockRenderers={blockRenderers}
|
|
295
|
+
/>
|
|
249
296
|
</details>
|
|
250
297
|
</S>
|
|
251
298
|
);
|
|
@@ -296,7 +343,13 @@ function youtubeEmbedUrl(value: unknown): string | null {
|
|
|
296
343
|
<S as={layoutTag} {...semantic} {...htmlAttributes}>
|
|
297
344
|
{block.innerHtml && children.length === 0
|
|
298
345
|
? <S as="div" set:html={block.innerHtml} />
|
|
299
|
-
:
|
|
346
|
+
: (
|
|
347
|
+
<Astro.self
|
|
348
|
+
blocks={children}
|
|
349
|
+
contract={contract}
|
|
350
|
+
blockRenderers={blockRenderers}
|
|
351
|
+
/>
|
|
352
|
+
)}
|
|
300
353
|
</S>
|
|
301
354
|
);
|
|
302
355
|
})}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AstroComponentFactory } from "astro/runtime/server/index.js";
|
|
2
|
+
import type { StyleflowContract } from "./styleflow.js";
|
|
3
|
+
import type { FlowpressBlock } from "./types.js";
|
|
4
|
+
export interface FlowpressBlockRendererProps {
|
|
5
|
+
block: FlowpressBlock;
|
|
6
|
+
contract?: StyleflowContract;
|
|
7
|
+
}
|
|
8
|
+
export type FlowpressBlockRenderer = AstroComponentFactory;
|
|
9
|
+
export type FlowpressBlockRenderers = Readonly<Record<string, FlowpressBlockRenderer>>;
|
|
10
|
+
//# sourceMappingURL=block-renderers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-renderers.d.ts","sourceRoot":"","sources":["../src/block-renderers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,+BAA+B,CAAC;AAC3E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACxD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,2BAA2B;IAC1C,KAAK,EAAE,cAAc,CAAC;IACtB,QAAQ,CAAC,EAAE,iBAAiB,CAAC;CAC9B;AAED,MAAM,MAAM,sBAAsB,GAAG,qBAAqB,CAAC;AAE3D,MAAM,MAAM,uBAAuB,GAAG,QAAQ,CAC5C,MAAM,CAAC,MAAM,EAAE,sBAAsB,CAAC,CACvC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"block-renderers.js","sourceRoot":"","sources":["../src/block-renderers.ts"],"names":[],"mappings":"","sourcesContent":["import type { AstroComponentFactory } from \"astro/runtime/server/index.js\";\nimport type { StyleflowContract } from \"./styleflow.js\";\nimport type { FlowpressBlock } from \"./types.js\";\n\nexport interface FlowpressBlockRendererProps {\n block: FlowpressBlock;\n contract?: StyleflowContract;\n}\n\nexport type FlowpressBlockRenderer = AstroComponentFactory;\n\nexport type FlowpressBlockRenderers = Readonly<\n Record<string, FlowpressBlockRenderer>\n>;\n"]}
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC"}
|
package/dist/index.js
CHANGED
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC","sourcesContent":["export * from \"./client.js\";\nexport * from \"./env.js\";\nexport * from \"./number-spinner.js\";\nexport * from \"./styleflow.js\";\nexport * from \"./style-classes.js\";\nexport * from \"./template-hierarchy.js\";\nexport * from \"./types.js\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC;AAC5B,cAAc,sBAAsB,CAAC;AACrC,cAAc,UAAU,CAAC;AACzB,cAAc,qBAAqB,CAAC;AACpC,cAAc,gBAAgB,CAAC;AAC/B,cAAc,oBAAoB,CAAC;AACnC,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC","sourcesContent":["export * from \"./client.js\";\nexport * from \"./block-renderers.js\";\nexport * from \"./env.js\";\nexport * from \"./number-spinner.js\";\nexport * from \"./styleflow.js\";\nexport * from \"./style-classes.js\";\nexport * from \"./template-hierarchy.js\";\nexport * from \"./types.js\";\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"number-spinner.d.ts","sourceRoot":"","sources":["../src/number-spinner.ts"],"names":[],"mappings":"AAIA,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS3D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAK3D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1E;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GACrB,MAAM,CAMR;AA2ED,wBAAgB,wBAAwB,CAAC,IAAI,GAAE,UAAqB,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"number-spinner.d.ts","sourceRoot":"","sources":["../src/number-spinner.ts"],"names":[],"mappings":"AAIA,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAS3D;AAED,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,GAAG,MAAM,CAK3D;AAED,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAI1E;AAED,wBAAgB,kBAAkB,CAChC,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,cAAc,EAAE,MAAM,GACrB,MAAM,CAMR;AA2ED,wBAAgB,wBAAwB,CAAC,IAAI,GAAE,UAAqB,GAAG,IAAI,CA0D1E"}
|
package/dist/number-spinner.js
CHANGED
|
@@ -96,13 +96,17 @@ export function initializeNumberSpinners(root = document) {
|
|
|
96
96
|
if (!elements.length) {
|
|
97
97
|
return;
|
|
98
98
|
}
|
|
99
|
+
if (typeof window.matchMedia !== "function") {
|
|
100
|
+
elements.forEach(finishSpinner);
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
99
103
|
const reducedMotion = window.matchMedia(REDUCED_MOTION_QUERY);
|
|
100
104
|
if (reducedMotion.matches) {
|
|
101
105
|
elements.forEach(finishSpinner);
|
|
102
106
|
return;
|
|
103
107
|
}
|
|
104
108
|
if (!("IntersectionObserver" in window)) {
|
|
105
|
-
elements.forEach(
|
|
109
|
+
elements.forEach(finishSpinner);
|
|
106
110
|
return;
|
|
107
111
|
}
|
|
108
112
|
const observer = new IntersectionObserver((entries) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"number-spinner.js","sourceRoot":"","sources":["../src/number-spinner.ts"],"names":[],"mappings":"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","sourcesContent":["const REDUCED_MOTION_QUERY = \"(prefers-reduced-motion: reduce)\";\nconst initializedSpinners = new WeakSet<HTMLElement>();\nconst runningFrames = new Map<HTMLElement, number>();\n\nexport function spinnerFractionDigits(value: number): number {\n if (!Number.isFinite(value)) {\n return 0;\n }\n\n const [coefficient = \"\", exponentText] = String(value).toLowerCase().split(\"e\");\n const coefficientDecimals = coefficient.split(\".\")[1]?.length ?? 0;\n const exponent = Number(exponentText ?? 0);\n return Math.min(20, Math.max(0, coefficientDecimals - exponent));\n}\n\nexport function clampSpinnerDuration(value: unknown): number {\n const duration = Number(value);\n return Number.isFinite(duration)\n ? Math.min(10_000, Math.max(0, duration))\n : 1200;\n}\n\nexport function spinnerFrameValue(target: number, progress: number): number {\n const normalized = Math.min(1, Math.max(0, progress));\n const eased = 1 - Math.pow(1 - normalized, 3);\n return target * eased;\n}\n\nexport function formatSpinnerValue(\n value: number,\n locale: string,\n fractionDigits: number\n): string {\n const digits = Math.min(20, Math.max(0, Math.trunc(fractionDigits)));\n return new Intl.NumberFormat(locale || undefined, {\n minimumFractionDigits: digits,\n maximumFractionDigits: digits\n }).format(value);\n}\n\nfunction spinnerDisplay(element: HTMLElement): HTMLElement | null {\n return element.querySelector<HTMLElement>(\n \"[data-flowpress-number-spinner-value]\"\n );\n}\n\nfunction spinnerTarget(element: HTMLElement): number {\n const target = Number(element.dataset.spinnerTarget);\n return Number.isFinite(target) ? target : 0;\n}\n\nfunction spinnerLocale(element: HTMLElement): string {\n return element.dataset.spinnerLocale\n || document.documentElement.lang\n || navigator.language\n || \"en\";\n}\n\nfunction renderSpinnerValue(\n element: HTMLElement,\n value: number,\n final = false\n): void {\n const display = spinnerDisplay(element);\n if (!display) {\n return;\n }\n const target = spinnerTarget(element);\n const digits = Number(element.dataset.spinnerFractionDigits) || 0;\n display.textContent = formatSpinnerValue(\n final ? target : value,\n spinnerLocale(element),\n digits\n );\n if (final) {\n element.dataset.spinnerState = \"complete\";\n }\n}\n\nfunction finishSpinner(element: HTMLElement): void {\n const frame = runningFrames.get(element);\n if (frame !== undefined) {\n cancelAnimationFrame(frame);\n runningFrames.delete(element);\n }\n renderSpinnerValue(element, spinnerTarget(element), true);\n}\n\nfunction animateSpinner(element: HTMLElement): void {\n const duration = clampSpinnerDuration(element.dataset.spinnerDuration);\n const target = spinnerTarget(element);\n if (duration === 0 || target === 0) {\n finishSpinner(element);\n return;\n }\n\n element.dataset.spinnerState = \"running\";\n renderSpinnerValue(element, 0);\n const startedAt = performance.now();\n\n const step = (now: number) => {\n const progress = Math.min(1, (now - startedAt) / duration);\n renderSpinnerValue(element, spinnerFrameValue(target, progress));\n if (progress >= 1) {\n finishSpinner(element);\n return;\n }\n runningFrames.set(element, requestAnimationFrame(step));\n };\n\n runningFrames.set(element, requestAnimationFrame(step));\n}\n\nexport function initializeNumberSpinners(root: ParentNode = document): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n const elements = Array.from(\n root.querySelectorAll<HTMLElement>(\"[data-flowpress-number-spinner]\")\n ).filter((element) => {\n if (initializedSpinners.has(element)) {\n return false;\n }\n initializedSpinners.add(element);\n return true;\n });\n if (!elements.length) {\n return;\n }\n\n const reducedMotion = window.matchMedia(REDUCED_MOTION_QUERY);\n if (reducedMotion.matches) {\n elements.forEach(finishSpinner);\n return;\n }\n\n if (!(\"IntersectionObserver\" in window)) {\n elements.forEach(animateSpinner);\n return;\n }\n\n const observer = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n continue;\n }\n const element = entry.target as HTMLElement;\n observer.unobserve(element);\n animateSpinner(element);\n }\n }, {\n threshold: 0.15\n });\n\n elements.forEach((element) => observer.observe(element));\n\n const stopForReducedMotion = (event: MediaQueryListEvent) => {\n if (!event.matches) {\n return;\n }\n observer.disconnect();\n elements.forEach(finishSpinner);\n reducedMotion.removeEventListener(\"change\", stopForReducedMotion);\n };\n reducedMotion.addEventListener(\"change\", stopForReducedMotion);\n}\n"]}
|
|
1
|
+
{"version":3,"file":"number-spinner.js","sourceRoot":"","sources":["../src/number-spinner.ts"],"names":[],"mappings":"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","sourcesContent":["const REDUCED_MOTION_QUERY = \"(prefers-reduced-motion: reduce)\";\nconst initializedSpinners = new WeakSet<HTMLElement>();\nconst runningFrames = new Map<HTMLElement, number>();\n\nexport function spinnerFractionDigits(value: number): number {\n if (!Number.isFinite(value)) {\n return 0;\n }\n\n const [coefficient = \"\", exponentText] = String(value).toLowerCase().split(\"e\");\n const coefficientDecimals = coefficient.split(\".\")[1]?.length ?? 0;\n const exponent = Number(exponentText ?? 0);\n return Math.min(20, Math.max(0, coefficientDecimals - exponent));\n}\n\nexport function clampSpinnerDuration(value: unknown): number {\n const duration = Number(value);\n return Number.isFinite(duration)\n ? Math.min(10_000, Math.max(0, duration))\n : 1200;\n}\n\nexport function spinnerFrameValue(target: number, progress: number): number {\n const normalized = Math.min(1, Math.max(0, progress));\n const eased = 1 - Math.pow(1 - normalized, 3);\n return target * eased;\n}\n\nexport function formatSpinnerValue(\n value: number,\n locale: string,\n fractionDigits: number\n): string {\n const digits = Math.min(20, Math.max(0, Math.trunc(fractionDigits)));\n return new Intl.NumberFormat(locale || undefined, {\n minimumFractionDigits: digits,\n maximumFractionDigits: digits\n }).format(value);\n}\n\nfunction spinnerDisplay(element: HTMLElement): HTMLElement | null {\n return element.querySelector<HTMLElement>(\n \"[data-flowpress-number-spinner-value]\"\n );\n}\n\nfunction spinnerTarget(element: HTMLElement): number {\n const target = Number(element.dataset.spinnerTarget);\n return Number.isFinite(target) ? target : 0;\n}\n\nfunction spinnerLocale(element: HTMLElement): string {\n return element.dataset.spinnerLocale\n || document.documentElement.lang\n || navigator.language\n || \"en\";\n}\n\nfunction renderSpinnerValue(\n element: HTMLElement,\n value: number,\n final = false\n): void {\n const display = spinnerDisplay(element);\n if (!display) {\n return;\n }\n const target = spinnerTarget(element);\n const digits = Number(element.dataset.spinnerFractionDigits) || 0;\n display.textContent = formatSpinnerValue(\n final ? target : value,\n spinnerLocale(element),\n digits\n );\n if (final) {\n element.dataset.spinnerState = \"complete\";\n }\n}\n\nfunction finishSpinner(element: HTMLElement): void {\n const frame = runningFrames.get(element);\n if (frame !== undefined) {\n cancelAnimationFrame(frame);\n runningFrames.delete(element);\n }\n renderSpinnerValue(element, spinnerTarget(element), true);\n}\n\nfunction animateSpinner(element: HTMLElement): void {\n const duration = clampSpinnerDuration(element.dataset.spinnerDuration);\n const target = spinnerTarget(element);\n if (duration === 0 || target === 0) {\n finishSpinner(element);\n return;\n }\n\n element.dataset.spinnerState = \"running\";\n renderSpinnerValue(element, 0);\n const startedAt = performance.now();\n\n const step = (now: number) => {\n const progress = Math.min(1, (now - startedAt) / duration);\n renderSpinnerValue(element, spinnerFrameValue(target, progress));\n if (progress >= 1) {\n finishSpinner(element);\n return;\n }\n runningFrames.set(element, requestAnimationFrame(step));\n };\n\n runningFrames.set(element, requestAnimationFrame(step));\n}\n\nexport function initializeNumberSpinners(root: ParentNode = document): void {\n if (typeof window === \"undefined\") {\n return;\n }\n\n const elements = Array.from(\n root.querySelectorAll<HTMLElement>(\"[data-flowpress-number-spinner]\")\n ).filter((element) => {\n if (initializedSpinners.has(element)) {\n return false;\n }\n initializedSpinners.add(element);\n return true;\n });\n if (!elements.length) {\n return;\n }\n\n if (typeof window.matchMedia !== \"function\") {\n elements.forEach(finishSpinner);\n return;\n }\n\n const reducedMotion = window.matchMedia(REDUCED_MOTION_QUERY);\n if (reducedMotion.matches) {\n elements.forEach(finishSpinner);\n return;\n }\n\n if (!(\"IntersectionObserver\" in window)) {\n elements.forEach(finishSpinner);\n return;\n }\n\n const observer = new IntersectionObserver((entries) => {\n for (const entry of entries) {\n if (!entry.isIntersecting) {\n continue;\n }\n const element = entry.target as HTMLElement;\n observer.unobserve(element);\n animateSpinner(element);\n }\n }, {\n threshold: 0.15\n });\n\n elements.forEach((element) => observer.observe(element));\n\n const stopForReducedMotion = (event: MediaQueryListEvent) => {\n if (!event.matches) {\n return;\n }\n observer.disconnect();\n elements.forEach(finishSpinner);\n reducedMotion.removeEventListener(\"change\", stopForReducedMotion);\n };\n reducedMotion.addEventListener(\"change\", stopForReducedMotion);\n}\n"]}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"root":["../src/astro.d.ts","../src/client.ts","../src/env.ts","../src/index.ts","../src/number-spinner.ts","../src/style-classes.ts","../src/styleflow.ts","../src/template-hierarchy.ts","../src/templates.ts","../src/types.ts","../src/astro/blocks/helpers.ts"],"version":"5.9.3"}
|
|
1
|
+
{"root":["../src/astro.d.ts","../src/block-renderers.ts","../src/client.ts","../src/env.ts","../src/index.ts","../src/number-spinner.ts","../src/style-classes.ts","../src/styleflow.ts","../src/template-hierarchy.ts","../src/templates.ts","../src/types.ts","../src/astro/blocks/helpers.ts"],"version":"5.9.3"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@styleflow.app/flowpress-astro-core",
|
|
3
|
-
"version": "0.2.0-beta.
|
|
3
|
+
"version": "0.2.0-beta.14",
|
|
4
4
|
"description": "Project-agnostic WordPress routing, templates, SEO and GraphQL runtime for FlowPress Astro apps.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"private": false,
|
|
@@ -36,7 +36,8 @@
|
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "tsc -b && node ./scripts/copy-astro.mjs",
|
|
38
38
|
"typecheck": "tsc -b --pretty false && node ./scripts/copy-astro.mjs && astro check",
|
|
39
|
-
"test": "vitest run",
|
|
39
|
+
"test": "vitest run && npm run test:renderer",
|
|
40
|
+
"test:renderer": "node ./scripts/test-custom-renderer.mjs",
|
|
40
41
|
"release:check": "npm run typecheck && npm test && npm pack --dry-run",
|
|
41
42
|
"release:publish:next": "npm publish --access public --tag next",
|
|
42
43
|
"release:publish:latest": "npm publish --access public --tag latest",
|