@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 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
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 blocks={children} contract={contract} />
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 blocks={children} contract={contract} />
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 blocks={children} contract={contract} />
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 blocks={children} contract={contract} />
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 blocks={resolvedBlocks[index]} contract={contract} />
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 blocks={children} contract={contract} />
267
+ <Astro.self
268
+ blocks={children}
269
+ contract={contract}
270
+ blockRenderers={blockRenderers}
271
+ />
233
272
  </div>
234
273
  ) : (
235
- <Astro.self blocks={children} contract={contract} />
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 blocks={children} contract={contract} />
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
- : <Astro.self blocks={children} contract={contract} />}
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,2 @@
1
+ export {};
2
+ //# sourceMappingURL=block-renderers.js.map
@@ -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
@@ -1,4 +1,5 @@
1
1
  export * from "./client.js";
2
+ export * from "./block-renderers.js";
2
3
  export * from "./env.js";
3
4
  export * from "./number-spinner.js";
4
5
  export * from "./styleflow.js";
@@ -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
@@ -1,4 +1,5 @@
1
1
  export * from "./client.js";
2
+ export * from "./block-renderers.js";
2
3
  export * from "./env.js";
3
4
  export * from "./number-spinner.js";
4
5
  export * from "./styleflow.js";
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,CAqD1E"}
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"}
@@ -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(animateSpinner);
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":"AAAA,MAAM,oBAAoB,GAAG,kCAAkC,CAAC;AAChE,MAAM,mBAAmB,GAAG,IAAI,OAAO,EAAe,CAAC;AACvD,MAAM,aAAa,GAAG,IAAI,GAAG,EAAuB,CAAC;AAErD,MAAM,UAAU,qBAAqB,CAAC,KAAa;IACjD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,CAAC,CAAC;IACX,CAAC;IAED,MAAM,CAAC,WAAW,GAAG,EAAE,EAAE,YAAY,CAAC,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAChF,MAAM,mBAAmB,GAAG,WAAW,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,MAAM,IAAI,CAAC,CAAC;IACnE,MAAM,QAAQ,GAAG,MAAM,CAAC,YAAY,IAAI,CAAC,CAAC,CAAC;IAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,mBAAmB,GAAG,QAAQ,CAAC,CAAC,CAAC;AACnE,CAAC;AAED,MAAM,UAAU,oBAAoB,CAAC,KAAc;IACjD,MAAM,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC,CAAC;IAC/B,OAAO,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAC9B,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC;QACzC,CAAC,CAAC,IAAI,CAAC;AACX,CAAC;AAED,MAAM,UAAU,iBAAiB,CAAC,MAAc,EAAE,QAAgB;IAChE,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC;IACtD,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,GAAG,UAAU,EAAE,CAAC,CAAC,CAAC;IAC9C,OAAO,MAAM,GAAG,KAAK,CAAC;AACxB,CAAC;AAED,MAAM,UAAU,kBAAkB,CAChC,KAAa,EACb,MAAc,EACd,cAAsB;IAEtB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC;IACrE,OAAO,IAAI,IAAI,CAAC,YAAY,CAAC,MAAM,IAAI,SAAS,EAAE;QAChD,qBAAqB,EAAE,MAAM;QAC7B,qBAAqB,EAAE,MAAM;KAC9B,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;AACnB,CAAC;AAED,SAAS,cAAc,CAAC,OAAoB;IAC1C,OAAO,OAAO,CAAC,aAAa,CAC1B,uCAAuC,CACxC,CAAC;AACJ,CAAC;AAED,SAAS,aAAa,CAAC,OAAoB;IACzC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IACrD,OAAO,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC;AAC9C,CAAC;AAED,SAAS,aAAa,CAAC,OAAoB;IACzC,OAAO,OAAO,CAAC,OAAO,CAAC,aAAa;WAC/B,QAAQ,CAAC,eAAe,CAAC,IAAI;WAC7B,SAAS,CAAC,QAAQ;WAClB,IAAI,CAAC;AACZ,CAAC;AAED,SAAS,kBAAkB,CACzB,OAAoB,EACpB,KAAa,EACb,KAAK,GAAG,KAAK;IAEb,MAAM,OAAO,GAAG,cAAc,CAAC,OAAO,CAAC,CAAC;IACxC,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC,qBAAqB,CAAC,IAAI,CAAC,CAAC;IAClE,OAAO,CAAC,WAAW,GAAG,kBAAkB,CACtC,KAAK,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,EACtB,aAAa,CAAC,OAAO,CAAC,EACtB,MAAM,CACP,CAAC;IACF,IAAI,KAAK,EAAE,CAAC;QACV,OAAO,CAAC,OAAO,CAAC,YAAY,GAAG,UAAU,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,SAAS,aAAa,CAAC,OAAoB;IACzC,MAAM,KAAK,GAAG,aAAa,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;IACzC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;QACxB,oBAAoB,CAAC,KAAK,CAAC,CAAC;QAC5B,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;IAChC,CAAC;IACD,kBAAkB,CAAC,OAAO,EAAE,aAAa,CAAC,OAAO,CAAC,EAAE,IAAI,CAAC,CAAC;AAC5D,CAAC;AAED,SAAS,cAAc,CAAC,OAAoB;IAC1C,MAAM,QAAQ,GAAG,oBAAoB,CAAC,OAAO,CAAC,OAAO,CAAC,eAAe,CAAC,CAAC;IACvE,MAAM,MAAM,GAAG,aAAa,CAAC,OAAO,CAAC,CAAC;IACtC,IAAI,QAAQ,KAAK,CAAC,IAAI,MAAM,KAAK,CAAC,EAAE,CAAC;QACnC,aAAa,CAAC,OAAO,CAAC,CAAC;QACvB,OAAO;IACT,CAAC;IAED,OAAO,CAAC,OAAO,CAAC,YAAY,GAAG,SAAS,CAAC;IACzC,kBAAkB,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC;IAC/B,MAAM,SAAS,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC;IAEpC,MAAM,IAAI,GAAG,CAAC,GAAW,EAAE,EAAE;QAC3B,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,GAAG,GAAG,SAAS,CAAC,GAAG,QAAQ,CAAC,CAAC;QAC3D,kBAAkB,CAAC,OAAO,EAAE,iBAAiB,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC,CAAC;QACjE,IAAI,QAAQ,IAAI,CAAC,EAAE,CAAC;YAClB,aAAa,CAAC,OAAO,CAAC,CAAC;YACvB,OAAO;QACT,CAAC;QACD,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC;IAC1D,CAAC,CAAC;IAEF,aAAa,CAAC,GAAG,CAAC,OAAO,EAAE,qBAAqB,CAAC,IAAI,CAAC,CAAC,CAAC;AAC1D,CAAC;AAED,MAAM,UAAU,wBAAwB,CAAC,OAAmB,QAAQ;IAClE,IAAI,OAAO,MAAM,KAAK,WAAW,EAAE,CAAC;QAClC,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,KAAK,CAAC,IAAI,CACzB,IAAI,CAAC,gBAAgB,CAAc,iCAAiC,CAAC,CACtE,CAAC,MAAM,CAAC,CAAC,OAAO,EAAE,EAAE;QACnB,IAAI,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,EAAE,CAAC;YACrC,OAAO,KAAK,CAAC;QACf,CAAC;QACD,mBAAmB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC;QACjC,OAAO,IAAI,CAAC;IACd,CAAC,CAAC,CAAC;IACH,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QACrB,OAAO;IACT,CAAC;IAED,MAAM,aAAa,GAAG,MAAM,CAAC,UAAU,CAAC,oBAAoB,CAAC,CAAC;IAC9D,IAAI,aAAa,CAAC,OAAO,EAAE,CAAC;QAC1B,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAChC,OAAO;IACT,CAAC;IAED,IAAI,CAAC,CAAC,sBAAsB,IAAI,MAAM,CAAC,EAAE,CAAC;QACxC,QAAQ,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QACjC,OAAO;IACT,CAAC;IAED,MAAM,QAAQ,GAAG,IAAI,oBAAoB,CAAC,CAAC,OAAO,EAAE,EAAE;QACpD,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,CAAC,KAAK,CAAC,cAAc,EAAE,CAAC;gBAC1B,SAAS;YACX,CAAC;YACD,MAAM,OAAO,GAAG,KAAK,CAAC,MAAqB,CAAC;YAC5C,QAAQ,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC;YAC5B,cAAc,CAAC,OAAO,CAAC,CAAC;QAC1B,CAAC;IACH,CAAC,EAAE;QACD,SAAS,EAAE,IAAI;KAChB,CAAC,CAAC;IAEH,QAAQ,CAAC,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;IAEzD,MAAM,oBAAoB,GAAG,CAAC,KAA0B,EAAE,EAAE;QAC1D,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,CAAC;YACnB,OAAO;QACT,CAAC;QACD,QAAQ,CAAC,UAAU,EAAE,CAAC;QACtB,QAAQ,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;QAChC,aAAa,CAAC,mBAAmB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;IACpE,CAAC,CAAC;IACF,aAAa,CAAC,gBAAgB,CAAC,QAAQ,EAAE,oBAAoB,CAAC,CAAC;AACjE,CAAC","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.12",
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",