@bettercms-ai/types 1.38.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.d.ts CHANGED
@@ -48,7 +48,7 @@ interface ContentModel {
48
48
  * by the API but normalized to `array` + zones on write, so stored/delivered
49
49
  * content never contains them.
50
50
  */
51
- type ContentModelFieldType = "text" | "richtext" | "document" | "image" | "boolean" | "number" | "select" | "reference" | "multi-reference" | "multiReference" | "array" | "date" | "datetime" | "longtext" | "slug" | "email" | "phone" | "link" | "color" | "json" | "file" | "component-ref" | "modular" | "sections" | "location" | "button";
51
+ type ContentModelFieldType = "text" | "richtext" | "document" | "image" | "boolean" | "number" | "select" | "reference" | "multi-reference" | "multiReference" | "array" | "date" | "datetime" | "longtext" | "slug" | "email" | "phone" | "link" | "color" | "json" | "file" | "component-ref" | "modular" | "sections" | "location" | "button" | "multiselect" | "icon";
52
52
  type ContentModelKind = "model" | "block";
53
53
  /**
54
54
  * How a fieldset renders in the dock. ABSENT ≡ today's flat list (V2): a legacy `{id, name}`
@@ -315,9 +315,10 @@ interface ContentModelFieldUi {
315
315
  /**
316
316
  * Which editor control a LEAF renders with. ABSENT ≡ the type's default (a dropdown for a
317
317
  * `select`, a plain input for a `number`). `segmented` is valid on a single-choice `select`;
318
- * `slider` on a `number` whose config.min < config.max.
318
+ * `slider` on a `number` whose config.min < config.max; `rating` on a `number` with numeric
319
+ * config.min, max and step (stars).
319
320
  */
320
- control?: "segmented" | "slider";
321
+ control?: "segmented" | "slider" | "rating";
321
322
  /** The branch row's icon. Nesting fields only. */
322
323
  icon?: DockIconName;
323
324
  /** What one item is called ("Slide", "Member"), 1–24 chars. Nesting fields only. */
@@ -545,6 +546,11 @@ interface BlockStyle {
545
546
  bgImage?: string;
546
547
  /** A button's look while the pointer is over it. Only the paint changes; unset keys keep the rest. */
547
548
  hover?: BlockHoverStyle;
549
+ /**
550
+ * Breakpoints the block is hidden on (`display:none` there only; the block stays in the HTML).
551
+ * Never all three: a block hidden everywhere is a delete. Bands: see `BREAKPOINTS`.
552
+ */
553
+ hideOn?: Array<"mobile" | "tablet" | "desktop">;
548
554
  }
549
555
  /** The paint a button swaps to on hover: fill, text colour, and the all-round border. */
550
556
  interface BlockHoverStyle {
@@ -553,6 +559,47 @@ interface BlockHoverStyle {
553
559
  textColor?: ColorToken;
554
560
  border?: boolean;
555
561
  }
562
+ /**
563
+ * Both bounds are INSTANTS: ISO 8601 with an offset, stored normalized to UTC seconds
564
+ * (`2026-11-27T00:00:00Z`). Shown from `from` (inclusive) until `until` (exclusive); a missing
565
+ * bound is open. An absent or null window is always shown.
566
+ */
567
+ interface VisibilityWindow {
568
+ from?: string;
569
+ until?: string;
570
+ }
571
+ type WindowState = "live" | "pending" | "expired";
572
+ /**
573
+ * A written window, normalized for storage, or the code it is refused with. null, undefined and
574
+ * an object with neither bound are all "no window". UTC at seconds precision makes the stored
575
+ * strings sort in time order, which the due scan (`list_due_window_projects`) relies on.
576
+ */
577
+ declare function normalizeWindow(value: unknown): {
578
+ window: VisibilityWindow | null;
579
+ } | {
580
+ error: "invalid_window" | "window_inverted";
581
+ };
582
+ /**
583
+ * Where `now` falls in a block's or Section's `window`. THE one reading of a window: every
584
+ * renderer, the delivery lanes and the in-place publish decide through it (or `inWindow`).
585
+ *
586
+ * `now` is an argument, never read from a clock here, so one request compares every block
587
+ * against the same instant. Bounds are compared as instants, so a stored offset cannot shift
588
+ * one. A bound that does not parse is read as open: a hand-edited row shows rather than vanishes.
589
+ */
590
+ declare function windowState(item: unknown, now: Date | number): WindowState;
591
+ /** Is the block or Section shown at `now`? No window is always shown. */
592
+ declare const inWindow: (item: unknown, now: Date | number) => boolean;
593
+ /**
594
+ * The blocks shown at `now`: every block outside its window removed, at any depth (a section's
595
+ * children, columns, slides, tab panels). What a published lane renders, collects from and
596
+ * delivers.
597
+ *
598
+ * Returns the SAME array when nothing is removed, so a tree with no window costs one walk and
599
+ * every consumer downstream is handed exactly what it was handed before windows existed.
600
+ * Non-array input is returned as it came.
601
+ */
602
+ declare function visibleBlocks<T>(blocks: T, now: Date | number): T;
556
603
  interface HeadingProps {
557
604
  text: string;
558
605
  level?: 1 | 2 | 3;
@@ -563,6 +610,8 @@ interface HeadingBlock {
563
610
  style?: BlockStyle;
564
611
  /** Editor-facing display name for this block's dock card. Not rendered. */
565
612
  label?: string;
613
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
614
+ window?: VisibilityWindow;
566
615
  props: HeadingProps;
567
616
  }
568
617
  interface TextProps {
@@ -576,6 +625,8 @@ interface TextBlock {
576
625
  style?: BlockStyle;
577
626
  /** Editor-facing display name for this block's dock card. Not rendered. */
578
627
  label?: string;
628
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
629
+ window?: VisibilityWindow;
579
630
  props: TextProps;
580
631
  }
581
632
  /**
@@ -646,6 +697,8 @@ interface RichTextBlock {
646
697
  style?: BlockStyle;
647
698
  /** Editor-facing display name for this block's dock card. Not rendered. */
648
699
  label?: string;
700
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
701
+ window?: VisibilityWindow;
649
702
  props: RichTextProps;
650
703
  }
651
704
  interface ImageProps {
@@ -661,6 +714,8 @@ interface ImageBlock {
661
714
  style?: BlockStyle;
662
715
  /** Editor-facing display name for this block's dock card. Not rendered. */
663
716
  label?: string;
717
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
718
+ window?: VisibilityWindow;
664
719
  props: ImageProps;
665
720
  }
666
721
  interface ButtonProps {
@@ -687,6 +742,8 @@ interface ButtonBlock {
687
742
  style?: BlockStyle;
688
743
  /** Editor-facing display name for this block's dock card. Not rendered. */
689
744
  label?: string;
745
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
746
+ window?: VisibilityWindow;
690
747
  props: ButtonProps;
691
748
  }
692
749
  interface SpacerProps {
@@ -698,6 +755,8 @@ interface SpacerBlock {
698
755
  style?: BlockStyle;
699
756
  /** Editor-facing display name for this block's dock card. Not rendered. */
700
757
  label?: string;
758
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
759
+ window?: VisibilityWindow;
701
760
  props: SpacerProps;
702
761
  }
703
762
  interface ColumnsProps {
@@ -710,6 +769,8 @@ interface ColumnsBlock {
710
769
  style?: BlockStyle;
711
770
  /** Editor-facing display name for this block's dock card. Not rendered. */
712
771
  label?: string;
772
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
773
+ window?: VisibilityWindow;
713
774
  props: ColumnsProps;
714
775
  }
715
776
  interface VideoProps {
@@ -730,6 +791,8 @@ interface VideoBlock {
730
791
  style?: BlockStyle;
731
792
  /** Editor-facing display name for this block's dock card. Not rendered. */
732
793
  label?: string;
794
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
795
+ window?: VisibilityWindow;
733
796
  props: VideoProps;
734
797
  }
735
798
  interface FormProps {
@@ -741,6 +804,8 @@ interface FormBlock {
741
804
  style?: BlockStyle;
742
805
  /** Editor-facing display name for this block's dock card. Not rendered. */
743
806
  label?: string;
807
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
808
+ window?: VisibilityWindow;
744
809
  props: FormProps;
745
810
  }
746
811
  interface SectionProps {
@@ -755,6 +820,8 @@ interface SectionBlock {
755
820
  style?: BlockStyle;
756
821
  /** Editor-facing display name for this block's dock card. Not rendered. */
757
822
  label?: string;
823
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
824
+ window?: VisibilityWindow;
758
825
  props: SectionProps;
759
826
  }
760
827
  interface NavLink {
@@ -781,6 +848,8 @@ interface NavbarBlock {
781
848
  style?: BlockStyle;
782
849
  /** Editor-facing display name for this block's dock card. Not rendered. */
783
850
  label?: string;
851
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
852
+ window?: VisibilityWindow;
784
853
  props: NavbarProps;
785
854
  }
786
855
  interface FooterColumn {
@@ -797,6 +866,8 @@ interface FooterBlock {
797
866
  style?: BlockStyle;
798
867
  /** Editor-facing display name for this block's dock card. Not rendered. */
799
868
  label?: string;
869
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
870
+ window?: VisibilityWindow;
800
871
  props: FooterProps;
801
872
  }
802
873
  interface SlideItem {
@@ -815,6 +886,8 @@ interface SliderBlock {
815
886
  style?: BlockStyle;
816
887
  /** Editor-facing display name for this block's dock card. Not rendered. */
817
888
  label?: string;
889
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
890
+ window?: VisibilityWindow;
818
891
  props: SliderProps;
819
892
  }
820
893
  interface TabItem {
@@ -831,6 +904,8 @@ interface TabsBlock {
831
904
  style?: BlockStyle;
832
905
  /** Editor-facing display name for this block's dock card. Not rendered. */
833
906
  label?: string;
907
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
908
+ window?: VisibilityWindow;
834
909
  props: TabsProps;
835
910
  }
836
911
  interface ComponentProps {
@@ -868,6 +943,8 @@ interface ComponentBlock {
868
943
  style?: BlockStyle;
869
944
  /** Editor-facing display name for this block's dock card. Not rendered. */
870
945
  label?: string;
946
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
947
+ window?: VisibilityWindow;
871
948
  props: ComponentProps;
872
949
  }
873
950
  interface CollectionProps {
@@ -913,6 +990,8 @@ interface CollectionBlock {
913
990
  style?: BlockStyle;
914
991
  /** Editor-facing display name for this block's dock card. Not rendered. */
915
992
  label?: string;
993
+ /** Shown only inside this window. Absent = always. @see VisibilityWindow */
994
+ window?: VisibilityWindow;
916
995
  props: CollectionProps;
917
996
  }
918
997
  type ContentBlock = CollectionBlock | HeadingBlock | TextBlock | RichTextBlock | ImageBlock | ButtonBlock | SpacerBlock | ColumnsBlock | VideoBlock | FormBlock | SectionBlock | NavbarBlock | FooterBlock | SliderBlock | TabsBlock | ComponentBlock;
@@ -1108,6 +1187,30 @@ declare function componentInstanceAddresses(instanceBlockId: string, props: read
1108
1187
  * render-page.ts. Unknown tokens are dropped — closed set, no arbitrary CSS.
1109
1188
  */
1110
1189
 
1190
+ /**
1191
+ * Where a block's `hideOn` bands start and end: mobile at most 767px, tablet 768-1023px, desktop
1192
+ * 1024px and up (the dock's 390 / 810 / 1280 canvases fall one in each band). A site's CSS
1193
+ * carries these numbers, so changing one shifts every published site's media queries.
1194
+ */
1195
+ declare const BREAKPOINTS: {
1196
+ readonly mobile: 767;
1197
+ readonly tablet: 1023;
1198
+ };
1199
+ /**
1200
+ * The ONE stylesheet behind `data-bcms-hide="mobile tablet"`. `!important` because a block's own
1201
+ * inline `display` (`fullHeight` writes `display:flex`) would otherwise beat it. `display:none`
1202
+ * also takes the block out of the accessibility tree at that width. Hosted pages inline it in
1203
+ * the head, `<BcmsBlocks>` emits it when a block uses `hideOn`, and a site that renders its own
1204
+ * markup puts it in its global CSS.
1205
+ */
1206
+ declare const RESPONSIVE_CSS: string;
1207
+ /**
1208
+ * The `data-bcms-hide` value for a block style (`"mobile tablet"`), or "" when it hides nowhere.
1209
+ * Closed set, fixed order, so a hand-edited row cannot put anything else into an attribute.
1210
+ */
1211
+ declare function hideOnAttr(style: unknown): string;
1212
+ /** True when any block in the tree hides somewhere; lets a lane skip `RESPONSIVE_CSS` when unused. */
1213
+ declare function usesHideOn(blocks: unknown): boolean;
1111
1214
  /**
1112
1215
  * 🔴 The ONE place a free-form string is allowed near CSS: `bgImage` is author-supplied, so it is
1113
1216
  * rejected — never escaped — unless it is an http(s) or root-relative url with nothing that could
@@ -1162,7 +1265,7 @@ declare function headingPaintCss(style: BlockStyle | undefined, opts?: {
1162
1265
  * painted a box BEHIND the anchor, which kept its variant's own fill. 🔴 Twin of `PLACEMENT_KEYS`
1163
1266
  * in src/lib/sites/render-page.ts; src/__tests__/sites/block-style-parity.test.ts pins the two.
1164
1267
  */
1165
- declare const BUTTON_PLACEMENT_KEYS: readonly ["align", "contentAlign", "contentWidth", "marginTop", "marginBottom", "fullHeight", "spaceTop", "spaceBottom"];
1268
+ declare const BUTTON_PLACEMENT_KEYS: readonly ["align", "contentAlign", "contentWidth", "marginTop", "marginBottom", "fullHeight", "spaceTop", "spaceBottom", "hideOn"];
1166
1269
  /**
1167
1270
  * A button's style split in two: `placement` (the keys its wrapper keeps — feed it to
1168
1271
  * `blockStyleToCss` / `blockStyleToInnerCss` exactly like any block's style) and `paint`, the
@@ -1253,6 +1356,10 @@ interface LayoutFieldDefinition {
1253
1356
  helpSource?: "auto" | "human" | "agent";
1254
1357
  /** `true`: no route the last release inspected renders this field — hidden, never deleted. `false`: a person keeps it shown. */
1255
1358
  stale?: boolean;
1359
+ /** false: a page may not override this value; the Global value renders on every page. Absent = true. */
1360
+ overridable?: boolean;
1361
+ /** Shown where the page editor would have offered the field. Required when overridable is false. */
1362
+ lockReason?: string;
1256
1363
  }
1257
1364
  /** The canonical public input contract all variants in a group implement. */
1258
1365
  type CanonicalInputDefinition = LayoutFieldDefinition;
@@ -1316,6 +1423,11 @@ interface LayoutSection {
1316
1423
  locale?: string;
1317
1424
  /** The stable id of the base Section this variant replaces on routes in `locale`. */
1318
1425
  variantOf?: string;
1426
+ /**
1427
+ * When the Section is shown (an announcement bar, a seasonal band). Absent = always. It is the
1428
+ * SLOT's: a locale variant shows when its base does, whatever the variant's own row says.
1429
+ */
1430
+ window?: VisibilityWindow;
1319
1431
  }
1320
1432
  interface LayoutStructureDocument {
1321
1433
  version: 1;
@@ -1453,6 +1565,7 @@ type ManagementLayoutCommand = {
1453
1565
  name: string;
1454
1566
  icon: LayoutSectionIcon;
1455
1567
  zone: "before_page" | "after_page";
1568
+ window?: VisibilityWindow;
1456
1569
  } | {
1457
1570
  type: "restore-section";
1458
1571
  section: LayoutSection;
@@ -1469,6 +1582,7 @@ type ManagementLayoutCommand = {
1469
1582
  zone?: LayoutZone;
1470
1583
  orderKey?: string;
1471
1584
  description?: string;
1585
+ window?: VisibilityWindow | null;
1472
1586
  } | {
1473
1587
  type: "remove-section";
1474
1588
  sectionId: string;
@@ -1542,6 +1656,8 @@ interface DeliveredLayout {
1542
1656
  */
1543
1657
  locale?: string;
1544
1658
  variantOf?: string;
1659
+ /** Present only on a Section that has one. A published read never delivers a Section outside it. */
1660
+ window?: VisibilityWindow;
1545
1661
  fields: Record<string, unknown>;
1546
1662
  items: Array<{
1547
1663
  id: string;
@@ -1575,6 +1691,19 @@ interface DeliveredLayout {
1575
1691
  }>;
1576
1692
  }>;
1577
1693
  }
1694
+ /**
1695
+ * A Layout structure as a visitor is served it at `now`: every Section outside its window
1696
+ * removed, and with a removed base its locale variants (a variant only ever renders in its base's
1697
+ * slot). The same document when nothing is removed, and anything that is not one as it came.
1698
+ */
1699
+ declare function visibleLayoutStructure<T>(structure: T, now: Date | number): T;
1700
+ /**
1701
+ * A delivered Layout as a visitor is served it at `now`: the nodes whose Section is outside its
1702
+ * window removed, with their `sections` entries and any variant delivered beside a removed base.
1703
+ * Applied AFTER any cache, so a cached Layout never serves a Section past its boundary. The same
1704
+ * object when nothing is removed.
1705
+ */
1706
+ declare function visibleDeliveredLayout<T extends DeliveredLayout | null | undefined>(layout: T, now: Date | number): T;
1578
1707
 
1579
1708
  /**
1580
1709
  * Component — a reusable, Webflow-style block tree (a "symbol").
@@ -1660,6 +1789,11 @@ interface ComponentPropDef {
1660
1789
  * `{ componentId, overrides }` and its `target.path` is `props`, so filling a slot writes
1661
1790
  * that object over an empty `component` block's props.
1662
1791
  *
1792
+ * With `config.multiple` it holds a LIST of them and targets a container's child list
1793
+ * (`props.children` on a `section`, `props.columns.<n>` on a `columns`): the resolver writes
1794
+ * one `component` block per item there, so every renderer draws them as ordinary children.
1795
+ * @see slotItems
1796
+ *
1663
1797
  * On a PAGE a slot fill is a LIVE reference; a published ENTRY freezes it (resolveDeep).
1664
1798
  */
1665
1799
  /**
@@ -1716,6 +1850,11 @@ interface ComponentPropDef {
1716
1850
  config?: {
1717
1851
  /** `slot` — a pick allowlist. */
1718
1852
  componentIds?: string[];
1853
+ /** `slot` — the value is a list of instances, not one. @see slotItems */
1854
+ multiple?: boolean;
1855
+ /** `slot` with `multiple` — bounds on the list. `maxItems` defaults to {@link SLOT_MAX_ITEMS}. */
1856
+ minItems?: number;
1857
+ maxItems?: number;
1719
1858
  /**
1720
1859
  * `select` / `multiselect` — the choices. Required, and a default must be one of them. `{ label, value }`
1721
1860
  * when the stored value is not what an author should read (a component id, for instance).
@@ -1801,6 +1940,34 @@ interface ComponentSubField {
1801
1940
  options?: string[];
1802
1941
  } & Record<string, unknown>;
1803
1942
  }
1943
+ /** The value a `slot` prop holds — the draft half of a `component-ref`, deliberately. */
1944
+ interface SlotValue {
1945
+ componentId: string;
1946
+ overrides?: Record<string, unknown>;
1947
+ }
1948
+ /**
1949
+ * One child of a `multiple` slot: a {@link SlotValue} that may also carry the two placement keys a
1950
+ * `component` block has. The child block is built with them, so it hides per breakpoint and
1951
+ * shows inside its window exactly as a block placed by hand does.
1952
+ */
1953
+ interface SlotItem extends SlotValue {
1954
+ style?: BlockStyle;
1955
+ window?: VisibilityWindow;
1956
+ }
1957
+ /** How many children a `multiple` slot holds when its prop sets no `config.maxItems`. */
1958
+ declare const SLOT_MAX_ITEMS = 12;
1959
+ /** The most `config.maxItems` may ask for: one placement never expands into more blocks than this. */
1960
+ declare const SLOT_ITEMS_CAP = 50;
1961
+ /**
1962
+ * A slot value as a list of its instances, whichever shape it was stored in: `[]` for nothing,
1963
+ * one item for a single `{ componentId, overrides }`, the array's own items for a `multiple`
1964
+ * slot. An item with no `componentId` names no component and is left out.
1965
+ *
1966
+ * THE one reading of a slot value. A walker that tests `value.componentId` alone sees a
1967
+ * `multiple` slot as empty, and for a "where used" answer or a delete guard that is a component
1968
+ * removed from under the pages that hold it.
1969
+ */
1970
+ declare function slotItems(value: unknown): SlotItem[];
1804
1971
  /**
1805
1972
  * A component is a DRAFT until published; only the published copy reaches a visitor.
1806
1973
  * Mirrors pages and entries. See migration 0186_component_draft_live_split.sql.
@@ -2294,7 +2461,7 @@ type FormStatus = "draft" | "published";
2294
2461
  interface FormField {
2295
2462
  key: string;
2296
2463
  label: string;
2297
- type: "text" | "email" | "textarea" | "select" | "checkbox" | "checkboxes" | "radio" | "number" | "phone" | "date" | "url" | "consent" | "hidden";
2464
+ type: "text" | "email" | "textarea" | "select" | "checkbox" | "checkboxes" | "radio" | "number" | "phone" | "date" | "url" | "consent" | "hidden" | "step";
2298
2465
  placeholder?: string;
2299
2466
  helpText?: string;
2300
2467
  required?: boolean;
@@ -2680,4 +2847,4 @@ declare function regionOf<E extends {
2680
2847
  el: E;
2681
2848
  }[], el: unknown): string | null;
2682
2849
 
2683
- export { type AlignToken, type ApiError, type ApiKey, type ApiKeyPermission, type ApiKeyTokenType, type ArrayZoneConfig, type AssertEqual, type AuthResponse, type AuthSession, type AuthUser, BLOCK_BINDINGS, BUTTON_ICONS, BUTTON_ICON_NAMES, BUTTON_LOOK_KEYS, BUTTON_PLACEMENT_KEYS, BUTTON_SIZES, BUTTON_VARIANTS, type BetterCMSErrorCode, type BgToken, type BindingKind, type BlockBinding, type BlockHoverStyle, type BlockStyle, type BlockType, type ButtonBlock, type ButtonIcon, type ButtonLinkCandidate, type ButtonLookKey, type ButtonProps, type ButtonSizeName, type ButtonValue, type ButtonVariant, COMPONENT_PROP_TYPES, type CanonicalInputDefinition, type CollectionBlock, type CollectionFilter, type CollectionProps, type ColorToken, type ColumnsBlock, type ColumnsProps, type Component, type ComponentBlock, type ComponentCategory, type ComponentFormPropRef, type ComponentPropDef, type ComponentPropType, type ComponentPropUi, type ComponentProps, type ComponentSubField, type ComponentVariantGroup, type Content, type ContentBlock, type ContentEntry, type ContentModel, type ContentModelField, type ContentModelFieldType, type ContentModelFieldUi, type ContentPageResult, type ContentResponse, type ContentWidthToken, type CornerToken, DIALOG_SELECTOR, DOCK_BUDGET, DOCK_ICON_NAMES, DOCK_ICON_NAME_SET, type DeepReadonly, type DeliveredLayout, type DeliveryComponent, type DeliveryEntry, type DeliveryEntrySeo, type DeliveryList, type DeliveryPage, type DerivedLocator, type DockBudget, type DockFieldLike, type DockFieldsetLike, type DockIconName, type DockInputRef, type DockLane, type DockNode, type DockPropGroupLike, type DockScreen, type ElementLike, type ExpandedBinding, FIELD_STYLE_KEYS, FOOTER_SECTION_ID, type FieldConfig, type FieldStyle, type FieldStyleKey, type FieldStyles, type Fieldset, type FieldsetKind, type FieldsetUiBefore, type FontSizeToken, type FooterBlock, type FooterColumn, type FooterProps, type Form, type FormBlock, type FormField, type FormProps, type FormSubmission, type HeadingBlock, type HeadingProps, type ImageBlock, type ImageProps, LANDMARK_SELECTOR, LAYOUT_SECTION_ICONS, LAYOUT_SECTION_ICON_SET, LINK_RELS, type LandmarkChainElement, type LandmarkElement, type LayoutComponentItem, type LayoutDataDocument, type LayoutFieldDefinition, type LayoutFieldItem, type LayoutFieldType, type LayoutScalarFieldType, type LayoutSection, type LayoutSectionIcon, type LayoutSectionItem, type LayoutStructureDocument, type LayoutZone, type LinkRel, type LocatorGrammar, MAIN_SELECTOR, type ManagementLayoutCommand, type MediaAsset, type MediaCaptionTrack, type MediaFieldValue, NAVIGATION_SECTION_ID, type NavLink, type NavbarBlock, type NavbarProps, PAGE_CONTENT_ANCHOR_ID, type Page, type PageLayoutOverrideDataDocument, type PageLayoutOverrideDocument, type PageLayoutOverrideState, type PageLayoutOverrideStructureDocument, type PageLayoutSectionOverride, type PageLayoutSectionStructureOverride, type PageMetaJson, type PageOnlyLayoutSection, type PageSectionPlacement, type PaginatedResult, type Perspective, type PortableTextDocument, type PortableTextNode, type PropGroup, type RadiusToken, type ReadButtonValue, type Redirect, type ReservedLayoutSection, type ResolvedLayoutNode, type RichTextBlock, type RichTextProps, SECTION_DOCTRINE, SECTION_SPACE_TOKENS, SPACE_BOTTOM_TOKENS, SPACE_STEP_TOKENS, type SectionBlock, type SectionProps, type SectionSpaceToken, type ShadowToken, type ShowIf, type ShowIfRule, type SignInInput, type SignUpInput, type SiteSeoDefaults, type SlideItem, type SliderBlock, type SliderProps, type SpaceStepToken, type SpaceToken, type SpacerBlock, type SpacerProps, type TabItem, type TabsBlock, type TabsProps, type TextBlock, type TextProps, type ThemeToken, type UiBefore, type UiOrigin, type VariantInputMapping, type VideoBlock, type VideoProps, type WeightToken, type Workspace, type ZoneField, anchorTargetRel, blockStyleToColumnCss, blockStyleToCss, blockStyleToInnerCss, buttonBlockProps, buttonIconOf, buttonIconSvg, buttonLinkPairs, buttonValueOfInstance, buttonValueProblem, buttonVariantOf, candidateRole, componentInstanceAddresses, componentPropTargetKey, derivedPairs, dockBudget, dockScreens, expandBlockBindings, fieldStyleCssText, fieldStyleToCss, formatPropsAttribute, getBlockType, headingPaintCss, isBlock, isChromeLandmark, keyWords, landmarkRegions, locatorFor, parsePropsAttribute, pickFieldStyle, readButtonValue, readPath, regionOf, safeCssUrl, spaceTokenCss, splitButtonStyle, withButtonLookProps };
2850
+ export { type AlignToken, type ApiError, type ApiKey, type ApiKeyPermission, type ApiKeyTokenType, type ArrayZoneConfig, type AssertEqual, type AuthResponse, type AuthSession, type AuthUser, BLOCK_BINDINGS, BREAKPOINTS, BUTTON_ICONS, BUTTON_ICON_NAMES, BUTTON_LOOK_KEYS, BUTTON_PLACEMENT_KEYS, BUTTON_SIZES, BUTTON_VARIANTS, type BetterCMSErrorCode, type BgToken, type BindingKind, type BlockBinding, type BlockHoverStyle, type BlockStyle, type BlockType, type ButtonBlock, type ButtonIcon, type ButtonLinkCandidate, type ButtonLookKey, type ButtonProps, type ButtonSizeName, type ButtonValue, type ButtonVariant, COMPONENT_PROP_TYPES, type CanonicalInputDefinition, type CollectionBlock, type CollectionFilter, type CollectionProps, type ColorToken, type ColumnsBlock, type ColumnsProps, type Component, type ComponentBlock, type ComponentCategory, type ComponentFormPropRef, type ComponentPropDef, type ComponentPropType, type ComponentPropUi, type ComponentProps, type ComponentSubField, type ComponentVariantGroup, type Content, type ContentBlock, type ContentEntry, type ContentModel, type ContentModelField, type ContentModelFieldType, type ContentModelFieldUi, type ContentPageResult, type ContentResponse, type ContentWidthToken, type CornerToken, DIALOG_SELECTOR, DOCK_BUDGET, DOCK_ICON_NAMES, DOCK_ICON_NAME_SET, type DeepReadonly, type DeliveredLayout, type DeliveryComponent, type DeliveryEntry, type DeliveryEntrySeo, type DeliveryList, type DeliveryPage, type DerivedLocator, type DockBudget, type DockFieldLike, type DockFieldsetLike, type DockIconName, type DockInputRef, type DockLane, type DockNode, type DockPropGroupLike, type DockScreen, type ElementLike, type ExpandedBinding, FIELD_STYLE_KEYS, FOOTER_SECTION_ID, type FieldConfig, type FieldStyle, type FieldStyleKey, type FieldStyles, type Fieldset, type FieldsetKind, type FieldsetUiBefore, type FontSizeToken, type FooterBlock, type FooterColumn, type FooterProps, type Form, type FormBlock, type FormField, type FormProps, type FormSubmission, type HeadingBlock, type HeadingProps, type ImageBlock, type ImageProps, LANDMARK_SELECTOR, LAYOUT_SECTION_ICONS, LAYOUT_SECTION_ICON_SET, LINK_RELS, type LandmarkChainElement, type LandmarkElement, type LayoutComponentItem, type LayoutDataDocument, type LayoutFieldDefinition, type LayoutFieldItem, type LayoutFieldType, type LayoutScalarFieldType, type LayoutSection, type LayoutSectionIcon, type LayoutSectionItem, type LayoutStructureDocument, type LayoutZone, type LinkRel, type LocatorGrammar, MAIN_SELECTOR, type ManagementLayoutCommand, type MediaAsset, type MediaCaptionTrack, type MediaFieldValue, NAVIGATION_SECTION_ID, type NavLink, type NavbarBlock, type NavbarProps, PAGE_CONTENT_ANCHOR_ID, type Page, type PageLayoutOverrideDataDocument, type PageLayoutOverrideDocument, type PageLayoutOverrideState, type PageLayoutOverrideStructureDocument, type PageLayoutSectionOverride, type PageLayoutSectionStructureOverride, type PageMetaJson, type PageOnlyLayoutSection, type PageSectionPlacement, type PaginatedResult, type Perspective, type PortableTextDocument, type PortableTextNode, type PropGroup, RESPONSIVE_CSS, type RadiusToken, type ReadButtonValue, type Redirect, type ReservedLayoutSection, type ResolvedLayoutNode, type RichTextBlock, type RichTextProps, SECTION_DOCTRINE, SECTION_SPACE_TOKENS, SLOT_ITEMS_CAP, SLOT_MAX_ITEMS, SPACE_BOTTOM_TOKENS, SPACE_STEP_TOKENS, type SectionBlock, type SectionProps, type SectionSpaceToken, type ShadowToken, type ShowIf, type ShowIfRule, type SignInInput, type SignUpInput, type SiteSeoDefaults, type SlideItem, type SliderBlock, type SliderProps, type SlotItem, type SlotValue, type SpaceStepToken, type SpaceToken, type SpacerBlock, type SpacerProps, type TabItem, type TabsBlock, type TabsProps, type TextBlock, type TextProps, type ThemeToken, type UiBefore, type UiOrigin, type VariantInputMapping, type VideoBlock, type VideoProps, type VisibilityWindow, type WeightToken, type WindowState, type Workspace, type ZoneField, anchorTargetRel, blockStyleToColumnCss, blockStyleToCss, blockStyleToInnerCss, buttonBlockProps, buttonIconOf, buttonIconSvg, buttonLinkPairs, buttonValueOfInstance, buttonValueProblem, buttonVariantOf, candidateRole, componentInstanceAddresses, componentPropTargetKey, derivedPairs, dockBudget, dockScreens, expandBlockBindings, fieldStyleCssText, fieldStyleToCss, formatPropsAttribute, getBlockType, headingPaintCss, hideOnAttr, inWindow, isBlock, isChromeLandmark, keyWords, landmarkRegions, locatorFor, normalizeWindow, parsePropsAttribute, pickFieldStyle, readButtonValue, readPath, regionOf, safeCssUrl, slotItems, spaceTokenCss, splitButtonStyle, usesHideOn, visibleBlocks, visibleDeliveredLayout, visibleLayoutStructure, windowState, withButtonLookProps };
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) {
@@ -346,6 +426,14 @@ var COMPONENT_PROP_TYPES = [
346
426
  "icon",
347
427
  "multiselect"
348
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
+ }
349
437
 
350
438
  // src/layout-lucide-icons.ts
351
439
  var LAYOUT_SECTION_ICONS = Object.freeze([
@@ -2313,6 +2401,36 @@ var LAYOUT_SECTION_ICON_SET = new Set(LAYOUT_SECTION_ICONS);
2313
2401
  var PAGE_CONTENT_ANCHOR_ID = "page-content";
2314
2402
  var NAVIGATION_SECTION_ID = "navigation";
2315
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
+ }
2316
2434
 
2317
2435
  // src/dock-icon-names.ts
2318
2436
  var DOCK_ICON_NAMES = [
@@ -2973,6 +3091,7 @@ function regionOf(regions, el) {
2973
3091
  }
2974
3092
  export {
2975
3093
  BLOCK_BINDINGS,
3094
+ BREAKPOINTS,
2976
3095
  BUTTON_ICONS,
2977
3096
  BUTTON_ICON_NAMES,
2978
3097
  BUTTON_LOOK_KEYS,
@@ -2993,8 +3112,11 @@ export {
2993
3112
  MAIN_SELECTOR,
2994
3113
  NAVIGATION_SECTION_ID,
2995
3114
  PAGE_CONTENT_ANCHOR_ID,
3115
+ RESPONSIVE_CSS,
2996
3116
  SECTION_DOCTRINE,
2997
3117
  SECTION_SPACE_TOKENS,
3118
+ SLOT_ITEMS_CAP,
3119
+ SLOT_MAX_ITEMS,
2998
3120
  SPACE_BOTTOM_TOKENS,
2999
3121
  SPACE_STEP_TOKENS,
3000
3122
  anchorTargetRel,
@@ -3020,19 +3142,28 @@ export {
3020
3142
  formatPropsAttribute,
3021
3143
  getBlockType,
3022
3144
  headingPaintCss,
3145
+ hideOnAttr,
3146
+ inWindow,
3023
3147
  isBlock,
3024
3148
  isChromeLandmark,
3025
3149
  keyWords,
3026
3150
  landmarkRegions,
3027
3151
  locatorFor,
3152
+ normalizeWindow,
3028
3153
  parsePropsAttribute,
3029
3154
  pickFieldStyle,
3030
3155
  readButtonValue,
3031
3156
  readPath,
3032
3157
  regionOf,
3033
3158
  safeCssUrl,
3159
+ slotItems,
3034
3160
  spaceTokenCss,
3035
3161
  splitButtonStyle,
3162
+ usesHideOn,
3163
+ visibleBlocks,
3164
+ visibleDeliveredLayout,
3165
+ visibleLayoutStructure,
3166
+ windowState,
3036
3167
  withButtonLookProps
3037
3168
  };
3038
3169
  //# sourceMappingURL=index.js.map