@platformatic/ui-components 0.1.31 → 0.1.33

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.
Files changed (102) hide show
  1. package/.github/workflows/ci.yml +26 -26
  2. package/.nvmrc +1 -1
  3. package/LICENSE +201 -201
  4. package/README.md +26 -26
  5. package/dist/main.css +31 -18
  6. package/fonts/Montserrat/README.txt +81 -81
  7. package/index.html +12 -12
  8. package/index.js +58 -58
  9. package/package.json +76 -76
  10. package/postcss.config.cjs +8 -8
  11. package/public/api-icon-closed.svg +6 -6
  12. package/public/api-icon.svg +9 -9
  13. package/src/App.jsx +12 -12
  14. package/src/components/BorderedBox.jsx +16 -16
  15. package/src/components/BorderedBox.module.css +2 -2
  16. package/src/components/BorderedText.jsx +8 -8
  17. package/src/components/BorderedText.module.css +2 -2
  18. package/src/components/Box.jsx +14 -14
  19. package/src/components/Box.module.css +7 -7
  20. package/src/components/Button.jsx +23 -23
  21. package/src/components/Button.module.css +20 -20
  22. package/src/components/Common.module.css +7 -7
  23. package/src/components/DetailedMetric.jsx +32 -32
  24. package/src/components/DetailedMetric.module.css +2 -2
  25. package/src/components/DropDown.jsx +35 -35
  26. package/src/components/DropDown.module.css +22 -22
  27. package/src/components/FollowUs.jsx +35 -35
  28. package/src/components/FollowUs.module.css +11 -11
  29. package/src/components/GHLoginButton.jsx +14 -14
  30. package/src/components/HorizontalSeparator.jsx +9 -5
  31. package/src/components/Input.jsx +11 -11
  32. package/src/components/Input.module.css +7 -7
  33. package/src/components/List.jsx +20 -20
  34. package/src/components/List.module.css +11 -11
  35. package/src/components/ListElement.jsx +23 -23
  36. package/src/components/ListElement.module.css +23 -23
  37. package/src/components/Loadable.jsx +34 -34
  38. package/src/components/Loadable.module.css +13 -13
  39. package/src/components/LoginButton.jsx +17 -17
  40. package/src/components/LoginButton.module.css +7 -7
  41. package/src/components/LoginButton.test.jsx +25 -25
  42. package/src/components/Logo.jsx +58 -58
  43. package/src/components/Main.jsx +33 -33
  44. package/src/components/MetricValue.jsx +15 -15
  45. package/src/components/MetricValue.module.css +7 -7
  46. package/src/components/Modal.jsx +29 -29
  47. package/src/components/Modal.module.css +24 -24
  48. package/src/components/Playground.jsx +9 -9
  49. package/src/components/SearchBar.jsx +63 -33
  50. package/src/components/SearchBar.module.css +20 -5
  51. package/src/components/SimpleMetric.jsx +32 -32
  52. package/src/components/SimpleMetric.module.css +6 -6
  53. package/src/components/Status.jsx +26 -26
  54. package/src/components/TabbedWindow.jsx +34 -34
  55. package/src/components/TabbedWindow.module.css +16 -16
  56. package/src/components/TextWithLabel.jsx +11 -11
  57. package/src/components/TextWithLabel.module.css +8 -8
  58. package/src/components/Versions.jsx +9 -9
  59. package/src/components/VerticalSeparator.jsx +5 -5
  60. package/src/components/icons/ApiEmptyIcon.jsx +107 -0
  61. package/src/components/icons/ApiIcon.jsx +29 -29
  62. package/src/components/icons/ApiIconClosed.jsx +19 -19
  63. package/src/components/icons/CloseModalIcon.jsx +26 -26
  64. package/src/components/icons/PullRequestIcon.jsx +31 -31
  65. package/src/components/icons/index.js +9 -8
  66. package/src/components/layouts/Layout.jsx +11 -11
  67. package/src/components/layouts/TwoColumnsLayout.jsx +12 -12
  68. package/src/components/layouts/TwoColumnsLayout.module.css +9 -9
  69. package/src/hooks/useEscapeKey.js +20 -20
  70. package/src/lib/utils.js +23 -23
  71. package/src/main.jsx +9 -9
  72. package/src/stories/BorderedBox.stories.jsx +33 -33
  73. package/src/stories/BorderedText.stories.jsx +24 -24
  74. package/src/stories/Button.stories.jsx +107 -107
  75. package/src/stories/DetailedMetric.stories.jsx +33 -33
  76. package/src/stories/DropDown.stories.jsx +62 -62
  77. package/src/stories/FollowUs.stories.jsx +14 -14
  78. package/src/stories/GHLoginButton.stories.jsx +14 -14
  79. package/src/stories/HorizontalSeparator.stories.jsx +12 -12
  80. package/src/stories/Input.stories.jsx +15 -15
  81. package/src/stories/Introduction.stories.mdx +211 -211
  82. package/src/stories/List.stories.jsx +29 -29
  83. package/src/stories/ListElement.stories.jsx +15 -15
  84. package/src/stories/Loadable.stories.jsx +75 -75
  85. package/src/stories/LoginButton.stories.jsx +15 -15
  86. package/src/stories/Logo.stories.jsx +17 -17
  87. package/src/stories/Modal.stories.jsx +65 -65
  88. package/src/stories/SearchBar.stories.jsx +28 -27
  89. package/src/stories/SimpleMetric.stories.jsx +41 -41
  90. package/src/stories/Status.stories.jsx +31 -31
  91. package/src/stories/TextWithLabel.stories.jsx +25 -25
  92. package/src/stories/TwoColumnsLayout.stories.jsx +42 -42
  93. package/src/styles/main.css +15 -15
  94. package/tailwind.config.cjs +37 -37
  95. package/vite.config.js +6 -6
  96. package/vitest.config.js +14 -14
  97. package/dist/api-icon-closed.svg +0 -6
  98. package/dist/api-icon.svg +0 -9
  99. package/dist/assets/index.5ac5acb7.js +0 -797
  100. package/dist/assets/index.d3f5882c.css +0 -1
  101. package/dist/index.html +0 -14
  102. package/dist/vite.svg +0 -1
@@ -1,34 +1,34 @@
1
- import React, { useState, cloneElement, Children } from 'react'
2
- import styles from './Loadable.module.css'
3
- import { SpinnerCircular } from 'spinners-react'
4
-
5
- export default function List ({ ...props }) {
6
- // If null then loading not started, if true then loading, if false then done loading
7
- const [loading, setLoading] = useState(null)
8
- const startLoading = (reset = false) => {
9
- // We can start only once (unless we reset)
10
- if (loading === null || reset) {
11
- setLoading(true)
12
- }
13
- }
14
- const stopLoading = () => setLoading(false)
15
-
16
- const children = Children.map(props.children, child => cloneElement(child, { startLoading, stopLoading }))
17
-
18
- return (
19
- <div className={styles.container} data-testid='loadable'>
20
- {
21
- loading
22
- ? (
23
- <>
24
- <div data-testid='loadable-content' className={styles.relative}>
25
- <SpinnerCircular className={styles.spinner} thickness={180} size={60} />
26
- <div className={styles.blurred}>{children}</div>
27
- </div>
28
- </>
29
- )
30
- : <>{children}</>
31
- }
32
- </div>
33
- )
34
- }
1
+ import React, { useState, cloneElement, Children } from 'react'
2
+ import styles from './Loadable.module.css'
3
+ import { SpinnerCircular } from 'spinners-react'
4
+
5
+ export default function List ({ ...props }) {
6
+ // If null then loading not started, if true then loading, if false then done loading
7
+ const [loading, setLoading] = useState(null)
8
+ const startLoading = (reset = false) => {
9
+ // We can start only once (unless we reset)
10
+ if (loading === null || reset) {
11
+ setLoading(true)
12
+ }
13
+ }
14
+ const stopLoading = () => setLoading(false)
15
+
16
+ const children = Children.map(props.children, child => cloneElement(child, { startLoading, stopLoading }))
17
+
18
+ return (
19
+ <div className={styles.container} data-testid='loadable'>
20
+ {
21
+ loading
22
+ ? (
23
+ <>
24
+ <div data-testid='loadable-content' className={styles.relative}>
25
+ <SpinnerCircular className={styles.spinner} thickness={180} size={60} />
26
+ <div className={styles.blurred}>{children}</div>
27
+ </div>
28
+ </>
29
+ )
30
+ : <>{children}</>
31
+ }
32
+ </div>
33
+ )
34
+ }
@@ -1,13 +1,13 @@
1
-
2
- .container {
3
- @apply relative
4
- }
5
-
6
- .spinner {
7
- @apply z-50 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2
8
- }
9
-
10
- .blurred {
11
- @apply blur-sm
12
- }
13
-
1
+
2
+ .container {
3
+ @apply relative
4
+ }
5
+
6
+ .spinner {
7
+ @apply z-50 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2
8
+ }
9
+
10
+ .blurred {
11
+ @apply blur-sm
12
+ }
13
+
@@ -1,17 +1,17 @@
1
- import React from 'react'
2
- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3
- import styles from './LoginButton.module.css'
4
-
5
- export default function LoginButton ({ icon, label, onClick, ...props }) {
6
- return (
7
- <div className={styles.container} onClick={onClick}>
8
- <div
9
- className={styles.button}
10
- data-testid='login-button'
11
- {...props}
12
- >
13
- {icon ? <FontAwesomeIcon icon={icon} className='mr-2' data-testid='login-button-icon' /> : null} {label}
14
- </div>
15
- </div>
16
- )
17
- }
1
+ import React from 'react'
2
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
3
+ import styles from './LoginButton.module.css'
4
+
5
+ export default function LoginButton ({ icon, label, onClick, ...props }) {
6
+ return (
7
+ <div className={styles.container} onClick={onClick}>
8
+ <div
9
+ className={styles.button}
10
+ data-testid='login-button'
11
+ {...props}
12
+ >
13
+ {icon ? <FontAwesomeIcon icon={icon} className='mr-2' data-testid='login-button-icon' /> : null} {label}
14
+ </div>
15
+ </div>
16
+ )
17
+ }
@@ -1,7 +1,7 @@
1
- .button {
2
- @apply bg-light-green text-main-dark-blue font-bold py-2 px-4 rounded w-full text-center cursor-pointer
3
- }
4
-
5
- .container {
6
- @apply w-64
7
- }
1
+ .button {
2
+ @apply bg-light-green text-main-dark-blue font-bold py-2 px-4 rounded w-full text-center cursor-pointer
3
+ }
4
+
5
+ .container {
6
+ @apply w-64
7
+ }
@@ -1,25 +1,25 @@
1
- import React from 'react'
2
- import { render, screen } from '@testing-library/react'
3
- import '@testing-library/jest-dom/extend-expect'
4
- import LoginButton from './LoginButton'
5
- import { faTwitter } from '@fortawesome/free-brands-svg-icons'
6
-
7
- test('<LoginButton /> with label', () => {
8
- const label = 'My Button'
9
- render(
10
- <LoginButton label={label} />
11
- )
12
- expect(screen.getByTestId('login-button')).toHaveTextContent(label)
13
- const icon = screen.queryByTestId('login-button-icon')
14
- expect(icon).not.toBeInTheDocument()
15
- })
16
-
17
- test('<LoginButton /> with label and twitter icon', () => {
18
- const label = 'My Twitter Button'
19
- render(
20
- <LoginButton label={label} icon={faTwitter} />
21
- )
22
- expect(screen.getByTestId('login-button')).toHaveTextContent(label)
23
- const icon = screen.queryByTestId('login-button-icon')
24
- expect(icon).toBeInTheDocument()
25
- })
1
+ import React from 'react'
2
+ import { render, screen } from '@testing-library/react'
3
+ import '@testing-library/jest-dom/extend-expect'
4
+ import LoginButton from './LoginButton'
5
+ import { faTwitter } from '@fortawesome/free-brands-svg-icons'
6
+
7
+ test('<LoginButton /> with label', () => {
8
+ const label = 'My Button'
9
+ render(
10
+ <LoginButton label={label} />
11
+ )
12
+ expect(screen.getByTestId('login-button')).toHaveTextContent(label)
13
+ const icon = screen.queryByTestId('login-button-icon')
14
+ expect(icon).not.toBeInTheDocument()
15
+ })
16
+
17
+ test('<LoginButton /> with label and twitter icon', () => {
18
+ const label = 'My Twitter Button'
19
+ render(
20
+ <LoginButton label={label} icon={faTwitter} />
21
+ )
22
+ expect(screen.getByTestId('login-button')).toHaveTextContent(label)
23
+ const icon = screen.queryByTestId('login-button-icon')
24
+ expect(icon).toBeInTheDocument()
25
+ })
@@ -1,58 +1,58 @@
1
- import React from 'react'
2
- export default function Logo ({ width = 107, heigth = 86 }) {
3
- return (
4
- <svg
5
- width={width}
6
- height={heigth}
7
- viewBox='0 0 107 86'
8
- fill='none'
9
- xmlns='http://www.w3.org/2000/svg'
10
- >
11
- <path
12
- d='M30.8197 62.5803H89.7919V62.5614C92.0266 62.4574 94.2096 61.8539 96.1799 60.7954C98.1502 59.7369 99.8577 58.2503 101.177 56.4452C102.496 54.6401 103.392 52.5624 103.801 50.3652C104.21 48.168 104.12 45.9072 103.538 43.7493C102.956 41.5915 101.897 39.5915 100.438 37.8967C98.9799 36.202 97.1596 34.8556 95.1114 33.9567C93.0632 33.0579 90.8392 32.6294 88.6032 32.703C86.3673 32.7765 84.1763 33.3502 82.1917 34.3817C80.9669 31.5572 79.1131 29.0491 76.7716 27.0482C74.43 25.0473 71.6623 23.6064 68.6792 22.8352M18.1691 22.8352C13.8266 23.9537 9.97973 26.4828 7.23367 30.0244C4.48761 33.566 2.99827 37.9192 3 42.399C3.00173 46.8787 4.49443 51.2307 7.24322 54.7702C9.992 58.3098 13.8409 60.8359 18.1841 61.951'
13
- stroke='white'
14
- stroke-width='4.21053'
15
- stroke-linecap='round'
16
- stroke-linejoin='round'
17
- />
18
- <path
19
- d='M41.4979 35.2219L33.6299 37.9838L37.0924 45.3197'
20
- stroke='#21FA90'
21
- stroke-width='4.21053'
22
- stroke-linecap='round'
23
- stroke-linejoin='round'
24
- />
25
- <path
26
- d='M33.8069 38.1138C37.3946 40.2213 41.5789 41.0853 45.709 40.5715C49.8391 40.0577 53.6834 38.1948 56.6441 35.2727C59.6047 32.3505 61.5157 28.5329 62.0797 24.4136C62.6437 20.2943 61.8292 16.1043 59.7628 12.4953C57.6964 8.88629 54.494 6.0606 50.6537 4.45774C46.8135 2.85489 42.5506 2.56474 38.5281 3.63241C34.5057 4.70009 30.9492 7.06574 28.4119 10.3614C25.8745 13.6571 24.4986 17.698 24.498 21.8556V49.9806'
27
- stroke='#21FA90'
28
- stroke-width='4.21053'
29
- stroke-linecap='round'
30
- stroke-linejoin='round'
31
- />
32
- <path
33
- opacity='0.2'
34
- d='M24.498 79.7146V83.0001'
35
- stroke='#21FA90'
36
- stroke-width='4.21053'
37
- stroke-linecap='round'
38
- stroke-linejoin='round'
39
- />
40
- <path
41
- opacity='0.4'
42
- d='M24.5283 69.3757V74.5658'
43
- stroke='#21FA90'
44
- stroke-width='4.21053'
45
- stroke-linecap='round'
46
- stroke-linejoin='round'
47
- />
48
- <path
49
- opacity='0.7'
50
- d='M24.498 55.3254V64.0028'
51
- stroke='#21FA90'
52
- stroke-width='4.21053'
53
- stroke-linecap='round'
54
- stroke-linejoin='round'
55
- />
56
- </svg>
57
- )
58
- }
1
+ import React from 'react'
2
+ export default function Logo ({ width = 107, heigth = 86 }) {
3
+ return (
4
+ <svg
5
+ width={width}
6
+ height={heigth}
7
+ viewBox='0 0 107 86'
8
+ fill='none'
9
+ xmlns='http://www.w3.org/2000/svg'
10
+ >
11
+ <path
12
+ d='M30.8197 62.5803H89.7919V62.5614C92.0266 62.4574 94.2096 61.8539 96.1799 60.7954C98.1502 59.7369 99.8577 58.2503 101.177 56.4452C102.496 54.6401 103.392 52.5624 103.801 50.3652C104.21 48.168 104.12 45.9072 103.538 43.7493C102.956 41.5915 101.897 39.5915 100.438 37.8967C98.9799 36.202 97.1596 34.8556 95.1114 33.9567C93.0632 33.0579 90.8392 32.6294 88.6032 32.703C86.3673 32.7765 84.1763 33.3502 82.1917 34.3817C80.9669 31.5572 79.1131 29.0491 76.7716 27.0482C74.43 25.0473 71.6623 23.6064 68.6792 22.8352M18.1691 22.8352C13.8266 23.9537 9.97973 26.4828 7.23367 30.0244C4.48761 33.566 2.99827 37.9192 3 42.399C3.00173 46.8787 4.49443 51.2307 7.24322 54.7702C9.992 58.3098 13.8409 60.8359 18.1841 61.951'
13
+ stroke='white'
14
+ strokeWidth={4.21053}
15
+ strokeLinecap='round'
16
+ strokeLinejoin='round'
17
+ />
18
+ <path
19
+ d='M41.4979 35.2219L33.6299 37.9838L37.0924 45.3197'
20
+ stroke='#21FA90'
21
+ strokeWidth={4.21053}
22
+ strokeLinecap='round'
23
+ strokeLinejoin='round'
24
+ />
25
+ <path
26
+ d='M33.8069 38.1138C37.3946 40.2213 41.5789 41.0853 45.709 40.5715C49.8391 40.0577 53.6834 38.1948 56.6441 35.2727C59.6047 32.3505 61.5157 28.5329 62.0797 24.4136C62.6437 20.2943 61.8292 16.1043 59.7628 12.4953C57.6964 8.88629 54.494 6.0606 50.6537 4.45774C46.8135 2.85489 42.5506 2.56474 38.5281 3.63241C34.5057 4.70009 30.9492 7.06574 28.4119 10.3614C25.8745 13.6571 24.4986 17.698 24.498 21.8556V49.9806'
27
+ stroke='#21FA90'
28
+ strokeWidth={4.21053}
29
+ strokeLinecap='round'
30
+ strokeLinejoin='round'
31
+ />
32
+ <path
33
+ opacity='0.2'
34
+ d='M24.498 79.7146V83.0001'
35
+ stroke='#21FA90'
36
+ strokeWidth={4.21053}
37
+ strokeLinecap='round'
38
+ strokeLinejoin='round'
39
+ />
40
+ <path
41
+ opacity='0.4'
42
+ d='M24.5283 69.3757V74.5658'
43
+ stroke='#21FA90'
44
+ strokeWidth={4.21053}
45
+ strokeLinecap='round'
46
+ strokeLinejoin='round'
47
+ />
48
+ <path
49
+ opacity='0.7'
50
+ d='M24.498 55.3254V64.0028'
51
+ stroke='#21FA90'
52
+ strokeWidth={4.21053}
53
+ strokeLinecap='round'
54
+ strokeLinejoin='round'
55
+ />
56
+ </svg>
57
+ )
58
+ }
@@ -1,33 +1,33 @@
1
- 'use strict'
2
-
3
- import SearchBar from './SearchBar'
4
- import TabbedWindow from './TabbedWindow'
5
- import Playground from './Playground'
6
- import Versions from './Versions'
7
- import React, { useState } from 'react'
8
- export default function Main () {
9
- const [currentSearch, setCurrentSearch] = useState('')
10
- const tabs = [
11
- {
12
- label: 'Playground',
13
- component: () => <Playground />
14
- },
15
- {
16
- label: 'Versions',
17
- component: () => <Versions />
18
- }
19
-
20
- ]
21
- return (
22
- <>
23
- <h1 className='text-white text-2xl font-bold'>My APIs</h1>
24
- <SearchBar action={(value) => setCurrentSearch(value)} />
25
- {currentSearch !== '' && (
26
- <span className='text-main-green'>You searched for {currentSearch}</span>
27
- )}
28
- <TabbedWindow
29
- tabs={tabs}
30
- />
31
- </>
32
- )
33
- }
1
+ 'use strict'
2
+
3
+ import SearchBar from './SearchBar'
4
+ import TabbedWindow from './TabbedWindow'
5
+ import Playground from './Playground'
6
+ import Versions from './Versions'
7
+ import React, { useState } from 'react'
8
+ export default function Main () {
9
+ const [currentSearch, setCurrentSearch] = useState('')
10
+ const tabs = [
11
+ {
12
+ label: 'Playground',
13
+ component: () => <Playground />
14
+ },
15
+ {
16
+ label: 'Versions',
17
+ component: () => <Versions />
18
+ }
19
+
20
+ ]
21
+ return (
22
+ <>
23
+ <h1 className='text-white text-2xl font-bold'>My APIs</h1>
24
+ <SearchBar action={(value) => setCurrentSearch(value)} />
25
+ {currentSearch !== '' && (
26
+ <span className='text-main-green'>You searched for {currentSearch}</span>
27
+ )}
28
+ <TabbedWindow
29
+ tabs={tabs}
30
+ />
31
+ </>
32
+ )
33
+ }
@@ -1,15 +1,15 @@
1
- 'use strict'
2
- import React from 'react'
3
- import styles from './MetricValue.module.css'
4
- import { getColor } from '../lib/utils'
5
- export default function MetricValue ({ pre, color, value, unit }) {
6
- return (
7
- <div className={styles.metric}>
8
- {pre && <span>{pre}</span>}
9
- <span className={`${styles.value} ${getColor('text', color)}`}>
10
- {value}
11
- </span>
12
- <span>{unit}</span>
13
- </div>
14
- )
15
- }
1
+ 'use strict'
2
+ import React from 'react'
3
+ import styles from './MetricValue.module.css'
4
+ import { getColor } from '../lib/utils'
5
+ export default function MetricValue ({ pre, color, value, unit }) {
6
+ return (
7
+ <div className={styles.metric}>
8
+ {pre && <span>{pre}</span>}
9
+ <span className={`${styles.value} ${getColor('text', color)}`}>
10
+ {value}
11
+ </span>
12
+ <span>{unit}</span>
13
+ </div>
14
+ )
15
+ }
@@ -1,8 +1,8 @@
1
- .metric {
2
- @apply flex justify-center items-center;
3
- gap: 1rem;
4
- }
5
-
6
- .value {
7
- @apply text-5xl
1
+ .metric {
2
+ @apply flex justify-center items-center;
3
+ gap: 1rem;
4
+ }
5
+
6
+ .value {
7
+ @apply text-5xl
8
8
  }
@@ -1,29 +1,29 @@
1
- import React from 'react'
2
- import useEscapeKey from '../hooks/useEscapeKey'
3
- import CloseModalIcon from './icons/CloseModalIcon'
4
- import styles from './Modal.module.css'
5
- export default function Modal (props) {
6
- const { setIsOpen, title } = props
7
- useEscapeKey(() => setIsOpen(false))
8
- return (
9
- <>
10
- <div className={styles.blur} onClick={() => setIsOpen(false)} />
11
- <div className={`${styles.container} ${styles.centered}`}>
12
- <div className={styles.modal}>
13
- <div className={styles.header}>
14
- <div className={styles.title}>{title}</div>
15
- <div className={styles.close} onClick={() => setIsOpen(false)}>
16
- <CloseModalIcon />
17
- </div>
18
- </div>
19
- <hr className={styles.hr} />
20
- <div>
21
- {props.children}
22
- </div>
23
- <hr className={styles.hr} />
24
- </div>
25
- </div>
26
- </>
27
-
28
- )
29
- }
1
+ import React from 'react'
2
+ import useEscapeKey from '../hooks/useEscapeKey'
3
+ import CloseModalIcon from './icons/CloseModalIcon'
4
+ import styles from './Modal.module.css'
5
+ export default function Modal (props) {
6
+ const { setIsOpen, title } = props
7
+ useEscapeKey(() => setIsOpen(false))
8
+ return (
9
+ <>
10
+ <div className={styles.blur} onClick={() => setIsOpen(false)} />
11
+ <div className={`${styles.container} ${styles.centered}`}>
12
+ <div className={styles.modal}>
13
+ <div className={styles.header}>
14
+ <div className={styles.title}>{title}</div>
15
+ <div className={styles.close} onClick={() => setIsOpen(false)}>
16
+ <CloseModalIcon />
17
+ </div>
18
+ </div>
19
+ <hr className={styles.hr} />
20
+ <div>
21
+ {props.children}
22
+ </div>
23
+ <hr className={styles.hr} />
24
+ </div>
25
+ </div>
26
+ </>
27
+
28
+ )
29
+ }
@@ -1,25 +1,25 @@
1
- .blur {
2
- @apply absolute top-[50%] left-[50%] z-0 w-[100vw] h-[100vh] translate-x-[-50%] translate-y-[-50%] z-[9999];
3
- background-color: rgba(0, 0, 0, 0.75);
4
- }
5
- .container {
6
- @apply z-[9999];
7
- }
8
- .centered {
9
- @apply fixed top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]
10
- }
11
- .modal {
12
- @apply bg-[#E9F7FF] p-4 rounded-md mx-auto my-auto min-w-[480px] max-h-[85vh] overflow-y-auto;
13
- }
14
- .header {
15
- @apply flex justify-between items-center;
16
- }
17
- .close {
18
- @apply hover:cursor-pointer;
19
- }
20
- .hr {
21
- @apply text-[#00283D] my-2 opacity-20;
22
- }
23
- .title {
24
- @apply font-bold text-lg;
1
+ .blur {
2
+ @apply absolute top-[50%] left-[50%] z-0 w-[100vw] h-[100vh] translate-x-[-50%] translate-y-[-50%] z-[9999];
3
+ background-color: rgba(0, 0, 0, 0.75);
4
+ }
5
+ .container {
6
+ @apply z-[9999];
7
+ }
8
+ .centered {
9
+ @apply fixed top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]
10
+ }
11
+ .modal {
12
+ @apply bg-[#E9F7FF] p-4 rounded-md mx-auto my-auto min-w-[480px] max-h-[85vh] overflow-y-auto;
13
+ }
14
+ .header {
15
+ @apply flex justify-between items-center;
16
+ }
17
+ .close {
18
+ @apply hover:cursor-pointer;
19
+ }
20
+ .hr {
21
+ @apply text-[#00283D] my-2 opacity-20;
22
+ }
23
+ .title {
24
+ @apply font-bold text-lg;
25
25
  }
@@ -1,9 +1,9 @@
1
- 'use strict'
2
- import React from 'react'
3
- import BorderedBox from './BorderedBox'
4
-
5
- export default function Playground () {
6
- return (
7
- <BorderedBox color='green'>Playground</BorderedBox>
8
- )
9
- }
1
+ 'use strict'
2
+ import React from 'react'
3
+ import BorderedBox from './BorderedBox'
4
+
5
+ export default function Playground () {
6
+ return (
7
+ <BorderedBox color='green'>Playground</BorderedBox>
8
+ )
9
+ }
@@ -1,33 +1,63 @@
1
- 'use strict'
2
- import React, { useRef } from 'react'
3
- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4
- import { faSearch } from '@fortawesome/free-solid-svg-icons'
5
- import styles from './SearchBar.module.css'
6
- import commonStyles from './Common.module.css'
7
- export default function SearchBar (props) {
8
- const inputRef = useRef()
9
- const { onSubmit, onChange } = props
10
- function handleSearch () {
11
- if (onSubmit) {
12
- const value = inputRef.current.value
13
- return onSubmit(value)
14
- }
15
- }
16
-
17
- function handleChange () {
18
- if (onChange) {
19
- const value = inputRef.current.value
20
- return onChange(value)
21
- }
22
- }
23
- return (
24
- <div className={`${styles.input} ${commonStyles.padded}`}>
25
- <input type='text' placeholder='Search' className='grow' ref={inputRef} onChange={handleChange} />
26
- <button onClick={handleSearch}>
27
- <FontAwesomeIcon color='white' icon={faSearch} />
28
- </button>
29
-
30
- </div>
31
-
32
- )
33
- }
1
+ 'use strict'
2
+ import React, { useRef, useState } from 'react'
3
+ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
4
+ import { faSearch } from '@fortawesome/free-solid-svg-icons'
5
+ import styles from './SearchBar.module.css'
6
+ import commonStyles from './Common.module.css'
7
+ export default function SearchBar (props) {
8
+ const inputRef = useRef()
9
+ const [wrapperClassName, setWrapperClassName] = useState(normalClassName())
10
+ const [isOnFocus, setIsOnFocus] = useState(false)
11
+ const { onSubmit, onChange, title } = props
12
+
13
+ function handleSearch () {
14
+ if (onSubmit) {
15
+ const value = inputRef.current.value
16
+ return onSubmit(value)
17
+ }
18
+ }
19
+
20
+ function handleChange () {
21
+ if (onChange) {
22
+ const value = inputRef.current.value
23
+ return onChange(value)
24
+ }
25
+ }
26
+
27
+ function onFocus () {
28
+ if (!isOnFocus) {
29
+ setIsOnFocus(true)
30
+ setWrapperClassName(onFocusClassName())
31
+ }
32
+ }
33
+
34
+ function onBlur () {
35
+ setIsOnFocus(false)
36
+ setWrapperClassName(normalClassName())
37
+ }
38
+
39
+ function onFocusClassName () {
40
+ return `${styles.input} ${commonStyles.padded} ${styles.onFocus}`
41
+ }
42
+
43
+ function normalClassName () {
44
+ return `${styles.input} ${commonStyles.padded} ${styles.onBlur}`
45
+ }
46
+
47
+ function iconClassName () {
48
+ return isOnFocus ? 'text-main-green' : 'text-white'
49
+ }
50
+
51
+ return (
52
+ <div>
53
+ {title && <h1 className={styles.title}>{title}</h1>}
54
+ <div className={wrapperClassName}>
55
+ <input type='text' placeholder='Search' className='grow' ref={inputRef} onChange={handleChange} onFocus={onFocus} onBlur={onBlur} />
56
+ <button onClick={handleSearch} onFocus={onFocus} onBlur={onBlur}>
57
+ <FontAwesomeIcon className={iconClassName()} icon={faSearch} />
58
+ </button>
59
+ </div>
60
+ </div>
61
+
62
+ )
63
+ }