line-flex-message-renderer 0.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/LICENSE +21 -0
- package/README.md +126 -0
- package/dist/index.cjs +472 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +153 -0
- package/dist/index.d.ts +153 -0
- package/dist/index.js +468 -0
- package/dist/index.js.map +1 -0
- package/package.json +75 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 kanketsu-jp
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# line-flex-message-renderer
|
|
2
|
+
|
|
3
|
+
React component to render LINE Flex Message JSON as a visual preview — pixel-accurate to the LINE app.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install line-flex-message-renderer
|
|
9
|
+
# or
|
|
10
|
+
pnpm add line-flex-message-renderer
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```tsx
|
|
16
|
+
import { FlexMessagePreview, LineChatFrame } from 'line-flex-message-renderer';
|
|
17
|
+
|
|
18
|
+
const flexJson = {
|
|
19
|
+
type: 'bubble',
|
|
20
|
+
body: {
|
|
21
|
+
type: 'box',
|
|
22
|
+
layout: 'vertical',
|
|
23
|
+
contents: [
|
|
24
|
+
{ type: 'text', text: 'Hello, World!', weight: 'bold', size: 'xl' },
|
|
25
|
+
],
|
|
26
|
+
},
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
function App() {
|
|
30
|
+
return (
|
|
31
|
+
<LineChatFrame>
|
|
32
|
+
<FlexMessagePreview json={flexJson} />
|
|
33
|
+
</LineChatFrame>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
## Components
|
|
39
|
+
|
|
40
|
+
### FlexMessagePreview
|
|
41
|
+
|
|
42
|
+
Renders a Flex Message bubble or carousel.
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
<FlexMessagePreview json={flexBubbleOrCarousel} className="my-class" style={{ margin: 8 }} />
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
| Prop | Type | Description |
|
|
49
|
+
|------|------|-------------|
|
|
50
|
+
| `json` | `FlexContainer` | Flex Message JSON (bubble or carousel) |
|
|
51
|
+
| `className` | `string?` | Additional CSS class |
|
|
52
|
+
| `style` | `CSSProperties?` | Additional inline styles |
|
|
53
|
+
|
|
54
|
+
### LineChatFrame
|
|
55
|
+
|
|
56
|
+
Wraps content in a LINE chat UI frame.
|
|
57
|
+
|
|
58
|
+
```tsx
|
|
59
|
+
<LineChatFrame accountName="My Bot" width={375}>
|
|
60
|
+
<FlexMessagePreview json={flexJson} />
|
|
61
|
+
</LineChatFrame>
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
| Prop | Type | Default | Description |
|
|
65
|
+
|------|------|---------|-------------|
|
|
66
|
+
| `children` | `ReactNode` | — | Content to display |
|
|
67
|
+
| `accountName` | `string?` | `"トーク"` | Header title |
|
|
68
|
+
| `avatarUrl` | `string?` | — | Avatar image URL (defaults to green BOT icon) |
|
|
69
|
+
| `width` | `number?` | `375` | Frame width in pixels |
|
|
70
|
+
|
|
71
|
+
### LineTextBubble
|
|
72
|
+
|
|
73
|
+
Renders a plain text message bubble.
|
|
74
|
+
|
|
75
|
+
```tsx
|
|
76
|
+
<LineTextBubble text="Hello!" />
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## Supported Flex Message Components
|
|
80
|
+
|
|
81
|
+
- **box** — vertical / horizontal / baseline layouts
|
|
82
|
+
- **text** — with span support, wrapping, max lines
|
|
83
|
+
- **image** — aspect ratio, aspect mode (cover/fit)
|
|
84
|
+
- **button** — primary / secondary / link styles
|
|
85
|
+
- **separator** — horizontal line
|
|
86
|
+
- **spacer** — flexible space
|
|
87
|
+
- **filler** — flex-grow space
|
|
88
|
+
- **icon** — inline icon image
|
|
89
|
+
|
|
90
|
+
## Bubble Sizes
|
|
91
|
+
|
|
92
|
+
| Size | Width |
|
|
93
|
+
|------|-------|
|
|
94
|
+
| nano | 120px |
|
|
95
|
+
| micro | 150px |
|
|
96
|
+
| kilo | 230px |
|
|
97
|
+
| mega (default) | 300px |
|
|
98
|
+
| giga | 386px |
|
|
99
|
+
|
|
100
|
+
## Carousel Support
|
|
101
|
+
|
|
102
|
+
Pass a carousel container to render multiple bubbles with horizontal scrolling:
|
|
103
|
+
|
|
104
|
+
```tsx
|
|
105
|
+
<FlexMessagePreview
|
|
106
|
+
json={{
|
|
107
|
+
type: 'carousel',
|
|
108
|
+
contents: [bubble1, bubble2, bubble3],
|
|
109
|
+
}}
|
|
110
|
+
/>
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Development
|
|
114
|
+
|
|
115
|
+
```bash
|
|
116
|
+
pnpm install
|
|
117
|
+
pnpm storybook # Start Storybook dev server
|
|
118
|
+
pnpm test # Run tests
|
|
119
|
+
pnpm build # Build for production
|
|
120
|
+
pnpm typecheck # Type check
|
|
121
|
+
pnpm lint # Lint
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## License
|
|
125
|
+
|
|
126
|
+
MIT
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,472 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
4
|
+
|
|
5
|
+
// src/constants.ts
|
|
6
|
+
var TEXT_SIZE = {
|
|
7
|
+
xxs: "11px",
|
|
8
|
+
xs: "12px",
|
|
9
|
+
sm: "13px",
|
|
10
|
+
md: "14px",
|
|
11
|
+
lg: "16px",
|
|
12
|
+
xl: "18px",
|
|
13
|
+
xxl: "22px",
|
|
14
|
+
"3xl": "26px",
|
|
15
|
+
"4xl": "30px",
|
|
16
|
+
"5xl": "36px"
|
|
17
|
+
};
|
|
18
|
+
var SPACING = {
|
|
19
|
+
none: "0px",
|
|
20
|
+
xs: "2px",
|
|
21
|
+
sm: "4px",
|
|
22
|
+
md: "8px",
|
|
23
|
+
lg: "12px",
|
|
24
|
+
xl: "16px",
|
|
25
|
+
xxl: "20px"
|
|
26
|
+
};
|
|
27
|
+
var IMAGE_SIZE = {
|
|
28
|
+
xxs: "40px",
|
|
29
|
+
xs: "60px",
|
|
30
|
+
sm: "80px",
|
|
31
|
+
md: "100px",
|
|
32
|
+
lg: "120px",
|
|
33
|
+
xl: "150px",
|
|
34
|
+
xxl: "180px",
|
|
35
|
+
"3xl": "220px",
|
|
36
|
+
"4xl": "260px",
|
|
37
|
+
"5xl": "300px",
|
|
38
|
+
full: "100%"
|
|
39
|
+
};
|
|
40
|
+
var ICON_SIZE = {
|
|
41
|
+
xxs: "14px",
|
|
42
|
+
xs: "16px",
|
|
43
|
+
sm: "18px",
|
|
44
|
+
md: "20px",
|
|
45
|
+
lg: "24px",
|
|
46
|
+
xl: "28px",
|
|
47
|
+
xxl: "32px"
|
|
48
|
+
};
|
|
49
|
+
var BUBBLE_WIDTH = {
|
|
50
|
+
nano: "120px",
|
|
51
|
+
micro: "150px",
|
|
52
|
+
kilo: "230px",
|
|
53
|
+
mega: "300px",
|
|
54
|
+
giga: "386px"
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/utils.ts
|
|
58
|
+
function resolveSize(value, map, fallback) {
|
|
59
|
+
if (!value) return fallback;
|
|
60
|
+
return map[value] ?? value;
|
|
61
|
+
}
|
|
62
|
+
function FlexButtonComponent({ component }) {
|
|
63
|
+
const isPrimary = component.style === "primary";
|
|
64
|
+
const isLink = component.style === "link";
|
|
65
|
+
const height = component.height === "sm" ? "40px" : "52px";
|
|
66
|
+
const style = {
|
|
67
|
+
width: "100%",
|
|
68
|
+
height,
|
|
69
|
+
display: "flex",
|
|
70
|
+
alignItems: "center",
|
|
71
|
+
justifyContent: "center",
|
|
72
|
+
borderRadius: isPrimary || component.style === "secondary" ? "9999px" : void 0,
|
|
73
|
+
backgroundColor: isPrimary ? component.color ?? "#17C950" : isLink ? "transparent" : "#EFEFEF",
|
|
74
|
+
color: isPrimary ? "#ffffff" : isLink ? component.color ?? "#42659A" : "#111111",
|
|
75
|
+
fontSize: TEXT_SIZE.sm,
|
|
76
|
+
fontWeight: 600,
|
|
77
|
+
border: "none",
|
|
78
|
+
cursor: "pointer",
|
|
79
|
+
marginTop: resolveSize(component.margin, SPACING, void 0),
|
|
80
|
+
flex: component.flex !== void 0 ? `${component.flex} 0 0%` : void 0
|
|
81
|
+
};
|
|
82
|
+
return /* @__PURE__ */ jsxRuntime.jsx("button", { type: "button", style, children: component.action.label });
|
|
83
|
+
}
|
|
84
|
+
function FlexIconComponent({ component }) {
|
|
85
|
+
const size = resolveSize(component.size, ICON_SIZE, ICON_SIZE.md);
|
|
86
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
87
|
+
"img",
|
|
88
|
+
{
|
|
89
|
+
src: component.url,
|
|
90
|
+
alt: "",
|
|
91
|
+
style: { width: size, height: size, objectFit: "contain" }
|
|
92
|
+
}
|
|
93
|
+
);
|
|
94
|
+
}
|
|
95
|
+
function FlexImageComponent({ component }) {
|
|
96
|
+
const [w, h] = (component.aspectRatio ?? "1:1").split(":").map(Number);
|
|
97
|
+
const style = {
|
|
98
|
+
width: component.size === "full" ? "100%" : resolveSize(component.size, IMAGE_SIZE, IMAGE_SIZE.md),
|
|
99
|
+
marginTop: resolveSize(component.margin, SPACING, void 0),
|
|
100
|
+
flex: component.flex !== void 0 ? `${component.flex} 0 0%` : void 0,
|
|
101
|
+
backgroundColor: component.backgroundColor
|
|
102
|
+
};
|
|
103
|
+
const innerStyle = {
|
|
104
|
+
width: "100%",
|
|
105
|
+
paddingTop: `${h / w * 100}%`,
|
|
106
|
+
position: "relative",
|
|
107
|
+
overflow: "hidden"
|
|
108
|
+
};
|
|
109
|
+
const imgStyle = {
|
|
110
|
+
position: "absolute",
|
|
111
|
+
top: 0,
|
|
112
|
+
left: 0,
|
|
113
|
+
width: "100%",
|
|
114
|
+
height: "100%",
|
|
115
|
+
objectFit: component.aspectMode === "fit" ? "contain" : "cover"
|
|
116
|
+
};
|
|
117
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style, children: /* @__PURE__ */ jsxRuntime.jsx("div", { style: innerStyle, children: /* @__PURE__ */ jsxRuntime.jsx("img", { src: component.url, alt: "", style: imgStyle }) }) });
|
|
118
|
+
}
|
|
119
|
+
function FlexSeparatorComponent({
|
|
120
|
+
component
|
|
121
|
+
}) {
|
|
122
|
+
const style = {
|
|
123
|
+
borderTop: `1px solid ${component.color ?? "#E5E5E5"}`,
|
|
124
|
+
marginTop: resolveSize(component.margin, SPACING, void 0),
|
|
125
|
+
width: "100%"
|
|
126
|
+
};
|
|
127
|
+
return /* @__PURE__ */ jsxRuntime.jsx("hr", { style });
|
|
128
|
+
}
|
|
129
|
+
function FlexSpanComponent({ span }) {
|
|
130
|
+
const style = {
|
|
131
|
+
fontSize: resolveSize(span.size, TEXT_SIZE, "inherit"),
|
|
132
|
+
color: span.color,
|
|
133
|
+
fontWeight: span.weight === "bold" ? 700 : void 0,
|
|
134
|
+
textDecoration: span.decoration !== "none" ? span.decoration : void 0,
|
|
135
|
+
fontStyle: span.style
|
|
136
|
+
};
|
|
137
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { style, children: span.text });
|
|
138
|
+
}
|
|
139
|
+
var alignMap = {
|
|
140
|
+
start: "left",
|
|
141
|
+
center: "center",
|
|
142
|
+
end: "right"
|
|
143
|
+
};
|
|
144
|
+
var gravityMap = {
|
|
145
|
+
top: "flex-start",
|
|
146
|
+
center: "center",
|
|
147
|
+
bottom: "flex-end"
|
|
148
|
+
};
|
|
149
|
+
function FlexTextComponent({ component }) {
|
|
150
|
+
const hasSpans = component.contents && component.contents.length > 0;
|
|
151
|
+
const fontSize = resolveSize(component.size, TEXT_SIZE, TEXT_SIZE.md);
|
|
152
|
+
const style = {
|
|
153
|
+
fontSize,
|
|
154
|
+
color: component.color,
|
|
155
|
+
fontWeight: component.weight === "bold" ? 700 : 400,
|
|
156
|
+
textAlign: alignMap[component.align ?? "start"] ?? "left",
|
|
157
|
+
marginTop: resolveSize(component.margin, SPACING, void 0),
|
|
158
|
+
flex: component.flex !== void 0 ? `${component.flex} 0 0%` : void 0,
|
|
159
|
+
alignSelf: component.gravity ? gravityMap[component.gravity] : void 0,
|
|
160
|
+
textDecoration: component.decoration !== "none" ? component.decoration : void 0,
|
|
161
|
+
fontStyle: component.style,
|
|
162
|
+
lineHeight: component.lineSpacing ?? 1.4,
|
|
163
|
+
...!component.wrap ? {
|
|
164
|
+
overflow: "hidden",
|
|
165
|
+
textOverflow: "ellipsis",
|
|
166
|
+
whiteSpace: "nowrap"
|
|
167
|
+
} : {
|
|
168
|
+
whiteSpace: "pre-wrap",
|
|
169
|
+
wordBreak: "break-word"
|
|
170
|
+
},
|
|
171
|
+
...component.maxLines && component.wrap ? {
|
|
172
|
+
display: "-webkit-box",
|
|
173
|
+
WebkitLineClamp: component.maxLines,
|
|
174
|
+
WebkitBoxOrient: "vertical",
|
|
175
|
+
overflow: "hidden"
|
|
176
|
+
} : {}
|
|
177
|
+
};
|
|
178
|
+
if (hasSpans) {
|
|
179
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { style, children: component.contents?.map((span, i) => /* @__PURE__ */ jsxRuntime.jsx(FlexSpanComponent, { span }, i)) });
|
|
180
|
+
}
|
|
181
|
+
return /* @__PURE__ */ jsxRuntime.jsx("p", { style, children: component.text });
|
|
182
|
+
}
|
|
183
|
+
function FlexComponentRenderer({
|
|
184
|
+
component
|
|
185
|
+
}) {
|
|
186
|
+
switch (component.type) {
|
|
187
|
+
case "box":
|
|
188
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FlexBoxComponent, { component });
|
|
189
|
+
case "text":
|
|
190
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FlexTextComponent, { component });
|
|
191
|
+
case "image":
|
|
192
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FlexImageComponent, { component });
|
|
193
|
+
case "button":
|
|
194
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FlexButtonComponent, { component });
|
|
195
|
+
case "separator":
|
|
196
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FlexSeparatorComponent, { component });
|
|
197
|
+
case "spacer":
|
|
198
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
199
|
+
"div",
|
|
200
|
+
{
|
|
201
|
+
style: {
|
|
202
|
+
flexGrow: 1,
|
|
203
|
+
height: resolveSize(component.size, SPACING, "0px")
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
);
|
|
207
|
+
case "filler":
|
|
208
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style: { flexGrow: component.flex ?? 1 } });
|
|
209
|
+
case "icon":
|
|
210
|
+
return /* @__PURE__ */ jsxRuntime.jsx(FlexIconComponent, { component });
|
|
211
|
+
default:
|
|
212
|
+
return null;
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
function FlexBoxComponent({ component }) {
|
|
216
|
+
const isVertical = component.layout === "vertical";
|
|
217
|
+
const isBaseline = component.layout === "baseline";
|
|
218
|
+
const style = {
|
|
219
|
+
display: "flex",
|
|
220
|
+
flexDirection: isVertical ? "column" : "row",
|
|
221
|
+
alignItems: isBaseline ? "baseline" : component.alignItems ?? (isVertical ? "stretch" : "center"),
|
|
222
|
+
justifyContent: component.justifyContent,
|
|
223
|
+
gap: resolveSize(component.spacing, SPACING, void 0),
|
|
224
|
+
marginTop: resolveSize(component.margin, SPACING, void 0),
|
|
225
|
+
padding: resolveSize(component.paddingAll, SPACING, void 0),
|
|
226
|
+
paddingTop: resolveSize(component.paddingTop, SPACING, void 0),
|
|
227
|
+
paddingBottom: resolveSize(component.paddingBottom, SPACING, void 0),
|
|
228
|
+
paddingLeft: resolveSize(component.paddingStart, SPACING, void 0),
|
|
229
|
+
paddingRight: resolveSize(component.paddingEnd, SPACING, void 0),
|
|
230
|
+
backgroundColor: component.backgroundColor,
|
|
231
|
+
borderRadius: component.cornerRadius,
|
|
232
|
+
borderColor: component.borderColor,
|
|
233
|
+
borderWidth: component.borderWidth,
|
|
234
|
+
borderStyle: component.borderWidth ? "solid" : void 0,
|
|
235
|
+
flex: component.flex !== void 0 ? `${component.flex} 0 0%` : void 0
|
|
236
|
+
};
|
|
237
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { style, children: component.contents.map((child, i) => /* @__PURE__ */ jsxRuntime.jsx(FlexComponentRenderer, { component: child }, i)) });
|
|
238
|
+
}
|
|
239
|
+
function BubbleRenderer({
|
|
240
|
+
json,
|
|
241
|
+
className,
|
|
242
|
+
style: extraStyle
|
|
243
|
+
}) {
|
|
244
|
+
const width = BUBBLE_WIDTH[json.size ?? "mega"];
|
|
245
|
+
const headerBg = json.styles?.header?.backgroundColor;
|
|
246
|
+
const bodyBg = json.styles?.body?.backgroundColor;
|
|
247
|
+
const footerBg = json.styles?.footer?.backgroundColor;
|
|
248
|
+
const footerSep = json.styles?.footer?.separator;
|
|
249
|
+
const bodySep = json.styles?.body?.separator;
|
|
250
|
+
const heroSep = json.styles?.hero?.separator;
|
|
251
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
252
|
+
"div",
|
|
253
|
+
{
|
|
254
|
+
className,
|
|
255
|
+
style: {
|
|
256
|
+
width,
|
|
257
|
+
maxWidth: "100%",
|
|
258
|
+
borderRadius: "16px",
|
|
259
|
+
overflow: "hidden",
|
|
260
|
+
backgroundColor: "#ffffff",
|
|
261
|
+
boxShadow: "0 1px 6px rgba(0,0,0,0.12)",
|
|
262
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Helvetica Neue", "Segoe UI", Arial, sans-serif',
|
|
263
|
+
...extraStyle
|
|
264
|
+
},
|
|
265
|
+
children: [
|
|
266
|
+
json.header && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "16px 16px 0", backgroundColor: headerBg }, children: /* @__PURE__ */ jsxRuntime.jsx(FlexBoxComponent, { component: json.header }) }),
|
|
267
|
+
heroSep && /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { borderTop: "1px solid #E5E5E5", margin: 0 } }),
|
|
268
|
+
json.hero && /* @__PURE__ */ jsxRuntime.jsx(FlexImageComponent, { component: json.hero }),
|
|
269
|
+
bodySep && /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { borderTop: "1px solid #E5E5E5", margin: 0 } }),
|
|
270
|
+
json.body && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "16px", backgroundColor: bodyBg }, children: /* @__PURE__ */ jsxRuntime.jsx(FlexBoxComponent, { component: json.body }) }),
|
|
271
|
+
footerSep && /* @__PURE__ */ jsxRuntime.jsx("hr", { style: { borderTop: "1px solid #E5E5E5", margin: 0 } }),
|
|
272
|
+
json.footer && /* @__PURE__ */ jsxRuntime.jsx("div", { style: { padding: "8px 16px 16px", backgroundColor: footerBg }, children: /* @__PURE__ */ jsxRuntime.jsx(FlexBoxComponent, { component: json.footer }) })
|
|
273
|
+
]
|
|
274
|
+
}
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
function FlexMessagePreview({
|
|
278
|
+
json,
|
|
279
|
+
className,
|
|
280
|
+
style
|
|
281
|
+
}) {
|
|
282
|
+
if (json.type === "carousel") {
|
|
283
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
284
|
+
"div",
|
|
285
|
+
{
|
|
286
|
+
className,
|
|
287
|
+
style: {
|
|
288
|
+
display: "flex",
|
|
289
|
+
gap: 8,
|
|
290
|
+
overflowX: "auto",
|
|
291
|
+
scrollSnapType: "x mandatory",
|
|
292
|
+
...style
|
|
293
|
+
},
|
|
294
|
+
children: json.contents.map((bubble, i) => /* @__PURE__ */ jsxRuntime.jsx("div", { style: { scrollSnapAlign: "start", flexShrink: 0 }, children: /* @__PURE__ */ jsxRuntime.jsx(BubbleRenderer, { json: bubble }) }, i))
|
|
295
|
+
}
|
|
296
|
+
);
|
|
297
|
+
}
|
|
298
|
+
return /* @__PURE__ */ jsxRuntime.jsx(BubbleRenderer, { json, className, style });
|
|
299
|
+
}
|
|
300
|
+
function DefaultAvatar() {
|
|
301
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
302
|
+
"div",
|
|
303
|
+
{
|
|
304
|
+
style: {
|
|
305
|
+
width: 36,
|
|
306
|
+
height: 36,
|
|
307
|
+
borderRadius: "50%",
|
|
308
|
+
backgroundColor: "#00B900",
|
|
309
|
+
display: "flex",
|
|
310
|
+
alignItems: "center",
|
|
311
|
+
justifyContent: "center",
|
|
312
|
+
flexShrink: 0,
|
|
313
|
+
color: "#fff",
|
|
314
|
+
fontSize: 11,
|
|
315
|
+
fontWeight: 700
|
|
316
|
+
},
|
|
317
|
+
children: "BOT"
|
|
318
|
+
}
|
|
319
|
+
);
|
|
320
|
+
}
|
|
321
|
+
function LineChatFrame({
|
|
322
|
+
children,
|
|
323
|
+
accountName = "\u30C8\u30FC\u30AF",
|
|
324
|
+
avatarUrl,
|
|
325
|
+
width = 375
|
|
326
|
+
}) {
|
|
327
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(
|
|
328
|
+
"div",
|
|
329
|
+
{
|
|
330
|
+
style: {
|
|
331
|
+
width,
|
|
332
|
+
minHeight: 500,
|
|
333
|
+
borderRadius: 12,
|
|
334
|
+
overflow: "hidden",
|
|
335
|
+
boxShadow: "0 4px 24px rgba(0,0,0,0.18)",
|
|
336
|
+
display: "flex",
|
|
337
|
+
flexDirection: "column",
|
|
338
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Helvetica Neue", "Segoe UI", Arial, sans-serif'
|
|
339
|
+
},
|
|
340
|
+
children: [
|
|
341
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
342
|
+
"div",
|
|
343
|
+
{
|
|
344
|
+
style: {
|
|
345
|
+
backgroundColor: "#00B900",
|
|
346
|
+
color: "#fff",
|
|
347
|
+
padding: "12px 16px",
|
|
348
|
+
fontSize: 16,
|
|
349
|
+
fontWeight: 700,
|
|
350
|
+
display: "flex",
|
|
351
|
+
alignItems: "center",
|
|
352
|
+
gap: 8
|
|
353
|
+
},
|
|
354
|
+
children: [
|
|
355
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
356
|
+
"svg",
|
|
357
|
+
{
|
|
358
|
+
width: "18",
|
|
359
|
+
height: "18",
|
|
360
|
+
viewBox: "0 0 24 24",
|
|
361
|
+
fill: "none",
|
|
362
|
+
stroke: "currentColor",
|
|
363
|
+
strokeWidth: "2.5",
|
|
364
|
+
strokeLinecap: "round",
|
|
365
|
+
strokeLinejoin: "round",
|
|
366
|
+
role: "img",
|
|
367
|
+
"aria-label": "Back",
|
|
368
|
+
children: /* @__PURE__ */ jsxRuntime.jsx("path", { d: "M15 18l-6-6 6-6" })
|
|
369
|
+
}
|
|
370
|
+
),
|
|
371
|
+
/* @__PURE__ */ jsxRuntime.jsx("span", { children: accountName })
|
|
372
|
+
]
|
|
373
|
+
}
|
|
374
|
+
),
|
|
375
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
376
|
+
"div",
|
|
377
|
+
{
|
|
378
|
+
style: {
|
|
379
|
+
backgroundColor: "#7B9EB0",
|
|
380
|
+
flex: 1,
|
|
381
|
+
padding: "16px 12px",
|
|
382
|
+
display: "flex",
|
|
383
|
+
flexDirection: "column",
|
|
384
|
+
gap: 8
|
|
385
|
+
},
|
|
386
|
+
children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
387
|
+
"div",
|
|
388
|
+
{
|
|
389
|
+
style: {
|
|
390
|
+
display: "flex",
|
|
391
|
+
alignItems: "flex-end",
|
|
392
|
+
gap: 8
|
|
393
|
+
},
|
|
394
|
+
children: [
|
|
395
|
+
avatarUrl ? /* @__PURE__ */ jsxRuntime.jsx(
|
|
396
|
+
"img",
|
|
397
|
+
{
|
|
398
|
+
src: avatarUrl,
|
|
399
|
+
alt: "",
|
|
400
|
+
style: {
|
|
401
|
+
width: 36,
|
|
402
|
+
height: 36,
|
|
403
|
+
borderRadius: "50%",
|
|
404
|
+
objectFit: "cover",
|
|
405
|
+
flexShrink: 0
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
) : /* @__PURE__ */ jsxRuntime.jsx(DefaultAvatar, {}),
|
|
409
|
+
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { maxWidth: "calc(100% - 52px)" }, children })
|
|
410
|
+
]
|
|
411
|
+
}
|
|
412
|
+
)
|
|
413
|
+
}
|
|
414
|
+
),
|
|
415
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
416
|
+
"div",
|
|
417
|
+
{
|
|
418
|
+
style: {
|
|
419
|
+
backgroundColor: "#F7F7F7",
|
|
420
|
+
padding: "10px 12px",
|
|
421
|
+
display: "flex",
|
|
422
|
+
alignItems: "center",
|
|
423
|
+
gap: 8,
|
|
424
|
+
borderTop: "1px solid #E0E0E0"
|
|
425
|
+
},
|
|
426
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(
|
|
427
|
+
"div",
|
|
428
|
+
{
|
|
429
|
+
style: {
|
|
430
|
+
flex: 1,
|
|
431
|
+
backgroundColor: "#fff",
|
|
432
|
+
borderRadius: 20,
|
|
433
|
+
padding: "8px 14px",
|
|
434
|
+
fontSize: 14,
|
|
435
|
+
color: "#999"
|
|
436
|
+
},
|
|
437
|
+
children: "Aa"
|
|
438
|
+
}
|
|
439
|
+
)
|
|
440
|
+
}
|
|
441
|
+
)
|
|
442
|
+
]
|
|
443
|
+
}
|
|
444
|
+
);
|
|
445
|
+
}
|
|
446
|
+
function LineTextBubble({ text }) {
|
|
447
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
448
|
+
"div",
|
|
449
|
+
{
|
|
450
|
+
style: {
|
|
451
|
+
backgroundColor: "#ffffff",
|
|
452
|
+
borderRadius: "16px 16px 16px 4px",
|
|
453
|
+
padding: "10px 14px",
|
|
454
|
+
fontSize: 14,
|
|
455
|
+
lineHeight: 1.5,
|
|
456
|
+
color: "#111111",
|
|
457
|
+
whiteSpace: "pre-wrap",
|
|
458
|
+
wordBreak: "break-word",
|
|
459
|
+
maxWidth: 260,
|
|
460
|
+
boxShadow: "0 1px 3px rgba(0,0,0,0.08)",
|
|
461
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Helvetica Neue", "Segoe UI", Arial, sans-serif'
|
|
462
|
+
},
|
|
463
|
+
children: text
|
|
464
|
+
}
|
|
465
|
+
);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
exports.FlexMessagePreview = FlexMessagePreview;
|
|
469
|
+
exports.LineChatFrame = LineChatFrame;
|
|
470
|
+
exports.LineTextBubble = LineTextBubble;
|
|
471
|
+
//# sourceMappingURL=index.cjs.map
|
|
472
|
+
//# sourceMappingURL=index.cjs.map
|