Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 60 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ when the `content-tree` is in
from its [ecosystem of utilities][unist-utilities].

`content-tree` relates to [JavaScript][js] in that it has an [ecosystem of
utilities][unist-utilities] for working with trees in JavaScript. However,
utilities][unist-utilities] for working with trees in JavaScript. However,
`content-tree` is not limited to JavaScript and can be used in other programming
languages.

Expand Down Expand Up @@ -106,6 +106,7 @@ type BodyBlock =
| Video
| YoutubeVideo
| Text
| Gallery
```

`BodyBlock` nodes are the only things that are valid as the top level of a `Body`.
Expand Down Expand Up @@ -348,7 +349,6 @@ _non normative note:_ the reason this is string properties and not children is
that it is more confusing if a pullquote falls back to text than if it
doesn't. The text is taken from elsewhere in the article.


### `ImageSet`

```ts
Expand Down Expand Up @@ -412,10 +412,8 @@ type ImageSource = {

**ImageSource** defines a single resource for an [image](#image).


### `Recommended`


```ts
interface Recommended extends Node {
type: "recommended"
Expand Down Expand Up @@ -491,7 +489,6 @@ type Teaser = {
}
```


### `Tweet`

```ts
Expand Down Expand Up @@ -641,7 +638,6 @@ The `layoutName` acts as a sort of theme for the component.

### `LayoutSlot`


```ts
interface LayoutSlot extends Parent {
type: "layout-slot"
Expand Down Expand Up @@ -736,7 +732,7 @@ interface Table extends Parent {

```ts
type CustomCodeComponentAttributes = {
[key: string]: string | boolean | undefined
[key: string]: string | boolean | undefined
}

interface CustomCodeComponent extends Node {
Expand All @@ -757,11 +753,67 @@ interface CustomCodeComponent extends Node {
}
```

- The **CustomCodeComponent*** allows for more experimental forms of journalism, allowing editors to provide properties via Spark.
- The **CustomCodeComponent\*** allows for more experimental forms of journalism, allowing editors to provide properties via Spark.
- The component itself lives off-platform, and an example might be a git repository with a standard structure. This structure would include the rendering instructions, and the data structure that is expected to be provided to the component for it to render if necessary.
- 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`).

### Gallery

```ts
type galleryItem = {
/**
* @description link for the image
*/
imageLink?: "text"
/**
* @description this is the first Image
* @default false
*/
firstImage: boolean
/**
* @description image description
*/

imageDescription?: string
/**
* @description select or upload image
*/
picture?: Image
}

/**
* @sparkGenerateStoryblock true
*/
interface Gallery extends Node {
type: "Gallery"

/**
* @description gallery description
* @default default text for the source field
*/
galleryDescription?: string
/**
* @description autoplay the gallery
* @default false
*/
autoPlay?: boolean

/**
* @description each gallery item
* @maxItems 10
* @minItems 1
*/
galleryItems: [galleryItem]
}
```
- The **Gallery\*** is the first story block in Spark to be powered entirely by the schema-driven structure of the ContentTree system.Instead of hardcoding its configuration, Spark dynamically inspects the BodyBlock definition in the ContentTree schema and extracts all block types annotated with the @sparkGenerateStoryblock: true flag.These block definitions are automatically converted into ProseMirror node specs and injected into the editor's schema at runtime. The following ContentTree types are currently mapped to Spark components:

- "string" → Rich text
- "text" → Text input
- "Image" → Responsive image container
- "Flourish" → Flourish chart
- "Video" → Video block

## License

Expand Down
6 changes: 3 additions & 3 deletions build.bash
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#!/bin/bash
node tools/maketypes <README.md> content-tree.ts
tsc -d content-tree.ts
typescript-json-schema --noExtraProps --required content-tree.ts ContentTree.full.Root > schemas/content-tree.schema.json
typescript-json-schema --noExtraProps --required content-tree.ts ContentTree.transit.Root > schemas/transit-tree.schema.json
typescript-json-schema --noExtraProps --required content-tree.ts ContentTree.transit.Body > schemas/body-tree.schema.json
typescript-json-schema --validationKeywords sparkGenerateStoryblock --noExtraProps --required content-tree.ts ContentTree.full.Root > schemas/content-tree.schema.json
typescript-json-schema --validationKeywords sparkGenerateStoryblock --noExtraProps --required content-tree.ts ContentTree.transit.Root > schemas/transit-tree.schema.json
typescript-json-schema --validationKeywords sparkGenerateStoryblock --noExtraProps --required content-tree.ts ContentTree.transit.Body > schemas/body-tree.schema.json
rm content-tree.ts
rm content-tree.js
172 changes: 168 additions & 4 deletions content-tree.d.ts
Original file line number Diff line number Diff line change
@@ -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 | Gallery;
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 {
Expand Down Expand Up @@ -278,8 +278,49 @@ export declare namespace ContentTree {
/** Configuration data to be passed to the component. */
attributes: CustomCodeComponentAttributes;
}
type galleryItem = {
/**
* @description link for the image
*/
imageLink?: "text";
/**
* @description this is the first Image
* @default false
*/
firstImage: boolean;
/**
* @description image description
*/
imageDescription?: string;
/**
* @description select or upload image
*/
picture?: Image;
};
/**
* @sparkGenerateStoryblock true
*/
interface Gallery extends Node {
type: "Gallery";
/**
* @description gallery description
* @default default text for the source field
*/
galleryDescription?: string;
/**
* @description autoplay the gallery
* @default false
*/
autoPlay?: boolean;
/**
* @description each gallery item
* @maxItems 10
* @minItems 1
*/
galleryItems: [galleryItem];
}
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 | Gallery;
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 {
Expand Down Expand Up @@ -558,9 +599,50 @@ export declare namespace ContentTree {
/** Configuration data to be passed to the component. */
attributes: CustomCodeComponentAttributes;
}
type galleryItem = {
/**
* @description link for the image
*/
imageLink?: "text";
/**
* @description this is the first Image
* @default false
*/
firstImage: boolean;
/**
* @description image description
*/
imageDescription?: string;
/**
* @description select or upload image
*/
picture?: Image;
};
/**
* @sparkGenerateStoryblock true
*/
interface Gallery extends Node {
type: "Gallery";
/**
* @description gallery description
* @default default text for the source field
*/
galleryDescription?: string;
/**
* @description autoplay the gallery
* @default false
*/
autoPlay?: boolean;
/**
* @description each gallery item
* @maxItems 10
* @minItems 1
*/
galleryItems: [galleryItem];
}
}
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 | Gallery;
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 {
Expand Down Expand Up @@ -824,9 +906,50 @@ export declare namespace ContentTree {
/** How the component should be presented in the article page according to the column layout system */
layoutWidth: LayoutWidth;
}
type galleryItem = {
/**
* @description link for the image
*/
imageLink?: "text";
/**
* @description this is the first Image
* @default false
*/
firstImage: boolean;
/**
* @description image description
*/
imageDescription?: string;
/**
* @description select or upload image
*/
picture?: Image;
};
/**
* @sparkGenerateStoryblock true
*/
interface Gallery extends Node {
type: "Gallery";
/**
* @description gallery description
* @default default text for the source field
*/
galleryDescription?: string;
/**
* @description autoplay the gallery
* @default false
*/
autoPlay?: boolean;
/**
* @description each gallery item
* @maxItems 10
* @minItems 1
*/
galleryItems: [galleryItem];
}
}
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 | Gallery;
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 {
Expand Down Expand Up @@ -1105,5 +1228,46 @@ export declare namespace ContentTree {
/** Configuration data to be passed to the component. */
attributes?: CustomCodeComponentAttributes;
}
type galleryItem = {
/**
* @description link for the image
*/
imageLink?: "text";
/**
* @description this is the first Image
* @default false
*/
firstImage: boolean;
/**
* @description image description
*/
imageDescription?: string;
/**
* @description select or upload image
*/
picture?: Image;
};
/**
* @sparkGenerateStoryblock true
*/
interface Gallery extends Node {
type: "Gallery";
/**
* @description gallery description
* @default default text for the source field
*/
galleryDescription?: string;
/**
* @description autoplay the gallery
* @default false
*/
autoPlay?: boolean;
/**
* @description each gallery item
* @maxItems 10
* @minItems 1
*/
galleryItems: [galleryItem];
}
}
}
Loading