@bettercms-ai/ui 0.14.0 → 0.15.1

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.
@@ -8,14 +8,22 @@ interface BlockRendererProps {
8
8
  renderBlock?: (block: ContentBlock) => React.ReactNode;
9
9
  /** Emit visual-editor field bindings. Default-off keeps published consumers clean. */
10
10
  annotate?: boolean;
11
+ /** The instant block windows are read against. Internal: `BlocksRenderer` passes it when annotating. */
12
+ stampAt?: Date;
11
13
  }
12
- declare function BlockRenderer({ block, className, renderBlock, annotate, }: BlockRendererProps): react_jsx_runtime.JSX.Element;
14
+ declare function BlockRenderer({ block, className, renderBlock, annotate, stampAt, }: BlockRendererProps): react_jsx_runtime.JSX.Element;
13
15
  interface BlocksRendererProps {
14
16
  blocks: ContentBlock[];
15
17
  className?: string;
16
18
  renderBlock?: (block: ContentBlock) => React.ReactNode;
17
19
  annotate?: boolean;
20
+ /**
21
+ * The instant block windows (`block.window`) are read against. Defaults to the render time.
22
+ * A block outside its window is not rendered, at any depth; with `annotate` it is rendered and
23
+ * marked `data-bcms-window="pending|expired"` instead.
24
+ */
25
+ now?: Date;
18
26
  }
19
- declare function BlocksRenderer({ blocks, className, renderBlock, annotate, }: BlocksRendererProps): react_jsx_runtime.JSX.Element | null;
27
+ declare function BlocksRenderer({ blocks: all, className, renderBlock, annotate, now, }: BlocksRendererProps): react_jsx_runtime.JSX.Element | null;
20
28
 
21
29
  export { BlockRenderer, BlocksRenderer };
package/dist/renderer.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use client";
2
2
 
3
3
  // src/renderer.tsx
4
- import { splitButtonStyle } from "@bettercms-ai/types";
4
+ import { RESPONSIVE_CSS, hideOnAttr, splitButtonStyle, usesHideOn, visibleBlocks, windowState } from "@bettercms-ai/types";
5
5
 
6
6
  // src/blocks/Heading.tsx
7
7
  import { jsx } from "react/jsx-runtime";
@@ -580,7 +580,7 @@ function Image({
580
580
  }
581
581
 
582
582
  // src/blocks/Button.tsx
583
- import { buttonIconOf, buttonIconSvg, buttonVariantOf } from "@bettercms-ai/types";
583
+ import { LINK_RELS, buttonIconOf, buttonIconSvg, buttonVariantOf, isScriptHref } from "@bettercms-ai/types";
584
584
  import { jsx as jsx5, jsxs as jsxs3 } from "react/jsx-runtime";
585
585
  function Button({
586
586
  text,
@@ -591,6 +591,7 @@ function Button({
591
591
  arrow = false,
592
592
  icon: iconProp,
593
593
  newTab = false,
594
+ rel,
594
595
  align = "left",
595
596
  className = "",
596
597
  style,
@@ -621,11 +622,13 @@ function Button({
621
622
  };
622
623
  const sizeClass = size === "sm" || size === "lg" ? sizeStyles[size] : sizeStyles.md;
623
624
  const fullClass = fullWidth === true ? "flex w-full justify-center" : "";
625
+ const relTokens = [...newTab === true ? ["noopener", "noreferrer"] : [], ...LINK_RELS.includes(rel) ? [rel] : []];
624
626
  return /* @__PURE__ */ jsxs3(
625
627
  "a",
626
628
  {
627
- href,
628
- ...newTab === true ? { target: "_blank", rel: "noopener noreferrer" } : {},
629
+ href: typeof href === "string" && isScriptHref(href) ? "#" : href,
630
+ ...newTab === true ? { target: "_blank" } : {},
631
+ ...relTokens.length ? { rel: relTokens.join(" ") } : {},
629
632
  "data-bcms-field": field,
630
633
  "data-bcms-kind": kind,
631
634
  style,
@@ -975,6 +978,12 @@ function styleOf(block) {
975
978
  const { placement, paint } = splitButtonStyle(block.style);
976
979
  return { wrapper: blockStyleToReactStyle(placement), paint: paint ? { style: paint } : {} };
977
980
  }
981
+ function withHide(block, wrapperStyle, el, stampAt) {
982
+ const hide = hideOnAttr(block.style);
983
+ const state = stampAt ? windowState(block, stampAt) : "live";
984
+ if (!wrapperStyle && !hide && state === "live") return el;
985
+ return /* @__PURE__ */ jsx15("div", { style: wrapperStyle, ...hide ? { "data-bcms-hide": hide } : {}, ...state === "live" ? {} : { "data-bcms-window": state }, children: el });
986
+ }
978
987
  function bindingProps(block, annotate) {
979
988
  if (!annotate || !block.id) return {};
980
989
  switch (block.type) {
@@ -990,7 +999,7 @@ function bindingProps(block, annotate) {
990
999
  return {};
991
1000
  }
992
1001
  }
993
- function resolveBlock(block, className, annotate = false) {
1002
+ function resolveBlock(block, className, annotate = false, stampAt) {
994
1003
  const entry = BLOCK_REGISTRY[block.type];
995
1004
  if (!entry) {
996
1005
  return /* @__PURE__ */ jsxs11(
@@ -1007,13 +1016,14 @@ function resolveBlock(block, className, annotate = false) {
1007
1016
  const Component = entry.component;
1008
1017
  const { wrapper: blockStyle, paint } = styleOf(block);
1009
1018
  const el = /* @__PURE__ */ jsx15(Component, { ...block.props, ...paint, className, ...bindingProps(block, annotate) });
1010
- return blockStyle ? /* @__PURE__ */ jsx15("div", { style: blockStyle, children: el }) : el;
1019
+ return withHide(block, blockStyle, el, stampAt);
1011
1020
  }
1012
1021
  function BlockRenderer({
1013
1022
  block,
1014
1023
  className = "",
1015
1024
  renderBlock,
1016
- annotate = false
1025
+ annotate = false,
1026
+ stampAt
1017
1027
  }) {
1018
1028
  const entry = BLOCK_REGISTRY[block.type];
1019
1029
  if (!entry) {
@@ -1029,7 +1039,7 @@ function BlockRenderer({
1029
1039
  );
1030
1040
  }
1031
1041
  const Component = entry.component;
1032
- const containerProps = CONTAINER_TYPES.has(block.type) ? { renderBlock: renderBlock ?? ((child) => resolveBlock(child, void 0, annotate)) } : {};
1042
+ const containerProps = CONTAINER_TYPES.has(block.type) ? { renderBlock: renderBlock ?? ((child) => resolveBlock(child, void 0, annotate, stampAt)) } : {};
1033
1043
  const { wrapper: blockStyle, paint } = styleOf(block);
1034
1044
  const el = /* @__PURE__ */ jsx15(
1035
1045
  Component,
@@ -1041,24 +1051,31 @@ function BlockRenderer({
1041
1051
  ...bindingProps(block, annotate)
1042
1052
  }
1043
1053
  );
1044
- return blockStyle ? /* @__PURE__ */ jsx15("div", { style: blockStyle, children: el }) : el;
1054
+ return withHide(block, blockStyle, el, stampAt);
1045
1055
  }
1046
1056
  function BlocksRenderer({
1047
- blocks,
1057
+ blocks: all,
1048
1058
  className = "",
1049
1059
  renderBlock,
1050
- annotate = false
1060
+ annotate = false,
1061
+ now
1051
1062
  }) {
1063
+ const at = now ?? /* @__PURE__ */ new Date();
1064
+ const blocks = annotate ? all : visibleBlocks(all, at);
1052
1065
  if (blocks.length === 0) return null;
1053
- return /* @__PURE__ */ jsx15("div", { className, children: blocks.map((block) => /* @__PURE__ */ jsx15(
1054
- BlockRenderer,
1055
- {
1056
- block,
1057
- renderBlock,
1058
- annotate
1059
- },
1060
- block.id
1061
- )) });
1066
+ return /* @__PURE__ */ jsxs11("div", { className, children: [
1067
+ usesHideOn(blocks) && /* @__PURE__ */ jsx15("style", { dangerouslySetInnerHTML: { __html: RESPONSIVE_CSS } }),
1068
+ blocks.map((block) => /* @__PURE__ */ jsx15(
1069
+ BlockRenderer,
1070
+ {
1071
+ block,
1072
+ renderBlock,
1073
+ annotate,
1074
+ stampAt: annotate ? at : void 0
1075
+ },
1076
+ block.id
1077
+ ))
1078
+ ] });
1062
1079
  }
1063
1080
  export {
1064
1081
  BlockRenderer,