@bettercms-ai/types 1.37.0 → 1.39.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/dist/index.js CHANGED
@@ -1,4 +1,67 @@
1
1
  // src/block.ts
2
+ var WINDOW_INSTANT = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(:\d{2}(\.\d+)?)?(Z|[+-]\d{2}:\d{2})$/;
3
+ function normalizeWindow(value) {
4
+ if (value == null) return { window: null };
5
+ if (typeof value !== "object" || Array.isArray(value)) return { error: "invalid_window" };
6
+ const out = {};
7
+ for (const [key, raw] of Object.entries(value)) {
8
+ if (key !== "from" && key !== "until") return { error: "invalid_window" };
9
+ if (raw == null) continue;
10
+ const at = typeof raw === "string" && WINDOW_INSTANT.test(raw) ? Date.parse(raw) : NaN;
11
+ const day = Number(String(raw).slice(8, 10));
12
+ if (Number.isNaN(at) || new Date(Date.UTC(Number(String(raw).slice(0, 4)), Number(String(raw).slice(5, 7)) - 1, day)).getUTCDate() !== day) {
13
+ return { error: "invalid_window" };
14
+ }
15
+ out[key] = `${new Date(at).toISOString().slice(0, 19)}Z`;
16
+ }
17
+ if (out.from && out.until && out.from >= out.until) return { error: "window_inverted" };
18
+ return { window: out.from || out.until ? out : null };
19
+ }
20
+ function windowState(item, now) {
21
+ const w = item && typeof item === "object" ? item.window : void 0;
22
+ if (!w || typeof w !== "object") return "live";
23
+ const at = typeof now === "number" ? now : now.getTime();
24
+ const { from, until } = w;
25
+ const start = typeof from === "string" ? Date.parse(from) : NaN;
26
+ const end = typeof until === "string" ? Date.parse(until) : NaN;
27
+ if (!Number.isNaN(end) && at >= end) return "expired";
28
+ if (!Number.isNaN(start) && at < start) return "pending";
29
+ return "live";
30
+ }
31
+ var inWindow = (item, now) => windowState(item, now) === "live";
32
+ function mapSame(list, fn) {
33
+ let changed = false;
34
+ const out = list.map((item) => {
35
+ const next = fn(item);
36
+ if (next !== item) changed = true;
37
+ return next;
38
+ });
39
+ return changed ? out : list;
40
+ }
41
+ function withVisibleChildren(block, now) {
42
+ const p = block.props ?? {};
43
+ const key = block.type === "section" ? "children" : block.type === "columns" ? "columns" : block.type === "slider" ? "slides" : block.type === "tabs" ? "tabs" : null;
44
+ if (!key || !Array.isArray(p[key])) return block;
45
+ const held = p[key];
46
+ const next = key === "children" ? visibleBlocks(held, now) : key === "columns" ? mapSame(held, (column) => visibleBlocks(column, now)) : (
47
+ // A slide or a tab panel: `{ id, children }`.
48
+ mapSame(held, (row) => {
49
+ const children = row?.children;
50
+ const shown = visibleBlocks(children, now);
51
+ return shown === children ? row : { ...row, children: shown };
52
+ })
53
+ );
54
+ return next === held ? block : { ...block, props: { ...p, [key]: next } };
55
+ }
56
+ function visibleBlocks(blocks, now) {
57
+ if (!Array.isArray(blocks)) return blocks;
58
+ const shown = blocks.filter((block) => inWindow(block, now));
59
+ const pruned = mapSame(
60
+ shown,
61
+ (block) => block && typeof block === "object" ? withVisibleChildren(block, now) : block
62
+ );
63
+ return shown.length === blocks.length && pruned === shown ? blocks : pruned;
64
+ }
2
65
  function isBlock(value) {
3
66
  return typeof value === "object" && value !== null && "type" in value && typeof value["type"] === "string";
4
67
  }
@@ -164,6 +227,22 @@ var SHADOW_CSS = {
164
227
  };
165
228
  var HAIRLINE = "1px solid rgba(0,0,0,.10)";
166
229
  var CONTENT_WIDTH_PX = { narrow: "640px", default: "1024px", wide: "1280px" };
230
+ var BREAKPOINTS = { mobile: 767, tablet: 1023 };
231
+ var RESPONSIVE_CSS = `@media(max-width:${BREAKPOINTS.mobile}px){[data-bcms-hide~="mobile"]{display:none!important}}@media(min-width:${BREAKPOINTS.mobile + 1}px) and (max-width:${BREAKPOINTS.tablet}px){[data-bcms-hide~="tablet"]{display:none!important}}@media(min-width:${BREAKPOINTS.tablet + 1}px){[data-bcms-hide~="desktop"]{display:none!important}}`;
232
+ function hideOnAttr(style) {
233
+ const raw = style && typeof style === "object" ? style.hideOn : void 0;
234
+ if (!Array.isArray(raw)) return "";
235
+ const on = ["mobile", "tablet", "desktop"].filter((b) => raw.includes(b));
236
+ return on.length === 3 ? "" : on.join(" ");
237
+ }
238
+ function usesHideOn(blocks) {
239
+ let found = false;
240
+ JSON.stringify(blocks ?? null, (k, v) => {
241
+ if (k === "hideOn" && hideOnAttr({ hideOn: v })) found = true;
242
+ return v;
243
+ });
244
+ return found;
245
+ }
167
246
  var HEX = /^#(?:[0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/;
168
247
  var clamp = (n, lo, hi) => Math.min(hi, Math.max(lo, n));
169
248
  var own = (map, key) => typeof key === "string" && Object.prototype.hasOwnProperty.call(map, key) ? map[key] : void 0;
@@ -276,7 +355,8 @@ var BUTTON_PLACEMENT_KEYS = [
276
355
  "marginBottom",
277
356
  "fullHeight",
278
357
  "spaceTop",
279
- "spaceBottom"
358
+ "spaceBottom",
359
+ "hideOn"
280
360
  ];
281
361
  var PLACEMENT = new Set(BUTTON_PLACEMENT_KEYS);
282
362
  function splitButtonStyle(style) {
@@ -335,8 +415,25 @@ var COMPONENT_PROP_TYPES = [
335
415
  "group",
336
416
  "table",
337
417
  "slot",
338
- "form"
418
+ "form",
419
+ // The seven below each store what the same-named content-model FIELD stores (`icon` and
420
+ // `multiselect` have no field twin; see the value notes on ComponentPropDef.type).
421
+ "link",
422
+ "button",
423
+ "date",
424
+ "color",
425
+ "file",
426
+ "icon",
427
+ "multiselect"
339
428
  ];
429
+ var SLOT_MAX_ITEMS = 12;
430
+ var SLOT_ITEMS_CAP = 50;
431
+ function slotItems(value) {
432
+ const list = Array.isArray(value) ? value : [value];
433
+ return list.filter(
434
+ (item) => !!item && typeof item === "object" && !Array.isArray(item) && typeof item.componentId === "string" && item.componentId !== ""
435
+ );
436
+ }
340
437
 
341
438
  // src/layout-lucide-icons.ts
342
439
  var LAYOUT_SECTION_ICONS = Object.freeze([
@@ -2304,6 +2401,36 @@ var LAYOUT_SECTION_ICON_SET = new Set(LAYOUT_SECTION_ICONS);
2304
2401
  var PAGE_CONTENT_ANCHOR_ID = "page-content";
2305
2402
  var NAVIGATION_SECTION_ID = "navigation";
2306
2403
  var FOOTER_SECTION_ID = "footer";
2404
+ function visibleLayoutStructure(structure, now) {
2405
+ const sections = structure?.sections;
2406
+ if (!Array.isArray(sections)) return structure;
2407
+ const hidden = new Set(
2408
+ sections.filter((section) => section && !section.variantOf && !inWindow(section, now)).map((section) => section.id)
2409
+ );
2410
+ if (hidden.size === 0) return structure;
2411
+ return {
2412
+ ...structure,
2413
+ sections: sections.filter((section) => !hidden.has(section?.variantOf ?? section?.id))
2414
+ };
2415
+ }
2416
+ function visibleDeliveredLayout(layout2, now) {
2417
+ if (!layout2) return layout2;
2418
+ const hiddenIds = /* @__PURE__ */ new Set();
2419
+ const hiddenSlugs = /* @__PURE__ */ new Set();
2420
+ for (const node of layout2.nodes) {
2421
+ if (node.kind !== "section" || inWindow(layout2.sections[node.slug], now)) continue;
2422
+ hiddenIds.add(node.id);
2423
+ hiddenSlugs.add(node.slug);
2424
+ }
2425
+ if (hiddenIds.size === 0) return layout2;
2426
+ return {
2427
+ ...layout2,
2428
+ nodes: layout2.nodes.filter((node) => node.kind !== "section" || !hiddenIds.has(node.id)),
2429
+ sections: Object.fromEntries(
2430
+ Object.entries(layout2.sections).filter(([slug, section]) => !hiddenSlugs.has(slug) && !(section.variantOf && hiddenIds.has(section.variantOf)))
2431
+ )
2432
+ };
2433
+ }
2307
2434
 
2308
2435
  // src/dock-icon-names.ts
2309
2436
  var DOCK_ICON_NAMES = [
@@ -2810,6 +2937,11 @@ function buttonIconSvg(name, cls) {
2810
2937
  return iconSvg(name, cls);
2811
2938
  }
2812
2939
  var BUTTON_SIZES = ["sm", "md", "lg"];
2940
+ var LINK_RELS = ["nofollow", "sponsored", "ugc"];
2941
+ function anchorTargetRel(newTab, rel) {
2942
+ const tokens = [...newTab ? ["noopener", "noreferrer"] : [], ...LINK_RELS.includes(rel) ? [rel] : []];
2943
+ return `${newTab ? ' target="_blank"' : ""}${tokens.length ? ` rel="${tokens.join(" ")}"` : ""}`;
2944
+ }
2813
2945
  var hrefOf = (href) => typeof href === "string" ? href : href && typeof href === "object" && typeof href.href === "string" ? href.href : null;
2814
2946
  function readButtonValue(value) {
2815
2947
  const v = value && typeof value === "object" ? value : {};
@@ -2820,7 +2952,8 @@ function readButtonValue(value) {
2820
2952
  size: BUTTON_SIZES.includes(v.size) ? v.size : "md",
2821
2953
  arrow: v.arrow === true,
2822
2954
  icon: buttonIconOf(v.icon),
2823
- newTab: v.newTab === true
2955
+ newTab: v.newTab === true,
2956
+ ...LINK_RELS.includes(v.rel) ? { rel: v.rel } : {}
2824
2957
  };
2825
2958
  }
2826
2959
  function buttonValueProblem(value) {
@@ -2832,10 +2965,21 @@ function buttonValueProblem(value) {
2832
2965
  if (v.size !== void 0 && !BUTTON_SIZES.includes(v.size)) return `has an unknown size "${String(v.size)}"`;
2833
2966
  if (v.arrow !== void 0 && typeof v.arrow !== "boolean") return "arrow must be true or false";
2834
2967
  if (v.newTab !== void 0 && typeof v.newTab !== "boolean") return "newTab must be true or false";
2968
+ if (v.rel !== void 0 && !LINK_RELS.includes(v.rel)) return `has an unknown rel "${String(v.rel)}" (nofollow, sponsored or ugc)`;
2835
2969
  if (v.icon !== void 0 && v.icon !== null && buttonIconOf(v.icon) === null) return `has an unknown icon "${String(v.icon)}"`;
2836
2970
  return null;
2837
2971
  }
2838
2972
  var BUTTON_LOOK_KEYS = ["variant", "size", "arrow", "icon", "newTab"];
2973
+ function buttonBlockProps(value) {
2974
+ if (!value || typeof value !== "object" || Array.isArray(value)) return {};
2975
+ const v = value;
2976
+ const out = {};
2977
+ if (typeof v.label === "string") out.text = v.label;
2978
+ const href = hrefOf(v.href);
2979
+ if (href !== null) out.href = href;
2980
+ for (const key of BUTTON_LOOK_KEYS) if (key in v) out[key] = v[key];
2981
+ return out;
2982
+ }
2839
2983
  function soleButtonId(blockJson) {
2840
2984
  const ids = [];
2841
2985
  const walk = (list) => {
@@ -2878,8 +3022,9 @@ function buttonValueOfInstance(def, overrides) {
2878
3022
  walk(def.blockJson);
2879
3023
  const props = { ...found[0]?.props ?? {} };
2880
3024
  for (const p of withButtonLookProps(def)) {
2881
- if (p.target.blockId !== blockId || !p.target.path.startsWith("props.") || !overrides || !(p.key in overrides)) continue;
2882
- props[p.target.path.slice("props.".length)] = overrides[p.key];
3025
+ if (p.target.blockId !== blockId || !overrides || !(p.key in overrides)) continue;
3026
+ if (p.type === "button") Object.assign(props, buttonBlockProps(overrides[p.key]));
3027
+ else if (p.target.path.startsWith("props.")) props[p.target.path.slice("props.".length)] = overrides[p.key];
2883
3028
  }
2884
3029
  return readButtonValue({ ...props, label: props.text });
2885
3030
  }
@@ -2946,6 +3091,7 @@ function regionOf(regions, el) {
2946
3091
  }
2947
3092
  export {
2948
3093
  BLOCK_BINDINGS,
3094
+ BREAKPOINTS,
2949
3095
  BUTTON_ICONS,
2950
3096
  BUTTON_ICON_NAMES,
2951
3097
  BUTTON_LOOK_KEYS,
@@ -2962,16 +3108,22 @@ export {
2962
3108
  LANDMARK_SELECTOR,
2963
3109
  LAYOUT_SECTION_ICONS,
2964
3110
  LAYOUT_SECTION_ICON_SET,
3111
+ LINK_RELS,
2965
3112
  MAIN_SELECTOR,
2966
3113
  NAVIGATION_SECTION_ID,
2967
3114
  PAGE_CONTENT_ANCHOR_ID,
3115
+ RESPONSIVE_CSS,
2968
3116
  SECTION_DOCTRINE,
2969
3117
  SECTION_SPACE_TOKENS,
3118
+ SLOT_ITEMS_CAP,
3119
+ SLOT_MAX_ITEMS,
2970
3120
  SPACE_BOTTOM_TOKENS,
2971
3121
  SPACE_STEP_TOKENS,
3122
+ anchorTargetRel,
2972
3123
  blockStyleToColumnCss,
2973
3124
  blockStyleToCss,
2974
3125
  blockStyleToInnerCss,
3126
+ buttonBlockProps,
2975
3127
  buttonIconOf,
2976
3128
  buttonIconSvg,
2977
3129
  buttonLinkPairs,
@@ -2990,19 +3142,28 @@ export {
2990
3142
  formatPropsAttribute,
2991
3143
  getBlockType,
2992
3144
  headingPaintCss,
3145
+ hideOnAttr,
3146
+ inWindow,
2993
3147
  isBlock,
2994
3148
  isChromeLandmark,
2995
3149
  keyWords,
2996
3150
  landmarkRegions,
2997
3151
  locatorFor,
3152
+ normalizeWindow,
2998
3153
  parsePropsAttribute,
2999
3154
  pickFieldStyle,
3000
3155
  readButtonValue,
3001
3156
  readPath,
3002
3157
  regionOf,
3003
3158
  safeCssUrl,
3159
+ slotItems,
3004
3160
  spaceTokenCss,
3005
3161
  splitButtonStyle,
3162
+ usesHideOn,
3163
+ visibleBlocks,
3164
+ visibleDeliveredLayout,
3165
+ visibleLayoutStructure,
3166
+ windowState,
3006
3167
  withButtonLookProps
3007
3168
  };
3008
3169
  //# sourceMappingURL=index.js.map