@doubao-dev/template 0.0.38-canary-22dd4cbf-20260720020201

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.

Potentially problematic release.


This version of @doubao-dev/template might be problematic. Click here for more details.

Files changed (57) hide show
  1. package/README.md +282 -0
  2. package/assets/ask-human-card.png +0 -0
  3. package/assets/checkout-card.png +0 -0
  4. package/assets/content-card.png +0 -0
  5. package/assets/price-action-card.png +0 -0
  6. package/assets/ticket-order-card.png +0 -0
  7. package/assets/transit-card.png +0 -0
  8. package/dist/ask-human-card/index.d.ts +44 -0
  9. package/dist/ask-human-card/index.js +79 -0
  10. package/dist/ask-human-card/styles.css +66 -0
  11. package/dist/checkout-card/index.d.ts +55 -0
  12. package/dist/checkout-card/index.js +40 -0
  13. package/dist/checkout-card/styles.css +34 -0
  14. package/dist/components/chevron-right-icon/index.d.ts +6 -0
  15. package/dist/components/chevron-right-icon/index.js +13 -0
  16. package/dist/components/extra-action/index.d.ts +50 -0
  17. package/dist/components/extra-action/index.js +31 -0
  18. package/dist/components/extra-action/styles.css +10 -0
  19. package/dist/components/footer/index.d.ts +27 -0
  20. package/dist/components/footer/index.js +15 -0
  21. package/dist/components/footer/styles.css +27 -0
  22. package/dist/components/header/index.d.ts +13 -0
  23. package/dist/components/header/index.js +27 -0
  24. package/dist/components/header/styles.css +62 -0
  25. package/dist/components/info-section/index.d.ts +47 -0
  26. package/dist/components/info-section/index.js +93 -0
  27. package/dist/components/info-section/styles.css +151 -0
  28. package/dist/components/product-summary/index.d.ts +26 -0
  29. package/dist/components/product-summary/index.js +55 -0
  30. package/dist/components/product-summary/styles.css +121 -0
  31. package/dist/components/route-section/index.d.ts +23 -0
  32. package/dist/components/route-section/index.js +39 -0
  33. package/dist/components/route-section/styles.css +54 -0
  34. package/dist/components/transport-summary/index.d.ts +31 -0
  35. package/dist/components/transport-summary/index.js +109 -0
  36. package/dist/components/transport-summary/styles.css +214 -0
  37. package/dist/content-card/index.d.ts +45 -0
  38. package/dist/content-card/index.js +72 -0
  39. package/dist/content-card/styles.css +160 -0
  40. package/dist/index.d.ts +11 -0
  41. package/dist/index.js +6 -0
  42. package/dist/price-action-card/index.d.ts +52 -0
  43. package/dist/price-action-card/index.js +64 -0
  44. package/dist/price-action-card/styles.css +161 -0
  45. package/dist/ticket-order-card/index.d.ts +70 -0
  46. package/dist/ticket-order-card/index.js +27 -0
  47. package/dist/ticket-order-card/styles.css +18 -0
  48. package/dist/transit-card/index.d.ts +52 -0
  49. package/dist/transit-card/index.js +29 -0
  50. package/dist/transit-card/styles.css +53 -0
  51. package/dist/types.d.ts +3 -0
  52. package/dist/types.js +0 -0
  53. package/dist/utils/app-info.d.ts +6 -0
  54. package/dist/utils/app-info.js +20 -0
  55. package/dist/utils/icons.d.ts +2 -0
  56. package/dist/utils/icons.js +28 -0
  57. package/package.json +40 -0
@@ -0,0 +1,50 @@
1
+ import type { TouchEvent } from '@lynx-js/types';
2
+ import type { ReactNode } from 'react';
3
+ import './styles.scss';
4
+ /** 模板右侧控件:按钮。 */
5
+ export interface TemplateExtraActionButton {
6
+ type: 'button';
7
+ /** 按钮文案。 */
8
+ text: string;
9
+ /** 是否禁用。 */
10
+ disabled?: boolean;
11
+ /** 是否展示加载态。 */
12
+ loading?: boolean;
13
+ /** 点击按钮时触发。 */
14
+ onClick?: (event?: TouchEvent) => void;
15
+ }
16
+ /** 模板右侧控件:开关。受控用法,必须传入 checked。 */
17
+ export interface TemplateExtraActionSwitch {
18
+ type: 'switch';
19
+ /** 当前开关状态(受控)。 */
20
+ checked: boolean;
21
+ /** 是否禁用。 */
22
+ disabled?: boolean;
23
+ /** 开关状态变化时触发。 */
24
+ onChange?: (checked: boolean) => void;
25
+ }
26
+ /** 模板右侧控件:播放/暂停按钮。受控用法,playing=true 表示播放中(应展示暂停态)。 */
27
+ export interface TemplateExtraActionPlayback {
28
+ type: 'playback';
29
+ /** 当前是否播放中(受控)。 */
30
+ playing: boolean;
31
+ /** 当前播放进度,范围 0-1;不传时不展示进度环。 */
32
+ progress?: number;
33
+ /** 是否禁用。 */
34
+ disabled?: boolean;
35
+ /** 切换播放/暂停时触发,参数为切换后的目标状态。 */
36
+ onToggle?: (playing: boolean) => void;
37
+ }
38
+ /** 模板右侧标准控件配置(判别联合,三选一)。 */
39
+ export type TemplateExtraAction = TemplateExtraActionButton | TemplateExtraActionSwitch | TemplateExtraActionPlayback;
40
+ export interface TemplateExtraActionClassNames {
41
+ button?: string;
42
+ switch?: string;
43
+ playback?: string;
44
+ playbackPlaying?: string;
45
+ playbackDisabled?: string;
46
+ playbackIcon?: string;
47
+ }
48
+ export declare function stopTemplateExtraActionTap(): void;
49
+ export declare function renderTemplateExtraAction(action: TemplateExtraAction, classNames?: TemplateExtraActionClassNames): ReactNode;
50
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,31 @@
1
+ import { Button, Switch, useTheme } from "@byted-doubao-apps/components";
2
+ import { clsx } from "clsx";
3
+ import { getTemplatePlaybackIconContent } from "../../utils/icons.js";
4
+ import "./styles.css";
5
+ function stopTemplateExtraActionTap() {}
6
+ function TemplatePlaybackButton(props) {
7
+ const { playing, progress, disabled = false, onToggle, className, playingClassName, disabledClassName, iconClassName } = props;
8
+ const theme = useTheme();
9
+ const neutral100 = 'dark' === theme ? '#ffffff' : '#000000';
10
+ const iconContent = getTemplatePlaybackIconContent(playing, progress, neutral100);
11
+ const handleTap = ()=>{
12
+ if (disabled) return;
13
+ onToggle?.(!playing);
14
+ };
15
+ return <view className={clsx(className, playing && playingClassName, disabled && disabledClassName)} catchtap={handleTap}>
16
+ <svg className={iconClassName} content={iconContent}/>
17
+ </view>;
18
+ }
19
+ function renderTemplateExtraAction(action, classNames = {}) {
20
+ switch(action.type){
21
+ case 'button':
22
+ return <Button className={clsx('doubao-template-extra-action-button', classNames.button)} type="default" text={action.text} disabled={action.disabled} loading={action.loading} onClick={action.onClick}/>;
23
+ case 'switch':
24
+ return <Switch className={classNames.switch} checked={action.checked} disabled={action.disabled} onChange={action.onChange}/>;
25
+ case 'playback':
26
+ return <TemplatePlaybackButton playing={action.playing} progress={action.progress} disabled={action.disabled} onToggle={action.onToggle} className={classNames.playback} playingClassName={classNames.playbackPlaying} disabledClassName={classNames.playbackDisabled} iconClassName={classNames.playbackIcon}/>;
27
+ default:
28
+ return null;
29
+ }
30
+ }
31
+ export { renderTemplateExtraAction, stopTemplateExtraActionTap };
@@ -0,0 +1,10 @@
1
+ .doubao-template-extra-action-button {
2
+ --doubao-button-border-radius: 8px;
3
+ --doubao-button-default-border-radius: 8px;
4
+ --doubao-button-default-height: 36px;
5
+ --doubao-button-default-padding-x: 12px;
6
+ --doubao-button-default-padding-y: 8px;
7
+ --doubao-button-default-font-size: 14px;
8
+ --doubao-button-default-line-height: 20px;
9
+ }
10
+
@@ -0,0 +1,27 @@
1
+ import type { CSSProperties, TouchEvent } from '@lynx-js/types';
2
+ import type { ReactElement } from 'react';
3
+ import './styles.scss';
4
+ export interface TemplateCardFooterActionButton {
5
+ /** 按钮文案。 */
6
+ text?: string;
7
+ /** 是否禁用。 */
8
+ disabled?: boolean;
9
+ /** 是否显示加载状态。 */
10
+ loading?: boolean;
11
+ /** onClick 的节流时间,单位为毫秒。 */
12
+ throttle?: number;
13
+ /** 点击按钮时触发。 */
14
+ onClick?: (event?: TouchEvent) => void;
15
+ }
16
+ export interface TemplateCardFooterProps {
17
+ /** 操作区根节点自定义类名。 */
18
+ className?: string;
19
+ /** 操作区根节点自定义样式。 */
20
+ style?: CSSProperties;
21
+ /** 底部主按钮;只传一个按钮时占据整行。 */
22
+ primaryActionButton?: TemplateCardFooterActionButton;
23
+ /** 底部次按钮;只传一个按钮时占据整行。 */
24
+ secondaryActionButton?: TemplateCardFooterActionButton;
25
+ }
26
+ export declare function TemplateCardFooter(props: TemplateCardFooterProps): ReactElement | null;
27
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,15 @@
1
+ import { Button } from "@byted-doubao-apps/components";
2
+ import { clsx } from "clsx";
3
+ import "./styles.css";
4
+ function TemplateCardFooter(props) {
5
+ const { className, style, primaryActionButton, secondaryActionButton } = props;
6
+ const hasPrimaryAction = primaryActionButton?.text != null && primaryActionButton.text.length > 0;
7
+ const hasSecondaryAction = secondaryActionButton?.text != null && secondaryActionButton.text.length > 0;
8
+ const hasDefaultActions = hasPrimaryAction || hasSecondaryAction;
9
+ if (!hasDefaultActions) return null;
10
+ return <view className={clsx('doubao-template-action-bar', className)} style={style}>
11
+ {hasSecondaryAction && <Button className="doubao-template-action-bar__action" type="default" {...secondaryActionButton} text={secondaryActionButton.text} onClick={secondaryActionButton.onClick}/>}
12
+ {hasPrimaryAction && <Button className="doubao-template-action-bar__action" type="primary" {...primaryActionButton} text={primaryActionButton.text} onClick={primaryActionButton.onClick}/>}
13
+ </view>;
14
+ }
15
+ export { TemplateCardFooter };
@@ -0,0 +1,27 @@
1
+ .doubao-template-action-bar {
2
+ box-sizing: border-box;
3
+ flex-direction: row;
4
+ width: 100%;
5
+ padding: 8px 12px 12px;
6
+ display: flex;
7
+ }
8
+
9
+ .doubao-template-action-bar__action {
10
+ --doubao-button-border-radius: 8px;
11
+ --doubao-button-default-border-radius: 8px;
12
+ --doubao-button-default-height: 44px;
13
+ --doubao-button-default-padding-x: 16px;
14
+ --doubao-button-default-padding-y: 11px;
15
+ --doubao-button-default-font-size: 16px;
16
+ --doubao-button-default-line-height: 22px;
17
+ border-radius: 8px;
18
+ flex: 1 1 0;
19
+ width: auto;
20
+ min-width: 0;
21
+ height: 44px;
22
+ }
23
+
24
+ .doubao-template-action-bar__action + .doubao-template-action-bar__action {
25
+ margin-left: 10px;
26
+ }
27
+
@@ -0,0 +1,13 @@
1
+ import type { TouchEvent } from '@lynx-js/types';
2
+ import type { ReactElement } from 'react';
3
+ import './styles.scss';
4
+ export interface TemplateCardHeaderProps {
5
+ /** 标题栏右侧操作文案。 */
6
+ actionText?: string;
7
+ /** 是否展示标题栏右侧操作;默认根据 actionText 是否存在决定。 */
8
+ showAction?: boolean;
9
+ /** 点击标题栏右侧操作时触发。 */
10
+ onActionClick?: (event: TouchEvent) => void;
11
+ }
12
+ export declare function TemplateCardHeader(props: TemplateCardHeaderProps): ReactElement;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,27 @@
1
+ import { getAppInfo } from "../../utils/app-info.js";
2
+ import { TemplateChevronRightIcon } from "../chevron-right-icon/index.js";
3
+ import "./styles.css";
4
+ function TemplateCardHeaderIcon({ appIconSrc }) {
5
+ if (null != appIconSrc && appIconSrc.length > 0) return <image className="doubao-template-title-bar__app-icon" src={appIconSrc} mode="aspectFill"/>;
6
+ return <view className="doubao-template-title-bar__app-icon"/>;
7
+ }
8
+ function TemplateCardHeader(props) {
9
+ const { actionText, showAction = true, onActionClick } = props;
10
+ const appInfo = getAppInfo();
11
+ const hasAction = showAction && null != actionText && actionText.length > 0;
12
+ return <view className="doubao-template-title-bar">
13
+ <view className="doubao-template-title-bar__app">
14
+ <TemplateCardHeaderIcon appIconSrc={appInfo.appIconSrc}/>
15
+ <text className="doubao-template-title-bar__app-name" text-maxline="1">
16
+ {appInfo.appName}
17
+ </text>
18
+ </view>
19
+ {hasAction && <view className="doubao-template-title-bar__more" catchtap={onActionClick}>
20
+ <text className="doubao-template-title-bar__more-text" text-maxline="1">
21
+ {actionText}
22
+ </text>
23
+ <TemplateChevronRightIcon className="doubao-template-title-bar__more-chevron"/>
24
+ </view>}
25
+ </view>;
26
+ }
27
+ export { TemplateCardHeader };
@@ -0,0 +1,62 @@
1
+ .doubao-template-title-bar {
2
+ box-sizing: border-box;
3
+ border-bottom: .5px solid var(--neutral-transparent-1);
4
+ flex-direction: row;
5
+ justify-content: space-between;
6
+ align-items: center;
7
+ width: 100%;
8
+ height: 40px;
9
+ padding: 12px;
10
+ display: flex;
11
+ }
12
+
13
+ .doubao-template-title-bar__app {
14
+ flex-direction: row;
15
+ align-items: center;
16
+ display: flex;
17
+ }
18
+
19
+ .doubao-template-title-bar__app-icon {
20
+ border-radius: 4px;
21
+ flex-shrink: 0;
22
+ width: 16px;
23
+ height: 16px;
24
+ overflow: hidden;
25
+ }
26
+
27
+ .doubao-template-title-bar__app-name, .doubao-template-title-bar__more-text {
28
+ color: var(--neutral-50);
29
+ text-overflow: ellipsis;
30
+ white-space: nowrap;
31
+ font-family: PingFang SC, sans-serif;
32
+ font-size: 12px;
33
+ font-style: normal;
34
+ line-height: 18px;
35
+ overflow: hidden;
36
+ }
37
+
38
+ .doubao-template-title-bar__app-name {
39
+ margin-left: 6px;
40
+ font-weight: 500;
41
+ }
42
+
43
+ .doubao-template-title-bar__more {
44
+ flex-direction: row;
45
+ flex-shrink: 0;
46
+ justify-content: flex-end;
47
+ align-items: center;
48
+ min-width: 62.5px;
49
+ display: flex;
50
+ }
51
+
52
+ .doubao-template-title-bar__more-text {
53
+ font-weight: 400;
54
+ }
55
+
56
+ .doubao-template-title-bar__more-chevron {
57
+ flex-shrink: 0;
58
+ width: 12px;
59
+ height: 12px;
60
+ margin-left: 2px;
61
+ }
62
+
@@ -0,0 +1,47 @@
1
+ import type { ReactElement, ReactNode } from 'react';
2
+ import type { TemplateKey, TemplateText } from '../../types.js';
3
+ import './styles.scss';
4
+ export interface TemplateInfoSectionProps {
5
+ /** 信息行列表。 */
6
+ infoItems?: TemplateInfoSectionInfoItem[];
7
+ /** 费用明细列表。 */
8
+ feeItems?: TemplateInfoSectionFeeItem[];
9
+ /** 优惠信息文案。 */
10
+ discountText?: TemplateText;
11
+ /** 合计价格前的标签文案。 */
12
+ totalLabel?: TemplateText;
13
+ /** 合计价格。 */
14
+ totalPrice?: TemplateText;
15
+ /** 合计区域自定义内容;优先级高于 totalLabel 和 totalPrice。 */
16
+ totalContent?: ReactNode;
17
+ /** 信息值排列方式。 */
18
+ valueLayout?: 'row' | 'column';
19
+ }
20
+ export interface TemplateInfoSectionInfoItem {
21
+ /** 信息项唯一标识;不传时使用数组下标。 */
22
+ key?: TemplateKey;
23
+ /** 信息项标签。 */
24
+ label?: TemplateText;
25
+ /** 信息项标签块内容;优先级高于 label。 */
26
+ labelContent?: ReactNode;
27
+ /** 信息项主内容。 */
28
+ value?: ReactNode;
29
+ /** 信息项主内容块内容;优先级高于 value。 */
30
+ valueContent?: ReactNode;
31
+ /** 信息项补充内容。 */
32
+ valueExtra?: ReactNode;
33
+ /** 信息项补充内容块内容;优先级高于 valueExtra。 */
34
+ valueExtraContent?: ReactNode;
35
+ }
36
+ export interface TemplateInfoSectionFeeItem {
37
+ /** 费用项唯一标识;不传时使用数组下标。 */
38
+ key?: TemplateKey;
39
+ /** 费用项标签。 */
40
+ label?: TemplateText;
41
+ /** 费用项金额或数值。 */
42
+ value?: TemplateText;
43
+ /** 费用项自定义内容;优先级高于 label 和 value。 */
44
+ content?: ReactNode;
45
+ }
46
+ export declare function TemplateInfoSection(props: TemplateInfoSectionProps): ReactElement | null;
47
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,93 @@
1
+ import { clsx } from "clsx";
2
+ import "./styles.css";
3
+ function isTemplateInfoSectionNodeEmpty(content) {
4
+ return null == content || 'boolean' == typeof content || '' === content;
5
+ }
6
+ function isTemplateInfoSectionTextEmpty(content) {
7
+ return null == content || '' === content;
8
+ }
9
+ function renderTemplateInfoSectionNode(textContent, nodeContent, textClassName, maxLines = 1) {
10
+ if (void 0 !== nodeContent) {
11
+ if (isTemplateInfoSectionNodeEmpty(nodeContent)) return null;
12
+ if ('string' == typeof nodeContent || 'number' == typeof nodeContent) return <text className={textClassName} text-maxline={maxLines}>
13
+ {nodeContent}
14
+ </text>;
15
+ return nodeContent;
16
+ }
17
+ if (isTemplateInfoSectionNodeEmpty(textContent)) return null;
18
+ if ('string' == typeof textContent || 'number' == typeof textContent) return <text className={textClassName} text-maxline={maxLines}>
19
+ {textContent}
20
+ </text>;
21
+ return textContent;
22
+ }
23
+ function hasTemplateInfoSectionInfoItemContent(item) {
24
+ return !isTemplateInfoSectionTextEmpty(item.label) || !isTemplateInfoSectionNodeEmpty(item.labelContent) || !isTemplateInfoSectionNodeEmpty(item.value) || !isTemplateInfoSectionNodeEmpty(item.valueContent) || !isTemplateInfoSectionNodeEmpty(item.valueExtra) || !isTemplateInfoSectionNodeEmpty(item.valueExtraContent);
25
+ }
26
+ function renderTemplateInfoSectionInfoItem(item, index) {
27
+ if (!hasTemplateInfoSectionInfoItemContent(item)) return null;
28
+ const labelNode = renderTemplateInfoSectionNode(item.label, item.labelContent, 'doubao-template-info-section__info-label');
29
+ const valueNode = renderTemplateInfoSectionNode(item.value, item.valueContent, 'doubao-template-info-section__info-value', 2);
30
+ const valueExtraNode = renderTemplateInfoSectionNode(item.valueExtra, item.valueExtraContent, 'doubao-template-info-section__info-value', 2);
31
+ return <view className="doubao-template-info-section__info-row" key={item.key ?? index}>
32
+ <view className="doubao-template-info-section__info-label-wrap">{labelNode}</view>
33
+ <view className="doubao-template-info-section__info-value-wrap">
34
+ {valueNode}
35
+ {valueExtraNode}
36
+ </view>
37
+ </view>;
38
+ }
39
+ function renderTemplateInfoSectionFeeItem(item, index) {
40
+ if (void 0 !== item.content) {
41
+ if (isTemplateInfoSectionNodeEmpty(item.content)) return null;
42
+ return <view className="doubao-template-info-section__fee" key={item.key ?? index}>
43
+ {item.content}
44
+ </view>;
45
+ }
46
+ if (isTemplateInfoSectionTextEmpty(item.label) && isTemplateInfoSectionTextEmpty(item.value)) return null;
47
+ return <text className="doubao-template-info-section__fee" text-maxline="1" key={item.key ?? index}>
48
+ {item.label}
49
+ {item.value}
50
+ </text>;
51
+ }
52
+ function renderTemplateInfoSectionTotal({ totalLabel, totalPrice, totalContent }) {
53
+ if (void 0 !== totalContent) {
54
+ if (isTemplateInfoSectionNodeEmpty(totalContent)) return null;
55
+ return <view className="doubao-template-info-section__total">{totalContent}</view>;
56
+ }
57
+ if (isTemplateInfoSectionTextEmpty(totalLabel) && isTemplateInfoSectionTextEmpty(totalPrice)) return null;
58
+ return <view className="doubao-template-info-section__total">
59
+ {!isTemplateInfoSectionTextEmpty(totalLabel) && <text className="doubao-template-info-section__total-label" text-maxline="1">
60
+ {totalLabel}
61
+ </text>}
62
+ {!isTemplateInfoSectionTextEmpty(totalPrice) && <text className="doubao-template-info-section__total-price" text-maxline="1">
63
+ {totalPrice}
64
+ </text>}
65
+ </view>;
66
+ }
67
+ function TemplateInfoSection(props) {
68
+ const { infoItems = [], feeItems = [], discountText, totalLabel, totalPrice, totalContent, valueLayout = 'row' } = props;
69
+ const infoNodes = infoItems.map(renderTemplateInfoSectionInfoItem).filter((node)=>null != node);
70
+ const feeNodes = feeItems.map(renderTemplateInfoSectionFeeItem).filter((node)=>null != node);
71
+ const hasDiscount = !isTemplateInfoSectionTextEmpty(discountText);
72
+ const totalNode = renderTemplateInfoSectionTotal({
73
+ totalLabel,
74
+ totalPrice,
75
+ totalContent
76
+ });
77
+ const hasInfo = infoNodes.length > 0;
78
+ const hasSummary = hasDiscount || feeNodes.length > 0 || null != totalNode;
79
+ if (!hasInfo && !hasSummary) return null;
80
+ return <view className={clsx('doubao-template-info-section', 'column' === valueLayout && 'doubao-template-info-section--value-column')}>
81
+ {hasInfo && <view className="doubao-template-info-section__info">{infoNodes}</view>}
82
+ {hasSummary && <view className="doubao-template-info-section__summary">
83
+ <view className="doubao-template-info-section__summary-content">
84
+ {hasDiscount && <text className="doubao-template-info-section__fee" text-maxline="1">
85
+ {discountText}
86
+ </text>}
87
+ {feeNodes}
88
+ {totalNode}
89
+ </view>
90
+ </view>}
91
+ </view>;
92
+ }
93
+ export { TemplateInfoSection };
@@ -0,0 +1,151 @@
1
+ .doubao-template-info-section {
2
+ box-sizing: border-box;
3
+ border-top: .5px solid var(--neutral-transparent-1);
4
+ flex-direction: column;
5
+ width: 100%;
6
+ padding: 12px;
7
+ display: flex;
8
+ }
9
+
10
+ .doubao-template-info-section__info {
11
+ flex-direction: column;
12
+ width: 100%;
13
+ display: flex;
14
+ }
15
+
16
+ .doubao-template-info-section__info-row {
17
+ flex-direction: row;
18
+ width: 100%;
19
+ display: flex;
20
+ }
21
+
22
+ .doubao-template-info-section__info-row + .doubao-template-info-section__info-row {
23
+ margin-top: 8px;
24
+ }
25
+
26
+ .doubao-template-info-section__info-label-wrap {
27
+ flex-direction: row;
28
+ flex: 0 0 66px;
29
+ align-items: center;
30
+ width: 66px;
31
+ display: flex;
32
+ overflow: hidden;
33
+ }
34
+
35
+ .doubao-template-info-section__info-label, .doubao-template-info-section__info-value, .doubao-template-info-section__fee, .doubao-template-info-section__total-label, .doubao-template-info-section__total-price {
36
+ text-overflow: ellipsis;
37
+ white-space: nowrap;
38
+ font-family: PingFang SC, sans-serif;
39
+ font-style: normal;
40
+ overflow: hidden;
41
+ }
42
+
43
+ .doubao-template-info-section__info-label {
44
+ width: 100%;
45
+ min-width: 0;
46
+ max-width: 100%;
47
+ color: var(--neutral-100);
48
+ font-size: 13px;
49
+ font-weight: 400;
50
+ line-height: 20px;
51
+ }
52
+
53
+ .doubao-template-info-section__info-value-wrap {
54
+ flex-direction: row;
55
+ flex: 1 1 0;
56
+ justify-content: flex-end;
57
+ align-items: center;
58
+ min-width: 0;
59
+ display: flex;
60
+ overflow: hidden;
61
+ }
62
+
63
+ .doubao-template-info-section__info-value {
64
+ min-width: 0;
65
+ color: var(--neutral-50);
66
+ text-align: right;
67
+ margin-left: 6px;
68
+ font-size: 13px;
69
+ font-weight: 400;
70
+ line-height: 20px;
71
+ }
72
+
73
+ .doubao-template-info-section__info-value:first-child {
74
+ margin-left: 0;
75
+ }
76
+
77
+ .doubao-template-info-section--value-column .doubao-template-info-section__info-value-wrap {
78
+ flex-direction: column;
79
+ align-items: flex-end;
80
+ }
81
+
82
+ .doubao-template-info-section--value-column .doubao-template-info-section__info-value {
83
+ max-width: 100%;
84
+ margin-left: 0;
85
+ }
86
+
87
+ .doubao-template-info-section__summary {
88
+ flex-direction: row;
89
+ justify-content: flex-end;
90
+ width: 100%;
91
+ margin-top: 8px;
92
+ display: flex;
93
+ }
94
+
95
+ .doubao-template-info-section__summary-content {
96
+ flex-direction: row;
97
+ justify-content: flex-end;
98
+ align-items: center;
99
+ max-width: 100%;
100
+ display: flex;
101
+ overflow: hidden;
102
+ }
103
+
104
+ .doubao-template-info-section__fee {
105
+ min-width: 0;
106
+ color: var(--neutral-50);
107
+ flex-shrink: 1;
108
+ margin-left: 6px;
109
+ font-size: 13px;
110
+ font-weight: 400;
111
+ line-height: 20px;
112
+ }
113
+
114
+ .doubao-template-info-section__fee:first-child {
115
+ margin-left: 0;
116
+ }
117
+
118
+ .doubao-template-info-section__total {
119
+ flex-flow: wrap;
120
+ flex-shrink: 0;
121
+ justify-content: flex-end;
122
+ align-items: center;
123
+ min-width: 0;
124
+ max-width: 100%;
125
+ margin-left: 6px;
126
+ display: flex;
127
+ }
128
+
129
+ .doubao-template-info-section__total:first-child {
130
+ margin-left: 0;
131
+ }
132
+
133
+ .doubao-template-info-section__total-label {
134
+ max-width: 100%;
135
+ color: var(--neutral-100);
136
+ flex-shrink: 0;
137
+ font-size: 13px;
138
+ font-weight: 500;
139
+ line-height: 20px;
140
+ }
141
+
142
+ .doubao-template-info-section__total-price {
143
+ max-width: 100%;
144
+ color: var(--neutral-100);
145
+ flex-shrink: 0;
146
+ margin-left: 2px;
147
+ font-size: 15px;
148
+ font-weight: 500;
149
+ line-height: 22px;
150
+ }
151
+
@@ -0,0 +1,26 @@
1
+ import type { TouchEvent } from '@lynx-js/types';
2
+ import type { ReactElement, ReactNode } from 'react';
3
+ import type { TemplateText } from '../../types.js';
4
+ import './styles.scss';
5
+ export interface TemplateProductSummaryProps {
6
+ /** 商品标题。 */
7
+ title?: string;
8
+ /** 商品规格说明。 */
9
+ spec?: string;
10
+ /** 商品图片地址。 */
11
+ imageSrc?: string;
12
+ /** 商品自定义图片节点;优先级高于 imageSrc。 */
13
+ image?: ReactNode;
14
+ /** 商品价格前缀文案,例如「券后」。 */
15
+ pricePrefix?: string;
16
+ /** 商品价格。 */
17
+ price?: TemplateText;
18
+ /** 商品数量。 */
19
+ quantity?: TemplateText;
20
+ /** 是否展示商品标题右侧箭头。 */
21
+ showChevron?: boolean;
22
+ /** 点击商品摘要时触发。 */
23
+ onClick?: (event: TouchEvent) => void;
24
+ }
25
+ export declare function TemplateProductSummary(props: TemplateProductSummaryProps): ReactElement | null;
26
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,55 @@
1
+ import { TemplateChevronRightIcon } from "../chevron-right-icon/index.js";
2
+ import "./styles.css";
3
+ function isTemplateProductSummaryTextEmpty(content) {
4
+ return null == content || '' === content;
5
+ }
6
+ function isTemplateProductSummaryNodeEmpty(content) {
7
+ return null == content || 'boolean' == typeof content || '' === content;
8
+ }
9
+ function TemplateProductSummaryImage(props) {
10
+ const { image: image1, imageSrc } = props;
11
+ if (!isTemplateProductSummaryNodeEmpty(image1)) return <view className="doubao-template-product-summary__image">{image1}</view>;
12
+ if (null != imageSrc && imageSrc.length > 0) return <image className="doubao-template-product-summary__image" src={imageSrc} mode="aspectFill"/>;
13
+ return <view className="doubao-template-product-summary__image"/>;
14
+ }
15
+ function TemplateProductSummary(props) {
16
+ const { title, spec, imageSrc, image: image1, pricePrefix, price, quantity, showChevron = true, onClick } = props;
17
+ const hasImage = !isTemplateProductSummaryNodeEmpty(image1) || null != imageSrc && imageSrc.length > 0;
18
+ const hasTitle = null != title && title.length > 0;
19
+ const hasPrice = null != pricePrefix && pricePrefix.length > 0 || !isTemplateProductSummaryTextEmpty(price);
20
+ const hasMainLine = hasTitle || hasPrice;
21
+ const hasSpec = null != spec && spec.length > 0;
22
+ const hasQuantity = !isTemplateProductSummaryTextEmpty(quantity);
23
+ const hasSecondaryLine = hasSpec || hasQuantity;
24
+ if (!hasImage && !hasMainLine && !hasSecondaryLine) return null;
25
+ return <view className="doubao-template-product-summary" catchtap={onClick}>
26
+ {hasImage && <TemplateProductSummaryImage image={image1} imageSrc={imageSrc}/>}
27
+ <view className={hasImage ? 'doubao-template-product-summary__content doubao-template-product-summary__content--with-image' : 'doubao-template-product-summary__content'}>
28
+ {hasMainLine && <view className="doubao-template-product-summary__line-main">
29
+ {hasTitle && <view className="doubao-template-product-summary__title-wrap">
30
+ <text className="doubao-template-product-summary__title" text-maxline="1">
31
+ {title}
32
+ </text>
33
+ {showChevron && <TemplateChevronRightIcon className="doubao-template-product-summary__chevron"/>}
34
+ </view>}
35
+ {hasPrice && <view className="doubao-template-product-summary__price">
36
+ {null != pricePrefix && pricePrefix.length > 0 && <text className="doubao-template-product-summary__price-prefix" text-maxline="1">
37
+ {pricePrefix}
38
+ </text>}
39
+ {!isTemplateProductSummaryTextEmpty(price) && <text className="doubao-template-product-summary__price-value" text-maxline="1">
40
+ {price}
41
+ </text>}
42
+ </view>}
43
+ </view>}
44
+ {hasSecondaryLine && <view className="doubao-template-product-summary__line-secondary">
45
+ {hasSpec && <text className="doubao-template-product-summary__spec" text-maxline="1">
46
+ {spec}
47
+ </text>}
48
+ {hasQuantity && <text className="doubao-template-product-summary__quantity" text-maxline="1">
49
+ ×{quantity}
50
+ </text>}
51
+ </view>}
52
+ </view>
53
+ </view>;
54
+ }
55
+ export { TemplateProductSummary };