{pictureUrl &&
}
- {header}
+ {header && ({header})}
{!open && }
{open && }
@@ -34,60 +77,16 @@ function DropDown ({ pictureUrl, header, items, align, backgroundColor, textColo
{items.map((item, index) => {
return (
-
+
{item}
)
})}
+ {lastButton ? (
{lastButton}
) : <>>}
)}
)
}
-DropDown.propTypes = {
- /**
- * pictureUrl
- */
- pictureUrl: PropTypes.string,
- /**
- * header
- */
- header: PropTypes.string,
- /**
- * align
- */
- align: PropTypes.string,
- /**
- * items
- */
- items: PropTypes.array,
- /**
- * backgroundColor
- */
- backgroundColor: PropTypes.oneOf([MAIN_DARK_BLUE, LIGHT_BLUE]),
- /**
- * textColor
- */
- textColor: PropTypes.oneOf([MAIN_DARK_BLUE, LIGHT_BLUE]),
- /**
- * borderColor
- */
- borderColor: PropTypes.oneOf([MAIN_DARK_BLUE, WHITE]),
- /**
- * headerColor
- */
- headerColor: PropTypes.oneOf([MAIN_DARK_BLUE, WHITE])
-}
-
-DropDown.defaultProps = {
- pictureUrl: '',
- header: '',
- align: 'left',
- items: [],
- backgroundColor: LIGHT_BLUE,
- textColor: MAIN_DARK_BLUE,
- headerColor: WHITE,
- borderColor: WHITE
-}
export default DropDown
diff --git a/src/components/DropDown.module.css b/src/components/DropDown.module.css
index 2750931e..7b782666 100644
--- a/src/components/DropDown.module.css
+++ b/src/components/DropDown.module.css
@@ -8,17 +8,37 @@
@apply items-start;
}
.arrow {
- @apply absolute top-[50%] right-0 translate-y-[-50%];
+ @apply absolute top-[50%] right-0;
+ transform: translateY(-50%);
}
.header {
- @apply flex items-center hover:cursor-pointer relative pr-6 tracking-more-widest uppercase text-sm font-normal;
+ @apply flex items-center hover:cursor-pointer relative pr-6;
+}
+.headerClassNameDefault {
+ @apply tracking-more-widest uppercase text-sm font-normal;
}
.picture {
@apply border border-transparent rounded-full mr-2;
}
.menu {
- @apply absolute border-solid border p-0 mt-8 rounded-md z-10 text-sm min-w-[190px];
+ @apply absolute border-solid border p-0 mt-8 rounded-md z-10 text-sm;
}
.item {
- @apply py-2 px-3 first:border-t-0 text-sm leading-4 uppercase hover:font-semibold hover:cursor-pointer tracking-more-widest border-t;
+ @apply hover:cursor-pointer;
+}
+.lastButton {
+ @apply border-t py-2 text-center mx-auto;
+ width: calc(100% - 1.5rem);
+}
+
+.hover--main-dark-blue {
+ @apply hover:bg-main-dark-blue hover:bg-opacity-10;
+}
+
+.hover--white {
+ @apply hover:bg-white hover:bg-opacity-10;
+}
+
+.hover--rich-black {
+ @apply hover:bg-rich-black hover:bg-opacity-10;
}
\ No newline at end of file
diff --git a/src/components/ExperimentalTag.jsx b/src/components/ExperimentalTag.jsx
new file mode 100644
index 00000000..dc5b5dee
--- /dev/null
+++ b/src/components/ExperimentalTag.jsx
@@ -0,0 +1,20 @@
+import React from 'react'
+import Tag from './Tag'
+import styles from './Tag.module.css'
+import { OPACITY_30, SMALL } from './constants'
+
+export default function ExperimentalTag () {
+ const color = 'tertiary-blue'
+ return (
+
+ )
+}
diff --git a/src/components/FollowUs.jsx b/src/components/FollowUs.jsx
index ad752772..79e70fbf 100644
--- a/src/components/FollowUs.jsx
+++ b/src/components/FollowUs.jsx
@@ -1,74 +1,69 @@
-import React from 'react'
-import PropTypes from 'prop-types'
-import commonStyles from './Common.module.css'
+import React, { useState } from 'react'
import styles from './FollowUs.module.css'
import Icons from './icons'
-import { MAIN_GREEN, WHITE, MEDIUM, SMALL, MAIN_DARK_BLUE, LIGHT_GREEN } from './constants'
+import { WHITE, MEDIUM } from './constants'
-function FollowUs ({ label, useOnFrontpage, iconColor, iconSize, labelColor }) {
+function SocialElement ({ href, iconName, iconClassName, iconColor, iconSize }) {
+ const [hover, setHover] = useState(false)
+
+ const icon = React.createElement(Icons[`${iconName}`], {
+ color: iconColor,
+ size: iconSize,
+ tip: '',
+ inactive: !hover
+ })
+
+ return (
+
setHover(false)} onMouseOver={() => setHover(true)}>
+
+ {icon}
+
+
+ )
+}
+
+function FollowUs ({
+ label = 'Follow us on',
+ labelClassName = '',
+ useOnFrontpage = true,
+ iconColor = WHITE,
+ iconSize = MEDIUM,
+ socialElements = [{
+ link: 'https://twitter.com/platformatic',
+ iconName: 'SocialXIcon'
+ }, {
+ link: 'https://www.linkedin.com/company/platformatic/',
+ iconName: 'SocialLinkedInIcon'
+ }, {
+ link: 'https://github.com/platformatic',
+ iconName: 'SocialGitHubIcon'
+ }, {
+ link: 'https://discord.gg/platformatic',
+ iconName: 'SocialDiscordIcon'
+ }]
+}) {
const suffix = useOnFrontpage ? 'Frontpage' : 'Dashboard'
const className = styles[`container${suffix}`]
const iconClassName = styles[`icon${suffix}`]
- let labelClassName = styles[`label${suffix}`]
- labelClassName += ' ' + commonStyles[`text--${labelColor}`]
return (
{label}
-
-
-
-
+
+ {socialElements.map(socialElement =>
+
+ )}
)
}
-FollowUs.propTypes = {
- /**
- * label
- */
- label: PropTypes.string,
- /**
- * frontPageAspect
- */
- frontPageAspect: PropTypes.bool,
- /**
- * labelColor
- */
- labelColor: PropTypes.oneOf([LIGHT_GREEN, WHITE, MAIN_DARK_BLUE]),
- /**
- * iconColor
- */
- iconColor: PropTypes.oneOf([MAIN_DARK_BLUE, WHITE, MAIN_GREEN]),
- /**
- * iconSize
- */
- iconSize: PropTypes.oneOf([MEDIUM, SMALL])
-}
-
-FollowUs.defaultProps = {
- label: 'Follow us on',
- useOnFrontpage: true,
- labelColor: LIGHT_GREEN,
- iconColor: WHITE,
- iconSize: MEDIUM
-}
export default FollowUs
diff --git a/src/components/FollowUs.module.css b/src/components/FollowUs.module.css
index d78e47df..15241c6b 100644
--- a/src/components/FollowUs.module.css
+++ b/src/components/FollowUs.module.css
@@ -1,17 +1,11 @@
.containerFrontpage {
- @apply flex w-full items-center;
+ @apply flex w-full items-center gap-x-2 md:gap-x-8;
}
.containerDashboard {
@apply flex items-center;
}
-.labelFrontpage {
- @apply uppercase tracking-more-widest text-sm mr-8;
-}
-.labelDashboard {
- @apply text-xs mr-2;
-}
.iconFrontpage {
- @apply text-white mx-4 text-xl hover:cursor-pointer hover:scale-125;
+ @apply text-white text-xl hover:cursor-pointer hover:scale-125;
}
.iconDashboard {
@apply mx-1 hover:cursor-pointer hover:scale-125;
diff --git a/src/components/HorizontalSeparator.jsx b/src/components/HorizontalSeparator.jsx
index e3f4423e..9fc369b6 100644
--- a/src/components/HorizontalSeparator.jsx
+++ b/src/components/HorizontalSeparator.jsx
@@ -1,12 +1,17 @@
-'use strict'
import React from 'react'
-import PropTypes from 'prop-types'
import commonStyles from './Common.module.css'
-import { DARK_GREEN, MAIN_DARK_BLUE } from './constants'
+import { DARK_GREEN, MARGIN_4 } from './constants'
import styles from './HorizontalSeparator.module.css'
-function HorizontalSeparator ({ marginTop, marginBottom, color, opacity }) {
+
+function HorizontalSeparator ({
+ marginTop = MARGIN_4,
+ marginBottom = MARGIN_4,
+ color = DARK_GREEN,
+ opacity = 1
+}) {
function getClassName () {
- let className = commonStyles[`text--${color}`] + ' '
+ let className = `${styles.style} `
+ className += commonStyles[`text--${color}`] + ' '
// margin
className += (marginTop === marginBottom ? `${styles['marginY-' + marginTop]}` : `${styles['marginT-' + marginTop]} ${styles['marginB-' + marginBottom]}`)
// opacity
@@ -17,30 +22,4 @@ function HorizontalSeparator ({ marginTop, marginBottom, color, opacity }) {
return
}
-HorizontalSeparator.propTypes = {
- /**
- * marginTop
- */
- marginTop: PropTypes.number,
- /**
- * marginBottom
- */
- marginBottom: PropTypes.number,
- /**
- * color
- */
- color: PropTypes.oneOf([DARK_GREEN, MAIN_DARK_BLUE]),
- /**
- * opacity
- */
- opacity: PropTypes.number
-}
-
-HorizontalSeparator.defaultProps = {
- marginTop: 4,
- marginBottom: 4,
- color: DARK_GREEN,
- opacity: 1
-}
-
export default HorizontalSeparator
diff --git a/src/components/HorizontalSeparator.module.css b/src/components/HorizontalSeparator.module.css
index d7f25a1c..0943b08a 100644
--- a/src/components/HorizontalSeparator.module.css
+++ b/src/components/HorizontalSeparator.module.css
@@ -1,3 +1,10 @@
+.style {
+ @apply border-b-0 border-t w-full;
+ margin-block-start: 0;
+ margin-block-end: 0;
+ margin-inline-start: 0;
+ margin-inline-end: 0;
+}
.marginY-4 {
@apply my-4;
}
@@ -10,6 +17,18 @@
@apply mb-4;
}
+.marginY-8 {
+ @apply my-8;
+}
+
+.marginT-8 {
+ @apply mt-8;
+}
+
+.marginB-8 {
+ @apply mb-8;
+}
+
.marginY-10 {
@apply my-10;
}
@@ -24,4 +43,12 @@
.apply-opacity-20 {
@apply opacity-20;
-}
\ No newline at end of file
+}
+
+.apply-opacity-30 {
+ @apply opacity-30;
+}
+
+.apply-opacity-15 {
+ @apply opacity-15;
+}
diff --git a/src/components/InfoBox.jsx b/src/components/InfoBox.jsx
index 756db08d..1ec80408 100644
--- a/src/components/InfoBox.jsx
+++ b/src/components/InfoBox.jsx
@@ -1,12 +1,18 @@
-'use strict'
import React from 'react'
-import PropTypes from 'prop-types'
import styles from './InfoBox.module.css'
import Button from './Button'
import PlatformaticIcon from './PlatformaticIcon'
-import { COLORS_BUTTON, COLORS_ICON, HOVER_EFFECTS_BUTTONS } from './constants'
+import { MAIN_GREEN } from './constants'
+
+function InfoBox (props) {
+ const {
+ children,
+ iconName = '',
+ iconColor = MAIN_GREEN,
+ helpText = '',
+ buttonProps = null
+ } = props
-function InfoBox ({ children, iconName, iconColor, helpText, buttonProps }) {
return (
@@ -17,42 +23,4 @@ function InfoBox ({ children, iconName, iconColor, helpText, buttonProps }) {
)
}
-InfoBox.propTypes = {
- /**
- * children
- */
- children: PropTypes.node,
- /**
- * iconName
- */
- iconName: PropTypes.string,
- /**
- * iconColor
- */
- iconColor: PropTypes.oneOf(COLORS_ICON),
- /**
- * helpText
- */
- helpText: PropTypes.string,
- /**
- * background color of the button
- */
- buttonProps: PropTypes.shape({
- label: PropTypes.string,
- backgroundColor: PropTypes.string,
- color: PropTypes.oneOf(COLORS_BUTTON),
- hoverEffect: PropTypes.oneOf(HOVER_EFFECTS_BUTTONS),
- bordered: PropTypes.bool,
- onClick: PropTypes.func
- })
-}
-
-InfoBox.defaultProps = {
- children: null,
- iconName: '',
- iconColor: 'main-green',
- helpText: '',
- buttonProps: null
-}
-
export default InfoBox
diff --git a/src/components/List.jsx b/src/components/List.jsx
index 8e2b14a4..2d3fb00e 100644
--- a/src/components/List.jsx
+++ b/src/components/List.jsx
@@ -1,19 +1,20 @@
import React from 'react'
import styles from './List.module.css'
-export default function List ({ title, ...props }) {
+export default function List (props) {
+ const { title, children, ...rest } = props
return (
{title &&
{title}
}
- {props.children}
+ {children}
)
diff --git a/src/components/ListElement.jsx b/src/components/ListElement.jsx
index 0fec87d7..37e21d92 100644
--- a/src/components/ListElement.jsx
+++ b/src/components/ListElement.jsx
@@ -1,11 +1,18 @@
import React from 'react'
-import PropTypes from 'prop-types'
import PlatformaticIcon from './PlatformaticIcon'
import commonStyles from './Common.module.css'
import styles from './ListElement.module.css'
-import { MAIN_DARK_BLUE, MEDIUM, SMALL, WHITE, MAIN_GREEN } from './constants'
+import { MEDIUM, WHITE, MAIN_GREEN } from './constants'
-function List ({ title, detail, marginSize, detailColor, titleColor, semiBold, iconColor }) {
+function List ({
+ title = '',
+ detail = '',
+ marginSize = MEDIUM,
+ detailColor = WHITE,
+ titleColor = MAIN_GREEN,
+ semiBold = true,
+ iconColor = MAIN_GREEN
+}) {
let className = `${styles.container} `
className += styles[`margin-${marginSize}`]
@@ -32,45 +39,5 @@ function List ({ title, detail, marginSize, detailColor, titleColor, semiBold, i
)
}
-List.propTypes = {
- /**
- * title
- */
- title: PropTypes.string,
- /**
- * detail
- */
- detail: PropTypes.string,
- /**
- * marginSize
- */
- marginSize: PropTypes.oneOf([SMALL, MEDIUM]),
- /**
- * color
- */
- titleColor: PropTypes.oneOf([MAIN_GREEN, MAIN_DARK_BLUE, WHITE]),
- /**
- * color
- */
- iconColor: PropTypes.oneOf([MAIN_GREEN, MAIN_DARK_BLUE]),
- /**
- * titleColor
- */
- detailColor: PropTypes.oneOf([WHITE, MAIN_DARK_BLUE]),
- /**
- * semiBold
- */
- semiBold: PropTypes.bool
-}
-
-List.defaultProps = {
- title: '',
- detail: '',
- marginSize: MEDIUM,
- detailColor: WHITE,
- titleColor: MAIN_GREEN,
- iconColor: MAIN_GREEN,
- semiBold: true
-}
export default List
diff --git a/src/components/ListElement.module.css b/src/components/ListElement.module.css
index e2554b50..ac594214 100644
--- a/src/components/ListElement.module.css
+++ b/src/components/ListElement.module.css
@@ -1,5 +1,5 @@
.container {
- @apply flex flex-col my-4
+ @apply flex flex-col my-4 w-full;
}
.margin-medium {
@apply my-4
@@ -14,5 +14,5 @@
@apply flex gap-x-2 items-center w-full;
}
.textCol {
- @apply flex flex-col text-justify w-full ml-8;
+ @apply flex flex-col text-justify w-full pl-8;
}
diff --git a/src/components/Loadable.jsx b/src/components/Loadable.jsx
index 34b6605c..6dacd2a4 100644
--- a/src/components/Loadable.jsx
+++ b/src/components/Loadable.jsx
@@ -1,6 +1,6 @@
import React, { useState, cloneElement, Children } from 'react'
import styles from './Loadable.module.css'
-import { SpinnerCircular } from 'spinners-react'
+import SpinnerCircular from './loaders/SpinnerCircular'
export default function Loadable ({ ...props }) {
// If null then loading not started, if true then loading, if false then done loading
@@ -22,7 +22,7 @@ export default function Loadable ({ ...props }) {
? (
<>
>
diff --git a/src/components/Loadable.module.css b/src/components/Loadable.module.css
index 3ed70430..29f6bffd 100644
--- a/src/components/Loadable.module.css
+++ b/src/components/Loadable.module.css
@@ -4,7 +4,7 @@
}
.spinner {
- @apply z-50 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2
+ @apply z-50 absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2
}
.blurred {
diff --git a/src/components/LoadingSpinner.jsx b/src/components/LoadingSpinner.jsx
index b8e4cbd2..55ce0652 100644
--- a/src/components/LoadingSpinner.jsx
+++ b/src/components/LoadingSpinner.jsx
@@ -1,30 +1,18 @@
import React from 'react'
-import PropTypes from 'prop-types'
import styles from './LoadingSpinner.module.css'
-import { SpinnerCircular } from 'spinners-react'
+import SpinnerCircular from './loaders/SpinnerCircular'
-function LoadingSpinner ({ loading }) {
+function LoadingSpinner ({ loading = false, size = 60 }) {
// If null then loading not started, if true then loading, if false then done loading
return loading
? (
)
: <>>
}
-LoadingSpinner.propTypes = {
- /**
- * loading
- */
- loading: PropTypes.bool
-}
-
-LoadingSpinner.defaultProps = {
- loading: false
-}
-
export default LoadingSpinner
diff --git a/src/components/LoadingSpinnerV2.jsx b/src/components/LoadingSpinnerV2.jsx
new file mode 100644
index 00000000..ec84f872
--- /dev/null
+++ b/src/components/LoadingSpinnerV2.jsx
@@ -0,0 +1,28 @@
+import React from 'react'
+import styles from './LoadingSpinnerV2.module.css'
+import SpinnerCircular from './loaders/SpinnerCircular'
+
+function LoadingSpinnerV2 ({
+ loading = false,
+ applySentences = {},
+ spinnerProps = {},
+ containerClassName = ''
+}) {
+ const defaultContainerClassName = containerClassName || `${styles.container}`
+ // If null then loading not started, if true then loading, if false then done loading
+ return loading
+ ? (
+
+
+
+ {applySentences?.sentences.length > 0 && (
+
+ {applySentences.sentences.map((sentence, index) => (
{sentence.text}
))}
+
)}
+
+
+ )
+ : <>>
+}
+
+export default LoadingSpinnerV2
diff --git a/src/components/LoadingSpinnerV2.module.css b/src/components/LoadingSpinnerV2.module.css
new file mode 100644
index 00000000..b6114af4
--- /dev/null
+++ b/src/components/LoadingSpinnerV2.module.css
@@ -0,0 +1,8 @@
+.container {
+ @apply fixed top-0 left-0 h-screen w-full min-h-screen min-w-full overflow-y-auto z-50;
+}
+.content {
+ @apply h-full flex flex-col justify-center items-center
+}
+
+
diff --git a/src/components/LoginButton.jsx b/src/components/LoginButton.jsx
index 7c58bab0..bd0c05c5 100644
--- a/src/components/LoginButton.jsx
+++ b/src/components/LoginButton.jsx
@@ -3,6 +3,6 @@ import Button from './Button'
export default function LoginButton ({ iconName, label, onClick, ...props }) {
return (
-
+
)
}
diff --git a/src/components/LoginButton.test.jsx b/src/components/LoginButton.test.jsx
index 658c79c9..0f033a14 100644
--- a/src/components/LoginButton.test.jsx
+++ b/src/components/LoginButton.test.jsx
@@ -1,6 +1,5 @@
import React from 'react'
import { render, screen } from '@testing-library/react'
-import '@testing-library/jest-dom/extend-expect'
import LoginButton from './LoginButton'
test('
with label', () => {
diff --git a/src/components/LogoDropDown.jsx b/src/components/LogoDropDown.jsx
index 0ad41f07..2f1f0e6a 100644
--- a/src/components/LogoDropDown.jsx
+++ b/src/components/LogoDropDown.jsx
@@ -1,11 +1,15 @@
-'use strict'
import React, { useState } from 'react'
-import PropTypes from 'prop-types'
import styles from './LogoDropDown.module.css'
import PlatformaticIcon from './PlatformaticIcon'
import { MAIN_DARK_BLUE, SMALL, WHITE } from './constants'
import Logo from './Logo'
-function LogoDropDown ({ itemSelected, items, width, height, onClickItemSelected }) {
+function LogoDropDown ({
+ itemSelected = '',
+ items = [],
+ width = 71,
+ height = 56,
+ onClickItemSelected = () => {}
+}) {
const [open, setOpen] = useState(false)
function handleOpen () {
@@ -55,38 +59,4 @@ function LogoDropDown ({ itemSelected, items, width, height, onClickItemSelected
)
}
-LogoDropDown.propTypes = {
- /**
- * width
- */
- width: PropTypes.number,
- /**
- * height
- */
- height: PropTypes.number,
- /**
- * itemSelected
- */
- itemSelected: PropTypes.string,
- /**
- * items
- */
- items: PropTypes.arrayOf(PropTypes.shape({
- id: PropTypes.string.isRequired,
- name: PropTypes.string,
- handleClick: PropTypes.func
- })),
- /**
- * onClickItemSelected
- */
- onClickItemSelected: PropTypes.func
-}
-
-LogoDropDown.defaultProps = {
- width: 71,
- height: 56,
- itemSelected: '',
- items: [],
- onClickItemSelected: () => {}
-}
export default LogoDropDown
diff --git a/src/components/Main.jsx b/src/components/Main.jsx
index d9e8d6e1..bb4bb68b 100644
--- a/src/components/Main.jsx
+++ b/src/components/Main.jsx
@@ -1,5 +1,3 @@
-'use strict'
-
import SearchBar from './SearchBar'
import TabbedWindow from './TabbedWindow'
import Playground from './Playground'
diff --git a/src/components/MetricInfoBox.jsx b/src/components/MetricInfoBox.jsx
new file mode 100644
index 00000000..1384b1d1
--- /dev/null
+++ b/src/components/MetricInfoBox.jsx
@@ -0,0 +1,50 @@
+import React, { useState } from 'react'
+import styles from './MetricInfoBox.module.css'
+import Tooltip from './Tooltip'
+import { DIRECTION_TOP, POSITION_CENTER, MEDIUM, WHITE } from './constants'
+import PlatformaticIcon from './PlatformaticIcon'
+
+export default function MetricInfoBox ({
+ title,
+ children,
+ tooltip = null
+}) {
+ const [tooltipVisible, setTooltipVisible] = useState(false)
+
+ return (
+
+
+
{title}
+ {/* on hover show tooltip */}
+ {tooltip && (
+
setTooltipVisible(true)}
+ onMouseLeave={() => setTooltipVisible(false)}
+ >
+
{tooltip}}
+ visible={tooltipVisible}
+ direction={DIRECTION_TOP}
+ position={POSITION_CENTER}
+ offset={24}
+ delay={100}
+ activeDependsOnVisible
+ >
+
+
+
+ )}
+
+
+ {children}
+
+
+
+ )
+}
diff --git a/src/components/MetricInfoBox.module.css b/src/components/MetricInfoBox.module.css
new file mode 100644
index 00000000..35f190f2
--- /dev/null
+++ b/src/components/MetricInfoBox.module.css
@@ -0,0 +1,20 @@
+.container {
+ @apply bg-black-russian rounded-lg p-4 box-border flex flex-col w-full gap-4;
+}
+
+.headerRow {
+ @apply flex justify-between items-center mb-4;
+}
+
+.title {
+ @apply text-[24px] font-semibold text-white;
+}
+
+.infoIcon {
+ @apply text-white/70 cursor-pointer text-[16px];
+}
+
+.content {
+ @apply flex items-center justify-center gap-4;
+}
+
diff --git a/src/components/MetricValue.jsx b/src/components/MetricValue.jsx
index 2583fbc2..05d54eb6 100644
--- a/src/components/MetricValue.jsx
+++ b/src/components/MetricValue.jsx
@@ -1,4 +1,3 @@
-'use strict'
import React from 'react'
import styles from './MetricValue.module.css'
import commonStyles from './Common.module.css'
diff --git a/src/components/Modal.jsx b/src/components/Modal.jsx
index 3c6d0fdc..5dd2e28a 100644
--- a/src/components/Modal.jsx
+++ b/src/components/Modal.jsx
@@ -1,6 +1,4 @@
-'use strict'
-import React from 'react'
-import PropTypes from 'prop-types'
+import React, { useRef } from 'react'
import ButtonFullRounded from './ButtonFullRounded'
import useEscapeKey from '../hooks/useEscapeKey'
import Logo from './Logo'
@@ -8,31 +6,48 @@ import Logos from './logos'
import styles from './Modal.module.css'
import commonStyles from './Common.module.css'
import {
- MODAL_SIZES,
SMALL,
- MODAL_LAYOUTS,
MODAL_COVER,
MODAL_POPUP,
+ MODAL_POPUP_V2,
MAIN_DARK_BLUE,
- BACKGROUND_COLOR_OPAQUE,
+ DULLS_BACKGROUND_COLOR,
LARGE,
- PROFILES,
FREE,
BASIC,
MODAL_FULL_DARK,
MODAL_FULL_LIGHT,
- WHITE
+ MODAL_FULL_RICH_BLACK,
+ MODAL_FULL_RICH_BLACK_V2,
+ WHITE,
+ RICH_BLACK,
+ MEDIUM
} from './constants'
import PlatformaticIcon from './PlatformaticIcon'
-function Modal ({ setIsOpen, title, layout, children, size, profile, backgroundClassName }) {
+function Modal ({
+ setIsOpen = () => {},
+ title = '',
+ layout = MODAL_POPUP,
+ children,
+ size = SMALL,
+ profile = '',
+ backgroundClassName = '',
+ titleClassName = '',
+ childrenClassContainer = '',
+ modalCloseClassName = '',
+ permanent = false,
+ showCloseButtonOnTop = true,
+ additionalModalClassName = ''
+}) {
let contentFullscreen
let titleFullscreen
- let modalClassName = `${styles.modal}`
+ let modalClassName = `${styles.modal} `
modalClassName += ' ' + styles[`modal--${layout}`]
modalClassName += ' ' + styles[`modal--${size}`]
-
+ modalClassName += ` ${additionalModalClassName || styles.modalDefaultFlex}`
let buttonFullRoundedClassName
+ const blurRef = useRef()
const headerClassName = styles[`header--${layout}`]
let modalCoverClassName = `${styles.container} ${styles.fullscreen} `
@@ -49,15 +64,28 @@ function Modal ({ setIsOpen, title, layout, children, size, profile, backgroundC
modalCoverClassName += commonStyles['background-color-main-dark-blue']
modalCoverClassName += ` ${backgroundClassName}`
break
+
case MODAL_FULL_LIGHT:
modalCoverClassName += commonStyles['background-color-light-blue']
break
+ case MODAL_FULL_RICH_BLACK:
+ case MODAL_FULL_RICH_BLACK_V2:
+ contentFullscreen = childrenClassContainer || styles[`content--${layout}`]
+ modalCoverClassName += commonStyles[`background-color-${RICH_BLACK}`]
+ modalCoverClassName += ` ${backgroundClassName}`
+ buttonFullRoundedClassName = `${styles['close--cover']} `
+ buttonFullRoundedClassName += commonStyles[`background-color-${RICH_BLACK}`]
+ break
+
default:
break
}
-
- useEscapeKey(() => setIsOpen(false))
+ useEscapeKey(() => {
+ if (showCloseButtonOnTop) {
+ setIsOpen(false)
+ }
+ })
let whichModal = <>>
function renderLogo () {
@@ -70,16 +98,55 @@ function Modal ({ setIsOpen, title, layout, children, size, profile, backgroundC
return
}
+ function closeModal (event) {
+ if (event.target === blurRef.current) {
+ event.preventDefault()
+ event.stopPropagation()
+ setIsOpen(false)
+ }
+ }
+
switch (layout) {
+ case MODAL_POPUP_V2:
+ whichModal = (
+
+
permanent ? {} : closeModal(event)} ref={blurRef}>
+
+
+
+
{title}
+
setIsOpen(false)}
+ internalOverHandling
+ />
+
+
+ {children}
+
+
+
+
+
+ )
+ break
case MODAL_POPUP:
whichModal = (
<>
-
setIsOpen(false)} />
+
permanent ? {} : setIsOpen(false)} />
{title}
-
setIsOpen(false)} />
+ setIsOpen(false)}
+ internalOverHandling
+ />
{children}
@@ -95,11 +162,11 @@ function Modal ({ setIsOpen, title, layout, children, size, profile, backgroundC
{ setIsOpen(false) }}
bordered={false}
alt='Close'
@@ -125,64 +192,70 @@ function Modal ({ setIsOpen, title, layout, children, size, profile, backgroundC
iconName='CircleCloseIcon'
iconSize={LARGE}
iconColor={layout === MODAL_FULL_LIGHT ? MAIN_DARK_BLUE : WHITE}
- hoverEffect={BACKGROUND_COLOR_OPAQUE}
+ hoverEffect={DULLS_BACKGROUND_COLOR}
+ onClick={() => { setIsOpen(false) }}
+ bordered={false}
+ alt='Close'
+ />
+
+
+
+ )
+ break
+
+ case MODAL_FULL_RICH_BLACK:
+ whichModal = (
+
+
+ { setIsOpen(false) }}
bordered={false}
alt='Close'
/>
)
break
+ case MODAL_FULL_RICH_BLACK_V2:
+ whichModal = (
+
+
+ {showCloseButtonOnTop && (
+
+
setIsOpen(false)}
+ internalOverHandling
+ />
+
+ )}
+
+ {children}
+
+
+ )
+ break
+
default:
break
}
return whichModal
}
-Modal.propTypes = {
- /**
- * children
- */
- children: PropTypes.node,
- /**
- * setIsOpen
- */
- setIsOpen: PropTypes.func,
- /**
- * title
- */
- title: PropTypes.string,
- /**
- * layout
- */
- layout: PropTypes.oneOf(MODAL_LAYOUTS),
- /**
- * Size
- */
- size: PropTypes.oneOf(MODAL_SIZES),
- /**
- * profile
- */
- profile: PropTypes.oneOf(PROFILES),
- /**
- * backgroundClassName
- */
- backgroundClassName: PropTypes.string
-}
-
-Modal.defaultProps = {
- children: null,
- setIsOpen: () => {},
- title: '',
- layout: MODAL_POPUP,
- size: SMALL,
- profile: '',
- backgroundClassName: ''
-}
-
export default Modal
diff --git a/src/components/Modal.module.css b/src/components/Modal.module.css
index 6becca01..ceb9bdc9 100644
--- a/src/components/Modal.module.css
+++ b/src/components/Modal.module.css
@@ -1,25 +1,49 @@
.blur {
- @apply fixed top-[50%] left-[50%] w-full h-screen translate-x-[-50%] translate-y-[-50%] z-10;
- background-color: rgba(0, 0, 0, 0.75);
+ @apply fixed top-0 left-0 w-full h-screen z-20;
+ background-color: rgba(0, 0, 0, 0.95);
}
+.blur-absolute {
+ @apply absolute top-0 left-0 w-full h-full z-20 bg-rich-black/95;
+}
+.blur-fixed {
+ @apply fixed top-0 left-0 w-screen h-full z-20 bg-rich-black/95 flex justify-center items-center;
+}
+.fixedContainer {
+ @apply fixed top-0 left-0 w-full h-screen z-20 ;
+}
+.content {
+ @apply fixed top-0 h-[100vH] flex justify-center items-center ;
+}
+
.container {
@apply z-20;
}
.centered {
- @apply fixed top-[50%] left-[50%] translate-x-[-50%] translate-y-[-50%]
+ @apply fixed top-[50%] left-[50%];
+ transform: translate(-50%, -50%);
}
.modal {
@apply p-4 rounded-md mx-auto my-auto max-h-[85vh] overflow-y-auto;
}
+.modalDefaultFlex {
+ @apply flex flex-col gap-y-2;
+}
+.modal--popup-v2 {
+ @apply bg-rich-black border border-solid;
+ border-color: rgba(255, 255, 255, .3);
+}
.modal--popup {
@apply bg-light-blue;
}
.fullscreen {
- @apply fixed top-0 left-0 h-screen w-full min-h-screen min-w-full py-4 pl-20 pr-4 overflow-y-auto;
+ @apply fixed top-0 left-0 h-screen w-full min-h-screen min-w-full overflow-y-auto;
}
+.header--popup-v2,
.header--popup {
@apply flex justify-between items-center;
}
+.header--full-rich-black-v2,
+.header--full-rich-black,
.header--full-dark,
.header--full-light,
.header--cover {
@@ -31,19 +55,21 @@
.title {
@apply font-bold text-[20px];
}
+.content--full-rich-black-v2,
+.content--full-rich-black,
.content--full-dark,
.header--full-light,
.content--cover {
- @apply h-auto w-full mt-4 mb-10;
+ @apply h-auto w-auto p-4 md:pt-4 md:pb-10 md:px-40;
}
.title--cover {
@apply inline-flex items-center;
}
.modal--small {
- @apply min-w-[480px] max-w-[480px];
+ @apply w-auto md:min-w-[480px] md:max-w-[480px];
}
.modal--medium {
- @apply min-w-[880px] max-w-[880px];
+ @apply w-[calc(100vW-2rem)] md:min-w-[480px] md:max-w-[480px] lg:min-w-[880px] lg:max-w-[880px];
}
.modal--full-width {
@apply w-screen min-w-full;
diff --git a/src/components/ModalDirectional.jsx b/src/components/ModalDirectional.jsx
new file mode 100644
index 00000000..3ffc4f63
--- /dev/null
+++ b/src/components/ModalDirectional.jsx
@@ -0,0 +1,57 @@
+import React, { useState, useEffect } from 'react'
+import useEscapeKey from '../hooks/useEscapeKey'
+import styles from './ModalDirectional.module.css'
+import {
+ MEDIUM,
+ WHITE
+} from './constants'
+import PlatformaticIcon from './PlatformaticIcon'
+
+function ModalDirectional ({
+ children,
+ setIsOpen = () => {},
+ smallLayout = false,
+ classNameModalLefty = '',
+ permanent = false,
+ blurClassName = ''
+}) {
+ const className = `${styles.container} ${styles.modalLeftCover} ${smallLayout ? styles.smallLayout : styles.normalLayout}`
+ const [modalClassName] = useState(className)
+ const [variantModalClassName, setVariantModalClassName] = useState(`${styles.container} ${styles.modalLeftCover}`)
+ const classNameLefty = classNameModalLefty || styles.modalLefty
+
+ useEffect(() => {
+ setVariantModalClassName(`${modalClassName} ${styles.modalLeftCoverEntering}`)
+ }, [])
+
+ useEscapeKey(() => closeModal())
+
+ function closeModal () {
+ setVariantModalClassName(`${modalClassName} ${styles.modalLeftCoverLeaving}`)
+ setTimeout(() => {
+ setIsOpen(false)
+ }, 300)
+ }
+
+ return (
+ <>
+
permanent ? {} : closeModal()} />
+
+
+
+
closeModal()}
+ internalOverHandling
+ />
+
+ {children}
+
+
+ >
+ )
+}
+
+export default ModalDirectional
diff --git a/src/components/ModalDirectional.module.css b/src/components/ModalDirectional.module.css
new file mode 100644
index 00000000..afba0358
--- /dev/null
+++ b/src/components/ModalDirectional.module.css
@@ -0,0 +1,37 @@
+.blur {
+ @apply fixed top-0 left-0 w-full h-full z-20 bg-rich-black/95;
+}
+.container {
+ @apply z-20 fixed top-0 -right-[100%] min-h-full h-full bg-rich-black border-solid border-white/30 border-l;
+}
+.normalLayout {
+ @apply max-w-[75%] min-w-[75%];
+}
+.smallLayout {
+ @apply max-w-[34%] min-w-[34%];
+}
+.modalLeftCover {
+ @apply overflow-y-auto w-full ;
+}
+.modalLeftCoverEntering {
+ animation: come-in 400ms forwards;
+}
+.modalLeftCoverLeaving {
+ animation: come-out 200ms forwards;
+}
+.headerClassName {
+ @apply flex items-center justify-end gap-x-2;
+}
+.modalLefty {
+ @apply p-5 xl:py-20 xl:px-[7.5rem] h-[calc(100%-2rem)] xl:h-[calc(100%-10rem)] flex flex-col gap-y-4;
+}
+
+@keyframes come-in {
+ from {right: -100%}
+ to {right: 0%}
+}
+
+@keyframes come-out {
+ from {right: 0%}
+ to {right: -100%}
+}
\ No newline at end of file
diff --git a/src/components/ModalStepsForward.jsx b/src/components/ModalStepsForward.jsx
new file mode 100644
index 00000000..6d8fad05
--- /dev/null
+++ b/src/components/ModalStepsForward.jsx
@@ -0,0 +1,57 @@
+import React from 'react'
+import styles from './ModalStepsForward.module.css'
+import ButtonFullRounded from './ButtonFullRounded'
+import useEscapeKey from '../hooks/useEscapeKey'
+import Logo from './Logo'
+import HorizontalSeparator from './HorizontalSeparator'
+import commonStyles from './Common.module.css'
+import {
+ MAIN_DARK_BLUE,
+ DULLS_BACKGROUND_COLOR,
+ LARGE,
+ MARGIN_0,
+ OPACITY_20
+} from './constants'
+
+function ModalStepsForward ({
+ setIsOpen = () => {},
+ right = null,
+ stepper = null,
+ left = null
+}) {
+ const buttonFullRoundedClassName = commonStyles['background-color-white']
+ let modalCoverClassName = `${styles.container} ${styles.fullscreen} `
+ modalCoverClassName += commonStyles['background-color-light-blue']
+
+ useEscapeKey(() => setIsOpen(false))
+
+ return (
+
+
+
+
+ {stepper}
+
+ {left}
+
+
+
+
+ { setIsOpen(false) }}
+ bordered={false}
+ alt='Close'
+ />
+
+ {right}
+
+
+ )
+}
+
+export default ModalStepsForward
diff --git a/src/components/ModalStepsForward.module.css b/src/components/ModalStepsForward.module.css
new file mode 100644
index 00000000..dc388078
--- /dev/null
+++ b/src/components/ModalStepsForward.module.css
@@ -0,0 +1,32 @@
+.container {
+ @apply z-10 flex;
+}
+.fullscreen {
+ @apply fixed top-0 left-0 min-h-screen w-full min-w-full overflow-y-auto;
+}
+.headerLeft {
+ @apply flex justify-between w-full mb-10;
+}
+.headerRight {
+ @apply flex justify-end mb-12;
+}
+.modal--full-width {
+ @apply w-screen min-w-full;
+}
+.leftWrapper {
+ @apply overflow-y-auto w-full h-full pb-40;
+ max-height: calc(100vh - 200px)
+}
+.left {
+ @apply min-w-[60%] max-w-[60%] w-full ;
+}
+.right {
+ @apply w-full bg-white;
+ box-shadow: 0px 0px 10px rgba(0, 40, 61, 0.25);
+}
+
+
+.left,
+.right {
+ @apply px-20 py-10 flex flex-col gap-y-10;
+}
diff --git a/src/components/PlatformaticIcon.jsx b/src/components/PlatformaticIcon.jsx
index c48eec28..6b8c5d77 100644
--- a/src/components/PlatformaticIcon.jsx
+++ b/src/components/PlatformaticIcon.jsx
@@ -1,68 +1,52 @@
-'use strict'
-import React from 'react'
-import PropTypes from 'prop-types'
-import ReactTooltip from 'react-tooltip'
+import React, { useState } from 'react'
import Icons from './icons'
import styles from './PlatformaticIcon.module.css'
-import { COLORS_ICON, SIZES } from './constants'
+import { MAIN_GREEN, SMALL } from './constants'
-function PlatformaticIcon ({ iconName, color, onClick, size, classes, tip, ...rest }) {
+function PlatformaticIcon ({
+ iconName = '',
+ color = MAIN_GREEN,
+ onClick = () => {},
+ size = SMALL,
+ classes = '',
+ tip = '',
+ disabled = false,
+ inactive = false,
+ internalOverHandling = false,
+ ...rest
+}) {
+ const [hover, setHover] = useState(false)
let icon = <>>
if (iconName) {
icon = React.createElement(Icons[`${iconName}`], {
color,
size,
tip,
+ disabled,
+ inactive: internalOverHandling ? !hover : inactive,
...rest
})
- if (onClick) {
- let className = styles.cursorPointer
+ if (onClick && !disabled) {
+ let className = `${styles.cursorPointer} ${tip ? styles.pltTooltip : ''}`
if (classes) className += ` ${classes}`
- icon = (
{icon})
+ icon = (
+
internalOverHandling && !disabled ? setHover(true) : {}}
+ onMouseLeave={() => internalOverHandling && !disabled ? setHover(false) : {}}
+ >
+ {icon}
+
+ )
}
}
+
return (
<>
{icon}
- {tip &&
}
>
)
}
-PlatformaticIcon.propTypes = {
- /**
- * iconName
- */
- iconName: PropTypes.string.isRequired,
- /**
- * color
- */
- color: PropTypes.oneOf(COLORS_ICON),
- /**
- * size
- */
- size: PropTypes.oneOf(SIZES),
- /**
- * onClick
- */
- onClick: PropTypes.func,
- /**
- * classes
- */
- classes: PropTypes.string,
- /**
- * tip
- */
- tip: PropTypes.string
-}
-
-PlatformaticIcon.defaultProps = {
- iconName: '',
- color: 'main-green',
- size: 'small',
- onClick: () => {},
- classes: null,
- tip: ''
-}
-
export default PlatformaticIcon
diff --git a/src/components/PlatformaticIcon.module.css b/src/components/PlatformaticIcon.module.css
index d2d74225..e1ecfb18 100644
--- a/src/components/PlatformaticIcon.module.css
+++ b/src/components/PlatformaticIcon.module.css
@@ -1,3 +1,6 @@
.cursorPointer {
@apply cursor-pointer;
+}
+.pltTooltip {
+ @apply relative;
}
\ No newline at end of file
diff --git a/src/components/Playground.jsx b/src/components/Playground.jsx
index 2c92f7cf..128d3a0d 100644
--- a/src/components/Playground.jsx
+++ b/src/components/Playground.jsx
@@ -1,4 +1,3 @@
-'use strict'
import React from 'react'
import BorderedBox from './BorderedBox'
diff --git a/src/components/Report.jsx b/src/components/Report.jsx
new file mode 100644
index 00000000..031ef939
--- /dev/null
+++ b/src/components/Report.jsx
@@ -0,0 +1,41 @@
+import React from 'react'
+import Icons from './icons'
+import { ERROR_RED, MAIN_GREEN, WARNING_YELLOW } from './constants'
+import styles from './Report.module.css'
+
+function getIcon (type) {
+ switch (type) {
+ case 'success':
+ return
+ case 'error':
+ return
+ case 'warning':
+ return
+ }
+}
+function Step ({
+ type,
+ label
+}) {
+ return (
+
+ {getIcon(type)}
+ {label}
+
+ )
+}
+export default function Report ({
+ steps = []
+}) {
+ function renderSteps () {
+ return steps.map((s, idx) => {
+ return
+ })
+ }
+ return (
+
+ {renderSteps()}
+
+
+ )
+}
diff --git a/src/components/Report.module.css b/src/components/Report.module.css
new file mode 100644
index 00000000..a44c5adc
--- /dev/null
+++ b/src/components/Report.module.css
@@ -0,0 +1,7 @@
+.container {
+ @apply text-white/70 flex flex-col gap-y-2;
+}
+
+.step {
+ @apply flex items-center gap-x-2;
+}
\ No newline at end of file
diff --git a/src/components/SearchBar.jsx b/src/components/SearchBar.jsx
index 5a1e8699..165125e5 100644
--- a/src/components/SearchBar.jsx
+++ b/src/components/SearchBar.jsx
@@ -1,4 +1,3 @@
-'use strict'
import React, { useRef, useState } from 'react'
import styles from './SearchBar.module.css'
import commonStyles from './Common.module.css'
diff --git a/src/components/SearchBar.module.css b/src/components/SearchBar.module.css
index 68cbe090..808dff11 100644
--- a/src/components/SearchBar.module.css
+++ b/src/components/SearchBar.module.css
@@ -1,5 +1,5 @@
.input {
- @apply flex justify-between w-full h-10 text-white border border-solid box-border rounded-md;
+ @apply flex justify-between w-full h-10 text-white border border-solid box-border rounded;
}
.onFocus {
@apply border-main-green;
diff --git a/src/components/SearchBarV2.jsx b/src/components/SearchBarV2.jsx
new file mode 100644
index 00000000..1d20ca14
--- /dev/null
+++ b/src/components/SearchBarV2.jsx
@@ -0,0 +1,92 @@
+import React, { useRef, useState } from 'react'
+import commonStyles from './Common.module.css'
+import styles from './SearchBarV2.module.css'
+import PlatformaticIcon from './PlatformaticIcon'
+import { RICH_BLACK, SMALL, WHITE } from './constants'
+function SearchBarV2 ({
+ color = WHITE,
+ backgroundColor = RICH_BLACK,
+ onSubmit = () => { },
+ onChange = () => { },
+ onClear = () => { },
+ placeholder = 'Search',
+ dataAttrName = '',
+ dataAttrValue = '',
+ inputTextClassName = '',
+ paddingClass = '',
+ disabled = false
+}) {
+ const inputRef = useRef()
+ const baseClassName = `${styles.container} ${paddingClass || styles.defaultPaddingClass} ` + commonStyles[`background-color-${backgroundColor}`]
+ const [wrapperClassName, setWrapperClassName] = useState(normalClassName())
+ const inputClassName = `${styles.input} ${inputTextClassName} ${disabled ? styles.disabled : ''} `
+ const [isOnFocus, setIsOnFocus] = useState(false)
+ const [showClear, setShowClear] = useState(false)
+ const dataProps = {}
+ if (dataAttrName && dataAttrValue) {
+ dataProps[`data-${dataAttrName}`] = dataAttrValue
+ }
+ function handleSearch () {
+ if (onSubmit) {
+ const value = inputRef.current.value
+ return onSubmit(value)
+ }
+ }
+
+ function handleChange () {
+ if (onChange) {
+ const value = inputRef.current.value
+ setShowClear(!!value)
+ return onChange(value)
+ }
+ }
+
+ function handleClear () {
+ inputRef.current.value = ''
+ setShowClear(false)
+ return onClear()
+ }
+
+ function onFocus () {
+ if (!isOnFocus) {
+ setIsOnFocus(true)
+ setWrapperClassName(onFocusClassName())
+ }
+ }
+
+ function onBlur () {
+ setIsOnFocus(false)
+ setWrapperClassName(normalClassName())
+ }
+
+ function onFocusClassName () {
+ return `${baseClassName} ${styles.onFocus}`
+ }
+
+ function normalClassName () {
+ return `${baseClassName} ${styles.onBlur}`
+ }
+
+ return (
+
+
+
+ {showClear && (
+
+ )}
+
+ )
+}
+
+export default SearchBarV2
diff --git a/src/components/SearchBarV2.module.css b/src/components/SearchBarV2.module.css
new file mode 100644
index 00000000..3ac8c6db
--- /dev/null
+++ b/src/components/SearchBarV2.module.css
@@ -0,0 +1,33 @@
+.container {
+ @apply flex items-center gap-x-2 w-full h-auto border border-solid box-border rounded relative ;
+}
+.input {
+ @apply w-full p-0 bg-transparent border-none text-white ;
+ padding-block: 0px;
+ padding-inline: 0px;
+}
+.onFocus {
+ @apply border-white;
+}
+.onBlur {
+ @apply border-white/70;
+}
+.fillWhite {
+ @apply fill-white;
+}
+.container > input:focus {
+ @apply outline-none;
+}
+.title {
+ @apply font-semibold text-white text-3xl pb-4;
+}
+.clearContainer {
+ @apply absolute right-[1rem];
+}
+.defaultPaddingClass {
+ @apply px-2 py-1.5 md:px-3 md:py-[8.5px];
+}
+
+.disabled::placeholder {
+ @apply text-white/30;
+}
diff --git a/src/components/Sidebar.jsx b/src/components/Sidebar.jsx
index 6968c629..342b7108 100644
--- a/src/components/Sidebar.jsx
+++ b/src/components/Sidebar.jsx
@@ -7,22 +7,20 @@ import styles from './Sidebar.module.css'
import ReactTooltip from 'react-tooltip'
import HorizontalSeparator from './HorizontalSeparator'
import PlatformaticIcon from './PlatformaticIcon'
-import PropTypes from 'prop-types'
-import { BACKGROUND_COLOR_OPAQUE, MEDIUM, WHITE } from './constants'
+import { DULLS_BACKGROUND_COLOR, MEDIUM, WHITE } from './constants'
-function Sidebar (props) {
- const {
- title,
- labelButtonSettings,
- defaultSelected,
- onClickItemSelectedParent,
- items,
- onClickAdd,
- addTitle,
- onClickSettings,
- disableClickAdd,
- disableClickSettings
- } = props
+function Sidebar ({
+ title = '',
+ labelButtonSettings = 'Settings',
+ defaultSelected = null,
+ onClickItemSelectedParent = () => {},
+ items = [],
+ onClickAdd = () => {},
+ addTitle = 'Add',
+ onClickSettings = () => {},
+ disableClickAdd = false,
+ disableClickSettings = false
+}) {
const [collapsed, setCollapsed] = useState(false)
const [selectedItem, setSelectedItem] = useState(null)
const disabledCreateButton = disableClickAdd ? styles.disabled : ''
@@ -40,162 +38,102 @@ function Sidebar (props) {
return (
- {collapsed
- ? (
- <>
-
{ setCollapsed(false) }}
- bordered={false}
- />
-
-
- {title}
-
-
-
-
-
- >
- )
- : (
- <>
- { setCollapsed(true) }}
- bordered={false}
- />
-
- {title}
-
-
- {items.map(item => {
- const isSelected = selectedItem === item.id
- return (
-
-
-
-
- )
- })}
-
- >
- )}
+
+ {title}
+
+
+ {items.map(item => {
+ const isSelected = selectedItem === item.id
+ return (
+
+ onClickItemSelected(item)}>
+ {item.iconName && ()}
+
+ {item.subTitle}
+ {item.title}
+
+
+
+
+ )
+ })}
+
+
+ {!collapsed && {addTitle}}
+
+
+
+ >
+ )}
+
+
+
+ {collapsed
+ ? (
+
+
+
+ )
+ : (
+
+ )}
+
)
}
-Sidebar.propTypes = {
- /**
- * title
- */
- title: PropTypes.string,
- /**
- * title
- */
- labelButtonSettings: PropTypes.string,
- /**
- * defaultSelected
- */
- defaultSelected: PropTypes.string,
- /**
- * addTitle
- */
- addTitle: PropTypes.string,
- /**
- * items: array with keys
- * id: id of the worspacedSeleted
- * title: name to display
- * subtitle: secondary title
- * icon: what Icon
- */
- items: PropTypes.arrayOf(PropTypes.shape({
- id: PropTypes.string.isRequired,
- title: PropTypes.string,
- subtitle: PropTypes.string,
- iconName: PropTypes.string
- })),
- /**
- * Apply onClickItemSelectedParent
- */
- onClickItemSelectedParent: PropTypes.func,
- /**
- * Apply onClickAdd: function called clicking on plus button
- */
- onClickAdd: PropTypes.func,
- /**
- * Apply onClickSettings: function called clicking on Settings button
- */
- onClickSettings: PropTypes.func,
- /**
- * disableClickAdd
- */
- disableClickAdd: PropTypes.bool,
- /**
- * disableClickSettings
- */
- disableClickSettings: PropTypes.bool
-}
-
-Sidebar.defaultProps = {
- title: '',
- labelButtonSettings: 'Settings',
- defaultSelected: null,
- onClickItemSelectedParent: () => {},
- items: [],
- onClickAdd: () => {},
- addTitle: 'Add',
- onClickSettings: () => {},
- disableClickAdd: false,
- disableClickSettings: false
-}
-
export default Sidebar
diff --git a/src/components/Sidebar.module.css b/src/components/Sidebar.module.css
index db80fee9..9517d74b 100644
--- a/src/components/Sidebar.module.css
+++ b/src/components/Sidebar.module.css
@@ -1,7 +1,10 @@
.container {
- @apply max-w-[216px] w-full p-4 border border-white rounded-md h-auto relative min-h-[80vh] z-10 bg-dark-blue;
+ @apply max-w-[216px] w-full p-4 border border-white rounded-md h-auto relative min-h-[80vh] z-10 bg-dark-blue flex flex-col gap-y-6 justify-between;
transition: max-width 0.2s linear;
}
+.content {
+ @apply flex flex-col justify-between
+}
.collapsed {
@apply max-w-[96px] text-center;
}
@@ -54,9 +57,6 @@
.items {
@apply flex flex-col gap-y-4;
}
-.bottom {
- @apply absolute left-0 bottom-[1rem] w-full px-4;
-}
.titleSettings {
@apply text-white font-semibold ml-3;
}
diff --git a/src/components/SimpleMetric.jsx b/src/components/SimpleMetric.jsx
index c75ffae1..3657d4fa 100644
--- a/src/components/SimpleMetric.jsx
+++ b/src/components/SimpleMetric.jsx
@@ -1,19 +1,18 @@
-'use strict'
-
import React from 'react'
import BorderedBox from './BorderedBox'
import styles from './SimpleMetric.module.css'
import MetricValue from './MetricValue'
import ButtonFullRounded from './ButtonFullRounded'
-import { BACKGROUND_COLOR_OPAQUE, MEDIUM, WHITE } from './constants'
+import { DULLS_BACKGROUND_COLOR, MEDIUM, WHITE } from './constants'
-export default function SimpleMetric ({ title, pre, color, unit, value, tooltip, children }) {
+export default function SimpleMetric (props) {
+ const { title, pre, color, unit, value, tooltip, children } = props
const withoutChildrenSingleMetric = !children ? styles.centerMetric : ''
return (
{title}
- {}} />
+ {}} />
diff --git a/src/components/SplashScreen.jsx b/src/components/SplashScreen.jsx
new file mode 100644
index 00000000..bf53baed
--- /dev/null
+++ b/src/components/SplashScreen.jsx
@@ -0,0 +1,68 @@
+import React, { useEffect, useState } from 'react'
+import styles from './SplashScreen.module.css'
+import { ERROR_RED, MAIN_GREEN, RICH_BLACK, WHITE } from './constants'
+import Icons from './icons'
+import Button from './Button'
+
+export default function SplashScreen (props) {
+ const {
+ success = true,
+ timeout = 5000,
+ message = '',
+ title = 'Operation completed',
+ blur = false,
+ children,
+ onDestroyed = () => {},
+ showDismissButton = true
+ } = props
+
+ const [destroyed, setDestroyed] = useState(false)
+ useEffect(() => {
+ if (destroyed) {
+ onDestroyed()
+ }
+ }, [destroyed])
+ useEffect(() => {
+ setTimeout(() => {
+ setDestroyed(true)
+ }, timeout)
+ }, [])
+
+ if (destroyed) {
+ return null
+ }
+
+ function renderIcon () {
+ if (success) {
+ return
+ } else {
+ return
+ }
+ }
+ return (
+
setDestroyed(true)}>
+
+
{renderIcon()}
+
{title}
+
{message}
+
+
+ {children}
+
+
+ {showDismissButton && (
+
+ setDestroyed(true)}
+ label='Dismiss'
+ paddingClass={styles.buttonPadding}
+ textClass={styles.buttonText}
+ />
+
+ )}
+
+
+ )
+}
diff --git a/src/components/SplashScreen.module.css b/src/components/SplashScreen.module.css
new file mode 100644
index 00000000..0a0a8aeb
--- /dev/null
+++ b/src/components/SplashScreen.module.css
@@ -0,0 +1,43 @@
+.container {
+ @apply fixed inset-0 flex items-center justify-center text-white;
+}
+
+.container.blur {
+ @apply backdrop-blur-sm bg-rich-black/50;
+}
+
+.container.solid {
+ @apply bg-rich-black;
+}
+
+.title {
+ @apply text-[16px];
+}
+
+.message {
+ @apply text-[14px] text-white/70 mb-4;
+}
+
+.icon {
+ @apply mb-4;
+}
+
+.content {
+ @apply flex flex-col gap-y-2 items-center justify-center text-center bg-transparent;
+}
+
+.childrenContainer {
+ @apply mb-4;
+}
+
+.button {
+ @apply flex items-center justify-center;
+}
+
+.buttonPadding {
+ @apply px-2 py-1;
+}
+
+.buttonText {
+ @apply text-[14px];
+}
diff --git a/src/components/Status.jsx b/src/components/Status.jsx
index 90400c12..6e8f30a0 100644
--- a/src/components/Status.jsx
+++ b/src/components/Status.jsx
@@ -1,10 +1,12 @@
-'use strict'
import React from 'react'
-import PropTypes from 'prop-types'
import PlatformaticIcon from './PlatformaticIcon'
-import { COLORS_ICON, EXTRA_SMALL, SIZES, WHITE } from './constants'
+import { TINY, WHITE } from './constants'
-function Status ({ color, status, size }) {
+function Status ({
+ color = WHITE,
+ status = '',
+ size = TINY
+}) {
const className = `inline-flex items-center text-${color}`
return (
@@ -14,26 +16,4 @@ function Status ({ color, status, size }) {
)
}
-Status.propTypes = {
- /**
- * color
- */
- color: PropTypes.oneOf(COLORS_ICON),
- /**
- * status
- */
- status: PropTypes.string,
- /**
- * Size
- */
- size: PropTypes.oneOf(SIZES)
-
-}
-
-Status.defaultProps = {
- color: WHITE,
- status: '',
- size: EXTRA_SMALL
-}
-
export default Status
diff --git a/src/components/TabbedWindow.jsx b/src/components/TabbedWindow.jsx
index 40ff44fb..8a72f8d1 100644
--- a/src/components/TabbedWindow.jsx
+++ b/src/components/TabbedWindow.jsx
@@ -1,9 +1,16 @@
-'use strict'
import React, { useEffect, useState } from 'react'
-import PropTypes from 'prop-types'
import styles from './TabbedWindow.module.css'
-
-function TabbedWindow ({ tabs, keySelected, callbackSelected }) {
+import { WHITE } from './constants'
+
+function TabbedWindow ({
+ tabs = [],
+ keySelected = '',
+ callbackSelected = () => {},
+ tabContainerClassName = '',
+ tabContentClassName = '',
+ textClassName = '',
+ horizontalSeparatorColor = WHITE
+}) {
const headers = []
const keys = []
const components = []
@@ -14,7 +21,8 @@ function TabbedWindow ({ tabs, keySelected, callbackSelected }) {
})
const [selected, setSelected] = useState(keySelected || tabs?.at(0).key || '')
const [currentComponent, setCurrentComponent] = useState(components[0])
-
+ const containerClassName = tabContainerClassName || styles.tabContainerClassName
+ const contentClassName = tabContentClassName || styles.tabContentClassName
useEffect(() => {
const indexKey = keys.findIndex(key => key === selected)
setCurrentComponent(components[indexKey])
@@ -26,43 +34,22 @@ function TabbedWindow ({ tabs, keySelected, callbackSelected }) {
}
return (
-
+
{headers.map((header, index) => {
return (
- setCurrentTab(index)} className={`${styles.tab} ${selected === keys[index] ? styles['selected-tab'] : ''}`} title={header}>
+ setCurrentTab(index)} className={`${styles.tab} ${textClassName} ${selected === keys[index] ? styles['selected-tab'] : ''}`} title={header}>
{header}
)
})}
+
-
{currentComponent}
+
{currentComponent}
)
}
-TabbedWindow.propTypes = {
- /**
- * tabs
- */
- tabs: PropTypes.array,
- /**
- * keySelected
- */
- keySelected: PropTypes.string,
- /**
- * callbackSelected
- */
- callbackSelected: PropTypes.func
-
-}
-
-TabbedWindow.defaultProps = {
- tabs: [],
- keySelected: '',
- callbackSelected: () => {}
-}
-
export default TabbedWindow
diff --git a/src/components/TabbedWindow.module.css b/src/components/TabbedWindow.module.css
index 575910e9..9045e39e 100644
--- a/src/components/TabbedWindow.module.css
+++ b/src/components/TabbedWindow.module.css
@@ -1,17 +1,17 @@
-.tabbed-container {
- @apply bg-main-dark-blue;
+.tabContainerClassName {
+ @apply bg-main-dark-blue flex flex-col gap-y-2 w-full;
}
.tabs-header {
- @apply flex justify-start text-white uppercase hover:cursor-pointer mb-4 tracking-super-widest h-[2rem];
+ @apply flex justify-start text-white uppercase hover:cursor-pointer tracking-super-widest h-[1.5rem] relative items-center;
}
.tab {
- @apply mx-8 min-w-[105px] text-center text-sm first:ml-0 last:mr-0 hover:font-semibold;
+ @apply mx-8 min-w-[105px] text-center text-sm first:ml-0 last:mr-0 font-light opacity-70 hover:opacity-100;
}
.selected-tab {
- @apply underline text-main-green underline-offset-8 font-semibold;
+ @apply underline underline-offset-8 opacity-100;
}
-.tabs-content {
- @apply min-h-screen
+.tabContentClassName {
+ @apply max-h-[calc(100vH-10rem)] overflow-y-auto;
}
.tab::before {
display: block;
@@ -21,3 +21,8 @@
overflow: hidden;
visibility: hidden;
}
+.divider {
+ @apply w-full border border-none rounded-md h-[1px] bg-white/15 absolute bottom-[-1px];
+ margin-block-start: 0;
+ margin-block-end: 0;
+}
\ No newline at end of file
diff --git a/src/components/Tag.jsx b/src/components/Tag.jsx
index 2f526c45..b121028e 100644
--- a/src/components/Tag.jsx
+++ b/src/components/Tag.jsx
@@ -1,55 +1,40 @@
-'use strict'
import React from 'react'
-import PropTypes from 'prop-types'
import commonStyles from './Common.module.css'
import styles from './Tag.module.css'
-import { COLORS_BUTTON } from './constants'
+import { OPACITY_100, WHITE } from './constants'
+import PlatformaticIcon from './PlatformaticIcon'
-function Tag ({ text, color, backgroundColor, bordered, opaque }) {
- const stylesColor = commonStyles[`text--${color}`]
+function Tag ({
+ text = '',
+ textClassName = '',
+ color = WHITE,
+ backgroundColor = '',
+ bordered = true,
+ opaque = OPACITY_100,
+ fullRounded = false,
+ platformaticIcon = null,
+ paddingClass = ''
+}) {
+ const stylesColor = textClassName || commonStyles[`text--${color}`]
let stylesBorderColor = ''
if (bordered) {
stylesBorderColor = `${styles.bordered} `
stylesBorderColor += commonStyles[`bordered--${color}`]
}
+ if (fullRounded) {
+ stylesBorderColor += ` ${styles.fullRounded}`
+ }
const stylesBackgroundColor = backgroundColor ? commonStyles[`background-color-${backgroundColor}`] : ''
const opacity = commonStyles[`background-color-opaque-${opaque}`]
- const className = `${styles.tag} ${stylesColor} ${stylesBorderColor} ${stylesBackgroundColor} ${opacity}`
+ let className = `${styles.container} ${styles.tag} ${stylesBorderColor} ${stylesBackgroundColor} ${opacity} `
+ className += paddingClass || `${styles.padding} `
return (
-
{text}
+
+ {platformaticIcon &&
}
+
{text}
+
)
}
-Tag.propTypes = {
- /**
- * color
- */
- color: PropTypes.oneOf(COLORS_BUTTON),
- /**
- * text
- */
- text: PropTypes.string,
- /**
- * backgroundColor
- */
- backgroundColor: PropTypes.oneOf(COLORS_BUTTON),
- /**
- * bordered
- */
- bordered: PropTypes.bool,
- /**
- * opaque
- */
- opaque: PropTypes.oneOf([100, 60, 30, 20])
-
-}
-
-Tag.defaultProps = {
- backgroundColor: '',
- color: 'white',
- text: '',
- bordered: true,
- opaque: 100
-}
export default Tag
diff --git a/src/components/Tag.module.css b/src/components/Tag.module.css
index d2527aa0..4c9264a0 100644
--- a/src/components/Tag.module.css
+++ b/src/components/Tag.module.css
@@ -1,7 +1,19 @@
.tag {
- @apply rounded-md px-2 py-1 tracking-super-widest w-fit;
+ @apply rounded-md w-fit;
}
-
.bordered {
@apply border border-solid;
+}
+.fullRounded {
+ @apply rounded-full;
+}
+.container {
+ @apply flex flex-row gap-x-2 items-center border-0;
+}
+.padding {
+ @apply px-3 py-1
+}
+
+.experimentalPadding {
+ @apply text-sm px-2 py-0.5 tracking-[0.15rem] text-[10px];
}
\ No newline at end of file
diff --git a/src/components/TextWithLabel.jsx b/src/components/TextWithLabel.jsx
index 641318c1..f00fcd90 100644
--- a/src/components/TextWithLabel.jsx
+++ b/src/components/TextWithLabel.jsx
@@ -1,9 +1,17 @@
import React from 'react'
-import PropTypes from 'prop-types'
-import { FONT_BASE, FONT_XS, MAIN_DARK_BLUE, WHITE } from './constants'
+import { FONT_BASE, WHITE } from './constants'
import commonStyles from './Common.module.css'
import styles from './TextWithLabel.module.css'
-function TextWithLabel ({ label, text, children, color, fontSize }) {
+
+function TextWithLabel (props) {
+ const {
+ label = '',
+ text = '',
+ children,
+ color = WHITE,
+ fontSize = FONT_BASE
+ } = props
+
const className = commonStyles[`text--${color}`] + ' ' + commonStyles[`text--${fontSize}`]
return (
@@ -14,35 +22,4 @@ function TextWithLabel ({ label, text, children, color, fontSize }) {
)
}
-TextWithLabel.propTypes = {
- /**
- * label
- */
- label: PropTypes.string,
- /**
- * text
- */
- text: PropTypes.oneOfType([PropTypes.string, PropTypes.number]),
- /**
- * children
- */
- children: PropTypes.node,
- /**
- * white
- */
- color: PropTypes.oneOf([WHITE, MAIN_DARK_BLUE]),
- /**
- * Font Size
- */
- fontSize: PropTypes.oneOf([FONT_BASE, FONT_XS])
-}
-
-TextWithLabel.defaultProps = {
- label: '',
- text: '',
- children: null,
- color: WHITE,
- fontSize: FONT_BASE
-}
-
export default TextWithLabel
diff --git a/src/components/Tooltip.jsx b/src/components/Tooltip.jsx
new file mode 100644
index 00000000..3b9edc9c
--- /dev/null
+++ b/src/components/Tooltip.jsx
@@ -0,0 +1,117 @@
+import React, { useEffect, useRef, useState } from 'react'
+import styles from './Tooltip.module.css'
+import { DIRECTION_BOTTOM, DIRECTION_LEFT, DIRECTION_RIGHT, DIRECTION_TOP, POSITION_CENTER, POSITION_END, POSITION_START } from './constants'
+
+function Tooltip ({
+ immediateActive = true,
+ direction = DIRECTION_TOP,
+ visible = false,
+ activeDependsOnVisible = false,
+ content = null,
+ delay = 0,
+ children,
+ tooltipClassName = '',
+ offset = 0,
+ position = POSITION_CENTER
+}) {
+ let timeout
+ // const [active, setActive] = useState(activeDependsOnVisible ? visible : false)
+ const [active, setActive] = useState(immediateActive)
+ let componentClassName = tooltipClassName || styles.tooltipTipBaseClass
+ componentClassName += ` ${styles.tooltipTip} ` + styles[`${direction}`]
+ if (direction === DIRECTION_BOTTOM || direction === DIRECTION_TOP) {
+ componentClassName += ' ' + styles[`${position}`]
+ }
+ const fixedStyle = { top: '0px', left: '0px' }
+ const wrapperRef = useRef(null)
+
+ useEffect(() => {
+ if (activeDependsOnVisible) {
+ setActive(visible)
+ }
+ }, [activeDependsOnVisible, visible])
+
+ if (wrapperRef.current) {
+ const referenceBoundingClientRect = wrapperRef.current.getBoundingClientRect()
+ if (referenceBoundingClientRect) {
+ let leftPosition
+
+ switch (direction) {
+ case DIRECTION_BOTTOM:
+ fixedStyle.top = `${referenceBoundingClientRect.y + referenceBoundingClientRect.height + offset}px`
+ fixedStyle.left = `${referenceBoundingClientRect.x + (referenceBoundingClientRect.width / 2)}px`
+ fixedStyle.transform = 'translateX(-50%)'
+ switch (position) {
+ case POSITION_END:
+ fixedStyle.transform = 'translateX(0%)'
+ fixedStyle.left = `${referenceBoundingClientRect.x + (referenceBoundingClientRect.width / 2) - 10}px`
+ break
+ case POSITION_START:
+ fixedStyle.transform = 'translateX(-100%)'
+ fixedStyle.left = `${referenceBoundingClientRect.x + (referenceBoundingClientRect.width / 2) + 10}px`
+ break
+ default:
+ fixedStyle.transform = 'translateX(-50%)'
+ break
+ }
+ break
+ case DIRECTION_LEFT:
+ fixedStyle.top = `${referenceBoundingClientRect.y + referenceBoundingClientRect.height / 2}px`
+ leftPosition = referenceBoundingClientRect.x - (2 * referenceBoundingClientRect.width) + offset
+ fixedStyle.left = `${leftPosition}px`
+ break
+ case DIRECTION_RIGHT:
+ fixedStyle.top = `${referenceBoundingClientRect.y + referenceBoundingClientRect.height / 2}px`
+ leftPosition = referenceBoundingClientRect.x + referenceBoundingClientRect.width + offset
+ fixedStyle.left = `${leftPosition}px`
+ break
+ default:
+ fixedStyle.top = `${referenceBoundingClientRect.y - referenceBoundingClientRect.height - offset}px`
+ fixedStyle.left = `${referenceBoundingClientRect.x + (referenceBoundingClientRect.width / 2)}px`
+ switch (position) {
+ case POSITION_END:
+ fixedStyle.transform = 'translateX(0%)'
+ fixedStyle.left = `${referenceBoundingClientRect.x + (referenceBoundingClientRect.width / 2) - 10}px`
+ break
+ case POSITION_START:
+ fixedStyle.transform = 'translateX(-100%)'
+ fixedStyle.left = `${referenceBoundingClientRect.x + (referenceBoundingClientRect.width / 2) + 10}px`
+ break
+ default:
+ fixedStyle.transform = 'translateX(-50%)'
+ break
+ }
+ break
+ }
+ }
+ }
+
+ const showTip = () => {
+ timeout = setTimeout(() => {
+ setActive(true)
+ }, delay)
+ }
+
+ const hideTip = () => {
+ clearInterval(timeout)
+ setActive(false)
+ }
+
+ return (
+
{}}
+ onMouseLeave={!activeDependsOnVisible ? hideTip : () => {}}
+ ref={el => { wrapperRef.current = el }}
+ >
+ {children}
+ {active && (
+
+ {content}
+
+ )}
+
+ )
+}
+
+export default Tooltip
diff --git a/src/components/Tooltip.module.css b/src/components/Tooltip.module.css
new file mode 100644
index 00000000..39d7575a
--- /dev/null
+++ b/src/components/Tooltip.module.css
@@ -0,0 +1,76 @@
+/* Wrapping */
+.tooltipWrapper {
+ display: inline-block;
+ position: relative;
+}
+
+/* Fixed positioning */
+.tooltipTip {
+ position: fixed;
+ border-radius: 4px;
+}
+
+.tooltipTipBaseClass {
+ @apply text-rich-black bg-white p-2 font-sans text-sm z-20 rounded;
+ white-space: nowrap;
+}
+
+/* CSS border triangles */
+.tooltipTip::before {
+ content: " ";
+ left: 50%;
+ border: solid transparent;
+ height: 0;
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+ border-width: 6px;
+ margin-left: calc(6px * -1);
+}
+
+/* CSS border triangles */
+.top::before {
+ @apply border-t-white;
+ top: 100%;
+}
+
+/* Absolute positioning */
+.right {
+ top: 50%;
+ transform: translateX(0) translateY(-50%);
+}
+/* CSS border triangles */
+.right::before {
+ @apply border-r-white;
+ left: calc(6px * -1);
+ top: 50%;
+ transform: translateX(0) translateY(-50%);
+}
+/* CSS border triangles */
+.bottom::before {
+ @apply border-b-white;
+ bottom: 100%;
+}
+
+/* Absolute positioning */
+.left {
+ left: auto;
+ top: 50%;
+ transform: translateX(0) translateY(-50%);
+}
+/* CSS border triangles */
+.left::before {
+ @apply border-l-white;
+ left: calc(100% + 6px);
+ right: calc(6px * -2);
+ top: 50%;
+ transform: translateX(0) translateY(-50%);
+}
+
+.start::before {
+ left: calc(100% - 10px);
+ right: 10px;
+}
+.end::before {
+ left: 10px;
+}
\ No newline at end of file
diff --git a/src/components/TooltipAbsolute.jsx b/src/components/TooltipAbsolute.jsx
new file mode 100644
index 00000000..913b09b8
--- /dev/null
+++ b/src/components/TooltipAbsolute.jsx
@@ -0,0 +1,71 @@
+import styles from './TooltipAbsolute.module.css'
+import { useEffect, useState } from 'react'
+import { DIRECTION_LEFT, DIRECTION_BOTTOM, DIRECTION_TOP, DIRECTION_RIGHT, POSITION_START } from './constants'
+
+const TooltipAbsolute = ({
+ direction = DIRECTION_TOP,
+ tooltipClassName = '',
+ delay = 0,
+ children,
+ content = null,
+ offset = 0,
+ position = POSITION_START,
+ visible = false,
+ activeDependsOnVisible = false
+}) => {
+ let timeout
+ const [active, setActive] = useState(activeDependsOnVisible ? visible : false)
+ let componentClassName = tooltipClassName || styles.tooltipTipBaseClass
+ componentClassName += ` ${styles.tooltipTip} ` + styles[`${position}`] + ' ' + styles[`${direction}`]
+
+ const absoluteStyle = {}
+
+ switch (direction) {
+ case DIRECTION_BOTTOM:
+ absoluteStyle.bottom = `calc(${offset}px * -1)`
+ break
+ case DIRECTION_RIGHT:
+ absoluteStyle.left = `calc(100% + ${offset}px)`
+ break
+ case DIRECTION_LEFT:
+ absoluteStyle.right = `calc(100% + ${offset}px)`
+ break
+ default:
+ absoluteStyle.top = `calc(${offset}px * -1)`
+ break
+ }
+
+ useEffect(() => {
+ if (activeDependsOnVisible) {
+ setActive(visible)
+ }
+ }, [activeDependsOnVisible, visible])
+
+ const showTip = () => {
+ timeout = setTimeout(() => {
+ setActive(true)
+ }, delay)
+ }
+
+ const hideTip = () => {
+ clearInterval(timeout)
+ setActive(false)
+ }
+ return (
+
+
+ {children}
+ {active && (
+
+ {content}
+
+ )}
+
+ )
+}
+
+export default TooltipAbsolute
diff --git a/src/components/TooltipAbsolute.module.css b/src/components/TooltipAbsolute.module.css
new file mode 100644
index 00000000..62a93159
--- /dev/null
+++ b/src/components/TooltipAbsolute.module.css
@@ -0,0 +1,88 @@
+/* Wrapping */
+.tooltipWrapper {
+ display: inline-block;
+ position: relative;
+}
+
+/* Absolute positioning */
+.tooltipTip {
+ position: absolute;
+ border-radius: 4px;
+}
+
+.tooltipTipBaseClass {
+ @apply text-rich-black bg-white p-2 font-sans text-sm z-20 rounded;
+ white-space: nowrap;
+}
+
+/* CSS border triangles */
+.tooltipTip::before {
+ content: " ";
+ border: solid transparent;
+ height: 0;
+ width: 0;
+ position: absolute;
+ pointer-events: none;
+ border-width: 6px;
+ margin-left: calc(6px * -1);
+}
+.start {
+ left: 0%;
+}
+.start::before {
+ left: 12px;
+}
+
+.center, .center::before {
+ left: 50%;
+ transform: translateX(-50%);
+}
+.end {
+ left: 100%;
+ transform: translateX(-100%);
+}
+.end::before {
+ right: 12px;
+}
+
+
+/* CSS border triangles */
+.top::before {
+ top: 100%;
+ border-top-color: white;
+}
+
+/* Absolute positioning */
+.right {
+ top: 50%;
+ transform: translateX(0) translateY(-50%);
+}
+/* CSS border triangles */
+.right::before {
+ left: calc(6px * -1);
+ top: 50%;
+ transform: translateX(0) translateY(-50%);
+ border-right-color: white;
+}
+
+/* CSS border triangles */
+.bottom::before {
+ bottom: 100%;
+ border-bottom-color: white;
+}
+
+/* Absolute positioning */
+.left {
+ left: auto;
+ top: 50%;
+ transform: translateX(0) translateY(-50%);
+}
+/* CSS border triangles */
+.left::before {
+ left: auto;
+ right: calc(6px * -2);
+ top: 50%;
+ transform: translateX(0) translateY(-50%);
+ border-left-color: white;
+ border-right-color: transparent;
+}
diff --git a/src/components/TrendLine.jsx b/src/components/TrendLine.jsx
new file mode 100644
index 00000000..3a7ded38
--- /dev/null
+++ b/src/components/TrendLine.jsx
@@ -0,0 +1,147 @@
+import React, { useRef, useEffect, useState } from 'react'
+import * as d3 from 'd3'
+import styles from './TrendLine.module.css'
+
+/**
+ * TrendLine component
+ * @param {Object} props
+ * @param {number[]} props.yValues - Array of y-values (equally spaced on x-axis)
+ * @param {number} [props.width=400] - Width of the SVG
+ * @param {number} [props.height=120] - Height of the SVG
+ */
+function TrendLine ({ yValues }) {
+ const ref = useRef(null)
+ const containerRef = useRef(null)
+ /** @type {React.RefObject
} */
+ // @ts-ignore
+ const typedContainerRef = containerRef
+ const [dimensions, setDimensions] = useState({ width: 400, height: 120 })
+
+ useEffect(() => {
+ if (!typedContainerRef.current) return
+ // Initial set
+ const bounding = typedContainerRef.current.getBoundingClientRect()
+ if (bounding) {
+ setDimensions({ width: bounding.width, height: bounding.height })
+ }
+ // Resize observer
+ const resizeObserver = new window.ResizeObserver(entries => {
+ for (const entry of entries) {
+ if (entry.target === typedContainerRef.current) {
+ const { width, height } = entry.contentRect
+ setDimensions({ width, height })
+ }
+ }
+ })
+ resizeObserver.observe(typedContainerRef.current)
+ return () => resizeObserver.disconnect()
+ }, [])
+
+ useEffect(() => {
+ const { width, height } = dimensions
+ if (!yValues || yValues.length === 0) return
+ // Limit to max 5 points by downsampling if needed
+ let limitedYValues = yValues
+ if (yValues.length > 5) {
+ const step = (yValues.length - 1) / 4
+ limitedYValues = Array.from({ length: 5 }, (_, i) => yValues[Math.round(i * step)])
+ }
+ const svg = d3.select(ref.current)
+ svg.selectAll('*').remove() // Clear previous
+
+ // Margins for padding
+ // const margin = { top: 10, right: 10, bottom: 10, left: 10 }
+ const margin = { top: 0, right: 0, bottom: 0, left: 0 }
+ const w = width - margin.left - margin.right
+ const h = height - margin.top - margin.bottom
+
+ // Ensure yValues is a number[] and filter out undefined/null
+ const cleanYValues = limitedYValues.filter(v => typeof v === 'number' && !isNaN(v))
+ if (cleanYValues.length === 0) return
+
+ // Helper to ensure [number, number][]
+ function toXYPairs (arr) {
+ const out = []
+ for (let i = 0; i < arr.length; i++) {
+ const x = i
+ const y = arr[i]
+ if (typeof x === 'number' && typeof y === 'number' && Number.isFinite(x) && Number.isFinite(y)) {
+ out.push([x, y])
+ }
+ }
+ return out
+ }
+ const points = toXYPairs(cleanYValues)
+ if (points.length < 2) return
+
+ // X and Y scales
+ const x = d3.scaleLinear()
+ .domain([0, points.length - 1])
+ .range([0, w])
+ const y = d3.scaleLinear()
+ .domain([
+ Number(d3.max(cleanYValues) ?? 1),
+ Number(d3.min(cleanYValues) ?? 0)
+ ]) // Invert so higher values are up
+ .range([margin.top, h + margin.top])
+
+ // Line generator for [x, y] pairs
+ const line = d3.line()
+ .x(d => x(d[0]) + margin.left)
+ .y(d => y(d[1]))
+ .curve(d3.curveCatmullRom.alpha(0.5))
+
+ // Area generator for [x, y] pairs
+ const area = d3.area()
+ .x(d => x(d[0]) + margin.left)
+ .y0(h + margin.top)
+ .y1(d => y(d[1]))
+ .curve(d3.curveCatmullRom.alpha(0.5))
+
+ // Draw area (shadow/fill)
+ svg.append('path')
+ // @ts-ignore
+ .attr('d', area(points))
+ .attr('fill', 'url(#trend-gradient)')
+ .attr('opacity', 1)
+
+ // Draw line
+ svg.append('path')
+ // @ts-ignore
+ .attr('d', line(points))
+ .attr('fill', 'none')
+ .attr('stroke', '#BFC3C7')
+ .attr('stroke-width', 2)
+ .attr('stroke-linecap', 'round')
+
+ // Gradient for area
+ const defs = svg.append('defs')
+ const gradient = defs.append('linearGradient')
+ .attr('id', 'trend-gradient')
+ .attr('x1', '0%')
+ .attr('y1', '0%')
+ .attr('x2', '0%')
+ .attr('y2', '100%')
+ gradient.append('stop')
+ .attr('offset', '0%')
+ .attr('stop-color', '#BFC3C7')
+ .attr('stop-opacity', 0.12)
+ gradient.append('stop')
+ .attr('offset', '100%')
+ .attr('stop-color', '#BFC3C7')
+ .attr('stop-opacity', 0)
+ }, [yValues, dimensions])
+
+ return (
+
+
+
+ )
+}
+
+export default TrendLine
diff --git a/src/components/TrendLine.module.css b/src/components/TrendLine.module.css
new file mode 100644
index 00000000..af4759ec
--- /dev/null
+++ b/src/components/TrendLine.module.css
@@ -0,0 +1,3 @@
+.line {
+ @apply w-full h-full;
+}
\ No newline at end of file
diff --git a/src/components/TrendMetric.jsx b/src/components/TrendMetric.jsx
new file mode 100644
index 00000000..690b19dd
--- /dev/null
+++ b/src/components/TrendMetric.jsx
@@ -0,0 +1,37 @@
+import React from 'react'
+import styles from './TrendMetric.module.css'
+import TrendUpIcon from './icons/TrendUpIcon'
+import TrendDownIcon from './icons/TrendDownIcon'
+import TrendLine from './TrendLine'
+
+export default function TrendMetric ({ value, unit, helper, showGraph = false, data }) {
+ // set trend variable up or down, depending on the first and last value of the data array
+ const trend = data[0] > data[data.length - 1] ? 'up' : 'down'
+
+ return (
+
+
+
+
+
{value}
+
{unit}
+
+ {trend === 'up' ? : }
+
+
+
+
+
+ {helper}
+
+
+
+
+ {showGraph && (
+
+
+
+ )}
+
+ )
+}
diff --git a/src/components/TrendMetric.module.css b/src/components/TrendMetric.module.css
new file mode 100644
index 00000000..0b726ad7
--- /dev/null
+++ b/src/components/TrendMetric.module.css
@@ -0,0 +1,25 @@
+.contentText {
+ @apply flex flex-col gap-2 items-center justify-center;
+}
+.value {
+ @apply flex gap-4 items-center justify-center;
+}
+
+.valueNumber {
+ @apply text-[24px] font-semibold text-white;
+}
+
+.valueUnit {
+ @apply text-[16px] font-normal text-white/70;
+}
+
+.helperBox {
+ @apply text-[14px] font-normal text-white/70;
+}
+
+.contentGraph {
+ @apply w-[210px] h-[25px];
+}
+.container {
+ @apply flex items-center justify-center gap-4;
+}
diff --git a/src/components/Versions.jsx b/src/components/Versions.jsx
index 9d4b06b4..2373573e 100644
--- a/src/components/Versions.jsx
+++ b/src/components/Versions.jsx
@@ -1,4 +1,3 @@
-'use strict'
import React from 'react'
import BorderedBox from './BorderedBox'
diff --git a/src/components/VerticalSeparator.jsx b/src/components/VerticalSeparator.jsx
index 6fd46a05..04590f1c 100644
--- a/src/components/VerticalSeparator.jsx
+++ b/src/components/VerticalSeparator.jsx
@@ -1,23 +1,17 @@
-'use strict'
import React from 'react'
-import PropTypes from 'prop-types'
-import { DARK_GREEN, MAIN_DARK_BLUE } from './constants'
+import { DARK_GREEN, OPACITY_100 } from './constants'
import commonStyles from './Common.module.css'
+import styles from './VerticalSeparator.module.css'
-function VerticalSeparator ({ color }) {
- const className = commonStyles[`text--${color}`] + ' my-auto mx-5'
- return |
-}
-
-VerticalSeparator.propTypes = {
- /**
- * color
- */
- color: PropTypes.oneOf([DARK_GREEN, MAIN_DARK_BLUE])
-}
-
-VerticalSeparator.defaultProps = {
- color: DARK_GREEN
+function VerticalSeparator ({
+ color = DARK_GREEN,
+ backgroundColorOpacity = OPACITY_100,
+ classes = ''
+}) {
+ const backgroundColor = commonStyles[`background-color-${color}`]
+ const backgroundOpacity = commonStyles[`background-color-opaque-${backgroundColorOpacity}`]
+ const className = `${backgroundColor} ${backgroundOpacity} ${styles.fullHeight} ${classes}`
+ return
}
export default VerticalSeparator
diff --git a/src/components/VerticalSeparator.module.css b/src/components/VerticalSeparator.module.css
new file mode 100644
index 00000000..7eff545a
--- /dev/null
+++ b/src/components/VerticalSeparator.module.css
@@ -0,0 +1,3 @@
+.fullHeight {
+ @apply h-full min-h-[18px] w-[1px];
+}
\ No newline at end of file
diff --git a/src/components/backgrounds/AppBackgroundIcon.jsx b/src/components/backgrounds/AppBackgroundIcon.jsx
index 110d2bfa..9de7c85a 100644
--- a/src/components/backgrounds/AppBackgroundIcon.jsx
+++ b/src/components/backgrounds/AppBackgroundIcon.jsx
@@ -1,5 +1,3 @@
-'use strict'
-
const AppBackgroundIcon = () => {
return (