@templatical/types
Advanced tools
+197
-173
@@ -330,153 +330,32 @@ //#region src/blocks.d.ts | ||
| //#endregion | ||
| //#region src/custom-blocks.d.ts | ||
| type CustomBlockFieldType = "text" | "textarea" | "image" | "color" | "number" | "select" | "boolean" | "repeatable"; | ||
| interface CustomBlockFieldBase { | ||
| key: string; | ||
| label: string; | ||
| required?: boolean; | ||
| placeholder?: string; | ||
| readOnly?: boolean; | ||
| //#region src/merge-tags.d.ts | ||
| interface SyntaxPreset { | ||
| value: RegExp; | ||
| logic: RegExp; | ||
| } | ||
| interface CustomBlockTextField extends CustomBlockFieldBase { | ||
| type: "text"; | ||
| default?: string; | ||
| } | ||
| interface CustomBlockTextareaField extends CustomBlockFieldBase { | ||
| type: "textarea"; | ||
| default?: string; | ||
| } | ||
| interface CustomBlockImageField extends CustomBlockFieldBase { | ||
| type: "image"; | ||
| default?: string; | ||
| } | ||
| interface CustomBlockColorField extends CustomBlockFieldBase { | ||
| type: "color"; | ||
| default?: string; | ||
| } | ||
| interface CustomBlockNumberField extends CustomBlockFieldBase { | ||
| type: "number"; | ||
| default?: number; | ||
| min?: number; | ||
| max?: number; | ||
| step?: number; | ||
| } | ||
| interface SelectOption { | ||
| label: string; | ||
| value: string; | ||
| } | ||
| interface CustomBlockSelectField extends CustomBlockFieldBase { | ||
| type: "select"; | ||
| options: SelectOption[]; | ||
| default?: string; | ||
| } | ||
| interface CustomBlockBooleanField extends CustomBlockFieldBase { | ||
| type: "boolean"; | ||
| default?: boolean; | ||
| } | ||
| interface CustomBlockRepeatableField extends CustomBlockFieldBase { | ||
| type: "repeatable"; | ||
| fields: Exclude<CustomBlockField, CustomBlockRepeatableField>[]; | ||
| default?: Record<string, unknown>[]; | ||
| minItems?: number; | ||
| maxItems?: number; | ||
| } | ||
| type CustomBlockField = CustomBlockTextField | CustomBlockTextareaField | CustomBlockImageField | CustomBlockColorField | CustomBlockNumberField | CustomBlockSelectField | CustomBlockBooleanField | CustomBlockRepeatableField; | ||
| interface CustomBlockDefinition { | ||
| type: string; | ||
| name: string; | ||
| icon?: string; | ||
| description?: string; | ||
| fields: CustomBlockField[]; | ||
| template: string; | ||
| dataSource?: DataSourceConfig; | ||
| /** | ||
| * Default block styles applied when a new instance of this custom block is | ||
| * created. Deep-merged over the built-in defaults — only specify the fields | ||
| * you want to override. Controls both the editor canvas wrapper and the | ||
| * rendered MJML/email output. | ||
| * | ||
| * @example | ||
| * defaultStyles: { | ||
| * padding: { top: 0, right: 0, bottom: 0, left: 0 }, | ||
| * } | ||
| */ | ||
| defaultStyles?: Partial<BlockStyles>; | ||
| /** | ||
| * Optional CSS rules attached to this custom block definition. Emitted once | ||
| * (deduped across instances) into `<mj-head><mj-style>…</mj-style></mj-head>` | ||
| * in the rendered MJML, and adopted into the editor canvas (shadow root or | ||
| * light-DOM mount) so authored responsive/hover/font behavior previews | ||
| * inside the editor. | ||
| * | ||
| * Use this for media queries, hover states, or any CSS that should apply | ||
| * once per definition rather than per block instance. Class names are not | ||
| * scoped by the SDK — namespace them yourself (e.g. `.tplc-<type>-<el>`) to | ||
| * avoid collisions with other definitions or built-in editor styles. | ||
| * | ||
| * @example | ||
| * stylesheet: ` | ||
| * @media (max-width: 480px) { | ||
| * .tplc-image-text-cell { display: block !important; width: 100% !important; } | ||
| * } | ||
| * ` | ||
| */ | ||
| stylesheet?: string; | ||
| } | ||
| interface DataSourceFetchContext { | ||
| fieldValues: Record<string, unknown>; | ||
| blockId: string; | ||
| } | ||
| interface DataSourceConfig { | ||
| label: string; | ||
| onFetch: (context: DataSourceFetchContext) => Promise<Record<string, unknown> | null>; | ||
| } | ||
| //#endregion | ||
| //#region src/factory.d.ts | ||
| declare function generateId(): string; | ||
| declare function createTitleBlock(partial?: Partial<TitleBlock>): TitleBlock; | ||
| declare function createParagraphBlock(partial?: Partial<ParagraphBlock>): ParagraphBlock; | ||
| declare function createImageBlock(partial?: Partial<ImageBlock>): ImageBlock; | ||
| declare function createButtonBlock(partial?: Partial<ButtonBlock>): ButtonBlock; | ||
| declare function createDividerBlock(partial?: Partial<DividerBlock>): DividerBlock; | ||
| declare function createSectionBlock(partial?: Partial<SectionBlock>): SectionBlock; | ||
| declare function createVideoBlock(partial?: Partial<VideoBlock>): VideoBlock; | ||
| declare function createSocialIconsBlock(partial?: Partial<SocialIconsBlock>): SocialIconsBlock; | ||
| declare function createSpacerBlock(partial?: Partial<SpacerBlock>): SpacerBlock; | ||
| declare function createHtmlBlock(partial?: Partial<HtmlBlock>): HtmlBlock; | ||
| declare function createMenuBlock(partial?: Partial<MenuBlock>): MenuBlock; | ||
| declare function createTableBlock(partial?: Partial<TableBlock>): TableBlock; | ||
| declare function createCountdownBlock(partial?: Partial<CountdownBlock>): CountdownBlock; | ||
| declare function createCustomBlock(definition: CustomBlockDefinition): CustomBlock; | ||
| declare function createBlock(type: BlockType, blockDefaults?: BlockDefaults): Block; | ||
| declare function cloneBlock(block: Block): Block; | ||
| //#endregion | ||
| //#region src/events.d.ts | ||
| declare class EventEmitter<TEvents extends Record<string, unknown> = Record<string, unknown>> { | ||
| private handlers; | ||
| on<K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void): () => void; | ||
| off<K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void): void; | ||
| emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void; | ||
| removeAllListeners(event?: keyof TEvents): void; | ||
| listenerCount(event: keyof TEvents): number; | ||
| } | ||
| //#endregion | ||
| //#region src/clone.d.ts | ||
| type SyntaxPresetName = "liquid" | "handlebars" | "mailchimp" | "ampscript"; | ||
| declare const SYNTAX_PRESETS: Record<SyntaxPresetName, SyntaxPreset>; | ||
| /** | ||
| * Cycle-safe deep clone via a JSON round-trip. | ||
| * | ||
| * A naked `JSON.stringify` throws `Converting circular structure to JSON` | ||
| * if the tree is self-referencing — e.g. a DOM element carrying a Sortable | ||
| * expando back-ref (`HTMLDivElement.SortableXXX -> instance -> el -> div`) | ||
| * leaks into block data through a drag handler inside a section. Both the | ||
| * editor's public `getContent()` export path and the history snapshot path | ||
| * must tolerate that: we drop the offending back-ref from the clone rather | ||
| * than throw. Losing a transient DOM expando is harmless; the block data is | ||
| * intact. | ||
| * | ||
| * The `WeakSet` replacer omits any object already seen on the current path, | ||
| * which covers every cyclic shape. Template content is tree-shaped (and is | ||
| * serialized to JSON for storage anyway), so dropping repeated references | ||
| * never costs real data. | ||
| * Resolves the autocomplete trigger string for a syntax preset. | ||
| * Returns null when the syntax doesn't match any built-in preset | ||
| * (custom regex syntax — autocomplete cannot be enabled safely). | ||
| */ | ||
| declare function safeClone<T>(value: T): T; | ||
| declare function getSyntaxTriggerChar(syntax: SyntaxPreset): string | null; | ||
| /** | ||
| * Resolves the closing delimiter for a syntax preset (e.g. `}}` for liquid, | ||
| * `|*` for mailchimp). The autocomplete trigger detector uses it to tell an | ||
| * open tag (`{{first`) from a completed one (`{{first_name}}`) so the popup | ||
| * doesn't reappear over a finished tag. Returns null for custom syntaxes — | ||
| * the same set for which `getSyntaxTriggerChar` returns null. | ||
| */ | ||
| declare function getSyntaxClosingChar(syntax: SyntaxPreset): string | null; | ||
| declare function resolveSyntax(syntax?: SyntaxPresetName | SyntaxPreset): SyntaxPreset; | ||
| declare function isMergeTagValue(value: string, syntax: SyntaxPreset): boolean; | ||
| declare function getMergeTagLabel(value: string, mergeTags: MergeTag[]): string; | ||
| declare function resolveHtmlMergeTagLabels(html: string, mergeTags: MergeTag[]): string; | ||
| declare function containsMergeTag(value: string, syntax: SyntaxPreset): boolean; | ||
| declare function restoreMergeTagMarkup(html: string, mergeTags: MergeTag[], syntax: SyntaxPreset): string; | ||
| declare function isLogicMergeTagValue(value: string, syntax: SyntaxPreset): boolean; | ||
| declare function getLogicMergeTagKeyword(value: string, syntax: SyntaxPreset): string; | ||
| declare function resolveHtmlLogicMergeTagLabels(html: string, syntax: SyntaxPreset): string; | ||
| //#endregion | ||
@@ -668,32 +547,177 @@ //#region src/config.d.ts | ||
| //#endregion | ||
| //#region src/merge-tags.d.ts | ||
| interface SyntaxPreset { | ||
| value: RegExp; | ||
| logic: RegExp; | ||
| //#region src/custom-blocks.d.ts | ||
| type CustomBlockFieldType = "text" | "textarea" | "image" | "color" | "number" | "select" | "boolean" | "repeatable"; | ||
| interface CustomBlockFieldBase { | ||
| key: string; | ||
| label: string; | ||
| required?: boolean; | ||
| placeholder?: string; | ||
| readOnly?: boolean; | ||
| } | ||
| type SyntaxPresetName = "liquid" | "handlebars" | "mailchimp" | "ampscript"; | ||
| declare const SYNTAX_PRESETS: Record<SyntaxPresetName, SyntaxPreset>; | ||
| interface CustomBlockTextField extends CustomBlockFieldBase { | ||
| type: "text"; | ||
| default?: string; | ||
| } | ||
| interface CustomBlockTextareaField extends CustomBlockFieldBase { | ||
| type: "textarea"; | ||
| default?: string; | ||
| } | ||
| interface CustomBlockImageField extends CustomBlockFieldBase { | ||
| type: "image"; | ||
| default?: string; | ||
| } | ||
| /** | ||
| * Resolves the autocomplete trigger string for a syntax preset. | ||
| * Returns null when the syntax doesn't match any built-in preset | ||
| * (custom regex syntax — autocomplete cannot be enabled safely). | ||
| * A color-picker field. Beyond the shared field props it accepts the same flat | ||
| * `presets` / `allowCustom` pair as the editor-wide `colors` config (their | ||
| * shapes are derived from `ColorsConfig`, so the two can't drift), scoping this | ||
| * one field to a color *role* — e.g. a button-color field offering only the | ||
| * brand's accent/ink pair while other color fields inherit the global palette. | ||
| * | ||
| * **Narrowing only — of the author's freedom, not the palette.** A field can | ||
| * restrict what authors may do (lock this field, or give it its own `presets`), | ||
| * but never unlock what the editor locked. Its `presets` *replace* the | ||
| * editor-wide grid rather than intersecting it, so a locked field may offer | ||
| * colors outside `colors.presets`: | ||
| * | ||
| * - `presets` — entries are validated exactly like editor-level presets | ||
| * (`#rgb` / `#rrggbb` hex; invalid ones are skipped with a console warning | ||
| * naming the block and field). A non-empty valid list replaces the editor's | ||
| * palette for this field; leaving it unset, passing `[]`, or passing only | ||
| * invalid entries all inherit the editor's palette. | ||
| * - `allowCustom` — `false` locks this field to its palette even while the rest | ||
| * of the editor allows free-form entry. `true` cannot unlock a field when the | ||
| * editor-wide `colors.allowCustom` is `false`; it is ignored with a warning. | ||
| * | ||
| * @see https://docs.templatical.com/guide/custom-blocks | ||
| */ | ||
| declare function getSyntaxTriggerChar(syntax: SyntaxPreset): string | null; | ||
| interface CustomBlockColorField extends CustomBlockFieldBase, Pick<ColorsConfig, "presets" | "allowCustom"> { | ||
| type: "color"; | ||
| default?: string; | ||
| } | ||
| interface CustomBlockNumberField extends CustomBlockFieldBase { | ||
| type: "number"; | ||
| default?: number; | ||
| min?: number; | ||
| max?: number; | ||
| step?: number; | ||
| } | ||
| interface SelectOption { | ||
| label: string; | ||
| value: string; | ||
| } | ||
| interface CustomBlockSelectField extends CustomBlockFieldBase { | ||
| type: "select"; | ||
| options: SelectOption[]; | ||
| default?: string; | ||
| } | ||
| interface CustomBlockBooleanField extends CustomBlockFieldBase { | ||
| type: "boolean"; | ||
| default?: boolean; | ||
| } | ||
| interface CustomBlockRepeatableField extends CustomBlockFieldBase { | ||
| type: "repeatable"; | ||
| fields: Exclude<CustomBlockField, CustomBlockRepeatableField>[]; | ||
| default?: Record<string, unknown>[]; | ||
| minItems?: number; | ||
| maxItems?: number; | ||
| } | ||
| type CustomBlockField = CustomBlockTextField | CustomBlockTextareaField | CustomBlockImageField | CustomBlockColorField | CustomBlockNumberField | CustomBlockSelectField | CustomBlockBooleanField | CustomBlockRepeatableField; | ||
| interface CustomBlockDefinition { | ||
| type: string; | ||
| name: string; | ||
| icon?: string; | ||
| description?: string; | ||
| fields: CustomBlockField[]; | ||
| template: string; | ||
| dataSource?: DataSourceConfig; | ||
| /** | ||
| * Default block styles applied when a new instance of this custom block is | ||
| * created. Deep-merged over the built-in defaults — only specify the fields | ||
| * you want to override. Controls both the editor canvas wrapper and the | ||
| * rendered MJML/email output. | ||
| * | ||
| * @example | ||
| * defaultStyles: { | ||
| * padding: { top: 0, right: 0, bottom: 0, left: 0 }, | ||
| * } | ||
| */ | ||
| defaultStyles?: Partial<BlockStyles>; | ||
| /** | ||
| * Optional CSS rules attached to this custom block definition. Emitted once | ||
| * (deduped across instances) into `<mj-head><mj-style>…</mj-style></mj-head>` | ||
| * in the rendered MJML, and adopted into the editor canvas (shadow root or | ||
| * light-DOM mount) so authored responsive/hover/font behavior previews | ||
| * inside the editor. | ||
| * | ||
| * Use this for media queries, hover states, or any CSS that should apply | ||
| * once per definition rather than per block instance. Class names are not | ||
| * scoped by the SDK — namespace them yourself (e.g. `.tplc-<type>-<el>`) to | ||
| * avoid collisions with other definitions or built-in editor styles. | ||
| * | ||
| * @example | ||
| * stylesheet: ` | ||
| * @media (max-width: 480px) { | ||
| * .tplc-image-text-cell { display: block !important; width: 100% !important; } | ||
| * } | ||
| * ` | ||
| */ | ||
| stylesheet?: string; | ||
| } | ||
| interface DataSourceFetchContext { | ||
| fieldValues: Record<string, unknown>; | ||
| blockId: string; | ||
| } | ||
| interface DataSourceConfig { | ||
| label: string; | ||
| onFetch: (context: DataSourceFetchContext) => Promise<Record<string, unknown> | null>; | ||
| } | ||
| //#endregion | ||
| //#region src/factory.d.ts | ||
| declare function generateId(): string; | ||
| declare function createTitleBlock(partial?: Partial<TitleBlock>): TitleBlock; | ||
| declare function createParagraphBlock(partial?: Partial<ParagraphBlock>): ParagraphBlock; | ||
| declare function createImageBlock(partial?: Partial<ImageBlock>): ImageBlock; | ||
| declare function createButtonBlock(partial?: Partial<ButtonBlock>): ButtonBlock; | ||
| declare function createDividerBlock(partial?: Partial<DividerBlock>): DividerBlock; | ||
| declare function createSectionBlock(partial?: Partial<SectionBlock>): SectionBlock; | ||
| declare function createVideoBlock(partial?: Partial<VideoBlock>): VideoBlock; | ||
| declare function createSocialIconsBlock(partial?: Partial<SocialIconsBlock>): SocialIconsBlock; | ||
| declare function createSpacerBlock(partial?: Partial<SpacerBlock>): SpacerBlock; | ||
| declare function createHtmlBlock(partial?: Partial<HtmlBlock>): HtmlBlock; | ||
| declare function createMenuBlock(partial?: Partial<MenuBlock>): MenuBlock; | ||
| declare function createTableBlock(partial?: Partial<TableBlock>): TableBlock; | ||
| declare function createCountdownBlock(partial?: Partial<CountdownBlock>): CountdownBlock; | ||
| declare function createCustomBlock(definition: CustomBlockDefinition): CustomBlock; | ||
| declare function createBlock(type: BlockType, blockDefaults?: BlockDefaults): Block; | ||
| declare function cloneBlock(block: Block): Block; | ||
| //#endregion | ||
| //#region src/events.d.ts | ||
| declare class EventEmitter<TEvents extends Record<string, unknown> = Record<string, unknown>> { | ||
| private handlers; | ||
| on<K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void): () => void; | ||
| off<K extends keyof TEvents>(event: K, handler: (data: TEvents[K]) => void): void; | ||
| emit<K extends keyof TEvents>(event: K, data: TEvents[K]): void; | ||
| removeAllListeners(event?: keyof TEvents): void; | ||
| listenerCount(event: keyof TEvents): number; | ||
| } | ||
| //#endregion | ||
| //#region src/clone.d.ts | ||
| /** | ||
| * Resolves the closing delimiter for a syntax preset (e.g. `}}` for liquid, | ||
| * `|*` for mailchimp). The autocomplete trigger detector uses it to tell an | ||
| * open tag (`{{first`) from a completed one (`{{first_name}}`) so the popup | ||
| * doesn't reappear over a finished tag. Returns null for custom syntaxes — | ||
| * the same set for which `getSyntaxTriggerChar` returns null. | ||
| * Cycle-safe deep clone via a JSON round-trip. | ||
| * | ||
| * A naked `JSON.stringify` throws `Converting circular structure to JSON` | ||
| * if the tree is self-referencing — e.g. a DOM element carrying a Sortable | ||
| * expando back-ref (`HTMLDivElement.SortableXXX -> instance -> el -> div`) | ||
| * leaks into block data through a drag handler inside a section. Both the | ||
| * editor's public `getContent()` export path and the history snapshot path | ||
| * must tolerate that: we drop the offending back-ref from the clone rather | ||
| * than throw. Losing a transient DOM expando is harmless; the block data is | ||
| * intact. | ||
| * | ||
| * The `WeakSet` replacer omits any object already seen on the current path, | ||
| * which covers every cyclic shape. Template content is tree-shaped (and is | ||
| * serialized to JSON for storage anyway), so dropping repeated references | ||
| * never costs real data. | ||
| */ | ||
| declare function getSyntaxClosingChar(syntax: SyntaxPreset): string | null; | ||
| declare function resolveSyntax(syntax?: SyntaxPresetName | SyntaxPreset): SyntaxPreset; | ||
| declare function isMergeTagValue(value: string, syntax: SyntaxPreset): boolean; | ||
| declare function getMergeTagLabel(value: string, mergeTags: MergeTag[]): string; | ||
| declare function resolveHtmlMergeTagLabels(html: string, mergeTags: MergeTag[]): string; | ||
| declare function containsMergeTag(value: string, syntax: SyntaxPreset): boolean; | ||
| declare function restoreMergeTagMarkup(html: string, mergeTags: MergeTag[], syntax: SyntaxPreset): string; | ||
| declare function isLogicMergeTagValue(value: string, syntax: SyntaxPreset): boolean; | ||
| declare function getLogicMergeTagKeyword(value: string, syntax: SyntaxPreset): string; | ||
| declare function resolveHtmlLogicMergeTagLabels(html: string, syntax: SyntaxPreset): string; | ||
| declare function safeClone<T>(value: T): T; | ||
| //#endregion | ||
@@ -700,0 +724,0 @@ //#region ../media-library/src/types.d.ts |
+2
-2
| { | ||
| "name": "@templatical/types", | ||
| "description": "Shared TypeScript types, block factory functions, and event emitter for Templatical email editor", | ||
| "version": "0.19.0", | ||
| "version": "0.20.0", | ||
| "bugs": "https://github.com/templatical/sdk/issues", | ||
@@ -9,3 +9,3 @@ "devDependencies": { | ||
| "vitest": "^4.1.10", | ||
| "@templatical/media-library": "0.19.0" | ||
| "@templatical/media-library": "0.20.0" | ||
| }, | ||
@@ -12,0 +12,0 @@ "exports": { |
144136
1.03%1793
1.36%