@trycourier/react-designer 0.6.0 → 0.8.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.
Files changed (105) hide show
  1. package/README.md +7 -5
  2. package/dist/cjs/index.js +70 -58
  3. package/dist/cjs/index.js.map +4 -4
  4. package/dist/cjs/styles.css +1473 -486
  5. package/dist/components/BrandEditor/Editor/BrandFooter/BrandFooter.d.ts +3 -1
  6. package/dist/components/Providers/TemplateProvider.d.ts +9 -0
  7. package/dist/components/Providers/store.d.ts +11 -0
  8. package/dist/components/TemplateEditor/Channels/Email/Email.d.ts +12 -2
  9. package/dist/components/TemplateEditor/Channels/Email/EmailLayout.d.ts +3 -1
  10. package/dist/components/TemplateEditor/Channels/Inbox/Inbox.d.ts +3 -0
  11. package/dist/components/TemplateEditor/Channels/Inbox/InboxLayout.d.ts +1 -1
  12. package/dist/components/TemplateEditor/Channels/Inbox/SideBar/useInboxButtonSync.d.ts +27 -0
  13. package/dist/components/TemplateEditor/Channels/MSTeams/MSTeamsLayout.d.ts +1 -1
  14. package/dist/components/TemplateEditor/Channels/Push/PushLayout.d.ts +1 -1
  15. package/dist/components/TemplateEditor/Channels/SMS/SMSLayout.d.ts +1 -1
  16. package/dist/components/TemplateEditor/Channels/Slack/SlackLayout.d.ts +1 -1
  17. package/dist/components/TemplateEditor/Layout/Layout.d.ts +1 -0
  18. package/dist/components/TemplateEditor/TemplateEditor.d.ts +19 -0
  19. package/dist/components/TemplateEditor/VariableViewModeSync.d.ts +3 -0
  20. package/dist/components/TemplateEditor/hooks/index.d.ts +1 -0
  21. package/dist/components/TemplateEditor/hooks/useEmailBackgroundColors.d.ts +9 -0
  22. package/dist/components/TemplateEditor/hooks/useEmailFontFamily.d.ts +10 -0
  23. package/dist/components/TemplateEditor/hooks/useFonts.d.ts +4 -0
  24. package/dist/components/TemplateEditor/hooks/useGoogleFontLoader.d.ts +5 -0
  25. package/dist/components/TemplateEditor/index.d.ts +1 -0
  26. package/dist/components/TemplateEditor/store.d.ts +20 -0
  27. package/dist/components/TranslationEditor/TranslationEditor.d.ts +17 -0
  28. package/dist/components/TranslationEditor/index.d.ts +1 -0
  29. package/dist/components/extensions/Blockquote/Blockquote.d.ts +4 -0
  30. package/dist/components/extensions/Button/Button.types.d.ts +10 -6
  31. package/dist/components/extensions/Button/inboxButtonStyle.d.ts +65 -0
  32. package/dist/components/extensions/ButtonRow/ButtonRow.types.d.ts +4 -0
  33. package/dist/components/extensions/Color/Color.d.ts +14 -0
  34. package/dist/components/extensions/ColumnCell/ColumnCell.types.d.ts +32 -0
  35. package/dist/components/extensions/{CustomCode/CustomCodeForm.d.ts → ColumnCell/ColumnCellForm.d.ts} +2 -2
  36. package/dist/components/extensions/ColumnCell/index.d.ts +2 -0
  37. package/dist/components/extensions/HTML/HTML.d.ts +11 -0
  38. package/dist/components/extensions/{CustomCode/CustomCode.types.d.ts → HTML/HTML.types.d.ts} +2 -2
  39. package/dist/components/extensions/HTML/HTMLComponent.d.ts +9 -0
  40. package/dist/components/extensions/HTML/HTMLForm.d.ts +9 -0
  41. package/dist/components/extensions/HTML/index.d.ts +6 -0
  42. package/dist/components/extensions/ImageBlock/components/ImageBlockView.d.ts +1 -3
  43. package/dist/components/extensions/Link/LinkBubble.d.ts +1 -0
  44. package/dist/components/extensions/List/List.types.d.ts +5 -0
  45. package/dist/components/extensions/MessagingChannelPaste/MessagingChannelPaste.d.ts +11 -0
  46. package/dist/components/extensions/MessagingChannelPaste/index.d.ts +2 -0
  47. package/dist/components/extensions/Variable/Variable.d.ts +5 -0
  48. package/dist/components/extensions/index.d.ts +3 -2
  49. package/dist/components/extensions/shared/conditionalAttribute.d.ts +19 -0
  50. package/dist/components/hooks/useVariables.d.ts +12 -7
  51. package/dist/components/ui/Blocks/HTMLBlock/HTMLBlock.d.ts +3 -0
  52. package/dist/components/ui/Blocks/HTMLBlock/index.d.ts +1 -0
  53. package/dist/components/ui/Blocks/index.d.ts +1 -1
  54. package/dist/components/ui/Conditions/ConditionGroupRow.d.ts +12 -0
  55. package/dist/components/ui/Conditions/ConditionRow.d.ts +11 -0
  56. package/dist/components/ui/Conditions/Conditions.d.ts +9 -0
  57. package/dist/components/ui/Conditions/ConditionsSection.d.ts +18 -0
  58. package/dist/components/ui/Conditions/index.d.ts +3 -0
  59. package/dist/components/ui/Conditions/useConditions.d.ts +13 -0
  60. package/dist/components/ui/ContentIcon/ContentIcon.d.ts +1 -1
  61. package/dist/components/ui/MainLayout/MainLayout.d.ts +1 -0
  62. package/dist/components/ui/TextMenu/components/ContentTypePicker.d.ts +1 -1
  63. package/dist/components/ui/TextMenu/components/TextColorButton.d.ts +6 -0
  64. package/dist/components/ui/TextMenu/config.d.ts +1 -0
  65. package/dist/components/ui/TextMenu/hooks/useTextmenuCommands.d.ts +2 -0
  66. package/dist/components/ui/TextMenu/hooks/useTextmenuStates.d.ts +2 -0
  67. package/dist/components/ui/Tooltip/types.d.ts +1 -1
  68. package/dist/components/ui/VariableEditor/VariableChipBase.d.ts +10 -0
  69. package/dist/components/ui/VariableEditor/VariableInput.d.ts +18 -0
  70. package/dist/components/ui-kit/Button/Button.d.ts +2 -2
  71. package/dist/components/ui-kit/FontSelect/FontSelect.d.ts +9 -0
  72. package/dist/components/ui-kit/FontSelect/index.d.ts +1 -0
  73. package/dist/components/ui-kit/Form/Form.d.ts +2 -1
  74. package/dist/components/ui-kit/InputColor/ColorPicker.d.ts +2 -1
  75. package/dist/components/ui-kit/InputColor/InputColor.d.ts +4 -1
  76. package/dist/components/ui-kit/PrefixInput/PrefixInput.d.ts +30 -0
  77. package/dist/components/ui-kit/PrefixInput/index.d.ts +2 -0
  78. package/dist/components/ui-kit/index.d.ts +2 -0
  79. package/dist/components/utils/resolveDataPath.d.ts +13 -0
  80. package/dist/components/utils/validateVariableName.d.ts +5 -2
  81. package/dist/esm/index.js +70 -58
  82. package/dist/esm/index.js.map +4 -4
  83. package/dist/esm/styles.css +1473 -486
  84. package/dist/hooks/useLocalization.d.ts +30 -0
  85. package/dist/index.d.ts +18 -4
  86. package/dist/lib/constants/block-defaults.d.ts +43 -0
  87. package/dist/lib/constants/email-editor-tiptap-styles.d.ts +62 -0
  88. package/dist/lib/index.d.ts +1 -0
  89. package/dist/lib/utils/brandColors.d.ts +15 -0
  90. package/dist/lib/utils/extractTextFields.d.ts +60 -0
  91. package/dist/lib/utils/fontFamily.d.ts +23 -0
  92. package/dist/lib/utils/index.d.ts +1 -0
  93. package/dist/styles.css +1473 -486
  94. package/dist/types/conditions.types.d.ts +21 -0
  95. package/dist/types/elemental.types.d.ts +70 -4
  96. package/dist/types/font.types.d.ts +8 -0
  97. package/dist/types/index.d.ts +1 -0
  98. package/dist/types/validation.types.d.ts +15 -3
  99. package/package.json +6 -1
  100. package/dist/components/extensions/CustomCode/CustomCode.d.ts +0 -11
  101. package/dist/components/extensions/CustomCode/CustomCodeComponent.d.ts +0 -9
  102. package/dist/components/extensions/CustomCode/index.d.ts +0 -6
  103. package/dist/components/ui/Blocks/CustomCodeBlock/CustomCodeBlock.d.ts +0 -3
  104. package/dist/components/ui/Blocks/CustomCodeBlock/index.d.ts +0 -1
  105. /package/dist/components/extensions/{CustomCode → HTML}/MonacoCodeEditor.d.ts +0 -0
@@ -0,0 +1,21 @@
1
+ export type ElementalConditionOperator = "equals" | "not_equals" | "greater_than" | "less_than" | "greater_than_or_equals" | "less_than_or_equals" | "contains" | "not_contains" | "is_empty" | "is_not_empty";
2
+ export interface ElementalCondition {
3
+ property: string;
4
+ operator: ElementalConditionOperator;
5
+ value?: string;
6
+ }
7
+ export interface ElementalConditionGroup {
8
+ conditions: ElementalCondition[];
9
+ logical_operator: "and" | "or";
10
+ }
11
+ /**
12
+ * Structured condition format for the Elemental `if` field.
13
+ * An array of condition groups, evaluated with OR logic between groups.
14
+ * Within each group, conditions are joined by the group's `logical_operator`.
15
+ */
16
+ export type ElementalConditionExpression = ElementalConditionGroup[];
17
+ /** Union type for the `if` field: legacy string expression or structured conditions. */
18
+ export type ElementalIfCondition = string | ElementalConditionExpression;
19
+ /** Operators that don't require a value (unary checks). */
20
+ export declare const UNARY_OPERATORS: ElementalConditionOperator[];
21
+ export declare const OPERATOR_LABELS: Record<ElementalConditionOperator, string>;
@@ -1,6 +1,7 @@
1
+ import type { ElementalConditionExpression } from "./conditions.types";
1
2
  export type Align = "left" | "center" | "right" | "full";
2
3
  export type IActionButtonStyle = "button" | "link";
3
- export type TextStyle = "text" | "h1" | "h2" | "subtext";
4
+ export type TextStyle = "text" | "h1" | "h2" | "h3" | "subtext";
4
5
  export interface IBorderConfig {
5
6
  enabled?: boolean;
6
7
  radius?: string;
@@ -17,7 +18,7 @@ export interface ElementalContent {
17
18
  version: "2022-01-01";
18
19
  elements: ElementalNode[];
19
20
  }
20
- export type ElementalNode = ElementalTextNode | ElementalMetaNode | ElementalChannelNode | ElementalImageNode | ElementalActionNode | ElementalDividerNode | ElementalGroupNode | ElementalQuoteNode | ElementalHtmlNode | ElementalCommentNode | ElementalTextContentNode | ElementalListNode | ElementalListItemNode;
21
+ export type ElementalNode = ElementalTextNode | ElementalMetaNode | ElementalChannelNode | ElementalImageNode | ElementalActionNode | ElementalDividerNode | ElementalGroupNode | ElementalColumnsNode | ElementalColumnNode | ElementalQuoteNode | ElementalHtmlNode | ElementalCommentNode | ElementalTextContentNode | ElementalListNode | ElementalListItemNode;
21
22
  export interface ElementalListNode extends IsElementalNode {
22
23
  type: "list";
23
24
  list_type: "ordered" | "unordered";
@@ -32,6 +33,11 @@ export interface ElementalListItemNode extends IsElementalNode {
32
33
  type: "list-item";
33
34
  background_color?: string;
34
35
  elements: (ElementalTextContentNode | ElementalListNode)[];
36
+ locales?: ElementalLocales<{
37
+ content?: string;
38
+ elements?: ElementalTextContentNode[];
39
+ _sourceHash?: string;
40
+ }>;
35
41
  }
36
42
  export type ElementalTextNode = ElementalTextNodeWithContent | ElementalTextNodeWithElements;
37
43
  export interface ElementalTextNodeWithElements extends IsElementalTextNode {
@@ -56,6 +62,7 @@ export interface IsElementalTextNode extends IsElementalNode {
56
62
  border?: IBorderConfig;
57
63
  locales?: ElementalLocales<{
58
64
  content?: string;
65
+ elements?: ElementalTextContentNode[];
59
66
  }>;
60
67
  }
61
68
  export type ElementalTextContentNode = ElementalStringTextContent | ElementalLinkTextContent | ElementalImageTextContent;
@@ -99,6 +106,9 @@ export interface ElementalChannelNode extends IsElementalNode {
99
106
  type: "channel";
100
107
  channel: string;
101
108
  elements?: ElementalNode[];
109
+ background_color?: string;
110
+ content_body_color?: string;
111
+ font_family?: string;
102
112
  raw?: {
103
113
  html?: string;
104
114
  transformers?: string[];
@@ -192,6 +202,62 @@ export interface ElementalGroupNode extends IsElementalNode {
192
202
  elements?: ElementalNode[];
193
203
  }>;
194
204
  }
205
+ /**
206
+ * Represents a columns container node that arranges child column elements horizontally.
207
+ * Used to create multi-column layouts in email templates.
208
+ */
209
+ export interface ElementalColumnsNode extends IsElementalNode {
210
+ /** Node type identifier */
211
+ type: "columns";
212
+ /** Array of column elements to be arranged horizontally */
213
+ elements: ElementalColumnNode[];
214
+ /** Background color for the columns container (e.g., "#ffffff", "transparent") */
215
+ background_color?: string;
216
+ /** Border color for the columns container (e.g., "#000000") */
217
+ border_color?: string;
218
+ /** Border radius for the columns container (e.g., "8px") */
219
+ border_radius?: string;
220
+ /** Border width for the columns container (e.g., "2px") */
221
+ border_width?: string;
222
+ /** Gap/spacing between columns (e.g., "10px") */
223
+ gap?: string;
224
+ /** Padding for the columns container (e.g., "10px 20px") */
225
+ padding?: string;
226
+ /** Vertical alignment of columns within the container */
227
+ vertical_align?: "top" | "middle" | "bottom";
228
+ /** Locale-specific overrides for elements */
229
+ locales?: ElementalLocales<{
230
+ elements?: ElementalColumnNode[];
231
+ }>;
232
+ }
233
+ /**
234
+ * Represents a single column within a columns container.
235
+ * Contains elements that will be stacked vertically within the column.
236
+ */
237
+ export interface ElementalColumnNode extends IsElementalNode {
238
+ /** Node type identifier */
239
+ type: "column";
240
+ /** Array of elements to be stacked vertically within this column */
241
+ elements: ElementalNode[];
242
+ /** Background color for the column (e.g., "#ffffff", "transparent") */
243
+ background_color?: string;
244
+ /** Border color for the column (e.g., "#000000") */
245
+ border_color?: string;
246
+ /** Border radius for the column (e.g., "8px") */
247
+ border_radius?: string;
248
+ /** Border width for the column (e.g., "2px") */
249
+ border_width?: string;
250
+ /** Internal padding for the column content (e.g., "10px", "10px 20px") */
251
+ padding?: string;
252
+ /** Vertical alignment of content within this column */
253
+ vertical_align?: "top" | "middle" | "bottom";
254
+ /** Column width (e.g., "50%", "200px", "auto") */
255
+ width?: string;
256
+ /** Locale-specific overrides for elements */
257
+ locales?: ElementalLocales<{
258
+ elements?: ElementalNode[];
259
+ }>;
260
+ }
195
261
  export interface ElementalQuoteNode extends IsElementalNode {
196
262
  type: "quote";
197
263
  content: string;
@@ -201,7 +267,7 @@ export interface ElementalQuoteNode extends IsElementalNode {
201
267
  padding_horizontal?: number;
202
268
  padding_vertical?: number;
203
269
  background_color?: string;
204
- text_style?: "text" | "h1" | "h2" | "subtext";
270
+ text_style?: "text" | "h1" | "h2" | "h3" | "subtext";
205
271
  locales?: ElementalLocales<{
206
272
  content?: string;
207
273
  }>;
@@ -222,7 +288,7 @@ interface IsElementalNode {
222
288
  type: string;
223
289
  channels?: string[];
224
290
  ref?: string;
225
- if?: string;
291
+ if?: string | ElementalConditionExpression;
226
292
  loop?: string;
227
293
  data?: Record<string, unknown>;
228
294
  }
@@ -0,0 +1,8 @@
1
+ export type FontProvider = "system" | "google";
2
+ export interface FontEntry {
3
+ name: string;
4
+ fontFamily: string;
5
+ sourceType: FontProvider;
6
+ fontUrl?: string;
7
+ previewUrl?: string;
8
+ }
@@ -2,3 +2,4 @@ export * from "./tiptap.types";
2
2
  export * from "./elemental.types";
3
3
  export * from "./elemental.schema";
4
4
  export * from "./validation.types";
5
+ export * from "./font.types";
@@ -1,3 +1,11 @@
1
+ /**
2
+ * Context about where a variable chip is positioned in the document tree.
3
+ * Passed to the validate function so consumers can make context-aware decisions.
4
+ */
5
+ export interface VariableValidationContext {
6
+ /** Whether the variable is inside a list node that has a loop configured */
7
+ isInsideLoop: boolean;
8
+ }
1
9
  /**
2
10
  * Configuration for custom variable validation in the editor.
3
11
  * Allows consumers to restrict which variable names are allowed and
@@ -9,15 +17,19 @@ export interface VariableValidationConfig {
9
17
  * (unless `overrideFormatValidation` is true).
10
18
  *
11
19
  * @param variableName - The variable name to validate (without curly braces)
20
+ * @param context - Positional context about where the variable chip lives
12
21
  * @returns true if the variable is allowed, false otherwise
13
22
  *
14
23
  * @example
15
24
  * ```tsx
16
- * // Only allow variables from a predefined list
17
- * validate: (name) => ['user.name', 'user.email', 'order.total'].includes(name)
25
+ * // Allow $.item.* only inside loops
26
+ * validate: (name, ctx) => {
27
+ * if (name.startsWith('$.')) return ctx?.isInsideLoop ?? false;
28
+ * return allowedPrefixes.some(p => name.startsWith(p));
29
+ * }
18
30
  * ```
19
31
  */
20
- validate?: (variableName: string) => boolean;
32
+ validate?: (variableName: string, context?: VariableValidationContext) => boolean;
21
33
  /**
22
34
  * Behavior when validation fails.
23
35
  * - 'mark': Keep the chip with invalid styling (default, shows red styling)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trycourier/react-designer",
3
- "version": "0.6.0",
3
+ "version": "0.8.0",
4
4
  "description": "React-based rich text designer component",
5
5
  "main": "./dist/cjs/index.js",
6
6
  "module": "./dist/esm/index.js",
@@ -86,7 +86,9 @@
86
86
  "@testing-library/jest-dom": "^6.0.0",
87
87
  "@testing-library/react": "^14.0.0",
88
88
  "@testing-library/user-event": "^14.0.0",
89
+ "@tiptap/extension-text-style": "^2.11.5",
89
90
  "@types/node": "^20.0.0",
91
+ "@types/pngjs": "^6.0.5",
90
92
  "@types/react": "^18.2.0",
91
93
  "@types/react-dom": "^18.0.0",
92
94
  "@vitejs/plugin-react": "^4.0.0",
@@ -98,6 +100,8 @@
98
100
  "esbuild-style-plugin": "^1.6.3",
99
101
  "jsdom": "^22.1.0",
100
102
  "nyc": "^17.1.0",
103
+ "pixelmatch": "^7.1.0",
104
+ "pngjs": "^7.0.0",
101
105
  "postcss": "^8.4.49",
102
106
  "postcss-import": "^16.1.0",
103
107
  "postcss-nested": "^7.0.2",
@@ -152,6 +156,7 @@
152
156
  "test:coverage:watch": "vitest --coverage",
153
157
  "test:e2e": "playwright test --workers=4",
154
158
  "test:e2e:fullcycle": "playwright test --config=playwright.fullcycle.config.ts",
159
+ "test:e2e:fullcycle:update-snapshots": "playwright test --config=playwright.fullcycle.config.ts --update-snapshots",
155
160
  "clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
156
161
  "analyze": "node build.js --analyze",
157
162
  "release": "node scripts/publish.js",
@@ -1,11 +0,0 @@
1
- import { Node } from "@tiptap/core";
2
- import type { CustomCodeProps } from "./CustomCode.types";
3
- declare module "@tiptap/core" {
4
- interface Commands<ReturnType> {
5
- customCode: {
6
- setCustomCode: (props: Partial<CustomCodeProps>) => ReturnType;
7
- };
8
- }
9
- }
10
- export declare const defaultCustomCodeProps: CustomCodeProps;
11
- export declare const CustomCode: Node<any, any>;
@@ -1,9 +0,0 @@
1
- import { type NodeViewProps } from "@tiptap/react";
2
- import React from "react";
3
- import type { CustomCodeProps } from "./CustomCode.types";
4
- export declare const CustomCodeComponent: React.FC<CustomCodeProps & {
5
- nodeKey?: string;
6
- selected?: boolean;
7
- updateAttributes?: (attrs: Partial<CustomCodeProps>) => void;
8
- }>;
9
- export declare const CustomCodeComponentNode: (props: NodeViewProps) => import("react/jsx-runtime").JSX.Element;
@@ -1,6 +0,0 @@
1
- export { CustomCode, defaultCustomCodeProps } from "./CustomCode";
2
- export { CustomCodeComponent, CustomCodeComponentNode } from "./CustomCodeComponent";
3
- export { MonacoCodeEditor } from "./MonacoCodeEditor";
4
- export { CustomCodeForm } from "./CustomCodeForm";
5
- export type { CustomCodeProps } from "./CustomCode.types";
6
- export { customCodeSchema } from "./CustomCode.types";
@@ -1,3 +0,0 @@
1
- import { type BlockBaseProps } from "../Block";
2
- export declare const CustomCodeBlockIcon: () => import("react/jsx-runtime").JSX.Element;
3
- export declare const CustomCodeBlock: ({ draggable }: Pick<BlockBaseProps, "draggable">) => import("react/jsx-runtime").JSX.Element;
@@ -1 +0,0 @@
1
- export * from "./CustomCodeBlock";