@record-evolution/widget-markdown 1.0.3 → 1.0.5

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.
package/package.json CHANGED
@@ -3,7 +3,11 @@
3
3
  "description": "Webcomponent widget-markdown following open-wc recommendations",
4
4
  "license": "MIT",
5
5
  "author": "widget-markdown",
6
- "version": "1.0.3",
6
+ "version": "1.0.5",
7
+ "engines": {
8
+ "node": ">=24.9.0",
9
+ "npm": ">=10.0.2"
10
+ },
7
11
  "type": "module",
8
12
  "main": "dist/widget-markdown.js",
9
13
  "types": "dist/src/widget-markdown.d.ts",
@@ -1,3 +1,9 @@
1
1
  {
2
- "markdown": "# Markdown Showcase\n\n## Text formatting\nNormal, **bold**, *italic*, ~~strikethrough~~, and `inline code`.\n\n> A blockquote with some *emphasis* and **bold**.\n\n---\n\n## Links & Images\n\n![Placeholder image](https://res.cloudinary.com/dk8bhmsdz/image/upload/c_pad,w_500/v1747230890/samples/imagecon-group.jpg)\n\n[Google](https://google.com)\n\n## Lists\n\n- Unordered item\n - Nested item\n- Another item\n\n1. Ordered list\n2. Second item\n 1. Nested ordered\n 2. Another\n\n---\n\n---\n\n## Code blocks\n\n```python\ndef greet(name):\n print(f\"Hello, {name}!\")\n\ngreet(\"Markdown\")\n```\n\n---\n\n## Tables\n\n| Name | Age | Role |\n|:---------|:---:|-------------:|\n| Alice | 25 | Developer |\n| Bob | 30 | Designer |\n| Charlie | 28 | Product Mgr |\n\n---\n\n## Task lists\n\n- [x] Write Markdown\n- [ ] Preview it\n- [ ] Share it\n\n---\n\n## \n"
2
+ "markdown": "# Markdown Showcase\n\n **Inline Data Demo:** Current Temperature {{temperature}} °C \n\n ## Text formatting\nNormal, **bold**, *italic*, ~~strikethrough~~, and `inline code`.\n\n> A blockquote with some *emphasis* and **bold**.\n\n---\n\n## Links & Images\n\n![Placeholder image](https://res.cloudinary.com/dk8bhmsdz/image/upload/c_pad,w_500/v1747230890/samples/imagecon-group.jpg)\n\n[Google](https://google.com)\n\n## Lists\n\n- Unordered item\n - Nested item\n- Another item\n\n1. Ordered list\n2. Second item\n 1. Nested ordered\n 2. Another\n\n---\n\n---\n\n## Code blocks\n\n```python\ndef greet(name):\n print(f\"Hello, {name}!\")\n\ngreet(\"Markdown\")\n```\n\n---\n\n## Tables\n\n| Name | Age | Role |\n|:---------|:---:|-------------:|\n| Alice | 25 | Developer |\n| Bob | 30 | Designer |\n| Charlie | 28 | Product Mgr |\n\n---\n\n## Task lists\n\n- [x] Write Markdown\n- [ ] Preview it\n- [ ] Share it\n\n---\n\n## \n",
3
+ "data": [
4
+ {
5
+ "label": "temperature",
6
+ "value": "23"
7
+ }
8
+ ]
3
9
  }
@@ -6,15 +6,15 @@
6
6
  */
7
7
 
8
8
  /**
9
- * This is the label of the variable to be used in the replacement text like {{myLabel}}
9
+ * The variable name used in the markdown text. Reference this in your markdown using the {{label}} syntax. Use descriptive names like 'temperature', 'status', 'deviceName'.
10
10
  */
11
11
  export type Label = string;
12
12
  /**
13
- * This value will be shown in the output in place of {{myLabel}}
13
+ * The replacement text that will appear in place of {{label}} in the rendered output. Can be a static value or bound to a data column for dynamic updates.
14
14
  */
15
15
  export type Value = string;
16
16
  /**
17
- * A list of variables to replace in the text. To replace a variable in the markdown text use the label of the variable with this notation {{variable}}
17
+ * Array of variable definitions for dynamic text substitution. Each variable has a label (used in the markdown as {{label}}) and a value (the replacement text). Values can be static or bound to data columns for real-time updates.
18
18
  */
19
19
  export type Variables = {
20
20
  label?: Label;
@@ -22,11 +22,17 @@ export type Variables = {
22
22
  [k: string]: unknown;
23
23
  }[];
24
24
 
25
+ /**
26
+ * A markdown rendering widget for displaying formatted text with dynamic variable substitution. Use this widget for documentation, instructions, dynamic reports, or any rich text content on dashboards. Supports full Markdown syntax including headers, lists, tables, links, and code blocks. Variables can be embedded using {{variableName}} notation and bound to data sources for real-time updates. Ideal for creating contextual help, status summaries, or narrative text that incorporates live data values.
27
+ */
25
28
  export interface InputData {
26
29
  markdown?: Markdown;
27
30
  data?: Variables;
28
31
  [k: string]: unknown;
29
32
  }
33
+ /**
34
+ * The markdown-formatted text content to display. Supports all standard Markdown syntax: headers (#), bold (**text**), italic (*text*), lists (- item), links ([text](url)), code blocks (```), tables, and more. Use {{variableName}} to insert dynamic variables.
35
+ */
30
36
  export interface Markdown {
31
37
  [k: string]: unknown;
32
38
  }
@@ -1,15 +1,17 @@
1
1
  {
2
2
  "title": "InputData",
3
+ "description": "A markdown rendering widget for displaying formatted text with dynamic variable substitution. Use this widget for documentation, instructions, dynamic reports, or any rich text content on dashboards. Supports full Markdown syntax including headers, lists, tables, links, and code blocks. Variables can be embedded using {{variableName}} notation and bound to data sources for real-time updates. Ideal for creating contextual help, status summaries, or narrative text that incorporates live data values.",
3
4
  "type": "object",
4
5
  "properties": {
5
6
  "markdown": {
6
7
  "title": "Markdown",
8
+ "description": "The markdown-formatted text content to display. Supports all standard Markdown syntax: headers (#), bold (**text**), italic (*text*), lists (- item), links ([text](url)), code blocks (```), tables, and more. Use {{variableName}} to insert dynamic variables.",
7
9
  "order": 1,
8
10
  "type": "textarea"
9
11
  },
10
12
  "data": {
11
13
  "title": "Variables",
12
- "description": "A list of variables to replace in the text. To replace a variable in the markdown text use the label of the variable with this notation {{variable}}",
14
+ "description": "Array of variable definitions for dynamic text substitution. Each variable has a label (used in the markdown as {{label}}) and a value (the replacement text). Values can be static or bound to data columns for real-time updates.",
13
15
  "type": "array",
14
16
  "order": 2,
15
17
  "items": {
@@ -17,13 +19,13 @@
17
19
  "properties": {
18
20
  "label": {
19
21
  "title": "Label",
20
- "description": "This is the label of the variable to be used in the replacement text like {{myLabel}}",
22
+ "description": "The variable name used in the markdown text. Reference this in your markdown using the {{label}} syntax. Use descriptive names like 'temperature', 'status', 'deviceName'.",
21
23
  "type": "string",
22
24
  "order": 1
23
25
  },
24
26
  "value": {
25
27
  "title": "Value",
26
- "description": "This value will be shown in the output in place of {{myLabel}}",
28
+ "description": "The replacement text that will appear in place of {{label}} in the rendered output. Can be a static value or bound to a data column for dynamic updates.",
27
29
  "type": "string",
28
30
  "order": 2
29
31
  }
@@ -45,6 +45,8 @@ export class WidgetMarkdown extends LitElement {
45
45
  @state() private themeBgColor?: string
46
46
  @state() private themeTitleColor?: string
47
47
  @state() private themeSubtitleColor?: string
48
+ @state() private cachedHtmlTemplate?: string
49
+ @state() private lastMarkdown?: string
48
50
 
49
51
  version: string = 'versionplaceholder'
50
52
  marked: Marked
@@ -92,33 +94,47 @@ export class WidgetMarkdown extends LitElement {
92
94
  }
93
95
 
94
96
  /**
95
- * Interpolates variables from inputData into the markdown text.
96
- * Replaces patterns like {{variableName}} with corresponding values from inputData.
97
+ * Converts {{variableName}} to spans with data attributes for efficient updating.
98
+ * Markdown is parsed once, then only variable spans are updated.
97
99
  */
98
- interpolateVariables(text: string): string {
99
- if (!this.inputData) return text
100
-
100
+ convertVariablesToSpans(text: string): string {
101
101
  return text.replace(/\{\{([^}]+)\}\}/g, (match, varName) => {
102
102
  const key = varName.trim()
103
- const dataItem = this.inputData?.data?.find((item) => item.label === key)
104
- const value = dataItem?.value
105
-
106
- // If variable doesn't exist, return the original placeholder
107
- if (value === null || value === undefined) {
108
- return match
109
- } else if (typeof value === 'object') {
110
- // For objects/arrays, return JSON representation
111
- try {
112
- return JSON.stringify(value)
113
- } catch {
114
- return match
115
- }
116
- } else {
117
- return String(value)
118
- }
103
+ return `<span class="md-var" data-var="${key}"></span>`
119
104
  })
120
105
  }
121
106
 
107
+ /**
108
+ * Updates variable spans in the rendered DOM without recreating the entire structure.
109
+ */
110
+ protected updated(changedProperties: PropertyValues): void {
111
+ super.updated(changedProperties)
112
+
113
+ if (changedProperties.has('inputData')) {
114
+ // Update only the variable spans
115
+ const varSpans = this.shadowRoot?.querySelectorAll('.md-var')
116
+ varSpans?.forEach((span) => {
117
+ const varName = span.getAttribute('data-var')
118
+ if (varName) {
119
+ const dataItem = this.inputData?.data?.find((item) => item.label === varName)
120
+ const value = dataItem?.value
121
+
122
+ if (value === null || value === undefined) {
123
+ span.textContent = `{{${varName}}}`
124
+ } else if (typeof value === 'object') {
125
+ try {
126
+ span.textContent = JSON.stringify(value)
127
+ } catch {
128
+ span.textContent = `{{${varName}}}`
129
+ }
130
+ } else {
131
+ span.textContent = String(value)
132
+ }
133
+ }
134
+ })
135
+ }
136
+ }
137
+
122
138
  static styles = [
123
139
  unsafeCSS(githubCss),
124
140
  unsafeCSS(highlightCSS),
@@ -172,12 +188,18 @@ export class WidgetMarkdown extends LitElement {
172
188
 
173
189
  render() {
174
190
  const mdText: string = String(this.inputData?.markdown ?? '')
175
- const interpolatedText: string = this.interpolateVariables(mdText)
176
- const mdHtml: string = this.marked.parse(interpolatedText) as string
191
+
192
+ // Only re-parse markdown if the markdown text actually changed
193
+ if (mdText !== this.lastMarkdown) {
194
+ this.lastMarkdown = mdText
195
+ const textWithSpans: string = this.convertVariablesToSpans(mdText)
196
+ this.cachedHtmlTemplate = this.marked.parse(textWithSpans) as string
197
+ }
198
+
177
199
  return html`
178
200
  <div class="wrapper" style="background-color: ${this.themeBgColor}">
179
201
  <div class="paging markdown-body" ?active=${this.inputData?.markdown}>
180
- ${unsafeHTML(mdHtml)}
202
+ ${unsafeHTML(this.cachedHtmlTemplate ?? '')}
181
203
  </div>
182
204
  </div>
183
205
  `