diff --git a/app/assets/css/vendor-override.css b/app/assets/css/vendor-override.css index 5c0fc7570..224c352ca 100644 --- a/app/assets/css/vendor-override.css +++ b/app/assets/css/vendor-override.css @@ -398,3 +398,50 @@ input:-webkit-autofill { border-bottom-right-radius: 8px; padding: 5px; } + +/* Tippy */ +/* For triangle arrow (Sharp) */ +/* Tippy */ +/* For triangle arrow (Sharp) */ +.tippy-box[data-placement^='top'] > .tippy-arrow:before { + border-top: 8px solid var(--bg-tooltip-color); +} + +.tippy-box[data-placement^='bottom'] > .tippy-arrow:before { + border-bottom: 8px solid var(--bg-tooltip-color); +} + +.tippy-box[data-placement^='right'] > .tippy-arrow:before { + border-right: 8px solid var(--bg-tooltip-color); +} + +.tippy-box[data-placement^='left'] > .tippy-arrow:before { + border-left: 8px solid var(--bg-tooltip-color); +} + +/* Sidebar */ +.sidebar .tippy-box { + font-size: 12px; +} + +.sidebar .tippy-box[data-placement^='right'] > .tippy-arrow { + width: 12px; + height: 12px; +} + +.sidebar .tippy-box[data-placement^='right'] > .tippy-arrow:before { + border-right: 8px solid var(--ui-gray-9); + border-width: 6px 8px 6px 0; +} + +[theme='dark'] .sidebar .tippy-box[data-placement^='right'] > .tippy-arrow:before { + border-right: 8px solid var(--ui-gray-true-9); +} + +.sidebar .tippy-content { + white-space: nowrap; +} + +.tippy-content { + padding: 0; +} diff --git a/app/kubernetes/react/views/yamlReplace/index.tsx b/app/kubernetes/react/views/yamlReplace/index.tsx index 1c45da9d2..35f24100d 100644 --- a/app/kubernetes/react/views/yamlReplace/index.tsx +++ b/app/kubernetes/react/views/yamlReplace/index.tsx @@ -14,15 +14,17 @@ export function YAMLReplace({ featureId }: Props) { BEFeatureID={featureId} message="Applies any changes that you make in the YAML editor by calling the Kubernetes API to patch the relevant resources. Any resource removals or unexpected resource additions that you make in the YAML will be ignored. Note that editing is disabled for resources in namespaces marked as system." > - +
+ +
); } diff --git a/app/react/components/BoxSelector/BoxOption.tsx b/app/react/components/BoxSelector/BoxOption.tsx index 181e70b24..0d7d7efc4 100644 --- a/app/react/components/BoxSelector/BoxOption.tsx +++ b/app/react/components/BoxSelector/BoxOption.tsx @@ -1,6 +1,7 @@ import clsx from 'clsx'; import { PropsWithChildren } from 'react'; -import ReactTooltip from 'react-tooltip'; + +import { Tooltip } from '@@/Tip/Tooltip'; import './BoxSelectorItem.css'; @@ -28,16 +29,8 @@ export function BoxOption({ type = 'radio', children, }: PropsWithChildren>) { - const tooltipId = `box-option-${radioName}-${option.id}`; return ( -
+
({ {children} {tooltip && ( - - {tooltip} - + message={tooltip} + /> )}
); diff --git a/app/react/components/BoxSelector/LimitedToBeIndicator.tsx b/app/react/components/BoxSelector/LimitedToBeIndicator.tsx index 10bc031dc..62ecb7324 100644 --- a/app/react/components/BoxSelector/LimitedToBeIndicator.tsx +++ b/app/react/components/BoxSelector/LimitedToBeIndicator.tsx @@ -1,8 +1,9 @@ -import ReactTooltip from 'react-tooltip'; import { HelpCircle } from 'lucide-react'; +import clsx from 'clsx'; import { FeatureId } from '@/react/portainer/feature-flags/enums'; +import { TooltipWithChildren } from '@@/Tip/TooltipWithChildren'; import { getFeatureDetails } from '@@/BEFeatureIndicator/utils'; interface Props { @@ -10,35 +11,24 @@ interface Props { featureId?: FeatureId; } -export function LimitedToBeIndicator({ tooltipId, featureId }: Props) { +export function LimitedToBeIndicator({ featureId, tooltipId }: Props) { const { url } = getFeatureDetails(featureId); return ( - <> -
-
- - Pro Feature - - -
+
+ - - Business Edition feature.
- This feature is currently limited to Business Edition users only. -
- +
); } diff --git a/app/react/components/Tip/Tooltip/Tooltip.module.css b/app/react/components/Tip/Tooltip/Tooltip.module.css deleted file mode 100644 index 2281cb971..000000000 --- a/app/react/components/Tip/Tooltip/Tooltip.module.css +++ /dev/null @@ -1,25 +0,0 @@ -:global(portainer-tooltip) { - @apply inline-flex; -} - -.tooltip-wrapper { - display: inline-block; - position: relative; -} - -.tooltip { - background-color: var(--bg-tooltip-color) !important; - padding: 0.833em 1em !important; - color: var(--text-tooltip-color) !important; - border-radius: 10px !important; - box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15) !important; - max-width: 400px; - text-align: center; - font-size: 12px !important; - font-weight: 400; -} - -.icon { - margin-left: 5px; - cursor: pointer; -} diff --git a/app/react/components/Tip/Tooltip/Tooltip.tsx b/app/react/components/Tip/Tooltip/Tooltip.tsx index 4b5055890..48f443e97 100644 --- a/app/react/components/Tip/Tooltip/Tooltip.tsx +++ b/app/react/components/Tip/Tooltip/Tooltip.tsx @@ -1,11 +1,6 @@ -import ReactTooltip from 'react-tooltip'; import { HelpCircle } from 'lucide-react'; -import clsx from 'clsx'; -import _ from 'lodash'; -import styles from './Tooltip.module.css'; - -type Position = 'top' | 'right' | 'bottom' | 'left'; +import { TooltipWithChildren, Position } from '../TooltipWithChildren'; export interface Props { position?: Position; @@ -14,24 +9,15 @@ export interface Props { } export function Tooltip({ message, position = 'bottom', className }: Props) { - const id = _.uniqueId('tooltip-'); - return ( - - + + + ); } diff --git a/app/react/components/Tip/TooltipWithChildren/TooltipWithChildren.module.css b/app/react/components/Tip/TooltipWithChildren/TooltipWithChildren.module.css index 856845d55..3f7edf69e 100644 --- a/app/react/components/Tip/TooltipWithChildren/TooltipWithChildren.module.css +++ b/app/react/components/Tip/TooltipWithChildren/TooltipWithChildren.module.css @@ -2,20 +2,14 @@ @apply inline-flex; } -.tooltip-wrapper { - display: inline-block; - position: relative; -} - .tooltip { background-color: var(--bg-tooltip-color) !important; - padding: 0.833em 1em !important; color: var(--text-tooltip-color) !important; border-radius: 10px !important; box-shadow: 0 2px 4px 0 rgba(34, 36, 38, 0.12), 0 2px 10px 0 rgba(34, 36, 38, 0.15) !important; - max-width: 400px; + min-width: 300px; + font-size: 1rem !important; text-align: left; - font-size: 12px !important; font-weight: 400; } @@ -26,6 +20,8 @@ .tooltip-container { line-height: 18px; + padding: 8px 10px !important; + font-size: 12px !important; } .tooltip-heading { diff --git a/app/react/components/Tip/TooltipWithChildren/TooltipWithChildren.tsx b/app/react/components/Tip/TooltipWithChildren/TooltipWithChildren.tsx index 6f0beb3b2..4e6b7f4f0 100644 --- a/app/react/components/Tip/TooltipWithChildren/TooltipWithChildren.tsx +++ b/app/react/components/Tip/TooltipWithChildren/TooltipWithChildren.tsx @@ -1,7 +1,9 @@ -import ReactTooltip from 'react-tooltip'; +import React, { MouseEvent } from 'react'; +import Tippy from '@tippyjs/react'; import clsx from 'clsx'; import _ from 'lodash'; -import ReactDOMServer from 'react-dom/server'; + +import 'tippy.js/dist/tippy.css'; import { FeatureId } from '@/react/portainer/feature-flags/enums'; @@ -9,13 +11,13 @@ import { getFeatureDetails } from '@@/BEFeatureIndicator/utils'; import styles from './TooltipWithChildren.module.css'; -type Position = 'top' | 'right' | 'bottom' | 'left'; +export type Position = 'top' | 'right' | 'bottom' | 'left'; export interface Props { position?: Position; message: string; className?: string; - children: React.ReactNode; + children: React.ReactElement; heading?: string; BEFeatureID?: FeatureId; } @@ -35,49 +37,53 @@ export function TooltipWithChildren({ : { url: '', limitedToBE: false }; const messageHTML = ( -
-
- {heading && {heading}} - {BEFeatureID && limitedToBE && ( - - Business Edition Only - - )} -
+ // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions +
+ {(heading || (BEFeatureID && limitedToBE)) && ( +
+ {heading} + {BEFeatureID && limitedToBE && ( + + Business Edition Only + + )} +
+ )}
{message}
); return ( - {children} - - + ); } + +// Preventing click bubbling to the parent as it is affecting +// mainly toggles when full row is clickable. +function onClickHandler(e: MouseEvent) { + const target = e.target as HTMLInputElement; + if (target.tagName.toLowerCase() === 'a') { + const url = target.getAttribute('href'); + if (url) { + window.open(url, '_blank'); + } + } + e.preventDefault(); +} diff --git a/app/react/components/Tip/TooltipWithChildren/index.ts b/app/react/components/Tip/TooltipWithChildren/index.ts index 42df25cc4..435a2cb7b 100644 --- a/app/react/components/Tip/TooltipWithChildren/index.ts +++ b/app/react/components/Tip/TooltipWithChildren/index.ts @@ -1 +1,2 @@ export { TooltipWithChildren } from './TooltipWithChildren'; +export type { Position } from './TooltipWithChildren'; diff --git a/app/react/sidebar/Sidebar.tsx b/app/react/sidebar/Sidebar.tsx index beb9a7ed5..778cb38c2 100644 --- a/app/react/sidebar/Sidebar.tsx +++ b/app/react/sidebar/Sidebar.tsx @@ -30,7 +30,7 @@ export function Sidebar() { return ( /* in the future (when we remove r2a) this should wrap the whole app - to change root styles */ -
+