diff --git a/package.json b/package.json
index 96bbec31..b72435e9 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "vocechat-web",
- "version": "0.9.58",
+ "version": "0.9.59",
"homepage": "https://voce.chat",
"dependencies": {
"@metamask/onboarding": "^1.0.1",
diff --git a/public/locales/en/setting.json b/public/locales/en/setting.json
index 5a213e3b..e70abf6a 100644
--- a/public/locales/en/setting.json
+++ b/public/locales/en/setting.json
@@ -354,6 +354,10 @@
"default_value": "Default Value",
"remark": "Remarks",
"custom_style_tip": "if you want customize the widget position more precisely, use the widget element ID in your HTML or CSS file, like this",
+ "open_widget_tip": "You can use JavaScript to control the widget programmatically",
+ "open_widget_example": "Open widget with a button",
+ "open_widget_example_custom": "Custom styled button",
+ "open_widget_example_toggle": "Toggle widget open/close",
"css_code_tip": "You can also customize CSS code, which will be loaded into the widget as a CSS file",
"param_id": "Widget ID (server version>=v0.3.12)",
"param_host": "Assign the user chatting with visitor(User ID)",
@@ -368,6 +372,10 @@
"param_open_height": "The height while widget opened",
"param_welcome": "Custom welcome message",
"param_position": "Widget position: left/right",
+ "param_icon_title": "Hover tooltip title on closed widget icon",
+ "param_icon_subtitle": "Hover tooltip subtitle on closed widget icon",
+ "param_icon_image": "Hover tooltip image URL on closed widget icon",
+ "param_icon_closable": "Whether the tooltip can be closed (true/false)",
"share_link": "Share the widget link so that people can chat with you directly",
"widget_faq": "Enable Let Everyone Join First"
},
diff --git a/public/locales/zh/setting.json b/public/locales/zh/setting.json
index 78a3fe87..3ad0cba8 100644
--- a/public/locales/zh/setting.json
+++ b/public/locales/zh/setting.json
@@ -353,6 +353,10 @@
"default_value": "默认值",
"remark": "备注",
"custom_style_tip": "如果你想更精确控制挂件的样式,可以借助 widget 元素的 ID,自行写 CSS 样式,加到自己的 html 或者 css 文件中,如下所示:",
+ "open_widget_tip": "你可以使用 JavaScript 来控制挂件的打开和关闭",
+ "open_widget_example": "使用按钮打开挂件",
+ "open_widget_example_custom": "自定义样式的按钮",
+ "open_widget_example_toggle": "切换挂件开关",
"css_code_tip": "也可以自定义 CSS 代码,会以 css 文件方式加载到挂件内",
"param_id": "挂件 ID,可选设置 (服务器版本不低于 v0.3.12)",
"param_host": "指定和谁聊天 (用户 ID)",
@@ -367,6 +371,10 @@
"param_open_height": "挂件打开态的高度",
"param_welcome": "自定义欢迎语",
"param_position": "设置挂件位置,居左 (left) 或居右 (right)",
+ "param_icon_title": "关闭状态悬浮提示框标题",
+ "param_icon_subtitle": "关闭状态悬浮提示框副标题",
+ "param_icon_image": "关闭状态悬浮提示框图片地址",
+ "param_icon_closable": "提示框是否可关闭 (true/false)",
"share_link": "分享此链接,别人可以通过该地址直接与你对话",
"widget_faq": "允许公开注册"
},
diff --git a/public/widget.js b/public/widget.js
index dd9c76e3..79d1cf18 100644
--- a/public/widget.js
+++ b/public/widget.js
@@ -13,11 +13,21 @@
title = "",
logo = "",
position = "right",
- welcome = ""
+ welcome = "",
+ iconTitle = "Need help?",
+ iconSubtitle = "Our staff are always ready to help!",
+ iconImage = "",
+ iconClosable = "true"
} = d.currentScript.dataset;
const _src = d.currentScript.src;
const wrapper = d.createElement("iframe");
wrapper.id = "VOCECHAT_WIDGET";
+
+ // 如果有提示框内容,关闭状态的宽度和高度需要更大
+ const hasTooltip = iconTitle || iconSubtitle;
+ const actualCloseWidth = hasTooltip ? 320 : closeWidth;
+ const actualCloseHeight = hasTooltip ? 120 : closeHeight;
+
const styles = {
position: "fixed",
borderRadius: "8px",
@@ -33,9 +43,9 @@
new URL(_src).origin
}/widget.html?id=${id}&host=${hostId}&autoReg=${autoReg}&token=${loginToken}&themeColor=${encodeURIComponent(
themeColor
- )}&from=${encodeURIComponent(location.hostname)}&welcome=${encodeURIComponent(welcome)}&title=${encodeURIComponent(title)}&logo=${encodeURIComponent(logo)}`;
- wrapper.width = closeWidth;
- wrapper.height = closeHeight;
+ )}&from=${encodeURIComponent(location.hostname)}&welcome=${encodeURIComponent(welcome)}&title=${encodeURIComponent(title)}&logo=${encodeURIComponent(logo)}&iconTitle=${encodeURIComponent(iconTitle)}&iconSubtitle=${encodeURIComponent(iconSubtitle)}&iconImage=${encodeURIComponent(iconImage)}&iconClosable=${iconClosable}`;
+ wrapper.width = actualCloseWidth;
+ wrapper.height = actualCloseHeight;
wrapper.frameborder = 0;
wrapper.referrerPolicy = "unsafe-url";
w.addEventListener(
@@ -48,8 +58,8 @@
wrapper.setAttribute("height", openHeight);
break;
case "CLOSE":
- wrapper.setAttribute("width", closeWidth);
- wrapper.setAttribute("height", closeHeight);
+ wrapper.setAttribute("width", actualCloseWidth);
+ wrapper.setAttribute("height", actualCloseHeight);
break;
case "RELOAD_WITH_OPEN":
{
@@ -68,4 +78,26 @@
false
);
d.body.appendChild(wrapper);
+
+ // 暴露全局函数用于打开 widget
+ w.VoceChatWidget = {
+ open: function() {
+ wrapper.setAttribute("width", openWidth);
+ wrapper.setAttribute("height", openHeight);
+ wrapper.contentWindow?.postMessage("OPEN_FROM_PARENT", "*");
+ },
+ close: function() {
+ wrapper.setAttribute("width", actualCloseWidth);
+ wrapper.setAttribute("height", actualCloseHeight);
+ wrapper.contentWindow?.postMessage("CLOSE_FROM_PARENT", "*");
+ },
+ toggle: function() {
+ const currentWidth = wrapper.getAttribute("width");
+ if (currentWidth == actualCloseWidth) {
+ this.open();
+ } else {
+ this.close();
+ }
+ }
+ };
})(window, document);
diff --git a/src/routes/setting/Widget/ConfigDetails.tsx b/src/routes/setting/Widget/ConfigDetails.tsx
index 17f402ea..c9dce61c 100644
--- a/src/routes/setting/Widget/ConfigDetails.tsx
+++ b/src/routes/setting/Widget/ConfigDetails.tsx
@@ -113,6 +113,26 @@ const ConfigDetails = ({}: Props) => {
paramKey: "welcome",
paramDefault: wt("welcome"),
remarks: t("param_welcome")
+ },
+ {
+ paramKey: "icon-title",
+ paramDefault: "Need help?",
+ remarks: t("param_icon_title")
+ },
+ {
+ paramKey: "icon-subtitle",
+ paramDefault: "Our staff are always ready to help!",
+ remarks: t("param_icon_subtitle")
+ },
+ {
+ paramKey: "icon-image",
+ paramDefault: "-",
+ remarks: t("param_icon_image")
+ },
+ {
+ paramKey: "icon-closable",
+ paramDefault: "true",
+ remarks: t("param_icon_closable")
}
].map((row) => (