flex-html-render 1.0.2 → 1.1.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.
- package/README.md +6 -0
- package/dist/flex-html-render.cjs.js +6 -1
- package/dist/flex-html-render.es.js +224 -79
- package/dist/flex-html-render.umd.js +6 -1
- package/index.d.ts +1 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -41,6 +41,12 @@ console.log(JSON.stringify(flexMessage, null, 2));
|
|
|
41
41
|
### `convertHtmlToFlexMessage(htmlString)`
|
|
42
42
|
- **參數**:`htmlString` (string) - Flex Message 對應的 HTML 字串
|
|
43
43
|
- **回傳**:Flex Message JSON 物件陣列
|
|
44
|
+
- **說明**:此函數將 Flex Message HTML 字串轉為 JSON 結構,可用於編輯或顯示目的
|
|
45
|
+
|
|
46
|
+
### `convertJsonToHtml(json)`
|
|
47
|
+
- **參數**:`json` (object) - Flex Message JSON 物件
|
|
48
|
+
- **回傳**:對應的格式化 HTML 字串
|
|
49
|
+
- **說明**:此函數將 Flex Message JSON 結構轉為 HTML 字串,可用於編輯或顯示目的
|
|
44
50
|
|
|
45
51
|
|
|
46
52
|
## 支援的 Flex Message 元素
|
|
@@ -1 +1,6 @@
|
|
|
1
|
-
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const
|
|
1
|
+
"use strict";Object.defineProperties(exports,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}});const J=require("htmlparser2"),E={ELEMENT:"element",TEXT:"text"},e={CAROUSEL:"carousel",BUBBLE:"bubble",HEADER:"header",HERO:"hero",BODY:"body",FOOTER:"footer",BOX:"box",TEXT:"text",SPAN:"span",IMAGE:"image",VIDEO:"video",ICON:"icon",SEPARATOR:"separator",BUTTON:"button",FILLER:"filler",SPACE:"space",STRONG:"strong",BASELINE:"baseline",ROW:"row",VERTICAL:"vertical",DIV:"div",ARTICLE:"article",ACTION:"action",BACKGROUND:"background"},Y=[e.HEADER,e.HERO,e.BODY,e.FOOTER],W=[e.BOX,e.VERTICAL,e.ROW,e.BASELINE,e.DIV,e.ARTICLE],q=[e.TEXT,e.STRONG];function z(t){const r=Number(t);return isNaN(r)?t:r}function _(t){return t==="true"?!0:t==="false"?!1:t}function Q(t){return[z,_].reduce((n,s)=>s(n),t)}function Z(t){const r={type:"root",children:[]},n=[r],s=new J.Parser({onopentag(l,i){const a={type:E.ELEMENT,tagName:l,attributes:Object.fromEntries(Object.entries(i).map(([p,f])=>[p,Q(f)])),children:[]};n[n.length-1].children.push(a),n.push(a)},ontext(l){const i=n[n.length-1];if(i.tagName==="span"){i.children.push({type:E.TEXT,content:l});return}l.trim()&&i.children.push({type:E.TEXT,content:l.replace(/\n/g,"").trim()})},onclosetag(){n.pop()},onerror(l){console.error("解析錯誤:",l)}},{xmlMode:!0,lowerCaseTags:!1,lowerCaseAttributeNames:!1});return s.write(t),s.end(),r.children}const h=t=>{throw Error(`[Flex Html Render]: ${t}`)},c={carousel:t=>({type:"carousel",contents:t}),bubble:(t={})=>({type:"bubble",...t}),box:(t,r={})=>({type:"box",contents:t,...r}),text:(t,r={})=>({type:"text",...typeof t=="string"?{text:t}:{contents:t},...r}),span:(t,r={})=>({type:"span",text:t,...r}),image:(t={})=>({type:"image",...t}),video:(t={})=>({type:"video",...t}),icon:(t={})=>({type:"icon",...t}),button:(t={})=>({type:"button",...t}),separator:(t={color:"#E0E3EA"})=>({type:"separator",...t}),filler:(t={})=>({type:"filler",...t}),uri:(t={})=>({type:"uri",altUri:t.altUri?{desktop:t.altUri}:void 0,...t}),postback:(t={})=>({type:"postback",...t}),message:(t={})=>({type:"message",...t}),datetimepicker:(t={})=>({type:"datetimepicker",...t}),camera:(t={})=>({type:"camera",...t}),cameraRoll:(t={})=>({type:"cameraRoll",...t}),location:(t={})=>({type:"location",...t}),richmenuswitch:(t={})=>({type:"richmenuswitch",...t}),clipboard:(t={})=>({type:"clipboard",...t})};function tt(t){if(t.tagName!==e.BACKGROUND&&t.children&&t.children.length>0){const r=t.children.filter(n=>n.tagName===e.BACKGROUND);return r.length>1&&h("A box node can only allowed with one background node inside"),t.children=t.children.filter(n=>n.tagName!==e.BACKGROUND),r[0]}return null}function et(t){if(t.tagName!==e.ACTION&&t.children&&t.children.length>0){const r=t.children.filter(n=>n.tagName===e.ACTION);return r.length>1&&h("A node can only allowed with one action node inside"),t.children=t.children.filter(n=>n.tagName!==e.ACTION),r[0]}return null}function rt(t){![...q,e.SPAN].includes(t.tagName)&&t.children?.some(l=>l.type===E.TEXT)&&(console.log(t),h("Span component only allowed inside Text component")),t.tagName===e.VIDEO&&t.children?.some(l=>![e.BOX,e.IMAGE].includes(l.tagName))&&h("Video component only allowed Box or Image as altContent child"),!W.includes(t.tagName)&&t.children?.some(l=>l.tagName===e.BACKGROUND)&&h("Only Box component allowed to have Background child")}function m(t){t.attributes=t.attributes||{},rt(t);const r=et(t);r&&(t.attributes.action=m(r));const n=tt(t);if(n&&(t.attributes.background=m(n)),t.type===E.TEXT)return c.span(t.content,t.attributes);if(t.tagName===e.BUBBLE){const s=t.children.find(u=>u.tagName===e.HEADER),l=t.children.find(u=>u.tagName===e.HERO),i=t.children.find(u=>u.tagName===e.BODY),a=t.children.find(u=>u.tagName===e.FOOTER);return c.bubble({...t.attributes,header:s?m(s):void 0,hero:l?m(l):void 0,body:i?m(i):void 0,footer:a?m(a):void 0})}if(t.tagName===e.CAROUSEL)return t.children.some(s=>s.tagName!==e.BUBBLE)&&h("Carousel can only have Bubble as children"),t.children.length>12&&h("Carousel can have maximum 12 bubbles"),c.carousel(t.children.map(m));if(Y.includes(t.tagName))return t.children.length!==1&&h(`${t.tagName} should have exactly one child node`),m(t.children[0]);if(t.tagName===e.BUTTON)return t.attributes.action||h("Button component should contain an action node"),c.button(t.attributes);if(t.tagName===e.SPAN){t.children.length>1&&h("Span component can only have one text child");const s=t.children[0];if(s&&s.type===E.TEXT)return c.span(s.content,t.attributes);h(s?"Span component only allowed text child":"Span component can not be empty")}if(t.tagName===e.TEXT)return c.text(t.children.map(m),t.attributes);if(t.tagName===e.SEPARATOR)return c.separator(t.attributes);if(t.tagName===e.IMAGE)return c.image(t.attributes);if(t.tagName===e.VIDEO)return t.children.length!==1&&h("Video component should only has one Box or Image as altContent"),t.attributes.altContent=m(t.children[0]),c.video(t.attributes);if(t.tagName===e.ICON)return c.icon(t.attributes);if(t.tagName===e.FILLER)return c.filler(t.attributes);if(t.tagName===e.BOX||t.tagName===e.DIV||t.tagName===e.ARTICLE)return t.attributes.layout||(t.attributes.layout="vertical"),c.box(t.children.map(m),t.attributes);if(t.tagName===e.SPACE)return c.span(" ".repeat(t.attributes.size||1));if(t.tagName===e.STRONG)return t.tagName=e.TEXT,t.attributes.weight="bold",m(t);if(t.tagName===e.BASELINE)return t.tagName=e.BOX,t.attributes.layout="baseline",m(t);if(t.tagName===e.ROW)return t.tagName=e.BOX,t.attributes.layout="horizontal",m(t);if(t.tagName===e.VERTICAL)return t.tagName=e.BOX,t.attributes.layout="vertical",m(t);if(t.tagName===e.ACTION){const s=t.attributes.type;switch(s){case"uri":return c.uri(t.attributes);case"postback":return c.postback(t.attributes);case"message":return c.message(t.attributes);case"datetimepicker":return c.datetimepicker(t.attributes);case"camera":return c.camera(t.attributes);case"cameraRoll":return c.cameraRoll(t.attributes);case"location":return c.location(t.attributes);case"richmenuswitch":return c.richmenuswitch(t.attributes);case"clipboard":return c.clipboard(t.attributes);default:h(`Unsupported action type: ${s}`)}}if(t.tagName===e.BACKGROUND)return t.attributes}const o={createElement:(t,r={},n=[],s=0)=>{const i=Object.entries(r).filter(([u,p])=>p!=null).map(([u,p])=>typeof p=="object"?`${u}="${JSON.stringify(p).replace(/"/g,""")}"`:`${u}="${p}"`).join(" "),a=i?` ${i}`:"";if(n.length===0)return` <${t}${a} />`;{const u=n.map(p=>typeof p=="string"?p.split(`
|
|
2
|
+
`).map(f=>f?" "+f:"").join(`
|
|
3
|
+
`):p).join(`
|
|
4
|
+
`);return` <${t}${a}>
|
|
5
|
+
${u}
|
|
6
|
+
</${t}>`}},createText:t=>t};function b(t,r=0){if(!t||typeof t!="object")return"";const{type:n,...s}=t,{action:l,...i}=s,a=l?b(l,r+1):"";switch(n){case"carousel":const{contents:u,...p}=i,f=u.map(N=>b(N,r+1));return a&&f.unshift(a),o.createElement(e.CAROUSEL,p,f,r);case"bubble":const{header:A,hero:C,body:R,footer:B,...L}=i,g=[];return a&&g.push(a),A&&g.push(o.createElement(e.HEADER,{},[b(A,r+1)],r+1)),C&&g.push(o.createElement(e.HERO,{},[b(C,r+1)],r+1)),R&&g.push(o.createElement(e.BODY,{},[b(R,r+1)],r+1)),B&&g.push(o.createElement(e.FOOTER,{},[b(B,r+1)],r+1)),o.createElement(e.BUBBLE,L,g,r);case"box":const{contents:w,background:d,...O}=i;O.layout||(O.layout="vertical");const T=[];return a&&T.push(a),d&&T.push(b({type:"background",...d},r+1)),T.push(...(w||[]).map(N=>b(N,r+1))),o.createElement(e.BOX,O,T,r);case"text":const{text:U,contents:y,...k}=i,x=y&&y.length>0?y.map(N=>b(N,r+1)):[U||""];return a&&x.unshift(a),o.createElement(e.TEXT,k,x,r);case"span":const{text:D,...X}=i,S=[D||""];return a&&S.unshift(a),o.createElement(e.SPAN,X,S,r);case"image":const{...$}=i,H=a?[a]:[];return o.createElement(e.IMAGE,$,H,r);case"video":const{altContent:I,...G}=i,v=I?[b(I,r+1)]:[];return a&&v.unshift(a),o.createElement(e.VIDEO,G,v,r);case"icon":const{...M}=i,F=a?[a]:[];return o.createElement(e.ICON,M,F,r);case"button":const{...P}=i,V=a?[a]:[];return o.createElement(e.BUTTON,P,V,r);case"separator":const j=a?[a]:[];return o.createElement(e.SEPARATOR,i,j,r);case"filler":const K=a?[a]:[];return o.createElement(e.FILLER,i,K,r);case"uri":case"message":case"postback":case"datetimepicker":case"camera":case"cameraRoll":case"location":case"richmenuswitch":case"clipboard":return o.createElement(e.ACTION,{type:n,...i},[],r);case"background":return o.createElement(e.BACKGROUND,i,[],r);default:return""}}function at(t){try{return Z(t).map(m)}catch(r){throw console.error("Error converting HTML to Flex Message:",r),r}}exports.convertJsonToHtml=b;exports.default=at;
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { Parser as
|
|
2
|
-
const
|
|
1
|
+
import { Parser as Y } from "htmlparser2";
|
|
2
|
+
const E = {
|
|
3
3
|
ELEMENT: "element",
|
|
4
4
|
TEXT: "text"
|
|
5
5
|
}, e = {
|
|
@@ -20,6 +20,7 @@ const m = {
|
|
|
20
20
|
ICON: "icon",
|
|
21
21
|
SEPARATOR: "separator",
|
|
22
22
|
BUTTON: "button",
|
|
23
|
+
FILLER: "filler",
|
|
23
24
|
// custom render tags
|
|
24
25
|
SPACE: "space",
|
|
25
26
|
STRONG: "strong",
|
|
@@ -32,60 +33,68 @@ const m = {
|
|
|
32
33
|
// custom utils tags
|
|
33
34
|
ACTION: "action",
|
|
34
35
|
BACKGROUND: "background"
|
|
35
|
-
},
|
|
36
|
+
}, J = [
|
|
36
37
|
e.HEADER,
|
|
37
38
|
e.HERO,
|
|
38
39
|
e.BODY,
|
|
39
40
|
e.FOOTER
|
|
40
|
-
],
|
|
41
|
+
], W = [
|
|
41
42
|
e.BOX,
|
|
42
43
|
e.VERTICAL,
|
|
43
44
|
e.ROW,
|
|
44
45
|
e.BASELINE,
|
|
45
46
|
e.DIV,
|
|
46
47
|
e.ARTICLE
|
|
47
|
-
],
|
|
48
|
+
], z = [
|
|
48
49
|
e.TEXT,
|
|
49
50
|
e.STRONG
|
|
50
51
|
];
|
|
51
|
-
function
|
|
52
|
-
const
|
|
53
|
-
return isNaN(
|
|
52
|
+
function q(t) {
|
|
53
|
+
const r = Number(t);
|
|
54
|
+
return isNaN(r) ? t : r;
|
|
54
55
|
}
|
|
55
|
-
function
|
|
56
|
+
function Q(t) {
|
|
56
57
|
return t === "true" ? !0 : t === "false" ? !1 : t;
|
|
57
58
|
}
|
|
58
|
-
function
|
|
59
|
+
function Z(t) {
|
|
59
60
|
return [
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
].reduce((
|
|
61
|
+
q,
|
|
62
|
+
Q
|
|
63
|
+
].reduce((n, s) => s(n), t);
|
|
63
64
|
}
|
|
64
|
-
function
|
|
65
|
-
const
|
|
65
|
+
function _(t) {
|
|
66
|
+
const r = { type: "root", children: [] }, n = [r], s = new Y(
|
|
66
67
|
{
|
|
67
|
-
onopentag(
|
|
68
|
-
const
|
|
69
|
-
type:
|
|
70
|
-
tagName:
|
|
68
|
+
onopentag(l, i) {
|
|
69
|
+
const a = {
|
|
70
|
+
type: E.ELEMENT,
|
|
71
|
+
tagName: l,
|
|
71
72
|
attributes: Object.fromEntries(
|
|
72
|
-
Object.entries(
|
|
73
|
+
Object.entries(i).map(([p, f]) => [p, Z(f)])
|
|
73
74
|
),
|
|
74
75
|
children: []
|
|
75
76
|
};
|
|
76
|
-
|
|
77
|
+
n[n.length - 1].children.push(a), n.push(a);
|
|
77
78
|
},
|
|
78
|
-
ontext(
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
79
|
+
ontext(l) {
|
|
80
|
+
const i = n[n.length - 1];
|
|
81
|
+
if (i.tagName === "span") {
|
|
82
|
+
i.children.push({
|
|
83
|
+
type: E.TEXT,
|
|
84
|
+
content: l
|
|
85
|
+
});
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
l.trim() && i.children.push({
|
|
89
|
+
type: E.TEXT,
|
|
90
|
+
content: l.replace(/\n/g, "").trim()
|
|
82
91
|
});
|
|
83
92
|
},
|
|
84
93
|
onclosetag() {
|
|
85
|
-
|
|
94
|
+
n.pop();
|
|
86
95
|
},
|
|
87
|
-
onerror(
|
|
88
|
-
console.error("解析錯誤:",
|
|
96
|
+
onerror(l) {
|
|
97
|
+
console.error("解析錯誤:", l);
|
|
89
98
|
}
|
|
90
99
|
},
|
|
91
100
|
{
|
|
@@ -96,9 +105,9 @@ function B(t) {
|
|
|
96
105
|
lowerCaseAttributeNames: !1
|
|
97
106
|
}
|
|
98
107
|
);
|
|
99
|
-
return
|
|
108
|
+
return s.write(t), s.end(), r.children;
|
|
100
109
|
}
|
|
101
|
-
const
|
|
110
|
+
const h = (t) => {
|
|
102
111
|
throw Error(`[Flex Html Render]: ${t}`);
|
|
103
112
|
}, c = {
|
|
104
113
|
carousel: (t) => ({
|
|
@@ -109,22 +118,22 @@ const o = (t) => {
|
|
|
109
118
|
type: "bubble",
|
|
110
119
|
...t
|
|
111
120
|
}),
|
|
112
|
-
box: (t,
|
|
121
|
+
box: (t, r = {}) => ({
|
|
113
122
|
type: "box",
|
|
114
123
|
contents: t,
|
|
115
|
-
...
|
|
124
|
+
...r
|
|
116
125
|
}),
|
|
117
|
-
text: (t,
|
|
126
|
+
text: (t, r = {}) => ({
|
|
118
127
|
type: "text",
|
|
119
128
|
...typeof t == "string" ? { text: t } : {
|
|
120
129
|
contents: t
|
|
121
130
|
},
|
|
122
|
-
...
|
|
131
|
+
...r
|
|
123
132
|
}),
|
|
124
|
-
span: (t,
|
|
133
|
+
span: (t, r = {}) => ({
|
|
125
134
|
type: "span",
|
|
126
135
|
text: t,
|
|
127
|
-
...
|
|
136
|
+
...r
|
|
128
137
|
}),
|
|
129
138
|
image: (t = {}) => ({
|
|
130
139
|
type: "image",
|
|
@@ -146,69 +155,105 @@ const o = (t) => {
|
|
|
146
155
|
type: "separator",
|
|
147
156
|
...t
|
|
148
157
|
}),
|
|
158
|
+
filler: (t = {}) => ({
|
|
159
|
+
type: "filler",
|
|
160
|
+
...t
|
|
161
|
+
}),
|
|
149
162
|
uri: (t = {}) => ({
|
|
150
163
|
type: "uri",
|
|
151
164
|
altUri: t.altUri ? {
|
|
152
165
|
desktop: t.altUri
|
|
153
166
|
} : void 0,
|
|
154
167
|
...t
|
|
168
|
+
}),
|
|
169
|
+
postback: (t = {}) => ({
|
|
170
|
+
type: "postback",
|
|
171
|
+
...t
|
|
172
|
+
}),
|
|
173
|
+
message: (t = {}) => ({
|
|
174
|
+
type: "message",
|
|
175
|
+
...t
|
|
176
|
+
}),
|
|
177
|
+
datetimepicker: (t = {}) => ({
|
|
178
|
+
type: "datetimepicker",
|
|
179
|
+
...t
|
|
180
|
+
}),
|
|
181
|
+
camera: (t = {}) => ({
|
|
182
|
+
type: "camera",
|
|
183
|
+
...t
|
|
184
|
+
}),
|
|
185
|
+
cameraRoll: (t = {}) => ({
|
|
186
|
+
type: "cameraRoll",
|
|
187
|
+
...t
|
|
188
|
+
}),
|
|
189
|
+
location: (t = {}) => ({
|
|
190
|
+
type: "location",
|
|
191
|
+
...t
|
|
192
|
+
}),
|
|
193
|
+
richmenuswitch: (t = {}) => ({
|
|
194
|
+
type: "richmenuswitch",
|
|
195
|
+
...t
|
|
196
|
+
}),
|
|
197
|
+
clipboard: (t = {}) => ({
|
|
198
|
+
type: "clipboard",
|
|
199
|
+
...t
|
|
155
200
|
})
|
|
156
201
|
};
|
|
157
|
-
function
|
|
202
|
+
function tt(t) {
|
|
158
203
|
if (t.tagName !== e.BACKGROUND && t.children && t.children.length > 0) {
|
|
159
|
-
const
|
|
160
|
-
return
|
|
204
|
+
const r = t.children.filter((n) => n.tagName === e.BACKGROUND);
|
|
205
|
+
return r.length > 1 && h("A box node can only allowed with one background node inside"), t.children = t.children.filter((n) => n.tagName !== e.BACKGROUND), r[0];
|
|
161
206
|
}
|
|
162
207
|
return null;
|
|
163
208
|
}
|
|
164
|
-
function
|
|
209
|
+
function et(t) {
|
|
165
210
|
if (t.tagName !== e.ACTION && t.children && t.children.length > 0) {
|
|
166
|
-
const
|
|
167
|
-
return
|
|
211
|
+
const r = t.children.filter((n) => n.tagName === e.ACTION);
|
|
212
|
+
return r.length > 1 && h("A node can only allowed with one action node inside"), t.children = t.children.filter((n) => n.tagName !== e.ACTION), r[0];
|
|
168
213
|
}
|
|
169
214
|
return null;
|
|
170
215
|
}
|
|
171
|
-
function
|
|
172
|
-
!
|
|
216
|
+
function rt(t) {
|
|
217
|
+
![...z, e.SPAN].includes(t.tagName) && t.children?.some((l) => l.type === E.TEXT) && (console.log(t), h("Span component only allowed inside Text component")), t.tagName === e.VIDEO && t.children?.some((l) => ![e.BOX, e.IMAGE].includes(l.tagName)) && h("Video component only allowed Box or Image as altContent child"), !W.includes(t.tagName) && t.children?.some((l) => l.tagName === e.BACKGROUND) && h("Only Box component allowed to have Background child");
|
|
173
218
|
}
|
|
174
|
-
function
|
|
175
|
-
t.attributes = t.attributes || {},
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
const
|
|
179
|
-
if (
|
|
219
|
+
function m(t) {
|
|
220
|
+
t.attributes = t.attributes || {}, rt(t);
|
|
221
|
+
const r = et(t);
|
|
222
|
+
r && (t.attributes.action = m(r));
|
|
223
|
+
const n = tt(t);
|
|
224
|
+
if (n && (t.attributes.background = m(n)), t.type === E.TEXT)
|
|
180
225
|
return c.span(t.content, t.attributes);
|
|
181
226
|
if (t.tagName === e.BUBBLE) {
|
|
182
|
-
const
|
|
227
|
+
const s = t.children.find((u) => u.tagName === e.HEADER), l = t.children.find((u) => u.tagName === e.HERO), i = t.children.find((u) => u.tagName === e.BODY), a = t.children.find((u) => u.tagName === e.FOOTER);
|
|
183
228
|
return c.bubble({
|
|
184
229
|
...t.attributes,
|
|
185
|
-
header:
|
|
186
|
-
hero:
|
|
187
|
-
body:
|
|
188
|
-
footer:
|
|
230
|
+
header: s ? m(s) : void 0,
|
|
231
|
+
hero: l ? m(l) : void 0,
|
|
232
|
+
body: i ? m(i) : void 0,
|
|
233
|
+
footer: a ? m(a) : void 0
|
|
189
234
|
});
|
|
190
235
|
}
|
|
191
236
|
if (t.tagName === e.CAROUSEL)
|
|
192
|
-
return t.children.some((
|
|
193
|
-
t.children.map(
|
|
237
|
+
return t.children.some((s) => s.tagName !== e.BUBBLE) && h("Carousel can only have Bubble as children"), t.children.length > 12 && h("Carousel can have maximum 12 bubbles"), c.carousel(
|
|
238
|
+
t.children.map(m)
|
|
194
239
|
);
|
|
195
|
-
if (
|
|
196
|
-
return t.children.length !== 1 &&
|
|
240
|
+
if (J.includes(t.tagName))
|
|
241
|
+
return t.children.length !== 1 && h(`${t.tagName} should have exactly one child node`), m(t.children[0]);
|
|
197
242
|
if (t.tagName === e.BUTTON)
|
|
198
|
-
return t.attributes.action ||
|
|
243
|
+
return t.attributes.action || h("Button component should contain an action node"), c.button(t.attributes);
|
|
199
244
|
if (t.tagName === e.SPAN) {
|
|
200
|
-
t.children.length > 1 &&
|
|
201
|
-
const
|
|
202
|
-
if (
|
|
245
|
+
t.children.length > 1 && h("Span component can only have one text child");
|
|
246
|
+
const s = t.children[0];
|
|
247
|
+
if (s && s.type === E.TEXT)
|
|
203
248
|
return c.span(
|
|
204
|
-
|
|
249
|
+
s.content,
|
|
205
250
|
t.attributes
|
|
206
251
|
);
|
|
207
|
-
|
|
252
|
+
h(s ? "Span component only allowed text child" : "Span component can not be empty");
|
|
208
253
|
}
|
|
209
254
|
if (t.tagName === e.TEXT)
|
|
210
255
|
return c.text(
|
|
211
|
-
t.children.map(
|
|
256
|
+
t.children.map(m),
|
|
212
257
|
t.attributes
|
|
213
258
|
);
|
|
214
259
|
if (t.tagName === e.SEPARATOR)
|
|
@@ -216,36 +261,136 @@ function i(t) {
|
|
|
216
261
|
if (t.tagName === e.IMAGE)
|
|
217
262
|
return c.image(t.attributes);
|
|
218
263
|
if (t.tagName === e.VIDEO)
|
|
219
|
-
return t.children.length !== 1 &&
|
|
264
|
+
return t.children.length !== 1 && h("Video component should only has one Box or Image as altContent"), t.attributes.altContent = m(t.children[0]), c.video(t.attributes);
|
|
220
265
|
if (t.tagName === e.ICON)
|
|
221
266
|
return c.icon(t.attributes);
|
|
267
|
+
if (t.tagName === e.FILLER)
|
|
268
|
+
return c.filler(t.attributes);
|
|
222
269
|
if (t.tagName === e.BOX || t.tagName === e.DIV || t.tagName === e.ARTICLE)
|
|
223
270
|
return t.attributes.layout || (t.attributes.layout = "vertical"), c.box(
|
|
224
|
-
t.children.map(
|
|
271
|
+
t.children.map(m),
|
|
225
272
|
t.attributes
|
|
226
273
|
);
|
|
227
274
|
if (t.tagName === e.SPACE)
|
|
228
275
|
return c.span(" ".repeat(t.attributes.size || 1));
|
|
229
276
|
if (t.tagName === e.STRONG)
|
|
230
|
-
return t.tagName = e.TEXT, t.attributes.weight = "bold",
|
|
277
|
+
return t.tagName = e.TEXT, t.attributes.weight = "bold", m(t);
|
|
231
278
|
if (t.tagName === e.BASELINE)
|
|
232
|
-
return t.tagName = e.BOX, t.attributes.layout = "baseline",
|
|
279
|
+
return t.tagName = e.BOX, t.attributes.layout = "baseline", m(t);
|
|
233
280
|
if (t.tagName === e.ROW)
|
|
234
|
-
return t.tagName = e.BOX, t.attributes.layout = "horizontal",
|
|
281
|
+
return t.tagName = e.BOX, t.attributes.layout = "horizontal", m(t);
|
|
235
282
|
if (t.tagName === e.VERTICAL)
|
|
236
|
-
return t.tagName = e.BOX, t.attributes.layout = "vertical",
|
|
237
|
-
if (t.tagName === e.ACTION
|
|
238
|
-
|
|
283
|
+
return t.tagName = e.BOX, t.attributes.layout = "vertical", m(t);
|
|
284
|
+
if (t.tagName === e.ACTION) {
|
|
285
|
+
const s = t.attributes.type;
|
|
286
|
+
switch (s) {
|
|
287
|
+
case "uri":
|
|
288
|
+
return c.uri(t.attributes);
|
|
289
|
+
case "postback":
|
|
290
|
+
return c.postback(t.attributes);
|
|
291
|
+
case "message":
|
|
292
|
+
return c.message(t.attributes);
|
|
293
|
+
case "datetimepicker":
|
|
294
|
+
return c.datetimepicker(t.attributes);
|
|
295
|
+
case "camera":
|
|
296
|
+
return c.camera(t.attributes);
|
|
297
|
+
case "cameraRoll":
|
|
298
|
+
return c.cameraRoll(t.attributes);
|
|
299
|
+
case "location":
|
|
300
|
+
return c.location(t.attributes);
|
|
301
|
+
case "richmenuswitch":
|
|
302
|
+
return c.richmenuswitch(t.attributes);
|
|
303
|
+
case "clipboard":
|
|
304
|
+
return c.clipboard(t.attributes);
|
|
305
|
+
default:
|
|
306
|
+
h(`Unsupported action type: ${s}`);
|
|
307
|
+
}
|
|
308
|
+
}
|
|
239
309
|
if (t.tagName === e.BACKGROUND)
|
|
240
310
|
return t.attributes;
|
|
241
311
|
}
|
|
242
|
-
|
|
312
|
+
const o = {
|
|
313
|
+
createElement: (t, r = {}, n = [], s = 0) => {
|
|
314
|
+
const i = Object.entries(r).filter(([u, p]) => p != null).map(([u, p]) => typeof p == "object" ? `${u}="${JSON.stringify(p).replace(/"/g, """)}"` : `${u}="${p}"`).join(" "), a = i ? ` ${i}` : "";
|
|
315
|
+
if (n.length === 0)
|
|
316
|
+
return ` <${t}${a} />`;
|
|
317
|
+
{
|
|
318
|
+
const u = n.map((p) => typeof p == "string" ? p.split(`
|
|
319
|
+
`).map((f) => f ? " " + f : "").join(`
|
|
320
|
+
`) : p).join(`
|
|
321
|
+
`);
|
|
322
|
+
return ` <${t}${a}>
|
|
323
|
+
${u}
|
|
324
|
+
</${t}>`;
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
createText: (t) => t
|
|
328
|
+
};
|
|
329
|
+
function b(t, r = 0) {
|
|
330
|
+
if (!t || typeof t != "object")
|
|
331
|
+
return "";
|
|
332
|
+
const { type: n, ...s } = t, { action: l, ...i } = s, a = l ? b(l, r + 1) : "";
|
|
333
|
+
switch (n) {
|
|
334
|
+
case "carousel":
|
|
335
|
+
const { contents: u, ...p } = i, f = u.map((N) => b(N, r + 1));
|
|
336
|
+
return a && f.unshift(a), o.createElement(e.CAROUSEL, p, f, r);
|
|
337
|
+
case "bubble":
|
|
338
|
+
const { header: y, hero: C, body: R, footer: B, ...L } = i, g = [];
|
|
339
|
+
return a && g.push(a), y && g.push(o.createElement(e.HEADER, {}, [b(y, r + 1)], r + 1)), C && g.push(o.createElement(e.HERO, {}, [b(C, r + 1)], r + 1)), R && g.push(o.createElement(e.BODY, {}, [b(R, r + 1)], r + 1)), B && g.push(o.createElement(e.FOOTER, {}, [b(B, r + 1)], r + 1)), o.createElement(e.BUBBLE, L, g, r);
|
|
340
|
+
case "box":
|
|
341
|
+
const { contents: w, background: d, ...O } = i;
|
|
342
|
+
O.layout || (O.layout = "vertical");
|
|
343
|
+
const T = [];
|
|
344
|
+
return a && T.push(a), d && T.push(b({ type: "background", ...d }, r + 1)), T.push(...(w || []).map((N) => b(N, r + 1))), o.createElement(e.BOX, O, T, r);
|
|
345
|
+
case "text":
|
|
346
|
+
const { text: U, contents: A, ...k } = i, x = A && A.length > 0 ? A.map((N) => b(N, r + 1)) : [U || ""];
|
|
347
|
+
return a && x.unshift(a), o.createElement(e.TEXT, k, x, r);
|
|
348
|
+
case "span":
|
|
349
|
+
const { text: D, ...X } = i, I = [D || ""];
|
|
350
|
+
return a && I.unshift(a), o.createElement(e.SPAN, X, I, r);
|
|
351
|
+
case "image":
|
|
352
|
+
const { ...$ } = i, G = a ? [a] : [];
|
|
353
|
+
return o.createElement(e.IMAGE, $, G, r);
|
|
354
|
+
case "video":
|
|
355
|
+
const { altContent: S, ...H } = i, v = S ? [b(S, r + 1)] : [];
|
|
356
|
+
return a && v.unshift(a), o.createElement(e.VIDEO, H, v, r);
|
|
357
|
+
case "icon":
|
|
358
|
+
const { ...F } = i, V = a ? [a] : [];
|
|
359
|
+
return o.createElement(e.ICON, F, V, r);
|
|
360
|
+
case "button":
|
|
361
|
+
const { ...M } = i, P = a ? [a] : [];
|
|
362
|
+
return o.createElement(e.BUTTON, M, P, r);
|
|
363
|
+
case "separator":
|
|
364
|
+
const K = a ? [a] : [];
|
|
365
|
+
return o.createElement(e.SEPARATOR, i, K, r);
|
|
366
|
+
case "filler":
|
|
367
|
+
const j = a ? [a] : [];
|
|
368
|
+
return o.createElement(e.FILLER, i, j, r);
|
|
369
|
+
// Action types
|
|
370
|
+
case "uri":
|
|
371
|
+
case "message":
|
|
372
|
+
case "postback":
|
|
373
|
+
case "datetimepicker":
|
|
374
|
+
case "camera":
|
|
375
|
+
case "cameraRoll":
|
|
376
|
+
case "location":
|
|
377
|
+
case "richmenuswitch":
|
|
378
|
+
case "clipboard":
|
|
379
|
+
return o.createElement(e.ACTION, { type: n, ...i }, [], r);
|
|
380
|
+
case "background":
|
|
381
|
+
return o.createElement(e.BACKGROUND, i, [], r);
|
|
382
|
+
default:
|
|
383
|
+
return "";
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
function nt(t) {
|
|
243
387
|
try {
|
|
244
|
-
return
|
|
245
|
-
} catch (
|
|
246
|
-
throw console.error("Error converting HTML to Flex Message:",
|
|
388
|
+
return _(t).map(m);
|
|
389
|
+
} catch (r) {
|
|
390
|
+
throw console.error("Error converting HTML to Flex Message:", r), r;
|
|
247
391
|
}
|
|
248
392
|
}
|
|
249
393
|
export {
|
|
250
|
-
|
|
394
|
+
b as convertJsonToHtml,
|
|
395
|
+
nt as default
|
|
251
396
|
};
|
|
@@ -1 +1,6 @@
|
|
|
1
|
-
(function(
|
|
1
|
+
(function(b,T){typeof exports=="object"&&typeof module<"u"?T(exports,require("htmlparser2")):typeof define=="function"&&define.amd?define(["exports","htmlparser2"],T):(b=typeof globalThis<"u"?globalThis:b||self,T(b.FlexHtmlRender={},b.htmlparser2))})(this,(function(b,T){"use strict";const E={ELEMENT:"element",TEXT:"text"},e={CAROUSEL:"carousel",BUBBLE:"bubble",HEADER:"header",HERO:"hero",BODY:"body",FOOTER:"footer",BOX:"box",TEXT:"text",SPAN:"span",IMAGE:"image",VIDEO:"video",ICON:"icon",SEPARATOR:"separator",BUTTON:"button",FILLER:"filler",SPACE:"space",STRONG:"strong",BASELINE:"baseline",ROW:"row",VERTICAL:"vertical",DIV:"div",ARTICLE:"article",ACTION:"action",BACKGROUND:"background"},U=[e.HEADER,e.HERO,e.BODY,e.FOOTER],k=[e.BOX,e.VERTICAL,e.ROW,e.BASELINE,e.DIV,e.ARTICLE],D=[e.TEXT,e.STRONG];function X(t){const r=Number(t);return isNaN(r)?t:r}function $(t){return t==="true"?!0:t==="false"?!1:t}function H(t){return[X,$].reduce((n,s)=>s(n),t)}function G(t){const r={type:"root",children:[]},n=[r],s=new T.Parser({onopentag(l,i){const a={type:E.ELEMENT,tagName:l,attributes:Object.fromEntries(Object.entries(i).map(([h,g])=>[h,H(g)])),children:[]};n[n.length-1].children.push(a),n.push(a)},ontext(l){const i=n[n.length-1];if(i.tagName==="span"){i.children.push({type:E.TEXT,content:l});return}l.trim()&&i.children.push({type:E.TEXT,content:l.replace(/\n/g,"").trim()})},onclosetag(){n.pop()},onerror(l){console.error("解析錯誤:",l)}},{xmlMode:!0,lowerCaseTags:!1,lowerCaseAttributeNames:!1});return s.write(t),s.end(),r.children}const p=t=>{throw Error(`[Flex Html Render]: ${t}`)},c={carousel:t=>({type:"carousel",contents:t}),bubble:(t={})=>({type:"bubble",...t}),box:(t,r={})=>({type:"box",contents:t,...r}),text:(t,r={})=>({type:"text",...typeof t=="string"?{text:t}:{contents:t},...r}),span:(t,r={})=>({type:"span",text:t,...r}),image:(t={})=>({type:"image",...t}),video:(t={})=>({type:"video",...t}),icon:(t={})=>({type:"icon",...t}),button:(t={})=>({type:"button",...t}),separator:(t={color:"#E0E3EA"})=>({type:"separator",...t}),filler:(t={})=>({type:"filler",...t}),uri:(t={})=>({type:"uri",altUri:t.altUri?{desktop:t.altUri}:void 0,...t}),postback:(t={})=>({type:"postback",...t}),message:(t={})=>({type:"message",...t}),datetimepicker:(t={})=>({type:"datetimepicker",...t}),camera:(t={})=>({type:"camera",...t}),cameraRoll:(t={})=>({type:"cameraRoll",...t}),location:(t={})=>({type:"location",...t}),richmenuswitch:(t={})=>({type:"richmenuswitch",...t}),clipboard:(t={})=>({type:"clipboard",...t})};function F(t){if(t.tagName!==e.BACKGROUND&&t.children&&t.children.length>0){const r=t.children.filter(n=>n.tagName===e.BACKGROUND);return r.length>1&&p("A box node can only allowed with one background node inside"),t.children=t.children.filter(n=>n.tagName!==e.BACKGROUND),r[0]}return null}function M(t){if(t.tagName!==e.ACTION&&t.children&&t.children.length>0){const r=t.children.filter(n=>n.tagName===e.ACTION);return r.length>1&&p("A node can only allowed with one action node inside"),t.children=t.children.filter(n=>n.tagName!==e.ACTION),r[0]}return null}function P(t){![...D,e.SPAN].includes(t.tagName)&&t.children?.some(l=>l.type===E.TEXT)&&(console.log(t),p("Span component only allowed inside Text component")),t.tagName===e.VIDEO&&t.children?.some(l=>![e.BOX,e.IMAGE].includes(l.tagName))&&p("Video component only allowed Box or Image as altContent child"),!k.includes(t.tagName)&&t.children?.some(l=>l.tagName===e.BACKGROUND)&&p("Only Box component allowed to have Background child")}function u(t){t.attributes=t.attributes||{},P(t);const r=M(t);r&&(t.attributes.action=u(r));const n=F(t);if(n&&(t.attributes.background=u(n)),t.type===E.TEXT)return c.span(t.content,t.attributes);if(t.tagName===e.BUBBLE){const s=t.children.find(m=>m.tagName===e.HEADER),l=t.children.find(m=>m.tagName===e.HERO),i=t.children.find(m=>m.tagName===e.BODY),a=t.children.find(m=>m.tagName===e.FOOTER);return c.bubble({...t.attributes,header:s?u(s):void 0,hero:l?u(l):void 0,body:i?u(i):void 0,footer:a?u(a):void 0})}if(t.tagName===e.CAROUSEL)return t.children.some(s=>s.tagName!==e.BUBBLE)&&p("Carousel can only have Bubble as children"),t.children.length>12&&p("Carousel can have maximum 12 bubbles"),c.carousel(t.children.map(u));if(U.includes(t.tagName))return t.children.length!==1&&p(`${t.tagName} should have exactly one child node`),u(t.children[0]);if(t.tagName===e.BUTTON)return t.attributes.action||p("Button component should contain an action node"),c.button(t.attributes);if(t.tagName===e.SPAN){t.children.length>1&&p("Span component can only have one text child");const s=t.children[0];if(s&&s.type===E.TEXT)return c.span(s.content,t.attributes);p(s?"Span component only allowed text child":"Span component can not be empty")}if(t.tagName===e.TEXT)return c.text(t.children.map(u),t.attributes);if(t.tagName===e.SEPARATOR)return c.separator(t.attributes);if(t.tagName===e.IMAGE)return c.image(t.attributes);if(t.tagName===e.VIDEO)return t.children.length!==1&&p("Video component should only has one Box or Image as altContent"),t.attributes.altContent=u(t.children[0]),c.video(t.attributes);if(t.tagName===e.ICON)return c.icon(t.attributes);if(t.tagName===e.FILLER)return c.filler(t.attributes);if(t.tagName===e.BOX||t.tagName===e.DIV||t.tagName===e.ARTICLE)return t.attributes.layout||(t.attributes.layout="vertical"),c.box(t.children.map(u),t.attributes);if(t.tagName===e.SPACE)return c.span(" ".repeat(t.attributes.size||1));if(t.tagName===e.STRONG)return t.tagName=e.TEXT,t.attributes.weight="bold",u(t);if(t.tagName===e.BASELINE)return t.tagName=e.BOX,t.attributes.layout="baseline",u(t);if(t.tagName===e.ROW)return t.tagName=e.BOX,t.attributes.layout="horizontal",u(t);if(t.tagName===e.VERTICAL)return t.tagName=e.BOX,t.attributes.layout="vertical",u(t);if(t.tagName===e.ACTION){const s=t.attributes.type;switch(s){case"uri":return c.uri(t.attributes);case"postback":return c.postback(t.attributes);case"message":return c.message(t.attributes);case"datetimepicker":return c.datetimepicker(t.attributes);case"camera":return c.camera(t.attributes);case"cameraRoll":return c.cameraRoll(t.attributes);case"location":return c.location(t.attributes);case"richmenuswitch":return c.richmenuswitch(t.attributes);case"clipboard":return c.clipboard(t.attributes);default:p(`Unsupported action type: ${s}`)}}if(t.tagName===e.BACKGROUND)return t.attributes}const o={createElement:(t,r={},n=[],s=0)=>{const i=Object.entries(r).filter(([m,h])=>h!=null).map(([m,h])=>typeof h=="object"?`${m}="${JSON.stringify(h).replace(/"/g,""")}"`:`${m}="${h}"`).join(" "),a=i?` ${i}`:"";if(n.length===0)return` <${t}${a} />`;{const m=n.map(h=>typeof h=="string"?h.split(`
|
|
2
|
+
`).map(g=>g?" "+g:"").join(`
|
|
3
|
+
`):h).join(`
|
|
4
|
+
`);return` <${t}${a}>
|
|
5
|
+
${m}
|
|
6
|
+
</${t}>`}},createText:t=>t};function f(t,r=0){if(!t||typeof t!="object")return"";const{type:n,...s}=t,{action:l,...i}=s,a=l?f(l,r+1):"";switch(n){case"carousel":const{contents:m,...h}=i,g=m.map(O=>f(O,r+1));return a&&g.unshift(a),o.createElement(e.CAROUSEL,h,g,r);case"bubble":const{header:d,hero:R,body:B,footer:x,...j}=i,N=[];return a&&N.push(a),d&&N.push(o.createElement(e.HEADER,{},[f(d,r+1)],r+1)),R&&N.push(o.createElement(e.HERO,{},[f(R,r+1)],r+1)),B&&N.push(o.createElement(e.BODY,{},[f(B,r+1)],r+1)),x&&N.push(o.createElement(e.FOOTER,{},[f(x,r+1)],r+1)),o.createElement(e.BUBBLE,j,N,r);case"box":const{contents:K,background:S,...A}=i;A.layout||(A.layout="vertical");const y=[];return a&&y.push(a),S&&y.push(f({type:"background",...S},r+1)),y.push(...(K||[]).map(O=>f(O,r+1))),o.createElement(e.BOX,A,y,r);case"text":const{text:J,contents:C,...Y}=i,I=C&&C.length>0?C.map(O=>f(O,r+1)):[J||""];return a&&I.unshift(a),o.createElement(e.TEXT,Y,I,r);case"span":const{text:W,...q}=i,v=[W||""];return a&&v.unshift(a),o.createElement(e.SPAN,q,v,r);case"image":const{...z}=i,_=a?[a]:[];return o.createElement(e.IMAGE,z,_,r);case"video":const{altContent:L,...Q}=i,w=L?[f(L,r+1)]:[];return a&&w.unshift(a),o.createElement(e.VIDEO,Q,w,r);case"icon":const{...Z}=i,tt=a?[a]:[];return o.createElement(e.ICON,Z,tt,r);case"button":const{...et}=i,rt=a?[a]:[];return o.createElement(e.BUTTON,et,rt,r);case"separator":const at=a?[a]:[];return o.createElement(e.SEPARATOR,i,at,r);case"filler":const nt=a?[a]:[];return o.createElement(e.FILLER,i,nt,r);case"uri":case"message":case"postback":case"datetimepicker":case"camera":case"cameraRoll":case"location":case"richmenuswitch":case"clipboard":return o.createElement(e.ACTION,{type:n,...i},[],r);case"background":return o.createElement(e.BACKGROUND,i,[],r);default:return""}}function V(t){try{return G(t).map(u)}catch(r){throw console.error("Error converting HTML to Flex Message:",r),r}}b.convertJsonToHtml=f,b.default=V,Object.defineProperties(b,{__esModule:{value:!0},[Symbol.toStringTag]:{value:"Module"}})}));
|
package/index.d.ts
CHANGED
|
@@ -3,9 +3,4 @@
|
|
|
3
3
|
* @param htmlString - Flex Message 對應的 HTML 字串
|
|
4
4
|
* @returns Flex Message JSON 物件陣列
|
|
5
5
|
*/
|
|
6
|
-
export default function convertHtmlToFlexMessage(htmlString: string): any[];
|
|
7
|
-
|
|
8
|
-
/**
|
|
9
|
-
* 官方範例 HTML 字串
|
|
10
|
-
*/
|
|
11
|
-
export const officialDemoString: string;
|
|
6
|
+
export default function convertHtmlToFlexMessage(htmlString: string): any[];
|