| import { attachFooterText } from "./common.js"; |
| import type { |
| Action, |
| CardAction, |
| FlexBox, |
| FlexBubble, |
| FlexButton, |
| FlexCarousel, |
| FlexComponent, |
| FlexImage, |
| FlexText, |
| ListItem, |
| } from "./types.js"; |
|
|
| |
| |
| |
| |
| |
| |
| export function createInfoCard(title: string, body: string, footer?: string): FlexBubble { |
| const bubble: FlexBubble = { |
| type: "bubble", |
| size: "mega", |
| body: { |
| type: "box", |
| layout: "vertical", |
| contents: [ |
| |
| { |
| type: "box", |
| layout: "horizontal", |
| contents: [ |
| { |
| type: "box", |
| layout: "vertical", |
| contents: [], |
| width: "4px", |
| backgroundColor: "#06C755", |
| cornerRadius: "2px", |
| } as FlexBox, |
| { |
| type: "text", |
| text: title, |
| weight: "bold", |
| size: "xl", |
| color: "#111111", |
| wrap: true, |
| flex: 1, |
| margin: "lg", |
| } as FlexText, |
| ], |
| } as FlexBox, |
| |
| { |
| type: "box", |
| layout: "vertical", |
| contents: [ |
| { |
| type: "text", |
| text: body, |
| size: "md", |
| color: "#444444", |
| wrap: true, |
| lineSpacing: "6px", |
| } as FlexText, |
| ], |
| margin: "xl", |
| paddingAll: "lg", |
| backgroundColor: "#F8F9FA", |
| cornerRadius: "lg", |
| } as FlexBox, |
| ], |
| paddingAll: "xl", |
| backgroundColor: "#FFFFFF", |
| }, |
| }; |
|
|
| if (footer) { |
| attachFooterText(bubble, footer); |
| } |
|
|
| return bubble; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function createListCard(title: string, items: ListItem[]): FlexBubble { |
| const itemContents: FlexComponent[] = items.slice(0, 8).map((item, index) => { |
| const itemContents: FlexComponent[] = [ |
| { |
| type: "text", |
| text: item.title, |
| size: "md", |
| weight: "bold", |
| color: "#1a1a1a", |
| wrap: true, |
| } as FlexText, |
| ]; |
|
|
| if (item.subtitle) { |
| itemContents.push({ |
| type: "text", |
| text: item.subtitle, |
| size: "sm", |
| color: "#888888", |
| wrap: true, |
| margin: "xs", |
| } as FlexText); |
| } |
|
|
| const itemBox: FlexBox = { |
| type: "box", |
| layout: "horizontal", |
| contents: [ |
| |
| { |
| type: "box", |
| layout: "vertical", |
| contents: [ |
| { |
| type: "box", |
| layout: "vertical", |
| contents: [], |
| width: "8px", |
| height: "8px", |
| backgroundColor: index === 0 ? "#06C755" : "#DDDDDD", |
| cornerRadius: "4px", |
| } as FlexBox, |
| ], |
| width: "20px", |
| alignItems: "center", |
| paddingTop: "sm", |
| } as FlexBox, |
| |
| { |
| type: "box", |
| layout: "vertical", |
| contents: itemContents, |
| flex: 1, |
| } as FlexBox, |
| ], |
| margin: index > 0 ? "lg" : undefined, |
| }; |
|
|
| if (item.action) { |
| itemBox.action = item.action; |
| } |
|
|
| return itemBox; |
| }); |
|
|
| return { |
| type: "bubble", |
| size: "mega", |
| body: { |
| type: "box", |
| layout: "vertical", |
| contents: [ |
| { |
| type: "text", |
| text: title, |
| weight: "bold", |
| size: "xl", |
| color: "#111111", |
| wrap: true, |
| } as FlexText, |
| { |
| type: "separator", |
| margin: "lg", |
| color: "#EEEEEE", |
| }, |
| { |
| type: "box", |
| layout: "vertical", |
| contents: itemContents, |
| margin: "lg", |
| } as FlexBox, |
| ], |
| paddingAll: "xl", |
| backgroundColor: "#FFFFFF", |
| }, |
| }; |
| } |
|
|
| |
| |
| |
| export function createImageCard( |
| imageUrl: string, |
| title: string, |
| body?: string, |
| options?: { |
| aspectRatio?: "1:1" | "1.51:1" | "1.91:1" | "4:3" | "16:9" | "20:13" | "2:1" | "3:1"; |
| aspectMode?: "cover" | "fit"; |
| action?: Action; |
| }, |
| ): FlexBubble { |
| const bubble: FlexBubble = { |
| type: "bubble", |
| hero: { |
| type: "image", |
| url: imageUrl, |
| size: "full", |
| aspectRatio: options?.aspectRatio ?? "20:13", |
| aspectMode: options?.aspectMode ?? "cover", |
| action: options?.action, |
| } as FlexImage, |
| body: { |
| type: "box", |
| layout: "vertical", |
| contents: [ |
| { |
| type: "text", |
| text: title, |
| weight: "bold", |
| size: "xl", |
| wrap: true, |
| } as FlexText, |
| ], |
| paddingAll: "lg", |
| }, |
| }; |
|
|
| if (body && bubble.body) { |
| bubble.body.contents.push({ |
| type: "text", |
| text: body, |
| size: "md", |
| wrap: true, |
| margin: "md", |
| color: "#666666", |
| } as FlexText); |
| } |
|
|
| return bubble; |
| } |
|
|
| |
| |
| |
| export function createActionCard( |
| title: string, |
| body: string, |
| actions: CardAction[], |
| options?: { |
| imageUrl?: string; |
| aspectRatio?: "1:1" | "1.51:1" | "1.91:1" | "4:3" | "16:9" | "20:13" | "2:1" | "3:1"; |
| }, |
| ): FlexBubble { |
| const bubble: FlexBubble = { |
| type: "bubble", |
| body: { |
| type: "box", |
| layout: "vertical", |
| contents: [ |
| { |
| type: "text", |
| text: title, |
| weight: "bold", |
| size: "xl", |
| wrap: true, |
| } as FlexText, |
| { |
| type: "text", |
| text: body, |
| size: "md", |
| wrap: true, |
| margin: "md", |
| color: "#666666", |
| } as FlexText, |
| ], |
| paddingAll: "lg", |
| }, |
| footer: { |
| type: "box", |
| layout: "vertical", |
| contents: actions.slice(0, 4).map( |
| (action, index) => |
| ({ |
| type: "button", |
| action: action.action, |
| style: index === 0 ? "primary" : "secondary", |
| margin: index > 0 ? "sm" : undefined, |
| }) as FlexButton, |
| ), |
| paddingAll: "md", |
| }, |
| }; |
|
|
| if (options?.imageUrl) { |
| bubble.hero = { |
| type: "image", |
| url: options.imageUrl, |
| size: "full", |
| aspectRatio: options.aspectRatio ?? "20:13", |
| aspectMode: "cover", |
| } as FlexImage; |
| } |
|
|
| return bubble; |
| } |
|
|
| |
| |
| |
| |
| export function createCarousel(bubbles: FlexBubble[]): FlexCarousel { |
| return { |
| type: "carousel", |
| contents: bubbles.slice(0, 12), |
| }; |
| } |
|
|
| |
| |
| |
| |
| |
| |
| export function createNotificationBubble( |
| text: string, |
| options?: { |
| icon?: string; |
| type?: "info" | "success" | "warning" | "error"; |
| title?: string; |
| }, |
| ): FlexBubble { |
| |
| const colors = { |
| info: { accent: "#3B82F6", bg: "#EFF6FF" }, |
| success: { accent: "#06C755", bg: "#F0FDF4" }, |
| warning: { accent: "#F59E0B", bg: "#FFFBEB" }, |
| error: { accent: "#EF4444", bg: "#FEF2F2" }, |
| }; |
| const typeColors = colors[options?.type ?? "info"]; |
|
|
| const contents: FlexComponent[] = []; |
|
|
| |
| contents.push({ |
| type: "box", |
| layout: "vertical", |
| contents: [], |
| width: "4px", |
| backgroundColor: typeColors.accent, |
| cornerRadius: "2px", |
| } as FlexBox); |
|
|
| |
| const textContents: FlexComponent[] = []; |
|
|
| if (options?.title) { |
| textContents.push({ |
| type: "text", |
| text: options.title, |
| size: "md", |
| weight: "bold", |
| color: "#111111", |
| wrap: true, |
| } as FlexText); |
| } |
|
|
| textContents.push({ |
| type: "text", |
| text, |
| size: options?.title ? "sm" : "md", |
| color: options?.title ? "#666666" : "#333333", |
| wrap: true, |
| margin: options?.title ? "sm" : undefined, |
| } as FlexText); |
|
|
| contents.push({ |
| type: "box", |
| layout: "vertical", |
| contents: textContents, |
| flex: 1, |
| paddingStart: "lg", |
| } as FlexBox); |
|
|
| return { |
| type: "bubble", |
| body: { |
| type: "box", |
| layout: "horizontal", |
| contents, |
| paddingAll: "xl", |
| backgroundColor: typeColors.bg, |
| }, |
| }; |
| } |
|
|