Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Feature/adding additional props #6

Merged
merged 20 commits into from
Dec 20, 2023
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,17 @@ You should be able to see the chatbot embedded in your app

<img width="858" alt="image" src="https://github.com/stackai/react-stackai/assets/32944505/6df0c532-c85c-4d82-b004-9b4612f52139">

## Props

These are all the props you can pass to the `<Stack />` component.

| Name | Type | Description
| ---- | ------ | -----------
| `project` | `string` | The URL of the project you want to embed
| `width` | `string` | Specifies the width of the iframe. The value must be a string with a numeric value followed by a unit (e.g., '35rem', '100px'). The default is '35rem'. If the width is less than the minimum width of 15 rem, a warning is logged, and the width is adjusted to the minimum. If the width is specified without a recognizable unit or is an invalid string, an error is thrown.
| `fixed` | `boolean` | Set to true if you want the chatbot to be fixed to the bottom of the screen, or false if you want it to be relative to the page.

`height` of the iframe is automatically set to 38.5 rem.

## Contributing

Expand Down
6 changes: 5 additions & 1 deletion example/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ function App() {
return (
<>
<h1>Demo website</h1>
<Stack project="https://www.stack-ai.com/embed/46bf5b6a-9b4d-48f6-8a13-cdfc4fe58520/11da0c81-afe2-4ccd-b498-807bbde8e7f1/653fefcfcc37c0093d55e6a9" />
<Stack
project="https://www.stack-ai.com/embed/46bf5b6a-9b4d-48f6-8a13-cdfc4fe58520/11da0c81-afe2-4ccd-b498-807bbde8e7f1/653fefcfcc37c0093d55e6a9"
width = {'35rem'}
fixed = {true}
/>
</>
);
}
Expand Down
20 changes: 19 additions & 1 deletion package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,7 @@
"lodash.debounce": "^4.0.8",
"react": ">=17.0.0",
"react-dom": ">=17.0.0",
"react-iframe": "^1.8.5",
"react-merge-refs": "^2.1.1"
"react-iframe": "^1.8.5"
},
"peerDependencies": {
"react": ">=17.0.0",
Expand Down
74 changes: 56 additions & 18 deletions src/Stack.tsx
Original file line number Diff line number Diff line change
@@ -1,48 +1,86 @@
import { forwardRef, useEffect, LegacyRef } from 'react';
import { forwardRef, LegacyRef, useEffect } from 'react';

type StackProps = {
project: string;
houmland marked this conversation as resolved.
Show resolved Hide resolved
innerRef?: LegacyRef<HTMLIFrameElement> | undefined;
width?: string;
fixed?: boolean;
};

// We are forwarding the ref to the iframe so that the user has access to it.
const Stack = forwardRef(function Stack({ project, innerRef }: StackProps) {
// Resizes based on the open/close state of the chatbot
const Stack = forwardRef(function Stack({
project,
innerRef,
width = '35rem',
fixed = true
}: StackProps) {

const height = '38.5rem';

const adjustWidth = (w: string) => {
const minWidth = '15rem';
const minWidthNumeric = parseFloat(minWidth);
const unitMatch = w.match(/(rem|px|em|%)$/);
let adjustedWidth = '';

if (unitMatch) {
const numericPart = parseFloat(w);
if (isNaN(numericPart)) {
throw new Error(`Invalid width: "${w}". The numeric part of the width is not a valid number.`);
}

if (numericPart < minWidthNumeric) {
console.warn(`Width is too small (${numericPart}${unitMatch[0]}). Adjusting to minimum width (${minWidth}${unitMatch[0]}).`);
adjustedWidth = minWidth;
} else {
adjustedWidth = w;
}
} else {
throw new Error(`Invalid width: "${w}". Width must be a numeric value followed by a unit (e.g., '35rem', '100px').`);
}

return { adjustedWidth };
};

useEffect(() => {
const iframe = document.getElementById('responsiveIframe');
if (iframe) {
iframe.style.width = '90px';
iframe.style.height = '90px';
try {
// Adjust width only as height is now constant
const { adjustedWidth } = adjustWidth(width);
iframe.style.width = adjustedWidth;
iframe.style.height = height;
} catch (error) {
console.error(error);
}
}

const handleMessage = (event: MessageEvent) => {
const iframe = document.getElementById('responsiveIframe');
if (iframe && event.data.type === 'chatbotStateChange') {
if (iframe && event.data.isClosed) {
setTimeout(() => {
iframe.style.width = '90px';
iframe.style.height = '90px';
}, 300);
} else {
try {
const isMobile = window.innerWidth < 1000;
if (isMobile) {
// Mobile
iframe.style.width = '100vw';
iframe.style.height = '38.5rem';
iframe.style.height = height;
} else {
// Desktop
iframe.style.width = '35rem';
iframe.style.height = '38.5rem';
const { adjustedWidth } = adjustWidth(width);
iframe.style.width = adjustedWidth;
iframe.style.height = height;
}
} catch (error) {
console.error(error);
}
}
};

window.addEventListener('message', handleMessage);

return () => {
window.removeEventListener('message', handleMessage);
};
}, []);
}, [width]);

return (
<iframe
Expand All @@ -52,13 +90,13 @@ const Stack = forwardRef(function Stack({ project, innerRef }: StackProps) {
className="chatbot-container"
allow="microphone"
style={{
position: 'fixed',
position: fixed ? 'fixed' : 'static',
zIndex: '100',
overflow: 'hidden',
bottom: '0',
right: '0',
border: 'none',
borderRadius: '10px',
borderRadius: '10px'
}}
/>
);
Expand Down
Loading