Skip to content

Commit

Permalink
enable button style customization
Browse files Browse the repository at this point in the history
  • Loading branch information
Mahmoudz committed Apr 16, 2024
1 parent dc9327b commit 39d0b40
Show file tree
Hide file tree
Showing 2 changed files with 160 additions and 156 deletions.
75 changes: 65 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,21 +292,76 @@ export default TodoApp;

## Customization

### Change Button Color
### Button Color

You can modify the colors of the `AiAssistantButton` at different states:
Modify the colors of the `AiAssistantButton` at different states:

```js
const colors = {
STATE_IDLE: '#4a6cf6', // Default
STATE_LISTENING_START: '#F64A7B', // Red
STATE_THINKING_START: '#4ac2f6', // Blue
STATE_SPEAKING_START: '#4af67f', // Green
};

<AiAssistantButton buttonColors={colors} />
const colors = {
STATE_IDLE: '#4a6cf6', // Default
STATE_LISTENING_START: '#F64A7B', // Red
STATE_THINKING_START: '#4ac2f6', // Blue
STATE_SPEAKING_START: '#4af67f', // Green
};

<AiAssistantButton buttonColors={colors} />
```

### Button Style & Position

Pass a `style` object to adjust dimensions, position, and appearance:

```js
const customStyle = {
width: "100px", // Button width
height: "100px", // Button height
fontSize: "50px", // Font size of the icon/text inside the button
borderRadius: "20%", // Border radius to control the curvature of the button corners
position: "relative", // Positioning of the button, 'absolute' or 'relative' to its normal position
bottom: "50px", // Distance from the bottom of its container (use with 'position: absolute')
right: "50px", // Distance from the right of its container (use with 'position: absolute')
zIndex: 999, // Z-index for layering controls
border: "none", // Border properties
boxShadow: "0px 4px 8px rgba(0, 0, 0, 0.5)", // Box shadow properties
transition: "background-color 0.3s ease-in-out", // Transition effect for hover or click events
display: "flex", // CSS display property
justifyContent: "center", // Aligns children (e.g., icon) horizontally
alignItems: "center", // Aligns children (e.g., icon) vertically
color: "#FFF", // Color of the text/icon inside the button
};

<AiAssistantButton style={customStyle} />
```

### Button Advanced Styling

Apply CSS classes for complex styling:

```js
.my-custom-button {
padding: 10px 20px;
transition: all 0.5s ease;

/* Hover effect */
&:hover {
background-color: #365f8c;
transform: scale(1.1);
}

/* Responsive adjustments */
@media (max-width: 600px) {
width: 100%;
font-size: 14px;
}
}

<AiAssistantButton className="my-custom-button" />
```

Use the `style` prop for inline adjustments or `className` for stylesheet-based customizations.



### Modify AI Responses

Customize AI assistant behavior via the [Admin Panel](https://admin.sista.ai/applications) by providing your `custom prompt` and `training data`.
Expand Down
241 changes: 95 additions & 146 deletions src/AiAssistantButton.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,161 +2,110 @@

import React, { useState, useEffect } from 'react';
import { useAiAssistant } from './AiAssistantContext';
import { FaMicrophone } from 'react-icons/fa';
import { FaVolumeUp } from 'react-icons/fa';
import { FaSatelliteDish } from 'react-icons/fa';
import styled, { keyframes, css } from 'styled-components';
import { FaMicrophone, FaVolumeUp, FaSatelliteDish } from 'react-icons/fa';

const pulse = keyframes`
0% {
box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.6);
}
100% {
box-shadow: 0 0 0 30px rgba(255, 255, 255, 0);
}
`;

const PulseAnimation = css`
animation: ${pulse} 2s cubic-bezier(0.4, 0, 0.2, 1) forwards;
`;
const spin = keyframes`
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
`;

const bounce = keyframes`
0%, 100% {
transform: scale(0.7);
}
50% {
transform: scale(1.2);
}
`;

// TODO: let user pass style object, and add the color to the initial colors prop
// styles={{
// width: '75px',
// height: '75px',
// fontSize: '35px',
// bottom: '75px',
// right: '75px',
// mobileWidth: '65px',
// mobileHeight: '65px',
// mobileFontSize: '30px',
// mobileBottom: '25px',
// mobileRight: '25px',
// }}

const Button = styled.button`
width: 75px;
height: 75px;
font-size: 35px;
bottom: 75px;
right: 75px;
@media (max-width: 768px) {
width: 65px;
height: 65px;
font-size: 30px;
bottom: 25px;
right: 25px;
}
color: #ffffff;
position: fixed;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
border: none;
box-shadow: 0px 0px 15px rgba(0, 0, 0, 0.7);
transition: background-color 0.3s ease-in-out;
background-color: ${(props) => props.color};
z-index: 9999;
&:hover {
border: 1.5px solid #fff;
}
&.STATE_LISTENING_START {
animation: ${spin} 2s infinite;
}
&.STATE_THINKING_START {
animation: ${bounce} 2s infinite linear;
}
const injectStyles = (keyframes) => {
const style = document.createElement('style');
style.textContent = keyframes;
document.head.appendChild(style);
};

&.STATE_SPEAKING_START {
animation: ${pulse} 1.5s infinite;
}
const keyframes = `
@keyframes spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}
@keyframes bounce {
0%, 100% { transform: scale(0.7); }
50% { transform: scale(1.2); }
}
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.6); }
100% { box-shadow: 0 0 0 30px rgba(255, 255, 255, 0); }
}
`;

const AiAssistantButton = ({
buttonText = 'Record',
buttonColors = {},
...props
buttonText = 'Record',
buttonColors = {},
style = {}, // Users can now pass custom styles including position properties
...props
}) => {
const aiAssistant = useAiAssistant();
const [recordingState, setRecordingState] = useState('STATE_IDLE');
const [isButtonDisabled, setButtonDisabled] = useState(false);

// Default buttonColors object
const defaultButtonColors = {
STATE_IDLE: '#4a6cf6', // Original color
STATE_LISTENING_START: '#F64A7B', // Red
STATE_THINKING_START: '#015589', // Blue
STATE_SPEAKING_START: '#019a9a', // Green
};

// Merge the provided buttonColors prop with the default colors
const colors = { ...defaultButtonColors, ...buttonColors };

const handleButtonClick = () => {
if (aiAssistant) {
aiAssistant.startProcessing();
}
};
useEffect(() => {
if (aiAssistant) {
const handleStateChange = (newState) => {
setRecordingState(newState);
setButtonDisabled(newState !== 'STATE_IDLE');
};
const aiAssistant = useAiAssistant();
const [recordingState, setRecordingState] = useState('STATE_IDLE');
const [isButtonDisabled, setButtonDisabled] = useState(false);

useEffect(() => {
injectStyles(keyframes);
}, []);

// Default buttonColors object
const defaultButtonColors = {
STATE_IDLE: '#4a6cf6',
STATE_LISTENING_START: '#F64A7B',
STATE_THINKING_START: '#015589',
STATE_SPEAKING_START: '#019a9a',
};

const colors = { ...defaultButtonColors, ...buttonColors };

const handleButtonClick = () => {
if (aiAssistant) {
aiAssistant.startProcessing();
}
};

aiAssistant.on('stateChange', handleStateChange);
useEffect(() => {
if (aiAssistant) {
const handleStateChange = (newState) => {
setRecordingState(newState);
setButtonDisabled(newState !== 'STATE_IDLE');
};

return () => {
aiAssistant.off('stateChange', handleStateChange);
};
}
}, [aiAssistant]);
aiAssistant.on('stateChange', handleStateChange);

return (
<Button
onClick={handleButtonClick}
disabled={isButtonDisabled}
className={recordingState}
color={colors[recordingState]}
{...props}
>
{recordingState === 'STATE_THINKING_START' ? (
<FaSatelliteDish />
) : recordingState === 'STATE_SPEAKING_START' ? (
<FaVolumeUp />
) : (
<FaMicrophone />
)}
</Button>
);
return () => {
aiAssistant.off('stateChange', handleStateChange);
};
}
}, [aiAssistant]);

const baseStyle = {
width: '75px',
height: '75px',
fontSize: '35px',
color: '#ffffff',
borderRadius: '50%',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
border: 'none',
boxShadow: '0px 0px 15px rgba(0, 0, 0, 0.7)',
transition: 'background-color 0.3s ease-in-out',
backgroundColor: colors[recordingState],
animation: recordingState === 'STATE_LISTENING_START' ? 'spin 2s infinite' :
recordingState === 'STATE_THINKING_START' ? 'bounce 2s infinite' :
recordingState === 'STATE_SPEAKING_START' ? 'pulse 1.5s infinite' : 'none',
...style, // Custom styles override default styles
position: style.position || 'fixed', // Use 'fixed' as default if not overridden
bottom: style.bottom || '75px',
right: style.right || '75px',
zIndex: style.zIndex || 9999
};


return (
<button
onClick={handleButtonClick}
disabled={isButtonDisabled}
style={baseStyle}
{...props}
>
{recordingState === 'STATE_THINKING_START' ? <FaSatelliteDish /> :
recordingState === 'STATE_SPEAKING_START' ? <FaVolumeUp /> :
<FaMicrophone />}
</button>
);
};

export { AiAssistantButton };

0 comments on commit 39d0b40

Please sign in to comment.