Skip to content

Commit b0af50f

Browse files
committed
feat: web - create custom html element
1 parent 6495904 commit b0af50f

File tree

3 files changed

+84
-48
lines changed

3 files changed

+84
-48
lines changed

examples/vanilla-js/index.html

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,24 @@
1313
</style>
1414
</head>
1515
<body>
16-
<div id="my-chatbot"></div>
17-
1816
<script src="../../dist/umd/docs-chatbot.min.js"></script>
1917
<script src="./config.js"></script>
2018

19+
<docs-chatbot
20+
title="SQLite Cloud Docs"
21+
empty-state-title="Ask questions about SQLite Cloud"
22+
empty-state-description="Get help with SQLite Cloud documentation"
23+
>
24+
</docs-chatbot>
25+
2126
<script>
22-
DocsChatbot.init({
23-
containerId: "my-chatbot",
24-
searchUrl: window.ChatbotConfig.searchUrl,
25-
apiKey: window.ChatbotConfig.apiKey,
26-
title: "SQLite Cloud Docs",
27-
emptyState: {
28-
title: "Ask questions about SQLite Cloud",
29-
description: "Get help with SQLite Cloud documentation",
30-
},
31-
});
27+
const chatbot = document.querySelector("docs-chatbot");
28+
chatbot.setAttribute("search-url", window.ChatbotConfig.searchUrl);
29+
chatbot.setAttribute("api-key", window.ChatbotConfig.apiKey);
30+
chatbot.setAttribute(
31+
"style",
32+
"--primary: #f5426c; --primary-foreground: #ffffff"
33+
);
3234
</script>
3335
</body>
3436
</html>

lib/web.ts

Lines changed: 69 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -4,57 +4,91 @@ import createShadowRoot from "./createShadowRoot";
44
import cssText from "../src/index.css?inline";
55
import { ShadowRootProvider } from "@/providers/ShadowRootProvider";
66

7-
type DocsChatbotConfig = {
8-
containerId: string;
9-
} & DocsChatbotProps;
7+
class DocsChatbotElement extends HTMLElement {
8+
private root: ReturnType<typeof createShadowRoot>["root"] | null = null;
9+
private portalContainer: HTMLDivElement | null = null;
1010

11-
class DocsChatbotWidget {
12-
init(config: DocsChatbotConfig): void {
13-
const { containerId, ...chatbotProps } = config;
14-
15-
const container = document.getElementById(containerId);
16-
if (!container) {
17-
console.error(
18-
`SqliteAi Chatbot: Container with id "${containerId}" not found`
19-
);
20-
return;
21-
}
11+
static get observedAttributes() {
12+
return [
13+
"search-url",
14+
"api-key",
15+
"title",
16+
"empty-state-title",
17+
"empty-state-description",
18+
];
19+
}
2220

21+
connectedCallback() {
2322
try {
24-
const { root, shadow } = createShadowRoot(container, cssText);
23+
const { root, shadow } = createShadowRoot(this, cssText);
24+
this.root = root;
2525

26-
// Create portal container for dialogs inside shadow root
2726
const portalContainer = document.createElement("div");
2827
portalContainer.id = "portal-container";
28+
this.portalContainer = portalContainer;
2929
shadow.appendChild(portalContainer);
3030

31-
root.render(
32-
React.createElement(
33-
ShadowRootProvider,
34-
{ value: portalContainer },
35-
React.createElement(DocsChatbot, chatbotProps)
36-
)
37-
);
31+
this.render();
3832
} catch (error) {
3933
console.error("SqliteAi Chatbot: Failed to initialize", error);
4034
}
4135
}
42-
}
4336

44-
const chatbotWidget = new DocsChatbotWidget();
37+
attributeChangedCallback() {
38+
if (this.root) {
39+
this.render();
40+
}
41+
}
42+
43+
private render() {
44+
if (!this.root || !this.portalContainer) return;
4545

46-
declare global {
47-
interface Window {
48-
DocsChatbot: DocsChatbotWidget;
46+
const searchUrl = this.getAttribute("search-url");
47+
const apiKey = this.getAttribute("api-key");
48+
const title = this.getAttribute("title");
49+
const emptyStateTitle = this.getAttribute("empty-state-title");
50+
const emptyStateDescription = this.getAttribute("empty-state-description");
51+
52+
if (!searchUrl || !apiKey || !title) {
53+
console.error(
54+
"SqliteAi Chatbot: Missing required attributes (search-url, api-key, title)"
55+
);
56+
return;
57+
}
58+
59+
const chatbotProps: DocsChatbotProps = {
60+
searchUrl,
61+
apiKey,
62+
title,
63+
...(emptyStateTitle &&
64+
emptyStateDescription && {
65+
emptyState: {
66+
title: emptyStateTitle,
67+
description: emptyStateDescription,
68+
},
69+
}),
70+
};
71+
72+
this.root.render(
73+
React.createElement(
74+
ShadowRootProvider,
75+
{ value: this.portalContainer },
76+
React.createElement(DocsChatbot, chatbotProps)
77+
)
78+
);
4979
}
50-
var DocsChatbot: DocsChatbotWidget | undefined;
51-
}
5280

53-
if (typeof window !== "undefined") {
54-
window.DocsChatbot = chatbotWidget;
81+
disconnectedCallback() {
82+
if (this.root) {
83+
this.root.unmount();
84+
this.root = null;
85+
this.portalContainer = null;
86+
}
87+
}
5588
}
56-
if (typeof globalThis !== "undefined") {
57-
globalThis.DocsChatbot = chatbotWidget;
89+
90+
if (typeof window !== "undefined" && !customElements.get("docs-chatbot")) {
91+
customElements.define("docs-chatbot", DocsChatbotElement);
5892
}
5993

60-
export default chatbotWidget;
94+
export default DocsChatbotElement;

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "@sqliteai/docs-chatbot",
33
"description": "Documentation search chatbot powered by SQLite and AI",
4-
"version": "0.0.4",
4+
"version": "0.0.5",
55
"license": "MIT",
66
"type": "module",
77
"main": "./dist/cjs/index.cjs.js",

0 commit comments

Comments
 (0)