diff --git a/README.md b/README.md index c3a9eac..5de12e2 100644 --- a/README.md +++ b/README.md @@ -233,6 +233,7 @@ _Non-normative note: this would be represented by an `
` in the html._ interface Paragraph extends Parent { type: "paragraph" children: Phrasing[] + fragmentIdentifier?: string } ``` @@ -245,6 +246,7 @@ interface Heading extends Parent { type: "heading" children: Text[] level: "chapter" | "subheading" | "label" + fragmentIdentifier?: string } ``` @@ -304,6 +306,7 @@ interface List extends Parent { type: "list" ordered: boolean children: ListItem[] + fragmentIdentifier?: string } ``` @@ -354,6 +357,7 @@ interface ImageSet extends Node { type: "image-set" id: string external picture: ImageSetPicture + fragmentIdentifier?: string } ``` @@ -512,6 +516,7 @@ interface Flourish extends Node { description?: string timestamp?: string external fallbackImage?: Image + fragmentIdentifier?: string } ``` @@ -521,9 +526,10 @@ interface Flourish extends Node { ```ts interface BigNumber extends Node { - type: "big-number" - number: string - description: string + type: "big-number" + number: string + description: string + fragmentIdentifier?: string } ``` @@ -536,6 +542,7 @@ interface Video extends Node { type: "video" id: string external title: string + fragmentIdentifier?: string } ``` @@ -551,6 +558,7 @@ TODO: Figure out how Clips work, how they are different? interface YoutubeVideo extends Node { type: "youtube-video" url: string + fragmentIdentifier?: string } ``` @@ -624,6 +632,7 @@ interface Layout extends Parent { layoutName: "auto" | "card" | "timeline" layoutWidth: string children: [Heading, LayoutImage, ...LayoutSlot[]] | [Heading, ...LayoutSlot[]] | LayoutSlot[] + fragmentIdentifier?: string } ``` @@ -658,6 +667,7 @@ interface LayoutImage extends Node { caption: string credit: string external picture: ImageSetPicture + fragmentIdentifier?: string } ``` @@ -715,6 +725,7 @@ interface Table extends Parent { responsiveStyle: 'overflow' | 'flat' | 'scroll' children: [TableCaption, TableBody, TableFooter] | [TableCaption, TableBody] | [TableBody, TableFooter] | [TableBody] columnSettings: TableColumnSettings[] + fragmentIdentifier?: string } ``` @@ -742,6 +753,8 @@ interface CustomCodeComponent extends Node { external attributesLastModified: string /** Configuration data to be passed to the component. */ external attributes: CustomCodeComponentAttributes + /** Unique fragmentIdentifier to identify the component, for things such as anchor links. */ + fragmentIdentifier?: string } ``` diff --git a/content-tree.d.ts b/content-tree.d.ts index c6c6a3c..82029b7 100644 --- a/content-tree.d.ts +++ b/content-tree.d.ts @@ -31,11 +31,13 @@ export declare namespace ContentTree { interface Paragraph extends Parent { type: "paragraph"; children: Phrasing[]; + fragmentIdentifier?: string; } interface Heading extends Parent { type: "heading"; children: Text[]; level: "chapter" | "subheading" | "label"; + fragmentIdentifier?: string; } interface Strong extends Parent { type: "strong"; @@ -59,6 +61,7 @@ export declare namespace ContentTree { type: "list"; ordered: boolean; children: ListItem[]; + fragmentIdentifier?: string; } interface ListItem extends Parent { type: "list-item"; @@ -77,6 +80,7 @@ export declare namespace ContentTree { type: "image-set"; id: string; picture: ImageSetPicture; + fragmentIdentifier?: string; } type ImageSetPicture = { layoutWidth: string; @@ -156,20 +160,24 @@ export declare namespace ContentTree { description?: string; timestamp?: string; fallbackImage?: Image; + fragmentIdentifier?: string; } interface BigNumber extends Node { type: "big-number"; number: string; description: string; + fragmentIdentifier?: string; } interface Video extends Node { type: "video"; id: string; title: string; + fragmentIdentifier?: string; } interface YoutubeVideo extends Node { type: "youtube-video"; url: string; + fragmentIdentifier?: string; } interface ScrollyBlock extends Parent { type: "scrolly-block"; @@ -203,6 +211,7 @@ export declare namespace ContentTree { layoutName: "auto" | "card" | "timeline"; layoutWidth: string; children: [Heading, LayoutImage, ...LayoutSlot[]] | [Heading, ...LayoutSlot[]] | LayoutSlot[]; + fragmentIdentifier?: string; } interface LayoutSlot extends Parent { type: "layout-slot"; @@ -215,6 +224,7 @@ export declare namespace ContentTree { caption: string; credit: string; picture: ImageSetPicture; + fragmentIdentifier?: string; } type TableColumnSettings = { hideOnMobile: boolean; @@ -251,6 +261,7 @@ export declare namespace ContentTree { responsiveStyle: 'overflow' | 'flat' | 'scroll'; children: [TableCaption, TableBody, TableFooter] | [TableCaption, TableBody] | [TableBody, TableFooter] | [TableBody]; columnSettings: TableColumnSettings[]; + fragmentIdentifier?: string; } type CustomCodeComponentAttributes = { [key: string]: string | boolean | undefined; @@ -270,6 +281,8 @@ export declare namespace ContentTree { attributesLastModified: string; /** Configuration data to be passed to the component. */ attributes: CustomCodeComponentAttributes; + /** Unique fragmentIdentifier to identify the component, for things such as anchor links. */ + fragmentIdentifier?: string; } namespace full { type BodyBlock = Paragraph | Heading | ImageSet | Flourish | BigNumber | CustomCodeComponent | Layout | List | Blockquote | Pullquote | ScrollyBlock | ThematicBreak | Table | Recommended | Tweet | Video | YoutubeVideo; @@ -304,11 +317,13 @@ export declare namespace ContentTree { interface Paragraph extends Parent { type: "paragraph"; children: Phrasing[]; + fragmentIdentifier?: string; } interface Heading extends Parent { type: "heading"; children: Text[]; level: "chapter" | "subheading" | "label"; + fragmentIdentifier?: string; } interface Strong extends Parent { type: "strong"; @@ -332,6 +347,7 @@ export declare namespace ContentTree { type: "list"; ordered: boolean; children: ListItem[]; + fragmentIdentifier?: string; } interface ListItem extends Parent { type: "list-item"; @@ -350,6 +366,7 @@ export declare namespace ContentTree { type: "image-set"; id: string; picture: ImageSetPicture; + fragmentIdentifier?: string; } type ImageSetPicture = { layoutWidth: string; @@ -429,20 +446,24 @@ export declare namespace ContentTree { description?: string; timestamp?: string; fallbackImage?: Image; + fragmentIdentifier?: string; } interface BigNumber extends Node { type: "big-number"; number: string; description: string; + fragmentIdentifier?: string; } interface Video extends Node { type: "video"; id: string; title: string; + fragmentIdentifier?: string; } interface YoutubeVideo extends Node { type: "youtube-video"; url: string; + fragmentIdentifier?: string; } interface ScrollyBlock extends Parent { type: "scrolly-block"; @@ -476,6 +497,7 @@ export declare namespace ContentTree { layoutName: "auto" | "card" | "timeline"; layoutWidth: string; children: [Heading, LayoutImage, ...LayoutSlot[]] | [Heading, ...LayoutSlot[]] | LayoutSlot[]; + fragmentIdentifier?: string; } interface LayoutSlot extends Parent { type: "layout-slot"; @@ -488,6 +510,7 @@ export declare namespace ContentTree { caption: string; credit: string; picture: ImageSetPicture; + fragmentIdentifier?: string; } type TableColumnSettings = { hideOnMobile: boolean; @@ -524,6 +547,7 @@ export declare namespace ContentTree { responsiveStyle: 'overflow' | 'flat' | 'scroll'; children: [TableCaption, TableBody, TableFooter] | [TableCaption, TableBody] | [TableBody, TableFooter] | [TableBody]; columnSettings: TableColumnSettings[]; + fragmentIdentifier?: string; } type CustomCodeComponentAttributes = { [key: string]: string | boolean | undefined; @@ -543,6 +567,8 @@ export declare namespace ContentTree { attributesLastModified: string; /** Configuration data to be passed to the component. */ attributes: CustomCodeComponentAttributes; + /** Unique fragmentIdentifier to identify the component, for things such as anchor links. */ + fragmentIdentifier?: string; } } namespace transit { @@ -578,11 +604,13 @@ export declare namespace ContentTree { interface Paragraph extends Parent { type: "paragraph"; children: Phrasing[]; + fragmentIdentifier?: string; } interface Heading extends Parent { type: "heading"; children: Text[]; level: "chapter" | "subheading" | "label"; + fragmentIdentifier?: string; } interface Strong extends Parent { type: "strong"; @@ -606,6 +634,7 @@ export declare namespace ContentTree { type: "list"; ordered: boolean; children: ListItem[]; + fragmentIdentifier?: string; } interface ListItem extends Parent { type: "list-item"; @@ -623,6 +652,7 @@ export declare namespace ContentTree { interface ImageSet extends Node { type: "image-set"; id: string; + fragmentIdentifier?: string; } type ImageSetPicture = { layoutWidth: string; @@ -699,19 +729,23 @@ export declare namespace ContentTree { flourishType: string; description?: string; timestamp?: string; + fragmentIdentifier?: string; } interface BigNumber extends Node { type: "big-number"; number: string; description: string; + fragmentIdentifier?: string; } interface Video extends Node { type: "video"; id: string; + fragmentIdentifier?: string; } interface YoutubeVideo extends Node { type: "youtube-video"; url: string; + fragmentIdentifier?: string; } interface ScrollyBlock extends Parent { type: "scrolly-block"; @@ -744,6 +778,7 @@ export declare namespace ContentTree { layoutName: "auto" | "card" | "timeline"; layoutWidth: string; children: [Heading, LayoutImage, ...LayoutSlot[]] | [Heading, ...LayoutSlot[]] | LayoutSlot[]; + fragmentIdentifier?: string; } interface LayoutSlot extends Parent { type: "layout-slot"; @@ -755,6 +790,7 @@ export declare namespace ContentTree { alt: string; caption: string; credit: string; + fragmentIdentifier?: string; } type TableColumnSettings = { hideOnMobile: boolean; @@ -791,6 +827,7 @@ export declare namespace ContentTree { responsiveStyle: 'overflow' | 'flat' | 'scroll'; children: [TableCaption, TableBody, TableFooter] | [TableCaption, TableBody] | [TableBody, TableFooter] | [TableBody]; columnSettings: TableColumnSettings[]; + fragmentIdentifier?: string; } type CustomCodeComponentAttributes = { [key: string]: string | boolean | undefined; @@ -802,6 +839,12 @@ export declare namespace ContentTree { 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]". */ + /** Semantic version of the code of the component, e.g. "^0.3.5". */ + /** Last date-time when the attributes for this block were modified, in ISO-8601 format. */ + /** Configuration data to be passed to the component. */ + /** Unique fragmentIdentifier to identify the component, for things such as anchor links. */ + fragmentIdentifier?: string; } } namespace loose { @@ -837,11 +880,13 @@ export declare namespace ContentTree { interface Paragraph extends Parent { type: "paragraph"; children: Phrasing[]; + fragmentIdentifier?: string; } interface Heading extends Parent { type: "heading"; children: Text[]; level: "chapter" | "subheading" | "label"; + fragmentIdentifier?: string; } interface Strong extends Parent { type: "strong"; @@ -865,6 +910,7 @@ export declare namespace ContentTree { type: "list"; ordered: boolean; children: ListItem[]; + fragmentIdentifier?: string; } interface ListItem extends Parent { type: "list-item"; @@ -883,6 +929,7 @@ export declare namespace ContentTree { type: "image-set"; id: string; picture?: ImageSetPicture; + fragmentIdentifier?: string; } type ImageSetPicture = { layoutWidth: string; @@ -962,20 +1009,24 @@ export declare namespace ContentTree { description?: string; timestamp?: string; fallbackImage?: Image; + fragmentIdentifier?: string; } interface BigNumber extends Node { type: "big-number"; number: string; description: string; + fragmentIdentifier?: string; } interface Video extends Node { type: "video"; id: string; title?: string; + fragmentIdentifier?: string; } interface YoutubeVideo extends Node { type: "youtube-video"; url: string; + fragmentIdentifier?: string; } interface ScrollyBlock extends Parent { type: "scrolly-block"; @@ -1009,6 +1060,7 @@ export declare namespace ContentTree { layoutName: "auto" | "card" | "timeline"; layoutWidth: string; children: [Heading, LayoutImage, ...LayoutSlot[]] | [Heading, ...LayoutSlot[]] | LayoutSlot[]; + fragmentIdentifier?: string; } interface LayoutSlot extends Parent { type: "layout-slot"; @@ -1021,6 +1073,7 @@ export declare namespace ContentTree { caption: string; credit: string; picture?: ImageSetPicture; + fragmentIdentifier?: string; } type TableColumnSettings = { hideOnMobile: boolean; @@ -1057,6 +1110,7 @@ export declare namespace ContentTree { responsiveStyle: 'overflow' | 'flat' | 'scroll'; children: [TableCaption, TableBody, TableFooter] | [TableCaption, TableBody] | [TableBody, TableFooter] | [TableBody]; columnSettings: TableColumnSettings[]; + fragmentIdentifier?: string; } type CustomCodeComponentAttributes = { [key: string]: string | boolean | undefined; @@ -1076,6 +1130,8 @@ export declare namespace ContentTree { attributesLastModified?: string; /** Configuration data to be passed to the component. */ attributes?: CustomCodeComponentAttributes; + /** Unique fragmentIdentifier to identify the component, for things such as anchor links. */ + fragmentIdentifier?: string; } } } diff --git a/content_tree.go b/content_tree.go index abd6b3d..64bd721 100644 --- a/content_tree.go +++ b/content_tree.go @@ -102,10 +102,11 @@ type ColumnSettingsItems struct { } type BigNumber struct { - Type string `json:"type"` - Data interface{} `json:"data,omitempty"` - Description string `json:"description,omitempty"` - Number string `json:"number,omitempty"` + Type string `json:"type"` + Data interface{} `json:"data,omitempty"` + Description string `json:"description,omitempty"` + Number string `json:"number,omitempty"` + FragmentIdentifier string `json:"fragmentIdentifier,omitempty"` } func (n *BigNumber) GetType() string { @@ -586,14 +587,15 @@ func (n *Emphasis) GetChildren() []Node { } type Flourish struct { - Type string `json:"type"` - Data interface{} `json:"data,omitempty"` - Description string `json:"description,omitempty"` - FallbackImage *FlourishFallbackImage `json:"fallbackImage,omitempty"` - FlourishType string `json:"flourishType,omitempty"` - Id string `json:"id,omitempty"` - LayoutWidth string `json:"layoutWidth,omitempty"` - Timestamp string `json:"timestamp,omitempty"` + Type string `json:"type"` + Data interface{} `json:"data,omitempty"` + Description string `json:"description,omitempty"` + FallbackImage *FlourishFallbackImage `json:"fallbackImage,omitempty"` + FlourishType string `json:"flourishType,omitempty"` + Id string `json:"id,omitempty"` + LayoutWidth string `json:"layoutWidth,omitempty"` + Timestamp string `json:"timestamp,omitempty"` + FragmentIdentifier string `json:"fragmentIdentifier,omitempty"` } func (n *Flourish) GetType() string { @@ -628,6 +630,7 @@ type Heading struct { Children []*Text `json:"children,omitempty"` Data interface{} `json:"data,omitempty"` Level string `json:"level,omitempty"` + FragmentIdentifier string `json:"fragmentIdentifier,omitempty"` } func (n *Heading) GetType() string { @@ -647,10 +650,11 @@ func (n *Heading) GetChildren() []Node { } type ImageSet struct { - Type string `json:"type"` - Data interface{} `json:"data,omitempty"` - ID string `json:"id,omitempty"` - Picture *Picture `json:"picture,omitempty"` + Type string `json:"type"` + Data interface{} `json:"data,omitempty"` + ID string `json:"id,omitempty"` + Picture *Picture `json:"picture,omitempty"` + FragmentIdentifier string `json:"fragmentIdentifier,omitempty"` } func (n *ImageSet) GetType() string { @@ -666,11 +670,12 @@ func (n *ImageSet) GetChildren() []Node { } type Layout struct { - Type string `json:"type"` - Children []*LayoutChild `json:"children,omitempty"` - Data interface{} `json:"data,omitempty"` - LayoutName string `json:"layoutName,omitempty"` - LayoutWidth string `json:"layoutWidth,omitempty"` + Type string `json:"type"` + Children []*LayoutChild `json:"children,omitempty"` + Data interface{} `json:"data,omitempty"` + LayoutName string `json:"layoutName,omitempty"` + LayoutWidth string `json:"layoutWidth,omitempty"` + FragmentIdentifier string `json:"fragmentIdentifier,omitempty"` } func (n *Layout) GetType() string { @@ -757,13 +762,14 @@ func (n *LayoutChild) UnmarshalJSON(data []byte) error { } type LayoutImage struct { - Type string `json:"type"` - Alt string `json:"alt,omitempty"` - Caption string `json:"caption,omitempty"` - Credit string `json:"credit,omitempty"` - Data interface{} `json:"data,omitempty"` - ID string `json:"id,omitempty"` - Picture *Picture `json:"picture,omitempty"` + Type string `json:"type"` + Alt string `json:"alt,omitempty"` + Caption string `json:"caption,omitempty"` + Credit string `json:"credit,omitempty"` + Data interface{} `json:"data,omitempty"` + ID string `json:"id,omitempty"` + Picture *Picture `json:"picture,omitempty"` + FragmentIdentifier string `json:"fragmentIdentifier,omitempty"` } func (n *LayoutImage) GetType() string { @@ -892,10 +898,11 @@ func (n *Link) GetChildren() []Node { } type List struct { - Type string `json:"type"` - Children []*ListItem `json:"children,omitempty"` - Data interface{} `json:"data,omitempty"` - Ordered bool `json:"ordered,omitempty"` + Type string `json:"type"` + Children []*ListItem `json:"children,omitempty"` + Data interface{} `json:"data,omitempty"` + Ordered bool `json:"ordered,omitempty"` + FragmentIdentifier string `json:"fragmentIdentifier,omitempty"` } func (n *List) GetType() string { @@ -1059,9 +1066,10 @@ func (n *ListItemChild) UnmarshalJSON(data []byte) error { } type Paragraph struct { - Type string `json:"type"` - Children []*Phrasing `json:"children,omitempty"` - Data interface{} `json:"data,omitempty"` + Type string `json:"type"` + Children []*Phrasing `json:"children,omitempty"` + Data interface{} `json:"data,omitempty"` + FragmentIdentifier string `json:"fragmentIdentifier,omitempty"` } func (n *Paragraph) GetType() string { @@ -1501,6 +1509,7 @@ type Table struct { LayoutWidth string `json:"layoutWidth,omitempty"` ResponsiveStyle string `json:"responsiveStyle,omitempty"` Stripes bool `json:"stripes,omitempty"` + FragmentIdentifier string `json:"fragmentIdentifier,omitempty"` } func (n *Table) GetType() string { @@ -1752,9 +1761,10 @@ func (n *Tweet) GetChildren() []Node { } type Video struct { - Type string `json:"type"` - Data interface{} `json:"data,omitempty"` - ID string `json:"id,omitempty"` + Type string `json:"type"` + Data interface{} `json:"data,omitempty"` + ID string `json:"id,omitempty"` + FragmentIdentifier string `json:"fragmentIdentifier,omitempty"` } func (n *Video) GetType() string { @@ -1770,9 +1780,10 @@ func (n *Video) GetChildren() []Node { } type YoutubeVideo struct { - Type string `json:"type"` - Data interface{} `json:"data,omitempty"` - URL string `json:"url,omitempty"` + Type string `json:"type"` + Data interface{} `json:"data,omitempty"` + URL string `json:"url,omitempty"` + FragmentIdentifier string `json:"fragmentIdentifier,omitempty"` } func (n *YoutubeVideo) GetType() string { @@ -1796,6 +1807,7 @@ type CustomCodeComponent struct { AttributesLastModified string `json:"attributesLastModified,omitempty"` Path string `json:"path,omitempty"` VersionRange string `json:"versionRange,omitempty"` + FragmentIdentifier string `json:"fragmentIdentifier,omitempty"` } func (n *CustomCodeComponent) GetType() string { diff --git a/libraries/from-bodyxml/index.js b/libraries/from-bodyxml/index.js index 344ecc5..fb2b7d7 100644 --- a/libraries/from-bodyxml/index.js +++ b/libraries/from-bodyxml/index.js @@ -56,44 +56,54 @@ export let defaultTransformers = { * @type {Transformer} */ h1(h1) { + const fragmentId = h1.attributes["data-fragment-id"] || h1.attributes["id"]; return { type: "heading", level: "chapter", + ...(fragmentId && { fragmentIdentifier: fragmentId }), }; }, /** * @type {Transformer} */ h2(h2) { + const fragmentId = h2.attributes["data-fragment-id"] || h2.attributes["id"]; return { type: "heading", level: "subheading", + ...(fragmentId && { fragmentIdentifier: fragmentId }), }; }, /** * @type {Transformer} */ h3(h3) { + const fragmentId = h3.attributes["data-fragment-id"] || h3.attributes["id"]; return { type: "heading", level: "subheading", + ...(fragmentId && { fragmentIdentifier: fragmentId }), }; }, /** * @type {Transformer} */ h4(h4) { + const fragmentId = h4.attributes["data-fragment-id"] || h4.attributes["id"]; return { type: "heading", level: "label", + ...(fragmentId && { fragmentIdentifier: fragmentId }), }; }, /** * @type {Transformer} */ p(p) { + const fragmentId = p.attributes["data-fragment-id"] || p.attributes["id"]; return { type: "paragraph", + ...(fragmentId && { fragmentIdentifier: fragmentId }), }; }, /** @@ -163,18 +173,22 @@ export let defaultTransformers = { * @type {Transformer} */ ol(ol) { + const fragmentId = ol.attributes["data-fragment-id"] || ol.attributes["id"]; return { type: "list", ordered: true, + ...(fragmentId && { fragmentIdentifier: fragmentId }), }; }, /** * @type {Transformer} */ ul(ul) { + const fragmentId = ul.attributes["data-fragment-id"] || ul.attributes["id"]; return { type: "list", ordered: false, + ...(fragmentId && { fragmentIdentifier: fragmentId }), }; }, /** @@ -210,12 +224,14 @@ export let defaultTransformers = { * @type {Transformer} */ ["big-number"](bn) { + const fragmentId = bn.attributes["data-fragment-id"] || bn.attributes["id"]; let number = find(bn, { name: "big-number-headline" }); let description = find(bn, { name: "big-number-intro" }); return { type: "big-number", number: number ? xastToString(number) : "", description: description ? xastToString(description) : "", + ...(fragmentId && { fragmentIdentifier: fragmentId }), children: null, }; }, @@ -223,6 +239,7 @@ export let defaultTransformers = { * @type {Transformer} */ img(img) { + const fragmentId = img.attributes["data-fragment-id"] || img.attributes["id"]; return { type: "layout-image", id: img.attributes.src ?? "", @@ -230,6 +247,7 @@ export let defaultTransformers = { // todo this can't be right alt: img.attributes.alt ?? "", caption: img.attributes.longdesc ?? "", + ...(fragmentId && { fragmentIdentifier: fragmentId }), children: null, }; }, @@ -237,9 +255,11 @@ export let defaultTransformers = { * @type {Transformer} */ [ContentType.imageset](content) { + const fragmentId = content.attributes["data-fragment-id"] || content.attributes["id"]; return { type: "image-set", id: content.attributes.url ?? "", + ...(fragmentId && { fragmentIdentifier: fragmentId }), children: null, }; }, @@ -247,9 +267,11 @@ export let defaultTransformers = { * @type {Transformer} */ [ContentType.video](content) { + const fragmentId = content.attributes["data-fragment-id"] || content.attributes["id"]; return { type: "video", id: content.attributes.url ?? "", + ...(fragmentId && { fragmentIdentifier: fragmentId }), children: null, }; }, @@ -260,6 +282,7 @@ export let defaultTransformers = { [ContentType.content](content) { const id = content.attributes.url ?? ""; const uuid = id.split("/").pop(); + const fragmentId = content.attributes["data-fragment-id"] || content.attributes["id"]; if (content.attributes["data-asset-type"] == "flourish") { return /** @type {ContentTree.transit.Flourish} */ ({ @@ -271,6 +294,7 @@ export let defaultTransformers = { ), description: content.attributes["alt"] || "", timestamp: content.attributes["data-time-stamp"] || "", + ...(fragmentId && { fragmentIdentifier: fragmentId }), children: null, }); } @@ -297,6 +321,7 @@ export let defaultTransformers = { * @type {Transformer} */ [ContentType.customCodeComponent](content) { + const fragmentId = content.attributes["data-fragment-id"] || content.attributes["id"]; const id = content.attributes.url ?? ""; const uuid = id.split("/").pop(); return { @@ -305,6 +330,7 @@ export let defaultTransformers = { layoutWidth: toValidLayoutWidth( content.attributes["data-layout-width"] || "" ), + ...(fragmentId && { fragmentIdentifier: fragmentId }), children: null, }; }, @@ -331,6 +357,8 @@ export let defaultTransformers = { * >} */ div(div) { + const fragmentId = div.attributes["data-fragment-id"] || div.attributes["id"]; + if (div.attributes.class === "n-content-layout") { return /** @type {ContentTree.transit.Layout} */ ({ type: "layout", @@ -338,6 +366,7 @@ export let defaultTransformers = { layoutWidth: toValidLayoutWidth( div.attributes["data-layout-width"] ?? "" ), + ...(fragmentId && { fragmentIdentifier: fragmentId }), }); } if (div.attributes.class === "n-content-layout__container") { diff --git a/schemas/body-tree.schema.json b/schemas/body-tree.schema.json index 5f2a150..28dd9e9 100644 --- a/schemas/body-tree.schema.json +++ b/schemas/body-tree.schema.json @@ -9,6 +9,9 @@ "description": { "type": "string" }, + "fragmentIdentifier": { + "type": "string" + }, "number": { "type": "string" }, @@ -140,6 +143,10 @@ "additionalProperties": false, "properties": { "data": {}, + "fragmentIdentifier": { + "description": "Unique fragmentIdentifier to identify the component, for things such as anchor links.", + "type": "string" + }, "id": { "description": "Id taken from the CAPI url", "type": "string" @@ -192,6 +199,9 @@ "flourishType": { "type": "string" }, + "fragmentIdentifier": { + "type": "string" + }, "id": { "type": "string" }, @@ -224,6 +234,9 @@ "type": "array" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "level": { "enum": [ "chapter", @@ -248,6 +261,9 @@ "additionalProperties": false, "properties": { "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "id": { "type": "string" }, @@ -303,6 +319,9 @@ ] }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "layoutName": { "enum": [ "auto", @@ -340,6 +359,9 @@ "type": "string" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "id": { "type": "string" }, @@ -440,6 +462,9 @@ "type": "array" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "ordered": { "type": "boolean" }, @@ -508,6 +533,9 @@ "type": "array" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "type": { "const": "paragraph", "type": "string" @@ -884,6 +912,9 @@ "type": "boolean" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "layoutWidth": { "enum": [ "auto", @@ -1083,6 +1114,9 @@ "additionalProperties": false, "properties": { "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "id": { "type": "string" }, @@ -1101,6 +1135,9 @@ "additionalProperties": false, "properties": { "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "type": { "const": "youtube-video", "type": "string" diff --git a/schemas/content-tree.schema.json b/schemas/content-tree.schema.json index 4a1114a..4f8d0f7 100644 --- a/schemas/content-tree.schema.json +++ b/schemas/content-tree.schema.json @@ -9,6 +9,9 @@ "description": { "type": "string" }, + "fragmentIdentifier": { + "type": "string" + }, "number": { "type": "string" }, @@ -179,6 +182,10 @@ "type": "string" }, "data": {}, + "fragmentIdentifier": { + "description": "Unique fragmentIdentifier to identify the component, for things such as anchor links.", + "type": "string" + }, "id": { "description": "Id taken from the CAPI url", "type": "string" @@ -303,6 +310,9 @@ "flourishType": { "type": "string" }, + "fragmentIdentifier": { + "type": "string" + }, "id": { "type": "string" }, @@ -335,6 +345,9 @@ "type": "array" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "level": { "enum": [ "chapter", @@ -359,6 +372,9 @@ "additionalProperties": false, "properties": { "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "id": { "type": "string" }, @@ -572,6 +588,9 @@ ] }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "layoutName": { "enum": [ "auto", @@ -609,6 +628,9 @@ "type": "string" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "id": { "type": "string" }, @@ -867,6 +889,9 @@ "type": "array" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "ordered": { "type": "boolean" }, @@ -935,6 +960,9 @@ "type": "array" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "type": { "const": "paragraph", "type": "string" @@ -1662,6 +1690,9 @@ "type": "boolean" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "layoutWidth": { "enum": [ "auto", @@ -1865,6 +1896,9 @@ "additionalProperties": false, "properties": { "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "id": { "type": "string" }, @@ -1887,6 +1921,9 @@ "additionalProperties": false, "properties": { "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "type": { "const": "youtube-video", "type": "string" diff --git a/schemas/transit-tree.schema.json b/schemas/transit-tree.schema.json index 9864746..577a8aa 100644 --- a/schemas/transit-tree.schema.json +++ b/schemas/transit-tree.schema.json @@ -9,6 +9,9 @@ "description": { "type": "string" }, + "fragmentIdentifier": { + "type": "string" + }, "number": { "type": "string" }, @@ -165,6 +168,10 @@ "additionalProperties": false, "properties": { "data": {}, + "fragmentIdentifier": { + "description": "Unique fragmentIdentifier to identify the component, for things such as anchor links.", + "type": "string" + }, "id": { "description": "Id taken from the CAPI url", "type": "string" @@ -217,6 +224,9 @@ "flourishType": { "type": "string" }, + "fragmentIdentifier": { + "type": "string" + }, "id": { "type": "string" }, @@ -249,6 +259,9 @@ "type": "array" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "level": { "enum": [ "chapter", @@ -273,6 +286,9 @@ "additionalProperties": false, "properties": { "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "id": { "type": "string" }, @@ -328,6 +344,9 @@ ] }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "layoutName": { "enum": [ "auto", @@ -365,6 +384,9 @@ "type": "string" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "id": { "type": "string" }, @@ -465,6 +487,9 @@ "type": "array" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "ordered": { "type": "boolean" }, @@ -533,6 +558,9 @@ "type": "array" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "type": { "const": "paragraph", "type": "string" @@ -909,6 +937,9 @@ "type": "boolean" }, "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "layoutWidth": { "enum": [ "auto", @@ -1108,6 +1139,9 @@ "additionalProperties": false, "properties": { "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "id": { "type": "string" }, @@ -1126,6 +1160,9 @@ "additionalProperties": false, "properties": { "data": {}, + "fragmentIdentifier": { + "type": "string" + }, "type": { "const": "youtube-video", "type": "string"