Skip to content

Commit 36326f0

Browse files
authored
Created PromptInput component and moved the other components to Metronome (#81)
* Add new button styles and button states * Initial implementation of PromptInput components and styles in Metronome * Updated button styling * Fix other button stylings and add useTypingSimulation hook * Add Popover component and fix styling issues * Add Desktop Color Swatch Picker * Add SwitchTabPicker component for selecting image styles * Updated ThreeDots icon * Modify Radio component to add support for displaying color swatch * Add MobileOptionsPopover component for displaying the options in mobile * Use MobileOptionsPopover component to display the mobile list and move the mockData * Fix issue with Popover width in mobile * Highlight button when menu is active * Removed typing animation when enhancing prompt and just do a simple fade-in animation * refactor: Use regular if/else and add comments to make it easier to read * refactor: Remove extra space and applied suggested usage of classnames * refactor: Refactored use of classnames * refactor: Refactored code to use classnames * refactor: Remove setTimeout initially added to make sure popup closes first before calling onEnhancePrompt * refactor: use classnames lib and add pointer-events: none in css * refactor: use classnames lib and combined isControlled and onToggle condition * Added comment to clarify what the line of code is doing * refactor: Used the css variables * refactor: Replace with css variables if available * refactor: Simplified the use of classnames * Increment version
1 parent 1936325 commit 36326f0

31 files changed

+2155
-33
lines changed

components/Icons.es6.js

Lines changed: 15 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1991,10 +1991,10 @@ function Microphone ({ className, width='16', height='16', color = '#766bff', vi
19911991
function ThreeDots ({ className, width='16', height='16', color = '#766bff', viewBox='0 0 16 16' }) {
19921992
return (
19931993
<svg width={width} height={height} viewBox={viewBox} fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
1994-
<circle cx="3" cy="8" r="1.5" fill={color} />
1995-
<circle cx="8" cy="8" r="1.5" fill={color} />
1996-
<circle cx="13" cy="8" r="1.5" fill={color} />
1997-
</svg>
1994+
<path d="M14.0844 3.86047C14.0844 5.50448 12.8558 6.83722 11.3404 6.83722C9.82492 6.83722 8.5964 5.50448 8.5964 3.86047C8.5964 2.21646 9.82492 0.883728 11.3404 0.883728C12.8558 0.883728 14.0844 2.21646 14.0844 3.86047Z" fill={color}/>
1995+
<path d="M5.85242 3.86047C5.85242 5.50448 4.6239 6.83722 3.10845 6.83722C1.59299 6.83722 0.364471 5.50448 0.364471 3.86047C0.364471 2.21646 1.59299 0.883728 3.10845 0.883728C4.6239 0.883728 5.85242 2.21646 5.85242 3.86047Z" fill={color}/>
1996+
<path d="M22.3163 3.86047C22.3163 5.50448 21.0878 6.83722 19.5723 6.83722C18.0568 6.83722 16.8283 5.50448 16.8283 3.86047C16.8283 2.21646 18.0568 0.883728 19.5723 0.883728C21.0878 0.883728 22.3163 2.21646 22.3163 3.86047Z" fill={color}/>
1997+
</svg>
19981998
)
19991999
}
20002000

@@ -2006,6 +2006,14 @@ function PencilFilled ({ className, width='16', height='16', color = '#766bff',
20062006
)
20072007
}
20082008

2009+
function Stop ({ className, width='16', height='16', color = '#766bff', viewBox='0 0 10 10' }) {
2010+
return (
2011+
<svg width={width} height={height} viewBox={viewBox} fill="none" xmlns="http://www.w3.org/2000/svg" className={className}>
2012+
<path d="M0 2C0 0.89543 0.895431 0 2 0H8C9.10457 0 10 0.895431 10 2V8C10 9.10457 9.10457 10 8 10H2C0.89543 10 0 9.10457 0 8V2Z" fill={color} />
2013+
</svg>
2014+
)
2015+
}
2016+
20092017
const icons = [
20102018
Check,
20112019
Checkmark,
@@ -2205,7 +2213,8 @@ const icons = [
22052213
AiLogo,
22062214
Microphone,
22072215
ThreeDots,
2208-
PencilFilled
2216+
PencilFilled,
2217+
Stop
22092218
]
22102219

22112220
each(icons, (icon) => {
@@ -2232,6 +2241,5 @@ export { Check, Checkmark, Lock, Notice, Disabled, Help, Search, Draggable,
22322241
Flag, Testimonials, Chart, Pause, HubspotLogo, AlignLeftV2, AlignRightV2, AlignCenterV2, AlignJustifyV2, Sidebar, Palette, Lightbox, Filter, Slider,
22332242
Flash, Dashboard, Guides, Article, Pulse, Edit, ContentApproved, Dragger, PaperClip, Invoicing, Banking, Checkbox, Circle, Bank, Card, Contract,
22342243
MinusCircle, RepeatCircle, BadgeCard, HeadphonesMic, CheckmarkLarge, NoticeOutlined, Archive, Questionnaire, Upload, ShareFeedback, Spaceship, Zap, Tip,
2235-
Webpage, LayoutAlternative, GlobeAlternative, AiAssist, Flows, Highlight, ArrowCircleUp, ArrowUp, EditColor, AiImage,
2236-
AiLogo, Microphone, ThreeDots, PencilFilled
2244+
Webpage, LayoutAlternative, GlobeAlternative, AiAssist, Flows, Highlight, ArrowCircleUp, ArrowUp, EditColor, AiImage, AiLogo, Microphone, ThreeDots, PencilFilled, Stop
22372245
}

components/form/button/Button.es6.js

Lines changed: 71 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,37 @@ import classnames from 'classnames'
55
/**
66
* Buttons trigger actions when clicked.
77
*/
8-
function Button ({ label, badge, primary, danger, loading, superSmall, small, large, wide, icon, iconWithLabel, active, block, disabled, onClick, className, buttonRef, id, alternative, chevron, type, title }) {
8+
function Button ({ label, badge, primary, danger, loading, superSmall, small, large, wide, icon, iconWithLabel, active, block, disabled, onClick, className, buttonRef, id, alternative, chevron, type, title, round, roundedRectangle, hasSelection, selected, recording, highlighted }) {
9+
10+
const renderButtonContent = () => {
11+
// Icon with label
12+
if (iconWithLabel) {
13+
return (
14+
<React.Fragment>{icon} {label}</React.Fragment>
15+
)
16+
}
17+
18+
// Round button
19+
if (round) {
20+
// Show spinner if loading, otherwise show icon
21+
if (loading) {
22+
return <span className="button__spinner" />
23+
} else {
24+
return icon
25+
}
26+
}
27+
28+
// Regular button
29+
// Show icon if provided, otherwise show spinner or label
30+
if (icon) {
31+
return icon
32+
} else if (loading) {
33+
return <span className="button__spinner" />
34+
} else {
35+
return label
36+
}
37+
}
38+
939
return (<button
1040
className={classnames({
1141
button: true,
@@ -21,6 +51,12 @@ function Button ({ label, badge, primary, danger, loading, superSmall, small, la
2151
'button--icon-with-label': iconWithLabel,
2252
'button--alternative': alternative,
2353
'button--has-chevron': chevron,
54+
'button--round': round,
55+
'button--rounded-rectangle': roundedRectangle,
56+
'button--has-selection': hasSelection,
57+
'button--selected': selected,
58+
'button--recording-indicator': recording && round,
59+
'button--highlighted': highlighted && (round || roundedRectangle),
2460
active,
2561
}, className)}
2662
disabled={disabled}
@@ -30,17 +66,12 @@ function Button ({ label, badge, primary, danger, loading, superSmall, small, la
3066
id={id}
3167
title={title}
3268
>
33-
{iconWithLabel
34-
? <React.Fragment>{icon} {label}</React.Fragment>
35-
: icon || (
36-
loading
37-
? <span className="button__spinner" />
38-
: label
39-
)
40-
}
69+
{renderButtonContent()}
70+
4171
{badge && (
4272
<span className="button__badge">{badge}</span>
4373
)}
74+
4475
{chevron && (
4576
<span className="button__chevron"></span>
4677
)}
@@ -67,6 +98,12 @@ Button.defaultProps = {
6798
type: 'button',
6899
onClick: () => {},
69100
title: '',
101+
round: false,
102+
roundedRectangle: false,
103+
hasSelection: false,
104+
selected: false,
105+
recording: false,
106+
highlighted: false,
70107
}
71108

72109
Button.propTypes = {
@@ -151,6 +188,31 @@ Button.propTypes = {
151188
* HTML title attribute of the button.
152189
*/
153190
title: PropTypes.string,
191+
/**
192+
* Whether this should be a rounded button.
193+
*/
194+
round: PropTypes.bool,
195+
/**
196+
* Whether this should be a rounded-rectangle button.
197+
*/
198+
roundedRectangle: PropTypes.bool,
199+
/**
200+
* Whether this should have a selected item.
201+
*/
202+
hasSelection: PropTypes.bool,
203+
/**
204+
* Whether this is actively selected.
205+
*/
206+
selected: PropTypes.bool,
207+
/**
208+
* Whether this button is recording
209+
*/
210+
recording: PropTypes.bool,
211+
212+
/**
213+
* Whether this button is highlighted
214+
*/
215+
highlighted: PropTypes.bool,
154216
}
155217

156218
export default Button
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import React from 'react'
2+
import PropTypes from 'prop-types'
3+
4+
function ColorSwatchPickerDesktop ({ colors, selectedColor, onChange }) {
5+
const handleColorClick = (value) => {
6+
if (selectedColor === value) {
7+
onChange(null)
8+
} else {
9+
onChange(value)
10+
}
11+
}
12+
13+
return (
14+
<div className="color-swatch-picker-desktop">
15+
{colors.map((colorOption) => (
16+
<div
17+
key={colorOption.value}
18+
className={`color-swatch-picker-desktop__color-swatch ${selectedColor === colorOption.value ? 'selected' : ''}`}
19+
style={{ backgroundColor: colorOption.color }}
20+
onClick={() => handleColorClick(colorOption.value)}
21+
title={colorOption.label || colorOption.value}
22+
/>
23+
))}
24+
</div>
25+
)
26+
}
27+
28+
ColorSwatchPickerDesktop.propTypes = {
29+
colors: PropTypes.arrayOf(
30+
PropTypes.shape({
31+
value: PropTypes.string.isRequired,
32+
color: PropTypes.string.isRequired,
33+
label: PropTypes.string
34+
})
35+
).isRequired,
36+
selectedColor: PropTypes.string,
37+
onChange: PropTypes.func.isRequired
38+
}
39+
40+
ColorSwatchPickerDesktop.defaultProps = {
41+
colors: [],
42+
selectedColor: null,
43+
onChange: () => {}
44+
}
45+
46+
export default ColorSwatchPickerDesktop
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react'
2+
import classnames from 'classnames'
3+
import PropTypes from 'prop-types'
4+
5+
function PromptInput ({ children, className }) {
6+
return (
7+
<div className={classnames('ds-prompt-input', className)}>
8+
{children}
9+
</div>
10+
)
11+
}
12+
13+
PromptInput.propTypes = {
14+
className: PropTypes.string,
15+
children: PropTypes.node
16+
}
17+
18+
PromptInput.defaultProps = {
19+
className: '',
20+
children: null
21+
}
22+
23+
export default PromptInput
24+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react'
2+
import classnames from 'classnames'
3+
import PropTypes from 'prop-types'
4+
5+
function PromptInputActions ({ children, className }) {
6+
return (
7+
<div className={classnames('ds-prompt-input__actions', className)}>
8+
{children}
9+
</div>
10+
)
11+
}
12+
13+
PromptInputActions.defaultProps = {
14+
className: '',
15+
children: null
16+
}
17+
18+
PromptInputActions.propTypes = {
19+
className: PropTypes.string,
20+
children: PropTypes.node.isRequired,
21+
}
22+
23+
export default PromptInputActions
24+
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import React from 'react'
2+
import classnames from 'classnames'
3+
import PropTypes from 'prop-types'
4+
5+
function PromptInputCommands (props) {
6+
return (
7+
<div className={classnames('ds-prompt-input__actions-commands', props.className)}>
8+
{props.children}
9+
</div>
10+
)
11+
}
12+
13+
PromptInputCommands.propTypes = {
14+
className: PropTypes.string,
15+
children: PropTypes.node,
16+
}
17+
18+
PromptInputCommands.defaultProps = {
19+
className: '',
20+
children: null,
21+
}
22+
23+
export default PromptInputCommands
24+
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import React from 'react'
2+
import classnames from 'classnames'
3+
import PropTypes from 'prop-types'
4+
5+
function PromptInputSelectors ({ children, className }) {
6+
return (
7+
<div className={classnames('ds-prompt-input__actions-selectors', className)}>
8+
{children}
9+
</div>
10+
)
11+
}
12+
PromptInputSelectors.propTypes = {
13+
className: PropTypes.string,
14+
children: PropTypes.node,
15+
}
16+
17+
PromptInputSelectors.defaultProps = {
18+
className: '',
19+
children: null,
20+
}
21+
export default PromptInputSelectors
22+
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
import React from 'react'
2+
import PropTypes from 'prop-types'
3+
import classnames from 'classnames'
4+
5+
function PromptInputTextarea ({ className, placeholder, rows, textareaRef, value, onChange, disabled }) {
6+
return (
7+
<textarea
8+
className={classnames('ds-prompt-input__textarea', className)}
9+
placeholder={placeholder}
10+
rows={rows}
11+
ref={textareaRef}
12+
value={value}
13+
onChange={onChange}
14+
disabled={disabled}
15+
/>
16+
)
17+
}
18+
19+
PromptInputTextarea.defaultProps = {
20+
placeholder: 'placeholder',
21+
rows: 3,
22+
textareaRef: null,
23+
value: '',
24+
onChange: () => {},
25+
disabled: false
26+
}
27+
28+
PromptInputTextarea.propTypes = {
29+
className: PropTypes.string,
30+
placeholder: PropTypes.string,
31+
rows: PropTypes.number,
32+
textareaRef: PropTypes.oneOfType([
33+
PropTypes.func,
34+
PropTypes.shape({ current: PropTypes.instanceOf(Element) })
35+
]),
36+
value: PropTypes.string,
37+
onChange: PropTypes.func,
38+
disabled: PropTypes.bool
39+
}
40+
41+
export default PromptInputTextarea
42+
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export { default as PromptInput } from './PromptInput.es6.js'
2+
export { default as PromptInputTextarea } from './PromptInputTextarea.es6.js'
3+
export { default as PromptInputActions } from './PromptInputActions.es6.js'
4+
export { default as PromptInputSelectors } from './PromptInputSelectors.es6.js'
5+
export { default as PromptInputCommands } from './PromptInputCommands.es6.js'

0 commit comments

Comments
 (0)