diff --git a/README.md b/README.md index 7e2e08c..f0dba0d 100644 --- a/README.md +++ b/README.md @@ -106,6 +106,7 @@ type BodyBlock = | Video | YoutubeVideo | Text + | Timeline ``` `BodyBlock` nodes are the only things that are valid as the top level of a `Body`. @@ -736,24 +737,24 @@ interface Table extends Parent { ```ts type CustomCodeComponentAttributes = { - [key: string]: string | boolean | undefined + [key: string]: string | boolean | undefined } interface CustomCodeComponent extends Node { - /** Component type */ - type: "custom-code-component" - /** Id taken from the CAPI url */ - id: string - /** How the component should be presented in the article page according to the column layout system */ - layoutWidth: LayoutWidth - /** Repository for the code of the component in the format "[github org]/[github repo]/[component name]". */ - external path: string - /** Semantic version of the code of the component, e.g. "^0.3.5". */ - external versionRange: string - /** Last date-time when the attributes for this block were modified, in ISO-8601 format. */ - external attributesLastModified: string - /** Configuration data to be passed to the component. */ - external attributes: CustomCodeComponentAttributes + /** Component type */ + type: "custom-code-component" + /** Id taken from the CAPI url */ + id: string + /** How the component should be presented in the article page according to the column layout system */ + layoutWidth: LayoutWidth + /** Repository for the code of the component in the format "[github org]/[github repo]/[component name]". */ + external path: string + /** Semantic version of the code of the component, e.g. "^0.3.5". */ + external versionRange: string + /** Last date-time when the attributes for this block were modified, in ISO-8601 format. */ + external attributesLastModified: string + /** Configuration data to be passed to the component. */ + external attributes: CustomCodeComponentAttributes } ``` @@ -762,6 +763,40 @@ interface CustomCodeComponent extends Node { - The basic interface in Spark to make reference to this system above (eg. the git repo URL or a public S3 bucket), and provide some data for it if necessary. This will be the Custom Component storyblock. - The data Spark receives from entering a specific ID will be used to render dynamic fields (the `attributes`). +### Timeline + +```ts +/** + * Allowed layout widths for a Timeline. + * @typedef {'full-width' | 'inset-left' | 'full-grid'} TimelineLayoutWidth + */ +type TimelineLayoutWidth = Extract + +/** + * Timeline nodes display a timeline of events in arbitrary order. + * + * @typedef Timeline + * @property {TimelineLayoutWidth} layoutWidth - How the component should be presented. + **/ +interface Timeline extends Parent { + type: "timeline" + layoutWidth: TimelineLayoutWidth + children: [Heading, ...TimelineEvent[]] +} + +/** + * TimelineEvent is the representation of a single event + * + * @typedef TimelineEvent + * @property {string} title - The title of the event + * @property {(Paragraph | ImageSet)[]} children - Elements that describe the event + **/ +interface TimelineEvent extends Parent { + type: "timeline-event" + title: string + children: (Paragraph | ImageSet)[] +} +``` ## License diff --git a/content-tree.d.ts b/content-tree.d.ts index bbe0cff..7e67da0 100644 --- a/content-tree.d.ts +++ b/content-tree.d.ts @@ -1,5 +1,5 @@ export declare namespace ContentTree { - type BodyBlock = Paragraph | Heading | ImageSet | Flourish | BigNumber | CustomCodeComponent | Layout | List | Blockquote | Pullquote | ScrollyBlock | ThematicBreak | Table | Recommended | Tweet | Video | YoutubeVideo | Text; + type BodyBlock = Paragraph | Heading | ImageSet | Flourish | BigNumber | CustomCodeComponent | Layout | List | Blockquote | Pullquote | ScrollyBlock | ThematicBreak | Table | Recommended | Tweet | Video | YoutubeVideo | Text | Timeline; type LayoutWidth = "auto" | "in-line" | "inset-left" | "inset-right" | "full-bleed" | "full-grid" | "mid-grid" | "full-width"; type Phrasing = Text | Break | Strong | Emphasis | Strikethrough | Link; interface Node { @@ -278,8 +278,19 @@ export declare namespace ContentTree { /** Configuration data to be passed to the component. */ attributes: CustomCodeComponentAttributes; } + type TimelineLayoutWidth = Extract; + interface Timeline extends Parent { + type: "timeline"; + layoutWidth: TimelineLayoutWidth; + children: [Heading, ...TimelineEvent[]]; + } + interface TimelineEvent extends Parent { + type: "timeline-event"; + dateLabel: string; + children: Paragraph[]; + } namespace full { - type BodyBlock = Paragraph | Heading | ImageSet | Flourish | BigNumber | CustomCodeComponent | Layout | List | Blockquote | Pullquote | ScrollyBlock | ThematicBreak | Table | Recommended | Tweet | Video | YoutubeVideo | Text; + type BodyBlock = Paragraph | Heading | ImageSet | Flourish | BigNumber | CustomCodeComponent | Layout | List | Blockquote | Pullquote | ScrollyBlock | ThematicBreak | Table | Recommended | Tweet | Video | YoutubeVideo | Text | Timeline; type LayoutWidth = "auto" | "in-line" | "inset-left" | "inset-right" | "full-bleed" | "full-grid" | "mid-grid" | "full-width"; type Phrasing = Text | Break | Strong | Emphasis | Strikethrough | Link; interface Node { @@ -558,9 +569,20 @@ export declare namespace ContentTree { /** Configuration data to be passed to the component. */ attributes: CustomCodeComponentAttributes; } + type TimelineLayoutWidth = Extract; + interface Timeline extends Parent { + type: "timeline"; + layoutWidth: TimelineLayoutWidth; + children: [Heading, ...TimelineEvent[]]; + } + interface TimelineEvent extends Parent { + type: "timeline-event"; + dateLabel: string; + children: Paragraph[]; + } } namespace transit { - type BodyBlock = Paragraph | Heading | ImageSet | Flourish | BigNumber | CustomCodeComponent | Layout | List | Blockquote | Pullquote | ScrollyBlock | ThematicBreak | Table | Recommended | Tweet | Video | YoutubeVideo | Text; + type BodyBlock = Paragraph | Heading | ImageSet | Flourish | BigNumber | CustomCodeComponent | Layout | List | Blockquote | Pullquote | ScrollyBlock | ThematicBreak | Table | Recommended | Tweet | Video | YoutubeVideo | Text | Timeline; type LayoutWidth = "auto" | "in-line" | "inset-left" | "inset-right" | "full-bleed" | "full-grid" | "mid-grid" | "full-width"; type Phrasing = Text | Break | Strong | Emphasis | Strikethrough | Link; interface Node { @@ -824,9 +846,20 @@ export declare namespace ContentTree { /** How the component should be presented in the article page according to the column layout system */ layoutWidth: LayoutWidth; } + type TimelineLayoutWidth = Extract; + interface Timeline extends Parent { + type: "timeline"; + layoutWidth: TimelineLayoutWidth; + children: [Heading, ...TimelineEvent[]]; + } + interface TimelineEvent extends Parent { + type: "timeline-event"; + dateLabel: string; + children: Paragraph[]; + } } namespace loose { - type BodyBlock = Paragraph | Heading | ImageSet | Flourish | BigNumber | CustomCodeComponent | Layout | List | Blockquote | Pullquote | ScrollyBlock | ThematicBreak | Table | Recommended | Tweet | Video | YoutubeVideo | Text; + type BodyBlock = Paragraph | Heading | ImageSet | Flourish | BigNumber | CustomCodeComponent | Layout | List | Blockquote | Pullquote | ScrollyBlock | ThematicBreak | Table | Recommended | Tweet | Video | YoutubeVideo | Text | Timeline; type LayoutWidth = "auto" | "in-line" | "inset-left" | "inset-right" | "full-bleed" | "full-grid" | "mid-grid" | "full-width"; type Phrasing = Text | Break | Strong | Emphasis | Strikethrough | Link; interface Node { @@ -1105,5 +1138,16 @@ export declare namespace ContentTree { /** Configuration data to be passed to the component. */ attributes?: CustomCodeComponentAttributes; } + type TimelineLayoutWidth = Extract; + interface Timeline extends Parent { + type: "timeline"; + layoutWidth: TimelineLayoutWidth; + children: [Heading, ...TimelineEvent[]]; + } + interface TimelineEvent extends Parent { + type: "timeline-event"; + dateLabel: string; + children: Paragraph[]; + } } } diff --git a/schemas/body-tree.schema.json b/schemas/body-tree.schema.json index 1c331ad..9f2a5c0 100644 --- a/schemas/body-tree.schema.json +++ b/schemas/body-tree.schema.json @@ -122,6 +122,9 @@ }, { "$ref": "#/definitions/ContentTree.transit.Text" + }, + { + "$ref": "#/definitions/ContentTree.transit.Timeline" } ] }, @@ -1089,6 +1092,70 @@ ], "type": "object" }, + "ContentTree.transit.Timeline": { + "additionalProperties": false, + "properties": { + "children": { + "additionalItems": { + "$ref": "#/definitions/ContentTree.transit.TimelineEvent" + }, + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.Heading" + } + ], + "minItems": 1, + "type": "array" + }, + "data": {}, + "layoutWidth": { + "$ref": "#/definitions/ContentTree.transit.TimelineLayoutWidth" + }, + "type": { + "const": "timeline", + "type": "string" + } + }, + "required": [ + "children", + "layoutWidth", + "type" + ], + "type": "object" + }, + "ContentTree.transit.TimelineEvent": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Paragraph" + }, + "type": "array" + }, + "data": {}, + "dateLabel": { + "type": "string" + }, + "type": { + "const": "timeline-event", + "type": "string" + } + }, + "required": [ + "children", + "dateLabel", + "type" + ], + "type": "object" + }, + "ContentTree.transit.TimelineLayoutWidth": { + "enum": [ + "full-grid", + "full-width", + "inset-left" + ], + "type": "string" + }, "ContentTree.transit.Tweet": { "additionalProperties": false, "properties": { diff --git a/schemas/content-tree.schema.json b/schemas/content-tree.schema.json index 703a637..097ad9e 100644 --- a/schemas/content-tree.schema.json +++ b/schemas/content-tree.schema.json @@ -147,6 +147,9 @@ }, { "$ref": "#/definitions/ContentTree.full.Text" + }, + { + "$ref": "#/definitions/ContentTree.full.Timeline" } ] }, @@ -1867,6 +1870,70 @@ ], "type": "object" }, + "ContentTree.full.Timeline": { + "additionalProperties": false, + "properties": { + "children": { + "additionalItems": { + "$ref": "#/definitions/ContentTree.full.TimelineEvent" + }, + "items": [ + { + "$ref": "#/definitions/ContentTree.full.Heading" + } + ], + "minItems": 1, + "type": "array" + }, + "data": {}, + "layoutWidth": { + "$ref": "#/definitions/ContentTree.full.TimelineLayoutWidth" + }, + "type": { + "const": "timeline", + "type": "string" + } + }, + "required": [ + "children", + "layoutWidth", + "type" + ], + "type": "object" + }, + "ContentTree.full.TimelineEvent": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.full.Paragraph" + }, + "type": "array" + }, + "data": {}, + "dateLabel": { + "type": "string" + }, + "type": { + "const": "timeline-event", + "type": "string" + } + }, + "required": [ + "children", + "dateLabel", + "type" + ], + "type": "object" + }, + "ContentTree.full.TimelineLayoutWidth": { + "enum": [ + "full-grid", + "full-width", + "inset-left" + ], + "type": "string" + }, "ContentTree.full.Tweet": { "additionalProperties": false, "properties": { diff --git a/schemas/transit-tree.schema.json b/schemas/transit-tree.schema.json index 273226b..1abd053 100644 --- a/schemas/transit-tree.schema.json +++ b/schemas/transit-tree.schema.json @@ -147,6 +147,9 @@ }, { "$ref": "#/definitions/ContentTree.transit.Text" + }, + { + "$ref": "#/definitions/ContentTree.transit.Timeline" } ] }, @@ -1114,6 +1117,70 @@ ], "type": "object" }, + "ContentTree.transit.Timeline": { + "additionalProperties": false, + "properties": { + "children": { + "additionalItems": { + "$ref": "#/definitions/ContentTree.transit.TimelineEvent" + }, + "items": [ + { + "$ref": "#/definitions/ContentTree.transit.Heading" + } + ], + "minItems": 1, + "type": "array" + }, + "data": {}, + "layoutWidth": { + "$ref": "#/definitions/ContentTree.transit.TimelineLayoutWidth" + }, + "type": { + "const": "timeline", + "type": "string" + } + }, + "required": [ + "children", + "layoutWidth", + "type" + ], + "type": "object" + }, + "ContentTree.transit.TimelineEvent": { + "additionalProperties": false, + "properties": { + "children": { + "items": { + "$ref": "#/definitions/ContentTree.transit.Paragraph" + }, + "type": "array" + }, + "data": {}, + "dateLabel": { + "type": "string" + }, + "type": { + "const": "timeline-event", + "type": "string" + } + }, + "required": [ + "children", + "dateLabel", + "type" + ], + "type": "object" + }, + "ContentTree.transit.TimelineLayoutWidth": { + "enum": [ + "full-grid", + "full-width", + "inset-left" + ], + "type": "string" + }, "ContentTree.transit.Tweet": { "additionalProperties": false, "properties": {