@platformatic/ui-components 0.2.15 → 0.2.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@platformatic/ui-components",
3
3
  "description": "Platformatic UI Components",
4
- "version": "0.2.15",
4
+ "version": "0.2.18",
5
5
  "type": "module",
6
6
  "license": "Apache-2.0",
7
7
  "repository": {
@@ -3,6 +3,6 @@ import Button from './Button'
3
3
 
4
4
  export default function LoginButton ({ iconName, label, onClick, ...props }) {
5
5
  return (
6
- <Button backgroundColor='main-green' alt={label} label={label} platformaticIcon={{ iconName, size: 'medium', color: 'main-dark-blue' }} data-testid='login-button' size='extra-large' onClick={onClick} {...props} bold />
6
+ <Button backgroundColor='main-green' alt={label} label={label} platformaticIcon={{ iconName, size: 'medium', color: 'main-dark-blue' }} data-testid='login-button' size='extra-large' onClick={onClick} {...props} />
7
7
  )
8
8
  }
@@ -13,7 +13,6 @@ function TooltipV2 ({ tooltipClassName, text, visible, alignment, elementClassNa
13
13
  if (visible) {
14
14
  setClassName(visibleClassName())
15
15
  if (ref.current) {
16
- console.log('elementClassName', elementClassName)
17
16
  const referenceBoundingClientRect = document.getElementsByClassName(elementClassName)[0]?.getBoundingClientRect()
18
17
  if (referenceBoundingClientRect) {
19
18
  const topPosition = referenceBoundingClientRect.y - (referenceBoundingClientRect.height)
@@ -17,12 +17,9 @@ function Select ({
17
17
  borderColor,
18
18
  borderListColor,
19
19
  errorMessage,
20
- onChange,
21
20
  onSelect,
22
- onClear,
23
21
  disabled,
24
22
  mainColor,
25
- optionSelected,
26
23
  dataAttrName,
27
24
  dataAttrValue,
28
25
  backgroundColor,
@@ -30,18 +27,19 @@ function Select ({
30
27
  }) {
31
28
  const inputRef = useRef()
32
29
  const [showOptions, setShowOptions] = useState(false)
30
+ // eslint-disable-next-line no-unused-vars
33
31
  const [isSelected, setIsSelected] = useState(false)
34
32
  const [isOnFocus, setIsOnFocus] = useState(false)
33
+
35
34
  const showError = errorMessage.length > 0
36
35
  const containerClassName = `${styles.container} ${defaultContainerClassName} `
37
36
  let inputClassName = `${commonStyles.fullWidth} ${styles.select} ${inputTextClassName}`
38
37
  inputClassName += ' ' + styles[`select-${mainColor}`]
39
38
  inputClassName += ' ' + commonStyles[`text--${borderColor}`]
40
39
  let optionsClassName = `${styles.options} ${defaultOptionsClassName} `
41
- if (showError) inputClassName += ' ' + commonStyles['bordered--error-red']
42
- if (disabled) inputClassName += ' ' + commonStyles['apply-opacity-30']
43
40
  inputClassName += ' ' + commonStyles[`background-color-${backgroundColor}`]
44
41
  optionsClassName += commonStyles[`background-color-${backgroundColor}`]
42
+ const optionSelectedClassName = styles.optionSelected
45
43
 
46
44
  if (borderListColor) {
47
45
  optionsClassName += ' ' + styles['bordered-options']
@@ -55,12 +53,20 @@ function Select ({
55
53
  const optionSpanClassName = commonStyles[`text--${mainColor}`]
56
54
 
57
55
  const [wrapperClassName, setWrapperClassName] = useState(normalClassName())
56
+
58
57
  function onFocusClassName () {
59
58
  return inputClassName + ' ' + commonStyles[`bordered--${borderColor}-100`]
60
59
  }
61
60
 
62
61
  function normalClassName () {
63
- return inputClassName + ' ' + commonStyles[`bordered--${borderColor}-70`]
62
+ const baseClassName = inputClassName
63
+ if (showError) {
64
+ inputClassName += ' ' + commonStyles['bordered--error-red']
65
+ } else {
66
+ inputClassName += ' ' + commonStyles[`bordered--${borderColor}-70`]
67
+ }
68
+ if (disabled) inputClassName += ' ' + commonStyles['apply-opacity-30']
69
+ return baseClassName
64
70
  }
65
71
 
66
72
  const dataProps = {}
@@ -87,24 +93,24 @@ function Select ({
87
93
  }
88
94
 
89
95
  useEffect(() => {
90
- if (value.length > 0 && !showOptions && !isSelected) {
91
- setShowOptions(true)
92
- }
93
96
  if (value.length === 0) {
94
97
  setIsSelected(false)
95
98
  }
96
99
  }, [value])
97
100
 
98
101
  useEffect(() => {
99
- if (optionSelected) {
100
- setSelected(optionSelected)
102
+ if (disabled) {
103
+ const className = normalClassName() + ' ' + commonStyles['apply-opacity-30']
104
+ setWrapperClassName(className)
105
+ } else {
106
+ setWrapperClassName(normalClassName())
101
107
  }
102
- }, [optionSelected])
108
+ }, [disabled])
103
109
 
104
- function renderLi (option, index) {
110
+ function renderLi (option, index, isOptionSelected) {
105
111
  return (
106
112
  <li
107
- key={index} className={singleOptionClassName} onClick={() => {
113
+ key={index} className={`${isOptionSelected ? optionSelectedClassName : singleOptionClassName}`} onClick={() => {
108
114
  if (option.notSelectable) {
109
115
  return handleNotSelectable(option.onClick && option.onClick())
110
116
  }
@@ -121,34 +127,15 @@ function Select ({
121
127
  }
122
128
 
123
129
  function renderOptions () {
124
- if (value.length === 0) {
125
- return (
126
- <ul className={optionsClassName}>
127
- {options.length > 0 ? options.map((option, index) => renderLi(option, index)) : <li className={singleOptionClassName}><div className={styles.liContent}><span className={commonStyles[`text--${mainColor}`]}>No data found</span></div></li>}
128
- </ul>
129
- )
130
- }
131
130
  const notFilterableOptions = options.filter(option => option.notFilterable)
132
- const filteredOptions = options.filter(option => !option.notFilterable).filter(option => option.label.toLowerCase().includes(value.toLowerCase()))
133
-
134
131
  return (
135
132
  <ul className={optionsClassName}>
136
- {filteredOptions.length > 0 ? filteredOptions.map((option, index) => renderLi(option, index)) : <li className={singleOptionClassName}><div className={styles.liContent}><span className={commonStyles[`text--${mainColor}`]}>No data found</span></div></li>}
137
133
  {notFilterableOptions.length > 0 && notFilterableOptions.map((option, index) => renderLi(option, index))}
134
+ {options.length > 0 ? options.map((option, index) => renderLi(option, index, option.label === value)) : <li className={singleOptionClassName}><div className={styles.liContent}><span className={commonStyles[`text--${mainColor}`]}>No data found</span></div></li>}
138
135
  </ul>
139
136
  )
140
137
  }
141
138
 
142
- // https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-change-or-input-event-in-react-js
143
- function clearValue () {
144
- const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set
145
- nativeInputValueSetter.call(inputRef.current, '')
146
- const ev2 = new Event('input', { bubbles: true })
147
- ev2.simulated = true
148
- inputRef.current.dispatchEvent(ev2)
149
- onClear()
150
- }
151
-
152
139
  function handleFocus () {
153
140
  if (!isOnFocus) {
154
141
  setIsOnFocus(true)
@@ -161,7 +148,7 @@ function Select ({
161
148
  event.preventDefault()
162
149
  setTimeout(() => {
163
150
  if (showOptions) {
164
- setShowOptions(false)
151
+ // setShowOptions(false)
165
152
  setIsOnFocus(false)
166
153
  setWrapperClassName(normalClassName())
167
154
  }
@@ -171,10 +158,9 @@ function Select ({
171
158
  return (
172
159
  <div className={containerClassName} {...dataProps}>
173
160
  <div className={styles.selectContainer}>
174
- <input type='text' name={name} value={value} className={wrapperClassName} ref={inputRef} onChange={onChange} disabled={disabled} placeholder={placeholder} onFocus={() => handleFocus()} onBlur={(e) => handleBlur(e)} />
161
+ <input type='text' name={name} value={value} className={wrapperClassName} ref={inputRef} disabled={disabled} placeholder={placeholder} onFocus={() => handleFocus()} onBlur={(e) => handleBlur(e)} readOnly />
175
162
  <div className={styles.icons}>
176
- {value?.length > 0 && <PlatformaticIcon iconName='CircleCloseIcon' color={borderColor} onClick={() => clearValue()} size={SMALL} />}
177
- <PlatformaticIcon iconName={showOptions ? 'ArrowUpIcon' : 'ArrowDownIcon'} color={borderColor} onClick={() => disabled ? null : setShowOptions(!showOptions)} size={SMALL} />
163
+ <PlatformaticIcon iconName={showOptions ? 'ArrowUpIcon' : 'ArrowDownIcon'} color={borderColor} disabled={disabled} onClick={() => setShowOptions(!showOptions)} size={SMALL} />
178
164
  </div>
179
165
  </div>
180
166
  {showOptions && !showError && renderOptions()}
@@ -236,18 +222,10 @@ Select.propTypes = {
236
222
  * color of border UL
237
223
  */
238
224
  borderListColor: PropTypes.oneOf([MAIN_GREEN, MAIN_DARK_BLUE, WHITE]),
239
- /**
240
- * onChange
241
- */
242
- onChange: PropTypes.func,
243
225
  /**
244
226
  * onSelect
245
227
  */
246
228
  onSelect: PropTypes.func,
247
- /**
248
- * onClear
249
- */
250
- onClear: PropTypes.func,
251
229
  /**
252
230
  * Disabled
253
231
  */
@@ -256,16 +234,6 @@ Select.propTypes = {
256
234
  * mainColor
257
235
  */
258
236
  mainColor: PropTypes.oneOf([MAIN_DARK_BLUE, WHITE]),
259
- /**
260
- * optionSelected
261
- */
262
- optionSelected: PropTypes.shape({
263
- label: PropTypes.string,
264
- value: PropTypes.oneOfType([
265
- PropTypes.string,
266
- PropTypes.number
267
- ])
268
- }),
269
237
  /**
270
238
  * dataAttrName
271
239
  */
@@ -296,12 +264,9 @@ Select.defaultProps = {
296
264
  borderColor: MAIN_GREEN,
297
265
  borderListColor: '',
298
266
  errorMessage: '',
299
- onChange: () => {},
300
267
  onSelect: () => {},
301
- onClear: () => {},
302
268
  disabled: false,
303
269
  mainColor: MAIN_DARK_BLUE,
304
- optionSelected: null,
305
270
  dataAttrName: '',
306
271
  dataAttrValue: '',
307
272
  backgroundColor: WHITE,
@@ -17,7 +17,7 @@
17
17
  @apply outline-none;
18
18
  }
19
19
  .options {
20
- @apply absolute left-0 top-[42px] w-full max-h-[216px] overflow-y-scroll z-20;
20
+ @apply absolute left-0 top-[42px] w-[calc(100%-1rem)] max-h-[216px] overflow-y-auto z-20 p-2;
21
21
  }
22
22
  .bordered-options {
23
23
  @apply border border-solid rounded
@@ -25,6 +25,9 @@
25
25
  .option {
26
26
  @apply font-light cursor-pointer z-10 flex flex-row justify-between items-center px-4;
27
27
  }
28
+ .optionSelected {
29
+ @apply font-light cursor-pointer z-10 flex flex-row justify-between items-center px-4 bg-white/15;
30
+ }
28
31
  .bordered-bottom {
29
32
  @apply border-b
30
33
  }
@@ -33,7 +36,7 @@
33
36
  transform: translateY(-50%);
34
37
  }
35
38
  .liContent {
36
- @apply py-2 flex items-center gap-x-1 relative z-[-1] w-auto basis-2/3;
39
+ @apply py-2 flex items-center gap-x-1 relative z-[-1] w-full;
37
40
  }
38
41
  .descriptionValue {
39
42
  @apply opacity-70
@@ -0,0 +1,327 @@
1
+ 'use strict'
2
+ import React, { useState, useEffect, useRef } from 'react'
3
+ import PropTypes from 'prop-types'
4
+ import styles from './SelectWithInput.module.css'
5
+ import commonStyles from '../Common.module.css'
6
+ import { MAIN_DARK_BLUE, MAIN_GREEN, RICH_BLACK, SMALL, WHITE } from '../constants'
7
+ import PlatformaticIcon from '../PlatformaticIcon'
8
+
9
+ function SelectWithInput ({
10
+ defaultContainerClassName,
11
+ placeholder,
12
+ name,
13
+ value,
14
+ options,
15
+ defaultOptionsClassName,
16
+ optionsBorderedBottom,
17
+ borderColor,
18
+ borderListColor,
19
+ errorMessage,
20
+ onChange,
21
+ onSelect,
22
+ onClear,
23
+ disabled,
24
+ mainColor,
25
+ optionSelected,
26
+ dataAttrName,
27
+ dataAttrValue,
28
+ backgroundColor,
29
+ inputTextClassName
30
+ }) {
31
+ const inputRef = useRef()
32
+ const [showOptions, setShowOptions] = useState(false)
33
+ const [isSelected, setIsSelected] = useState(false)
34
+ const [isOnFocus, setIsOnFocus] = useState(false)
35
+
36
+ const showError = errorMessage.length > 0
37
+ const containerClassName = `${styles.container} ${defaultContainerClassName} `
38
+ let inputClassName = `${commonStyles.fullWidth} ${styles.select} ${inputTextClassName}`
39
+ inputClassName += ' ' + styles[`select-${mainColor}`]
40
+ inputClassName += ' ' + commonStyles[`text--${borderColor}`]
41
+ let optionsClassName = `${styles.options} ${defaultOptionsClassName} `
42
+ inputClassName += ' ' + commonStyles[`background-color-${backgroundColor}`]
43
+ optionsClassName += commonStyles[`background-color-${backgroundColor}`]
44
+
45
+ if (borderListColor) {
46
+ optionsClassName += ' ' + styles['bordered-options']
47
+ optionsClassName += ' ' + commonStyles[`bordered--${borderListColor}-30`]
48
+ }
49
+ let singleOptionClassName = `${styles.option} ` + commonStyles[`bordered--${mainColor}-70`] + ' ' + commonStyles[`hover-background-color-opaque-${mainColor}`]
50
+ if (optionsBorderedBottom) {
51
+ singleOptionClassName += ` ${styles['bordered-bottom']}`
52
+ }
53
+
54
+ const optionSpanClassName = commonStyles[`text--${mainColor}`]
55
+
56
+ const [wrapperClassName, setWrapperClassName] = useState(normalClassName())
57
+
58
+ function onFocusClassName () {
59
+ return inputClassName + ' ' + commonStyles[`bordered--${borderColor}-100`]
60
+ }
61
+
62
+ function normalClassName () {
63
+ const baseClassName = inputClassName
64
+ if (showError) {
65
+ inputClassName += ' ' + commonStyles['bordered--error-red']
66
+ } else {
67
+ inputClassName += ' ' + commonStyles[`bordered--${borderColor}-70`]
68
+ }
69
+ if (disabled) inputClassName += ' ' + commonStyles['apply-opacity-30']
70
+ return baseClassName
71
+ }
72
+
73
+ const dataProps = {}
74
+ if (dataAttrName && dataAttrValue) {
75
+ dataProps[`data-${dataAttrName}`] = dataAttrValue
76
+ }
77
+
78
+ function handleNotSelectable (callback = () => {}) {
79
+ setIsSelected(true)
80
+ setShowOptions(false)
81
+ callback()
82
+ }
83
+ function setSelected ({ label, value }) {
84
+ setIsSelected(true)
85
+ setShowOptions(false)
86
+ /* eslint-disable-next-line no-undef */
87
+ onSelect(new CustomEvent('valueSelected', {
88
+ detail: {
89
+ name,
90
+ label,
91
+ value
92
+ }
93
+ }))
94
+ }
95
+
96
+ useEffect(() => {
97
+ if (value.length > 0 && !showOptions && !isSelected) {
98
+ setShowOptions(true)
99
+ }
100
+ if (value.length === 0) {
101
+ setIsSelected(false)
102
+ }
103
+ }, [value])
104
+
105
+ useEffect(() => {
106
+ if (optionSelected) {
107
+ setSelected(optionSelected)
108
+ }
109
+ }, [optionSelected])
110
+
111
+ useEffect(() => {
112
+ if (disabled) {
113
+ const className = normalClassName() + ' ' + commonStyles['apply-opacity-30']
114
+ setWrapperClassName(className)
115
+ } else {
116
+ setWrapperClassName(normalClassName())
117
+ }
118
+ }, [disabled])
119
+
120
+ function renderLi (option, index) {
121
+ return (
122
+ <li
123
+ key={index} className={singleOptionClassName} onClick={() => {
124
+ if (option.notSelectable) {
125
+ return handleNotSelectable(option.onClick && option.onClick())
126
+ }
127
+ return setSelected(option)
128
+ }}
129
+ >
130
+ <div className={styles.liContent}>
131
+ {option.iconName && <PlatformaticIcon iconName={option.iconName} color={option.iconColor || mainColor} size={option.iconSize ?? SMALL} onClick={null} />}
132
+ <span className={optionSpanClassName}>{option.label}</span>
133
+ </div>
134
+ {option.descriptionValue && <span className={`${optionSpanClassName} ${styles.descriptionValue}`}>{option.descriptionValue}</span>}
135
+ </li>
136
+ )
137
+ }
138
+
139
+ function renderOptions () {
140
+ if (value.length === 0) {
141
+ return (
142
+ <ul className={optionsClassName}>
143
+ {options.length > 0 ? options.map((option, index) => renderLi(option, index)) : <li className={singleOptionClassName}><div className={styles.liContent}><span className={commonStyles[`text--${mainColor}`]}>No data found</span></div></li>}
144
+ </ul>
145
+ )
146
+ }
147
+ const notFilterableOptions = options.filter(option => option.notFilterable)
148
+ const filteredOptions = options.filter(option => !option.notFilterable).filter(option => option.label.toLowerCase().includes(value.toLowerCase()))
149
+
150
+ return (
151
+ <ul className={optionsClassName}>
152
+ {filteredOptions.length > 0 ? filteredOptions.map((option, index) => renderLi(option, index)) : <li className={singleOptionClassName}><div className={styles.liContent}><span className={commonStyles[`text--${mainColor}`]}>No data found</span></div></li>}
153
+ {notFilterableOptions.length > 0 && notFilterableOptions.map((option, index) => renderLi(option, index))}
154
+ </ul>
155
+ )
156
+ }
157
+
158
+ // https://stackoverflow.com/questions/23892547/what-is-the-best-way-to-trigger-change-or-input-event-in-react-js
159
+ function clearValue () {
160
+ const nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set
161
+ nativeInputValueSetter.call(inputRef.current, '')
162
+ const ev2 = new Event('input', { bubbles: true })
163
+ ev2.simulated = true
164
+ inputRef.current.dispatchEvent(ev2)
165
+ onClear()
166
+ }
167
+
168
+ function handleFocus () {
169
+ if (!isOnFocus) {
170
+ setIsOnFocus(true)
171
+ setWrapperClassName(onFocusClassName())
172
+ }
173
+ setShowOptions(true)
174
+ }
175
+
176
+ function handleBlur (event) {
177
+ event.preventDefault()
178
+ setTimeout(() => {
179
+ if (showOptions) {
180
+ setShowOptions(false)
181
+ setIsOnFocus(false)
182
+ setWrapperClassName(normalClassName())
183
+ }
184
+ }, 250)
185
+ }
186
+
187
+ return (
188
+ <div className={containerClassName} {...dataProps}>
189
+ <div className={styles.selectContainer}>
190
+ <input type='text' name={name} value={value} className={wrapperClassName} ref={inputRef} onChange={onChange} disabled={disabled} placeholder={placeholder} onFocus={() => handleFocus()} onBlur={(e) => handleBlur(e)} />
191
+ <div className={styles.icons}>
192
+ {value?.length > 0 && !disabled && <PlatformaticIcon iconName='CircleCloseIcon' color={borderColor} onClick={() => clearValue()} size={SMALL} />}
193
+ <PlatformaticIcon iconName={showOptions ? 'ArrowUpIcon' : 'ArrowDownIcon'} color={borderColor} disabled={disabled} onClick={() => setShowOptions(!showOptions)} size={SMALL} />
194
+ </div>
195
+ </div>
196
+ {showOptions && !showError && renderOptions()}
197
+ {showError && <span className={commonStyles['error-message']}>{errorMessage}</span>}
198
+ </div>
199
+ )
200
+ }
201
+
202
+ SelectWithInput.propTypes = {
203
+ /**
204
+ * defaultContainerClassName
205
+ */
206
+ defaultContainerClassName: PropTypes.string,
207
+ /**
208
+ * placeholder
209
+ */
210
+ placeholder: PropTypes.string,
211
+ /**
212
+ * name
213
+ */
214
+ name: PropTypes.oneOfType([
215
+ PropTypes.string,
216
+ PropTypes.number
217
+ ]),
218
+ /**
219
+ * value
220
+ */
221
+ value: PropTypes.string,
222
+ /**
223
+ * options
224
+ */
225
+ options: PropTypes.arrayOf(PropTypes.shape({
226
+ label: PropTypes.string,
227
+ value: PropTypes.oneOfType([
228
+ PropTypes.string,
229
+ PropTypes.number
230
+ ]),
231
+ iconName: PropTypes.string,
232
+ iconSize: PropTypes.string,
233
+ iconColor: PropTypes.string,
234
+ notSelectable: PropTypes.bool,
235
+ notFilterable: PropTypes.bool,
236
+ onClick: PropTypes.func,
237
+ descriptionValue: PropTypes.string
238
+ })),
239
+ /**
240
+ * defaultOptionsClassName
241
+ */
242
+ defaultOptionsClassName: PropTypes.string,
243
+ /**
244
+ * optionsBorderedBottom
245
+ */
246
+ optionsBorderedBottom: PropTypes.bool,
247
+ /**
248
+ * color of border
249
+ */
250
+ borderColor: PropTypes.oneOf([MAIN_GREEN, MAIN_DARK_BLUE, WHITE]),
251
+ /**
252
+ * color of border UL
253
+ */
254
+ borderListColor: PropTypes.oneOf([MAIN_GREEN, MAIN_DARK_BLUE, WHITE]),
255
+ /**
256
+ * onChange
257
+ */
258
+ onChange: PropTypes.func,
259
+ /**
260
+ * onSelect
261
+ */
262
+ onSelect: PropTypes.func,
263
+ /**
264
+ * onClear
265
+ */
266
+ onClear: PropTypes.func,
267
+ /**
268
+ * Disabled
269
+ */
270
+ disabled: PropTypes.bool,
271
+ /**
272
+ * mainColor
273
+ */
274
+ mainColor: PropTypes.oneOf([MAIN_DARK_BLUE, WHITE]),
275
+ /**
276
+ * optionSelected
277
+ */
278
+ optionSelected: PropTypes.shape({
279
+ label: PropTypes.string,
280
+ value: PropTypes.oneOfType([
281
+ PropTypes.string,
282
+ PropTypes.number
283
+ ])
284
+ }),
285
+ /**
286
+ * dataAttrName
287
+ */
288
+ dataAttrName: PropTypes.string,
289
+ /**
290
+ * dataAttrValue
291
+ */
292
+ dataAttrValue: PropTypes.string,
293
+ /**
294
+ * backgroundColor
295
+ */
296
+ backgroundColor: PropTypes.oneOf([MAIN_DARK_BLUE, WHITE, RICH_BLACK]),
297
+ /**
298
+ * inputTextClassName
299
+ */
300
+ inputTextClassName: PropTypes.string
301
+ }
302
+
303
+ SelectWithInput.defaultProps = {
304
+ defaultContainerClassName: '',
305
+ placeholder: 'this is the default',
306
+ name: '',
307
+ value: '',
308
+ id: '',
309
+ options: [],
310
+ defaultOptionsClassName: '',
311
+ optionsBorderedBottom: true,
312
+ borderColor: MAIN_GREEN,
313
+ borderListColor: '',
314
+ errorMessage: '',
315
+ onChange: () => {},
316
+ onSelect: () => {},
317
+ onClear: () => {},
318
+ disabled: false,
319
+ mainColor: MAIN_DARK_BLUE,
320
+ optionSelected: null,
321
+ dataAttrName: '',
322
+ dataAttrValue: '',
323
+ backgroundColor: WHITE,
324
+ inputTextClassName: ''
325
+ }
326
+
327
+ export default SelectWithInput
@@ -0,0 +1,40 @@
1
+ .container {
2
+ @apply flex flex-col w-full relative
3
+ }
4
+ .selectContainer {
5
+ @apply w-full flex items-center relative z-10;
6
+ }
7
+ .select {
8
+ @apply py-1.5 px-2 md:px-3 md:py-[10.5px] h-full border border-solid box-border rounded;
9
+ }
10
+
11
+ .select-main-dark-blue.active,
12
+ .select-main-dark-blue:focus {
13
+ @apply shadow-main-dark-blue outline-none;
14
+ }
15
+ .select-white.active,
16
+ .select-white:focus {
17
+ @apply outline-none;
18
+ }
19
+ .options {
20
+ @apply absolute left-0 top-[42px] w-full max-h-[216px] overflow-y-scroll z-20;
21
+ }
22
+ .bordered-options {
23
+ @apply border border-solid rounded
24
+ }
25
+ .option {
26
+ @apply font-light cursor-pointer z-10 flex flex-row justify-between items-center px-4;
27
+ }
28
+ .bordered-bottom {
29
+ @apply border-b
30
+ }
31
+ .icons {
32
+ @apply absolute top-[50%] right-[0.5rem] flex gap-x-2;
33
+ transform: translateY(-50%);
34
+ }
35
+ .liContent {
36
+ @apply py-2 flex items-center gap-x-1 relative z-[-1] w-auto basis-2/3;
37
+ }
38
+ .descriptionValue {
39
+ @apply opacity-70
40
+ }
@@ -5,6 +5,7 @@ import Password from './Password'
5
5
  import Preview from './Preview'
6
6
  import RadioGroup from './RadioGroup'
7
7
  import Select from './Select'
8
+ import SelectWithInput from './SelectWithInput'
8
9
  import TextArea from './TextArea'
9
10
  import ToggleSwitch from './ToggleSwitch'
10
11
 
@@ -16,6 +17,7 @@ export default {
16
17
  Preview,
17
18
  RadioGroup,
18
19
  Select,
20
+ SelectWithInput,
19
21
  TextArea,
20
22
  ToggleSwitch
21
23
  }
@@ -35,18 +35,13 @@ Default.args = {
35
35
  const TemplateBorderMainDarkBlue = (args) => {
36
36
  const [value, setValue] = useState('')
37
37
 
38
- function handleChange (event) {
39
- setValue(event.target.value)
40
- }
41
-
42
38
  function handleSelect (event) {
43
39
  setValue(event.detail.label)
44
40
  }
45
41
 
46
42
  return (
47
43
  <div style={{ backgroundColor: 'white', padding: '20px 10px' }}>
48
- <p>Value of the input {value}</p>
49
- <Select {...args} value={value} onChange={handleChange} onSelect={handleSelect} />
44
+ <Select {...args} value={value} onSelect={handleSelect} />
50
45
  </div>
51
46
  )
52
47
  }
@@ -54,10 +49,6 @@ const TemplateBorderMainDarkBlue = (args) => {
54
49
  const TemplateBorderMainDarkBlue2 = (args) => {
55
50
  const [value, setValue] = useState('')
56
51
 
57
- function handleChange (event) {
58
- setValue(event.target.value)
59
- }
60
-
61
52
  function handleSelect (event) {
62
53
  setValue(event.detail.label)
63
54
  }
@@ -66,7 +57,7 @@ const TemplateBorderMainDarkBlue2 = (args) => {
66
57
  <>
67
58
  <div style={{ backgroundColor: 'white', padding: '20px 10px' }}>
68
59
  <p>Value of the input {value}</p>
69
- <Select {...args} value={value} onChange={handleChange} onSelect={handleSelect} />
60
+ <Select {...args} value={value} onSelect={handleSelect} />
70
61
  <br />
71
62
  <Select placeholder='Disabled' disabled />
72
63
 
@@ -146,10 +137,6 @@ githubRepoExample.args = {
146
137
  const TemplateTransparent = (args) => {
147
138
  const [value, setValue] = useState('')
148
139
 
149
- function handleChange (event) {
150
- setValue(event.target.value)
151
- }
152
-
153
140
  function handleSelect (event) {
154
141
  setValue(event.detail.label)
155
142
  }
@@ -157,7 +144,7 @@ const TemplateTransparent = (args) => {
157
144
  return (
158
145
  <div style={{ padding: '20px 10px' }}>
159
146
  <p>Value of the input {value}</p>
160
- <Select {...args} value={value} onChange={handleChange} onSelect={handleSelect} />
147
+ <Select {...args} value={value} onSelect={handleSelect} />
161
148
  </div>
162
149
  )
163
150
  }
@@ -0,0 +1,176 @@
1
+ 'use strict'
2
+ import React, { useState } from 'react'
3
+ import SelectWithInput from '../../components/forms/SelectWithInput'
4
+ import { MAIN_DARK_BLUE, MAIN_GREEN, RICH_BLACK, WHITE } from '../../components/constants'
5
+
6
+ const divStyle = {
7
+ width: '100%',
8
+ height: 'auto',
9
+ padding: '20px',
10
+ backgroundColor: 'transparent'
11
+ }
12
+
13
+ export default {
14
+ title: 'Platformatic/Forms/SelectWithInput',
15
+ component: SelectWithInput,
16
+ decorators: [
17
+ (Story) => (
18
+ <div style={divStyle}>
19
+ <Story />
20
+ </div>
21
+ )
22
+ ]
23
+ }
24
+
25
+ const Template = (args) => <SelectWithInput {...args} />
26
+
27
+ export const Default = Template.bind({})
28
+
29
+ Default.args = {
30
+ name: 'test',
31
+ placeholder: 'Platformatic empty select',
32
+ borderColor: MAIN_GREEN
33
+ }
34
+
35
+ const TemplateBorderMainDarkBlue = (args) => {
36
+ const [value, setValue] = useState('')
37
+
38
+ function handleChange (event) {
39
+ setValue(event.target.value)
40
+ }
41
+
42
+ function handleSelect (event) {
43
+ setValue(event.detail.label)
44
+ }
45
+
46
+ return (
47
+ <div style={{ backgroundColor: 'white', padding: '20px 10px' }}>
48
+ <p>Value of the input {value}</p>
49
+ <SelectWithInput {...args} value={value} onChange={handleChange} onSelect={handleSelect} />
50
+ </div>
51
+ )
52
+ }
53
+
54
+ const TemplateBorderMainDarkBlue2 = (args) => {
55
+ const [value, setValue] = useState('')
56
+
57
+ function handleChange (event) {
58
+ setValue(event.target.value)
59
+ }
60
+
61
+ function handleSelect (event) {
62
+ setValue(event.detail.label)
63
+ }
64
+
65
+ return (
66
+ <>
67
+ <div style={{ backgroundColor: 'white', padding: '20px 10px' }}>
68
+ <p>Value of the input {value}</p>
69
+ <SelectWithInput {...args} value={value} onChange={handleChange} onSelect={handleSelect} />
70
+ <br />
71
+ <SelectWithInput placeholder='Disabled' disabled />
72
+
73
+ </div>
74
+ </>
75
+ )
76
+ }
77
+
78
+ export const BorderMainDarkBlue = TemplateBorderMainDarkBlue.bind({})
79
+
80
+ BorderMainDarkBlue.args = {
81
+ name: 'test',
82
+ placeholder: 'Defaul option',
83
+ options: [...Array(20).keys()].map(ele => ({ label: `Option ${ele}`, value: `Value${ele}`, iconName: 'SocialGitHubIcon' })),
84
+ borderColor: MAIN_DARK_BLUE
85
+ }
86
+
87
+ export const DefaultInvalid = TemplateBorderMainDarkBlue.bind({})
88
+
89
+ DefaultInvalid.args = {
90
+ name: 'test',
91
+ placeholder: 'Platformatic empty select',
92
+ options: [{ label: 'Option 1', value: '1' }, { label: 'Option 2', value: '2' }, { label: 'Option 3', value: '3' }],
93
+ borderColor: MAIN_GREEN,
94
+ errorMessage: 'This is an error message'
95
+ }
96
+
97
+ export const withDifferentOptions = TemplateBorderMainDarkBlue2.bind({})
98
+
99
+ withDifferentOptions.args = {
100
+ name: 'test',
101
+ placeholder: 'Platformatic',
102
+ options: [...Array(20).keys()].map(ele => ({
103
+ label: `Option ${ele}`,
104
+ value: `Value${ele}`,
105
+ iconName: 'SocialGitHubIcon'
106
+ })).concat({
107
+ label: 'Last but not least',
108
+ iconName: 'AddIcon',
109
+ value: 123123,
110
+ notSelectable: true,
111
+ onClick: () => alert('you clicked last but not least')
112
+ }).concat({
113
+ label: 'Play',
114
+ iconName: 'PlayIcon',
115
+ value: 123123,
116
+ notSelectable: true
117
+ }),
118
+ borderColor: MAIN_DARK_BLUE
119
+ }
120
+
121
+ export const githubRepoExample = TemplateBorderMainDarkBlue2.bind({})
122
+
123
+ githubRepoExample.args = {
124
+ name: 'test',
125
+ placeholder: 'Platformatic',
126
+ options: [...Array(4).keys()].map(ele => ({
127
+ label: `GitHub Repo ${ele}`,
128
+ value: `Value${ele}`,
129
+ iconName: 'SocialGitHubIcon'
130
+ })).concat({
131
+ label: 'this is an option that is not selectable',
132
+ iconName: 'AddIcon',
133
+ value: 123123,
134
+ notSelectable: true,
135
+ onClick: () => alert('you clicked last but not least')
136
+ }).concat({
137
+ label: 'Add Github repo',
138
+ iconName: 'PlayIcon',
139
+ value: 123123,
140
+ notSelectable: true,
141
+ notFilterable: true
142
+ }),
143
+ borderColor: MAIN_DARK_BLUE
144
+ }
145
+
146
+ const TemplateTransparent = (args) => {
147
+ const [value, setValue] = useState('')
148
+
149
+ function handleChange (event) {
150
+ setValue(event.target.value)
151
+ }
152
+
153
+ function handleSelect (event) {
154
+ setValue(event.detail.label)
155
+ }
156
+
157
+ return (
158
+ <div style={{ padding: '20px 10px' }}>
159
+ <p>Value of the input {value}</p>
160
+ <SelectWithInput {...args} value={value} onChange={handleChange} onSelect={handleSelect} />
161
+ </div>
162
+ )
163
+ }
164
+
165
+ export const TemplateBackgroundTransparent = TemplateTransparent.bind({})
166
+
167
+ TemplateBackgroundTransparent.args = {
168
+ name: 'test',
169
+ placeholder: 'Defaul option',
170
+ optionsBorderedBottom: false,
171
+ options: [...Array(20).keys()].map(ele => ({ label: `Option ${ele}`, value: `Value${ele}`, iconName: 'OrganizationIcon' })),
172
+ borderColor: WHITE,
173
+ mainColor: WHITE,
174
+ backgroundColor: RICH_BLACK,
175
+ borderListColor: WHITE
176
+ }