mirror of
https://github.com/portainer/portainer.git
synced 2025-08-05 05:45:22 +02:00
refactor(icons): replace fa icons [EE-4459] (#7907)
refactor(icons): remove fontawesome EE-4459 refactor(icon) replace feather with lucide EE-4472
This commit is contained in:
parent
9dfac98a26
commit
d78b762f7b
498 changed files with 2102 additions and 2817 deletions
|
@ -1,6 +1,7 @@
|
|||
import { Package } from 'react-feather';
|
||||
import { Package } from 'lucide-react';
|
||||
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
import Subscription from '@/assets/ico/subscription.svg?c';
|
||||
|
||||
import { PageHeader } from '@@/PageHeader';
|
||||
import { DashboardItem } from '@@/DashboardItem';
|
||||
|
@ -9,8 +10,6 @@ import { DashboardGrid } from '@@/DashboardItem/DashboardGrid';
|
|||
import { useResourceGroups } from '../queries/useResourceGroups';
|
||||
import { useSubscriptions } from '../queries/useSubscriptions';
|
||||
|
||||
import SubscriptionsIcon from './icon-subscription.svg?c';
|
||||
|
||||
export function DashboardView() {
|
||||
const environmentId = useEnvironmentId();
|
||||
|
||||
|
@ -35,7 +34,7 @@ export function DashboardView() {
|
|||
<DashboardGrid>
|
||||
<DashboardItem
|
||||
value={subscriptionsCount as number}
|
||||
icon={SubscriptionsIcon}
|
||||
icon={Subscription}
|
||||
type="Subscription"
|
||||
/>
|
||||
{!resourceGroupsQuery.isError && !resourceGroupsQuery.isLoading && (
|
||||
|
|
|
@ -1,3 +0,0 @@
|
|||
<svg width="22" height="20" viewBox="0 0 22 20" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.8641 8.08333H1.69743M10.3224 16.7083L17.7974 16.7083C18.8709 16.7083 19.4076 16.7083 19.8176 16.4994C20.1782 16.3157 20.4714 16.0225 20.6552 15.6618C20.8641 15.2518 20.8641 14.7151 20.8641 13.6417V6.35833C20.8641 5.2849 20.8641 4.74818 20.6552 4.33819C20.4714 3.97754 20.1782 3.68433 19.8176 3.50057C19.4076 3.29167 18.8709 3.29167 17.7974 3.29167H16.0724M10.3224 16.7083L12.2391 18.625M10.3224 16.7083L12.2391 14.7917M6.48909 16.7083H4.76409C3.69066 16.7083 3.15394 16.7083 2.74394 16.4994C2.3833 16.3157 2.09009 16.0225 1.90633 15.6618C1.69743 15.2518 1.69743 14.7151 1.69743 13.6417V6.35833C1.69743 5.2849 1.69743 4.74818 1.90633 4.33818C2.09009 3.97754 2.3833 3.68433 2.74394 3.50057C3.15394 3.29167 3.69066 3.29167 4.76409 3.29167H12.2391M12.2391 3.29167L10.3224 5.20833M12.2391 3.29167L10.3224 1.375" stroke="#344054" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round"/>
|
||||
</svg>
|
Before Width: | Height: | Size: 1,008 B |
|
@ -1,5 +1,6 @@
|
|||
import { Field, Form, Formik } from 'formik';
|
||||
import { useRouter } from '@uirouter/react';
|
||||
import { Plus } from 'lucide-react';
|
||||
|
||||
import { ContainerInstanceFormValues } from '@/react/azure/types';
|
||||
import * as notifications from '@/portainer/services/notifications';
|
||||
|
@ -194,8 +195,8 @@ export function CreateContainerInstanceForm() {
|
|||
disabled={!isValid}
|
||||
isLoading={isSubmitting}
|
||||
loadingText="Deployment in progress..."
|
||||
icon={Plus}
|
||||
>
|
||||
<i className="fa fa-plus space-right" aria-hidden="true" />
|
||||
Deploy the container
|
||||
</LoadingButton>
|
||||
</div>
|
||||
|
|
|
@ -1,10 +1,12 @@
|
|||
import { FormikErrors } from 'formik';
|
||||
import { ArrowRight } from 'lucide-react';
|
||||
|
||||
import { ButtonSelector } from '@@/form-components/ButtonSelector/ButtonSelector';
|
||||
import { FormError } from '@@/form-components/FormError';
|
||||
import { InputGroup } from '@@/form-components/InputGroup';
|
||||
import { InputList } from '@@/form-components/InputList';
|
||||
import { ItemProps } from '@@/form-components/InputList/InputList';
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import styles from './PortsMappingField.module.css';
|
||||
|
||||
|
@ -82,7 +84,7 @@ function Item({
|
|||
</InputGroup>
|
||||
|
||||
<span className="mx-3">
|
||||
<i className="fa fa-long-arrow-alt-right" aria-hidden="true" />
|
||||
<Icon icon={ArrowRight} />
|
||||
</span>
|
||||
|
||||
<InputGroup size="small">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Box, Plus, Trash2 } from 'react-feather';
|
||||
import { Box, Plus, Trash2 } from 'lucide-react';
|
||||
import { useStore } from 'zustand';
|
||||
|
||||
import { ContainerGroup } from '@/react/azure/types';
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { CellProps, Column } from 'react-table';
|
||||
import { ExternalLink } from 'lucide-react';
|
||||
|
||||
import { ContainerGroup } from '@/react/azure/types';
|
||||
import { getPorts } from '@/react/azure/utils';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
export const ports: Column<ContainerGroup> = {
|
||||
Header: 'Published Ports',
|
||||
accessor: (container) => getPorts(container),
|
||||
|
@ -26,8 +29,8 @@ function PortsCell({
|
|||
|
||||
return ports.map((port) => (
|
||||
<a className="image-tag" href={`http://${ip}:${port.host}`} key={port.host}>
|
||||
<i className="fa fa-external-link-alt" aria-hidden="true" /> {ip}:
|
||||
{port.host}
|
||||
<Icon icon={ExternalLink} className="mr-1" />
|
||||
{ip}:{port.host}
|
||||
</a>
|
||||
));
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
import { PropsWithChildren } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { Briefcase } from 'react-feather';
|
||||
import { Briefcase } from 'lucide-react';
|
||||
|
||||
import './BEFeatureIndicator.css';
|
||||
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import { getFeatureDetails } from './utils';
|
||||
|
||||
export interface Props {
|
||||
|
@ -33,8 +35,8 @@ export function BEFeatureIndicator({
|
|||
rel="noopener noreferrer"
|
||||
>
|
||||
{children}
|
||||
{showIcon && <Briefcase className="icon icon-sm vertical-center" />}
|
||||
<span className="be-indicator-label break-words space-left">
|
||||
{showIcon && <Icon icon={Briefcase} className="mr-1 be-indicator-icon" />}
|
||||
<span className="be-indicator-label break-words">
|
||||
Business Edition Feature
|
||||
</span>
|
||||
</a>
|
||||
|
|
|
@ -29,7 +29,7 @@ function Template({
|
|||
size?: BadgeSize;
|
||||
icon: string;
|
||||
}) {
|
||||
return <BadgeIcon icon={icon} size={size} featherIcon />;
|
||||
return <BadgeIcon icon={icon} size={size} />;
|
||||
}
|
||||
|
||||
export const Example = Template.bind({});
|
|
@ -8,7 +8,7 @@ export interface Props extends IconProps {
|
|||
size?: BadgeSize;
|
||||
}
|
||||
|
||||
export function BadgeIcon({ icon, featherIcon, size = '3xl' }: Props) {
|
||||
export function BadgeIcon({ icon, size = '3xl' }: Props) {
|
||||
const sizeClasses = iconSizeToClasses(size);
|
||||
return (
|
||||
<div
|
||||
|
@ -22,7 +22,7 @@ export function BadgeIcon({ icon, featherIcon, size = '3xl' }: Props) {
|
|||
`
|
||||
)}
|
||||
>
|
||||
<Icon icon={icon} feather={featherIcon} className="feather !flex" />
|
||||
<Icon icon={icon} className="!flex" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Meta } from '@storybook/react';
|
||||
import { useState } from 'react';
|
||||
import { User } from 'lucide-react';
|
||||
|
||||
import { init as initFeatureService } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||
import { Edition, FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
|
@ -21,14 +22,14 @@ function Example() {
|
|||
const options: BoxSelectorOption<number>[] = [
|
||||
{
|
||||
description: 'description 1',
|
||||
icon: 'fa fa-rocket',
|
||||
icon: User,
|
||||
id: '1',
|
||||
value: 3,
|
||||
label: 'option 1',
|
||||
},
|
||||
{
|
||||
description: 'description 2',
|
||||
icon: 'fa fa-rocket',
|
||||
icon: User,
|
||||
id: '2',
|
||||
value: 4,
|
||||
label: 'option 2',
|
||||
|
@ -53,14 +54,14 @@ function LimitedFeature() {
|
|||
const options: BoxSelectorOption<number>[] = [
|
||||
{
|
||||
description: 'description 1',
|
||||
icon: 'fa fa-rocket',
|
||||
icon: User,
|
||||
id: '1',
|
||||
value: 3,
|
||||
label: 'option 1',
|
||||
},
|
||||
{
|
||||
description: 'description 2',
|
||||
icon: 'fa fa-rocket',
|
||||
icon: User,
|
||||
id: '2',
|
||||
value: 4,
|
||||
label: 'option 2',
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { Rocket } from 'lucide-react';
|
||||
|
||||
import { render, fireEvent } from '@/react-tools/test-utils';
|
||||
|
||||
import { BoxSelector, Props } from './BoxSelector';
|
||||
|
@ -23,14 +25,14 @@ test('should render with the initial value selected and call onChange when click
|
|||
const options: BoxSelectorOption<number>[] = [
|
||||
{
|
||||
description: 'description 1',
|
||||
icon: 'fa fa-rocket',
|
||||
icon: Rocket,
|
||||
id: '1',
|
||||
value: 3,
|
||||
label: 'option 1',
|
||||
},
|
||||
{
|
||||
description: 'description 2',
|
||||
icon: 'fa fa-rocket',
|
||||
icon: Rocket,
|
||||
id: '2',
|
||||
value: 4,
|
||||
label: 'option 2',
|
||||
|
|
|
@ -13,11 +13,6 @@
|
|||
color: var(--text-boxselector-header);
|
||||
}
|
||||
|
||||
.boxselector_header .fa,
|
||||
.fab {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.boxselector_wrapper input[type='radio'],
|
||||
.box-selector-item input[type='radio'] {
|
||||
display: none;
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { Meta } from '@storybook/react';
|
||||
import { User } from 'lucide-react';
|
||||
|
||||
import { init as initFeatureService } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||
import { Edition, FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
|
||||
import { IconProps } from '@@/Icon';
|
||||
|
||||
import { BoxSelectorItem } from './BoxSelectorItem';
|
||||
import { BoxSelectorOption } from './types';
|
||||
|
||||
|
@ -11,7 +14,7 @@ const meta: Meta = {
|
|||
args: {
|
||||
selected: false,
|
||||
description: 'description',
|
||||
icon: 'fa-rocket',
|
||||
icon: User,
|
||||
label: 'label',
|
||||
},
|
||||
};
|
||||
|
@ -21,7 +24,7 @@ export default meta;
|
|||
interface ExampleProps {
|
||||
selected?: boolean;
|
||||
description?: string;
|
||||
icon?: string;
|
||||
icon?: IconProps['icon'];
|
||||
label?: string;
|
||||
feature?: FeatureId;
|
||||
}
|
||||
|
@ -35,7 +38,7 @@ function Template({
|
|||
}: ExampleProps) {
|
||||
const option: BoxSelectorOption<number> = {
|
||||
description,
|
||||
icon: `fa ${icon}`,
|
||||
icon,
|
||||
id: 'id',
|
||||
label,
|
||||
value: 1,
|
||||
|
|
|
@ -54,7 +54,6 @@ export function BoxSelectorItem<T extends number | string>({
|
|||
{!!option.icon && (
|
||||
<Icon
|
||||
icon={option.icon}
|
||||
feather={option.featherIcon}
|
||||
className="boxselector_icon !flex items-center"
|
||||
/>
|
||||
)}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import ReactTooltip from 'react-tooltip';
|
||||
import { HelpCircle } from 'react-feather';
|
||||
import { HelpCircle } from 'lucide-react';
|
||||
|
||||
import { FeatureId } from '@/react/portainer/feature-flags/enums';
|
||||
|
||||
|
@ -21,7 +21,7 @@ export function LimitedToBeIndicator({ tooltipId, featureId }: Props) {
|
|||
<span className="text-warning-9">Pro Feature</span>
|
||||
</a>
|
||||
<HelpCircle
|
||||
className="feather !text-warning-7"
|
||||
className="lucide !text-warning-7"
|
||||
data-tip
|
||||
data-for={tooltipId}
|
||||
tooltip-append-to-body="true"
|
||||
|
|
|
@ -2,15 +2,15 @@ import { Icon, IconProps } from '@@/Icon';
|
|||
|
||||
type Props = IconProps;
|
||||
|
||||
export function LogoIcon({ icon, featherIcon }: Props) {
|
||||
export function LogoIcon({ icon }: Props) {
|
||||
return (
|
||||
<div
|
||||
className={`
|
||||
text-6xl h-14 w-14
|
||||
inline-flex items-center justify-center
|
||||
inline-flex items-center justify-center
|
||||
`}
|
||||
>
|
||||
<Icon icon={icon} feather={featherIcon} className="feather !flex" />
|
||||
<Icon icon={icon} className="!flex" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Edit, FileText, Globe, Upload } from 'react-feather';
|
||||
import { Edit, FileText, Globe, Upload } from 'lucide-react';
|
||||
|
||||
import GitIcon from '@/assets/ico/git.svg?c';
|
||||
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import clsx from 'clsx';
|
||||
import { Check, Copy } from 'lucide-react';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
import { useCopy } from '@@/buttons/CopyButton/useCopy';
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import styles from './Code.module.css';
|
||||
|
||||
|
@ -19,12 +20,10 @@ export function Code({ children, showCopyButton }: Props) {
|
|||
|
||||
{showCopyButton && (
|
||||
<Button color="link" className={styles.copyButton} onClick={handleCopy}>
|
||||
<i
|
||||
className={clsx(
|
||||
'fa',
|
||||
copiedSuccessfully ? 'fa-check green-icon' : 'fa-copy '
|
||||
)}
|
||||
aria-hidden="true"
|
||||
<Icon
|
||||
icon={copiedSuccessfully ? Check : Copy}
|
||||
className="!ml-1"
|
||||
mode={copiedSuccessfully ? 'success' : undefined}
|
||||
/>
|
||||
</Button>
|
||||
)}
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
import { Meta, Story } from '@storybook/react';
|
||||
import { List } from 'lucide-react';
|
||||
|
||||
import { Link } from '@@/Link';
|
||||
import { IconProps } from '@@/Icon';
|
||||
|
||||
import { DashboardItem } from './DashboardItem';
|
||||
|
||||
|
@ -12,7 +14,7 @@ export default meta;
|
|||
|
||||
interface StoryProps {
|
||||
value: number;
|
||||
icon: string;
|
||||
icon: IconProps['icon'];
|
||||
type: string;
|
||||
}
|
||||
|
||||
|
@ -23,21 +25,21 @@ function Template({ value, icon, type }: StoryProps) {
|
|||
export const Primary: Story<StoryProps> = Template.bind({});
|
||||
Primary.args = {
|
||||
value: 1,
|
||||
icon: 'fa fa-th-list',
|
||||
icon: List,
|
||||
type: 'Example resource',
|
||||
};
|
||||
|
||||
export function WithLink() {
|
||||
return (
|
||||
<Link to="example.page">
|
||||
<DashboardItem value={1} icon="fa fa-th-list" type="Example resource" />
|
||||
<DashboardItem value={1} icon={List} type="Example resource" />
|
||||
</Link>
|
||||
);
|
||||
}
|
||||
|
||||
export function WithChildren() {
|
||||
return (
|
||||
<DashboardItem value={1} icon="fa fa-th-list" type="Example resource">
|
||||
<DashboardItem value={1} icon={List} type="Example resource">
|
||||
<div>Children</div>
|
||||
</DashboardItem>
|
||||
);
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { User } from 'lucide-react';
|
||||
|
||||
import { render } from '@/react-tools/test-utils';
|
||||
|
||||
import { DashboardItem } from './DashboardItem';
|
||||
|
@ -11,7 +13,7 @@ test('should show provided resource value', async () => {
|
|||
});
|
||||
|
||||
test('should show provided resource type', async () => {
|
||||
const { getByLabelText } = renderComponent(0, '', 'Test');
|
||||
const { getByLabelText } = renderComponent(0, User, 'Test');
|
||||
const title = getByLabelText('resourceType');
|
||||
|
||||
expect(title).toBeVisible();
|
||||
|
@ -19,11 +21,11 @@ test('should show provided resource type', async () => {
|
|||
});
|
||||
|
||||
test('should have accessibility label created from the provided resource type', async () => {
|
||||
const { getByLabelText } = renderComponent(0, '', 'testLabel');
|
||||
const { getByLabelText } = renderComponent(0, User, 'testLabel');
|
||||
|
||||
expect(getByLabelText('testLabel')).toBeTruthy();
|
||||
});
|
||||
|
||||
function renderComponent(value = 0, icon = '', type = '') {
|
||||
function renderComponent(value = 0, icon = User, type = '') {
|
||||
return render(<DashboardItem value={value} icon={icon} type={type} />);
|
||||
}
|
||||
|
|
|
@ -10,13 +10,7 @@ interface Props extends IconProps {
|
|||
children?: ReactNode;
|
||||
}
|
||||
|
||||
export function DashboardItem({
|
||||
value,
|
||||
icon,
|
||||
type,
|
||||
children,
|
||||
featherIcon,
|
||||
}: Props) {
|
||||
export function DashboardItem({ value, icon, type, children }: Props) {
|
||||
return (
|
||||
<div
|
||||
className={clsx(
|
||||
|
@ -35,7 +29,7 @@ export function DashboardItem({
|
|||
'th-highcontrast:bg-blue-3 th-highcontrast:text-blue-8'
|
||||
)}
|
||||
>
|
||||
<Icon icon={icon} feather={featherIcon} className="feather" />
|
||||
<Icon icon={icon} />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col justify-around">
|
||||
|
|
|
@ -9,8 +9,6 @@ interface Props {
|
|||
alt?: string;
|
||||
size?: BadgeSize;
|
||||
className?: string;
|
||||
// additional fallback badge props
|
||||
feather?: boolean;
|
||||
}
|
||||
|
||||
export function FallbackImage({
|
||||
|
@ -19,7 +17,6 @@ export function FallbackImage({
|
|||
alt,
|
||||
size,
|
||||
className,
|
||||
feather,
|
||||
}: Props) {
|
||||
const [error, setError] = useState(false);
|
||||
|
||||
|
@ -39,5 +36,5 @@ export function FallbackImage({
|
|||
}
|
||||
|
||||
// fallback icon if there is an error loading the image
|
||||
return <BadgeIcon icon={fallbackIcon} featherIcon={feather} size={size} />;
|
||||
return <BadgeIcon icon={fallbackIcon} size={size} />;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,12 @@
|
|||
import clsx from 'clsx';
|
||||
import { ComponentType, ReactNode } from 'react';
|
||||
import * as featherIcons from 'react-feather';
|
||||
import * as lucideIcons from 'lucide-react';
|
||||
import { isValidElementType } from 'react-is';
|
||||
|
||||
import Svg, { SvgIcons } from './Svg';
|
||||
|
||||
export interface IconProps {
|
||||
icon: ReactNode | ComponentType<unknown>;
|
||||
featherIcon?: boolean;
|
||||
}
|
||||
|
||||
export type IconMode =
|
||||
|
@ -27,16 +26,15 @@ export type IconSize = 'xs' | 'sm' | 'md' | 'lg' | 'xl';
|
|||
|
||||
interface Props {
|
||||
icon: ReactNode | ComponentType<{ size?: string | number }>;
|
||||
feather?: boolean;
|
||||
className?: string;
|
||||
size?: IconSize;
|
||||
mode?: IconMode;
|
||||
}
|
||||
|
||||
export function Icon({ icon, feather, className, mode, size }: Props) {
|
||||
export function Icon({ icon, className, mode, size }: Props) {
|
||||
const classes = clsx(
|
||||
className,
|
||||
'icon',
|
||||
'icon inline-flex',
|
||||
{ [`icon-${mode}`]: mode },
|
||||
{ [`icon-${size}`]: size }
|
||||
);
|
||||
|
@ -44,9 +42,13 @@ export function Icon({ icon, feather, className, mode, size }: Props) {
|
|||
if (typeof icon !== 'string') {
|
||||
const Icon = isValidElementType(icon) ? icon : null;
|
||||
|
||||
if (Icon) {
|
||||
return <Icon className={classes} aria-hidden="true" role="img" />;
|
||||
}
|
||||
|
||||
return (
|
||||
<span className={classes} aria-hidden="true" role="img">
|
||||
{Icon == null ? <>{icon}</> : <Icon size="1em" />}
|
||||
{icon}
|
||||
</span>
|
||||
);
|
||||
}
|
||||
|
@ -56,28 +58,25 @@ export function Icon({ icon, feather, className, mode, size }: Props) {
|
|||
return (
|
||||
<Svg
|
||||
icon={svgIcon as keyof typeof SvgIcons}
|
||||
className={clsx(classes, '!flex')}
|
||||
className={classes}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
if (feather) {
|
||||
const iconName = icon
|
||||
.split('-')
|
||||
.map((s) => s.slice(0, 1).toUpperCase() + s.slice(1))
|
||||
.join('') as keyof typeof featherIcons;
|
||||
const IconComponent = featherIcons[iconName];
|
||||
if (!IconComponent) {
|
||||
throw new Error(`Feather icon not found: ${iconName}`);
|
||||
}
|
||||
return <IconComponent className={classes} />;
|
||||
const iconName = icon
|
||||
.split('-')
|
||||
.map((s) => s.slice(0, 1).toUpperCase() + s.slice(1))
|
||||
.join('') as keyof typeof lucideIcons;
|
||||
const IconComponent = lucideIcons[iconName] as React.FC<
|
||||
React.SVGProps<SVGSVGElement>
|
||||
>;
|
||||
if (!IconComponent) {
|
||||
// console error so that the error is logged but no functionality is broken
|
||||
// eslint-disable-next-line no-console
|
||||
console.error(`Icon not found: '${icon}'`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<i
|
||||
className={clsx(icon.startsWith('fa-') ? `fa ${icon}` : icon, classes)}
|
||||
aria-hidden="true"
|
||||
role="img"
|
||||
/>
|
||||
);
|
||||
return <IconComponent className={classes} aria-hidden="true" />;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { PropsWithChildren } from 'react';
|
||||
import { X } from 'lucide-react';
|
||||
|
||||
import { Widget, WidgetBody } from './Widget';
|
||||
import { Button } from './buttons';
|
||||
|
@ -32,8 +33,8 @@ export function InformationPanel({
|
|||
style={{ float: 'right' }}
|
||||
ng-if="dismissAction"
|
||||
>
|
||||
<Button color="link" onClick={() => onDismiss()}>
|
||||
<i className="fa fa-times" /> dismiss
|
||||
<Button color="link" icon={X} onClick={() => onDismiss()}>
|
||||
dismiss
|
||||
</Button>
|
||||
</span>
|
||||
)}
|
||||
|
|
|
@ -4,7 +4,7 @@ import { ReactNode } from 'react';
|
|||
import styles from './NavTabs.module.css';
|
||||
|
||||
export interface Option<T extends string | number = string> {
|
||||
label: string | ReactNode;
|
||||
label: ReactNode;
|
||||
children?: ReactNode;
|
||||
id: T;
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { HelpCircle } from 'react-feather';
|
||||
import { HelpCircle } from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
|
||||
import { getDocURL } from '@@/PageHeader/ContextHelp/docURLs';
|
||||
|
@ -24,7 +24,7 @@ export function ContextHelp() {
|
|||
)}
|
||||
title="Help"
|
||||
>
|
||||
<HelpCircle className="feather" onClick={onHelpClick} />
|
||||
<HelpCircle className="lucide" onClick={onHelpClick} />
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -9,6 +9,7 @@ import { UISrefProps, useSref } from '@uirouter/react';
|
|||
import Moment from 'moment';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { useStore } from 'zustand';
|
||||
import { AlertCircle, Bell, CheckCircle, Trash2 } from 'lucide-react';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
import { useUser } from '@/react/hooks/useUser';
|
||||
|
@ -69,7 +70,7 @@ export function NotificationsMenu() {
|
|||
'th-dark:text-gray-warm-7'
|
||||
)}
|
||||
>
|
||||
<Icon icon="bell" feather />
|
||||
<Icon icon={Bell} />
|
||||
<span className={badge ? notificationStyles.badge : ''} />
|
||||
</div>
|
||||
</MenuButton>
|
||||
|
@ -126,7 +127,7 @@ export function NotificationsMenu() {
|
|||
</>
|
||||
) : (
|
||||
<div className="flex flex-col items-center">
|
||||
<Icon icon="bell" feather size="xl" />
|
||||
<Icon icon={Bell} size="xl" />
|
||||
<p className="my-5">You have no notifications yet.</p>
|
||||
</div>
|
||||
)}
|
||||
|
@ -160,9 +161,9 @@ function MenuLink({ to, params, notification, onDelete }: MenuLinkProps) {
|
|||
<div className={notificationStyles.container}>
|
||||
<div className={notificationStyles.notificationIcon}>
|
||||
{notification.type === 'success' ? (
|
||||
<Icon icon="check-circle" feather size="lg" mode="success" />
|
||||
<Icon icon={CheckCircle} size="lg" mode="success" />
|
||||
) : (
|
||||
<Icon icon="alert-circle" feather size="lg" mode="danger" />
|
||||
<Icon icon={AlertCircle} size="lg" mode="danger" />
|
||||
)}
|
||||
</div>
|
||||
<div className={notificationStyles.notificationBody}>
|
||||
|
@ -186,9 +187,8 @@ function MenuLink({ to, params, notification, onDelete }: MenuLinkProps) {
|
|||
}}
|
||||
data-cy="notification-deleteButton"
|
||||
size="large"
|
||||
>
|
||||
<Icon icon="trash-2" feather />
|
||||
</Button>
|
||||
icon={Trash2}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</ReachMenuLink>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useRouter } from '@uirouter/react';
|
||||
import { RefreshCw } from 'react-feather';
|
||||
import { RefreshCw } from 'lucide-react';
|
||||
|
||||
import { Button } from '../buttons';
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@ import {
|
|||
} from '@reach/menu-button';
|
||||
import { UISrefProps, useSref } from '@uirouter/react';
|
||||
import clsx from 'clsx';
|
||||
import { User, ChevronDown } from 'react-feather';
|
||||
import { User, ChevronDown } from 'lucide-react';
|
||||
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
import { useUser } from '@/react/hooks/useUser';
|
||||
|
@ -34,7 +34,7 @@ export function UserMenu() {
|
|||
'th-dark:text-gray-warm-7'
|
||||
)}
|
||||
>
|
||||
<User className="feather" />
|
||||
<User className="lucide" />
|
||||
</div>
|
||||
{user && <span>{user.Username}</span>}
|
||||
<ChevronDown className={styles.arrowDown} />
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { AlertTriangle, Check } from 'lucide-react';
|
||||
|
||||
import { usePublicSettings } from '@/react/portainer/settings/queries';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
@ -17,12 +19,12 @@ export function PasswordCheckHint({
|
|||
return (
|
||||
<div>
|
||||
<p className="text-warning vertical-center">
|
||||
<Icon icon="alert-triangle" className="icon-warning" feather />
|
||||
<Icon icon={AlertTriangle} className="icon-warning" />
|
||||
{forceChangePassword &&
|
||||
'An administrator has changed your password requirements, '}
|
||||
The password must be at least {minPasswordLength} characters long.
|
||||
{passwordValid && (
|
||||
<i className="fa fa-check green-icon space-left" aria-hidden="true" />
|
||||
<Icon icon={Check} className="!ml-1" mode="success" />
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
|
|
|
@ -3,57 +3,19 @@ import automode from '@/assets/ico/theme/auto.svg?c';
|
|||
import darkmode from '@/assets/ico/theme/darkmode.svg?c';
|
||||
import lightmode from '@/assets/ico/theme/lightmode.svg?c';
|
||||
import highcontrastmode from '@/assets/ico/theme/highcontrastmode.svg?c';
|
||||
// wizard icons
|
||||
import agent from '@/assets/ico/wizard/agent.svg?c';
|
||||
import api from '@/assets/ico/wizard/api.svg?c';
|
||||
import edgeagent from '@/assets/ico/wizard/edge-agent.svg?c';
|
||||
import cloudimport from '@/assets/ico/wizard/import.svg?c';
|
||||
import socket from '@/assets/ico/wizard/socket.svg?c';
|
||||
// general icons
|
||||
import arrowsupdown from '@/assets/ico/arrows-updown.svg?c';
|
||||
import arrowright from '@/assets/ico/arrow-right-long.svg?c';
|
||||
import bomb from '@/assets/ico/bomb.svg?c';
|
||||
import checked from '@/assets/ico/checked.svg?c';
|
||||
import circlenotch from '@/assets/ico/circle-notch.svg?c';
|
||||
import clockrewind from '@/assets/ico/clock-rewind.svg?c';
|
||||
import compress from '@/assets/ico/compress.svg?c';
|
||||
import cubes from '@/assets/ico/cubes.svg?c';
|
||||
import custom from '@/assets/ico/custom.svg?c';
|
||||
import dataflow from '@/assets/ico/dataflow-1.svg?c';
|
||||
import dataflow2 from '@/assets/ico/dataflow-2.svg?c';
|
||||
import expand from '@/assets/ico/expand.svg?c';
|
||||
import filecode from '@/assets/ico/file-code.svg?c';
|
||||
import filesignature from '@/assets/ico/file-signature.svg?c';
|
||||
import fileupload from '@/assets/ico/file-upload.svg?c';
|
||||
import flask from '@/assets/ico/flask.svg?c';
|
||||
import git from '@/assets/ico/git.svg?c';
|
||||
import hacker from '@/assets/ico/hacker.svg?c';
|
||||
import heartbeat from '@/assets/ico/heartbeat.svg?c';
|
||||
import laptop from '@/assets/ico/laptop.svg?c';
|
||||
import kube from '@/assets/ico/kube.svg?c';
|
||||
import laptopcode from '@/assets/ico/laptop-code.svg?c';
|
||||
import ldap from '@/assets/ico/ldap.svg?c';
|
||||
import magic from '@/assets/ico/magic.svg?c';
|
||||
import magicwand from '@/assets/ico/magic-wand.svg?c';
|
||||
import linux from '@/assets/ico/linux.svg?c';
|
||||
import memory from '@/assets/ico/memory.svg?c';
|
||||
import objectgroup from '@/assets/ico/object-group.svg?c';
|
||||
import palette from '@/assets/ico/palette.svg?c';
|
||||
import plug from '@/assets/ico/plug.svg?c';
|
||||
import restore from '@/assets/ico/restore.svg?c';
|
||||
import restorewindow from '@/assets/ico/restore-window.svg?c';
|
||||
import rocket from '@/assets/ico/rocket.svg?c';
|
||||
import route from '@/assets/ico/route.svg?c';
|
||||
import share from '@/assets/ico/share.svg?c';
|
||||
import sort from '@/assets/ico/sort.svg?c';
|
||||
import tachometer from '@/assets/ico/tachometer.svg?c';
|
||||
import template from '@/assets/ico/template.svg?c';
|
||||
import tag from '@/assets/ico/tag-2.svg?c';
|
||||
import tag2 from '@/assets/ico/tags.svg?c';
|
||||
import tools from '@/assets/ico/tools.svg?c';
|
||||
import upload from '@/assets/ico/upload.svg?c';
|
||||
import url from '@/assets/ico/url.svg?c';
|
||||
import usercircle from '@/assets/ico/user-circle.svg?c';
|
||||
import userlock from '@/assets/ico/user-lock.svg?c';
|
||||
import kube from '@/assets/ico/kube.svg?c';
|
||||
import subscription from '@/assets/ico/subscription.svg?c';
|
||||
import Placeholder from '@/assets/ico/placeholder.svg?c'; // Placeholder is used when an icon name cant be matched
|
||||
// vendor icons
|
||||
import aws from '@/assets/ico/vendor/aws.svg?c';
|
||||
|
@ -61,6 +23,7 @@ import azure from '@/assets/ico/vendor/azure.svg?c';
|
|||
import civo from '@/assets/ico/vendor/civo.svg?c';
|
||||
import digitalocean from '@/assets/ico/vendor/digitalocean.svg?c';
|
||||
import docker from '@/assets/ico/vendor/docker.svg?c';
|
||||
import dockericon from '@/assets/ico/vendor/docker-icon.svg?c';
|
||||
import dockercompose from '@/assets/ico/vendor/docker-compose.svg?c';
|
||||
import ecr from '@/assets/ico/vendor/ecr.svg?c';
|
||||
import github from '@/assets/ico/vendor/github.svg?c';
|
||||
|
@ -71,68 +34,33 @@ import kubernetes from '@/assets/ico/vendor/kubernetes.svg?c';
|
|||
import helm from '@/assets/ico/vendor/helm.svg?c';
|
||||
import linode from '@/assets/ico/vendor/linode.svg?c';
|
||||
import microsoft from '@/assets/ico/vendor/microsoft.svg?c';
|
||||
import microsofticon from '@/assets/ico/vendor/microsoft-icon.svg?c';
|
||||
import nomad from '@/assets/ico/vendor/nomad.svg?c';
|
||||
import nomadicon from '@/assets/ico/vendor/nomad-icon.svg?c';
|
||||
import openldap from '@/assets/ico/vendor/openldap.svg?c';
|
||||
import proget from '@/assets/ico/vendor/proget.svg?c';
|
||||
import quay from '@/assets/ico/vendor/quay.svg?c';
|
||||
import internal from '@/assets/ico/vendor/internal.svg?c';
|
||||
|
||||
const placeholder = Placeholder;
|
||||
|
||||
export const SvgIcons = {
|
||||
agent,
|
||||
api,
|
||||
edgeagent,
|
||||
cloudimport,
|
||||
socket,
|
||||
automode,
|
||||
darkmode,
|
||||
lightmode,
|
||||
highcontrastmode,
|
||||
dataflow,
|
||||
dataflow2,
|
||||
arrowsupdown,
|
||||
arrowright,
|
||||
bomb,
|
||||
checked,
|
||||
circlenotch,
|
||||
clockrewind,
|
||||
compress,
|
||||
cubes,
|
||||
custom,
|
||||
expand,
|
||||
filecode,
|
||||
filesignature,
|
||||
fileupload,
|
||||
flask,
|
||||
dockericon,
|
||||
git,
|
||||
hacker,
|
||||
heartbeat,
|
||||
laptop,
|
||||
laptopcode,
|
||||
ldap,
|
||||
magic,
|
||||
magicwand,
|
||||
linux,
|
||||
memory,
|
||||
objectgroup,
|
||||
palette,
|
||||
placeholder,
|
||||
plug,
|
||||
restore,
|
||||
restorewindow,
|
||||
rocket,
|
||||
route,
|
||||
share,
|
||||
sort,
|
||||
tachometer,
|
||||
template,
|
||||
tag,
|
||||
tag2,
|
||||
tools,
|
||||
upload,
|
||||
url,
|
||||
usercircle,
|
||||
userlock,
|
||||
subscription,
|
||||
aws,
|
||||
azure,
|
||||
civo,
|
||||
|
@ -148,11 +76,12 @@ export const SvgIcons = {
|
|||
helm,
|
||||
linode,
|
||||
microsoft,
|
||||
microsofticon,
|
||||
nomad,
|
||||
nomadicon,
|
||||
openldap,
|
||||
proget,
|
||||
quay,
|
||||
internal,
|
||||
kube,
|
||||
};
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import clsx from 'clsx';
|
||||
import _ from 'lodash';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
|
||||
import { TagId } from '@/portainer/tags/types';
|
||||
import { Icon } from '@/react/components/Icon';
|
||||
|
@ -74,7 +75,7 @@ export function TagSelector({ value, allowCreate = false, onChange }: Props) {
|
|||
key={tag.value}
|
||||
>
|
||||
{tag.label}
|
||||
<Icon icon="trash-2" feather />
|
||||
<Icon icon={Trash2} />
|
||||
</button>
|
||||
))}
|
||||
</FormControl>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import clsx from 'clsx';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { AlertCircle } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
|
@ -30,8 +31,7 @@ export function TextTip({
|
|||
<p className="small vertical-center">
|
||||
<i className="icon-container">
|
||||
<Icon
|
||||
icon="alert-circle"
|
||||
feather
|
||||
icon={AlertCircle}
|
||||
className={clsx(`${iconClass}`, 'space-right')}
|
||||
/>
|
||||
</i>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import ReactTooltip from 'react-tooltip';
|
||||
import { HelpCircle } from 'react-feather';
|
||||
import { HelpCircle } from 'lucide-react';
|
||||
import clsx from 'clsx';
|
||||
import _ from 'lodash';
|
||||
|
||||
|
@ -22,7 +22,7 @@ export function Tooltip({ message, position = 'bottom', className }: Props) {
|
|||
data-for={id}
|
||||
className={clsx(styles.icon, 'inline-flex text-base')}
|
||||
>
|
||||
<HelpCircle className="feather" aria-hidden="true" />
|
||||
<HelpCircle className="lucide" aria-hidden="true" />
|
||||
<ReactTooltip
|
||||
id={id}
|
||||
multiline
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import clsx from 'clsx';
|
||||
import { Settings } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import styles from './ViewLoading.module.css';
|
||||
|
||||
|
@ -18,7 +21,7 @@ export function ViewLoading({ message }: Props) {
|
|||
{message && (
|
||||
<span className={styles.message}>
|
||||
{message}
|
||||
<i className="fa fa-cog fa-spin space-left" />
|
||||
<Icon icon={Settings} className="animate-spin-slow !ml-1" />
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
import { ReactNode } from 'react';
|
||||
import type { Meta } from '@storybook/react';
|
||||
import { User } from 'lucide-react';
|
||||
|
||||
import { Widget } from './Widget';
|
||||
import { WidgetBody } from './WidgetBody';
|
||||
|
@ -9,7 +11,7 @@ import { WidgetTaskbar } from './WidgetTaskbar';
|
|||
interface WidgetProps {
|
||||
loading: boolean;
|
||||
title: string;
|
||||
icon: string;
|
||||
icon: ReactNode;
|
||||
bodyText: string;
|
||||
footerText: string;
|
||||
}
|
||||
|
@ -20,7 +22,7 @@ const meta: Meta<WidgetProps> = {
|
|||
args: {
|
||||
loading: false,
|
||||
title: 'Title',
|
||||
icon: 'fa-rocket',
|
||||
icon: User,
|
||||
bodyText: 'Body',
|
||||
footerText: 'Footer',
|
||||
},
|
||||
|
@ -52,11 +54,15 @@ function WidgetWithCustomImage({
|
|||
<WidgetTitle
|
||||
title={title}
|
||||
icon={
|
||||
<img
|
||||
className="custom-header-ico space-right"
|
||||
src={icon}
|
||||
alt="header-icon"
|
||||
/>
|
||||
typeof icon === 'string' ? (
|
||||
<img
|
||||
className="custom-header-ico space-right"
|
||||
src={icon}
|
||||
alt="header-icon"
|
||||
/>
|
||||
) : (
|
||||
icon
|
||||
)
|
||||
}
|
||||
/>
|
||||
<WidgetBody loading={loading}>{bodyText}</WidgetBody>
|
||||
|
|
|
@ -8,7 +8,6 @@ import { useWidgetContext } from './Widget';
|
|||
interface Props {
|
||||
title: ReactNode;
|
||||
icon: ReactNode;
|
||||
featherIcon?: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
|
@ -17,7 +16,6 @@ export function WidgetTitle({
|
|||
icon,
|
||||
className,
|
||||
children,
|
||||
featherIcon,
|
||||
}: PropsWithChildren<Props>) {
|
||||
useWidgetContext();
|
||||
|
||||
|
@ -26,11 +24,7 @@ export function WidgetTitle({
|
|||
<div className="row">
|
||||
<span className={clsx('pull-left vertical-center', className)}>
|
||||
<div className="widget-icon">
|
||||
<Icon
|
||||
icon={icon}
|
||||
feather={featherIcon}
|
||||
className="space-right feather"
|
||||
/>
|
||||
<Icon icon={icon} className="space-right" />
|
||||
</div>
|
||||
<span>{title}</span>
|
||||
</span>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import clsx from 'clsx';
|
||||
import { PlusCircle } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@/react/components/Icon';
|
||||
|
||||
|
@ -27,7 +28,7 @@ export function AddButton({ label, onClick, className, disabled }: Props) {
|
|||
onClick={onClick}
|
||||
disabled={disabled}
|
||||
>
|
||||
<Icon icon="plus-circle" feather />
|
||||
<Icon icon={PlusCircle} />
|
||||
{label}
|
||||
</button>
|
||||
);
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Meta, Story } from '@storybook/react';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { Download } from 'react-feather';
|
||||
import { Download } from 'lucide-react';
|
||||
|
||||
import { Button, Props } from './Button';
|
||||
|
||||
|
|
|
@ -27,7 +27,6 @@ type Size = 'xsmall' | 'small' | 'medium' | 'large';
|
|||
|
||||
export interface Props extends AriaAttributes, AutomationTestingProps {
|
||||
icon?: ReactNode | ComponentType<unknown>;
|
||||
featherIcon?: boolean;
|
||||
|
||||
color?: Color;
|
||||
size?: Size;
|
||||
|
@ -47,7 +46,6 @@ export function Button({
|
|||
onClick,
|
||||
title,
|
||||
icon,
|
||||
featherIcon,
|
||||
children,
|
||||
|
||||
...ariaProps
|
||||
|
@ -64,12 +62,7 @@ export function Button({
|
|||
{...ariaProps}
|
||||
>
|
||||
{icon && (
|
||||
<Icon
|
||||
icon={icon}
|
||||
size={getIconSize(size)}
|
||||
className="inline-flex"
|
||||
feather={featherIcon}
|
||||
/>
|
||||
<Icon icon={icon} size={getIconSize(size)} className="inline-flex" />
|
||||
)}
|
||||
{children}
|
||||
</button>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { Meta, Story } from '@storybook/react';
|
||||
import { PropsWithChildren } from 'react';
|
||||
import { Play, RefreshCw, Square, Trash2 } from 'lucide-react';
|
||||
|
||||
import { Button } from './Button';
|
||||
import { ButtonGroup, Props } from './ButtonGroup';
|
||||
|
@ -14,28 +15,19 @@ function Template({
|
|||
}: JSX.IntrinsicAttributes & PropsWithChildren<Props>) {
|
||||
return (
|
||||
<ButtonGroup size={size}>
|
||||
<Button color="primary" onClick={() => {}}>
|
||||
<i className="fa fa-play space-right" aria-hidden="true" />
|
||||
<Button icon={Play} color="primary" onClick={() => {}}>
|
||||
Start
|
||||
</Button>
|
||||
<Button color="danger" onClick={() => {}}>
|
||||
<i className="fa fa-stop space-right" aria-hidden="true" />
|
||||
<Button icon={Square} color="danger" onClick={() => {}}>
|
||||
Stop
|
||||
</Button>
|
||||
<Button color="danger" onClick={() => {}}>
|
||||
<i className="fa fa-bomb space-right" aria-hidden="true" />
|
||||
Kill
|
||||
</Button>
|
||||
<Button color="primary" onClick={() => {}}>
|
||||
<i className="fa fa-sync space-right" aria-hidden="true" />
|
||||
<Button icon={RefreshCw} color="primary" onClick={() => {}}>
|
||||
Restart
|
||||
</Button>
|
||||
<Button color="primary" disabled onClick={() => {}}>
|
||||
<i className="fa fa-play space-right" aria-hidden="true" />
|
||||
<Button icon={Play} color="primary" disabled onClick={() => {}}>
|
||||
Resume
|
||||
</Button>
|
||||
<Button color="danger" onClick={() => {}}>
|
||||
<i className="fa fa-trash-alt space-right" aria-hidden="true" />
|
||||
<Button icon={Trash2} color="danger" onClick={() => {}}>
|
||||
Remove
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
@ -50,20 +42,16 @@ Primary.args = {
|
|||
export function Xsmall() {
|
||||
return (
|
||||
<ButtonGroup size="xsmall">
|
||||
<Button color="primary" onClick={() => {}}>
|
||||
<i className="fa fa-play space-right" aria-hidden="true" />
|
||||
<Button icon={Play} color="primary" onClick={() => {}}>
|
||||
Start
|
||||
</Button>
|
||||
<Button color="danger" onClick={() => {}}>
|
||||
<i className="fa fa-stop space-right" aria-hidden="true" />
|
||||
<Button icon={Square} color="danger" onClick={() => {}}>
|
||||
Stop
|
||||
</Button>
|
||||
<Button color="primary" onClick={() => {}}>
|
||||
<i className="fa fa-play space-right" aria-hidden="true" />
|
||||
<Button icon={Play} color="primary" onClick={() => {}}>
|
||||
Start
|
||||
</Button>
|
||||
<Button color="primary" onClick={() => {}}>
|
||||
<i className="fa fa-sync space-right" aria-hidden="true" />
|
||||
<Button icon={RefreshCw} color="primary" onClick={() => {}}>
|
||||
Restart
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
@ -73,20 +61,16 @@ export function Xsmall() {
|
|||
export function Small() {
|
||||
return (
|
||||
<ButtonGroup size="small">
|
||||
<Button color="primary" onClick={() => {}}>
|
||||
<i className="fa fa-play space-right" aria-hidden="true" />
|
||||
<Button icon={Play} color="primary" onClick={() => {}}>
|
||||
Start
|
||||
</Button>
|
||||
<Button color="danger" onClick={() => {}}>
|
||||
<i className="fa fa-stop space-right" aria-hidden="true" />
|
||||
<Button icon={Square} color="danger" onClick={() => {}}>
|
||||
Stop
|
||||
</Button>
|
||||
<Button color="primary" onClick={() => {}}>
|
||||
<i className="fa fa-play space-right" aria-hidden="true" />
|
||||
<Button icon={Play} color="primary" onClick={() => {}}>
|
||||
Start
|
||||
</Button>
|
||||
<Button color="primary" onClick={() => {}}>
|
||||
<i className="fa fa-sync space-right" aria-hidden="true" />
|
||||
<Button icon={RefreshCw} color="primary" onClick={() => {}}>
|
||||
Restart
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
@ -96,20 +80,16 @@ export function Small() {
|
|||
export function Large() {
|
||||
return (
|
||||
<ButtonGroup size="large">
|
||||
<Button color="primary" onClick={() => {}}>
|
||||
<i className="fa fa-play space-right" aria-hidden="true" />
|
||||
<Button icon={Play} color="primary" onClick={() => {}}>
|
||||
Start
|
||||
</Button>
|
||||
<Button color="danger" onClick={() => {}}>
|
||||
<i className="fa fa-stop space-right" aria-hidden="true" />
|
||||
<Button icon={Square} color="danger" onClick={() => {}}>
|
||||
Stop
|
||||
</Button>
|
||||
<Button color="light" onClick={() => {}}>
|
||||
<i className="fa fa-play space-right" aria-hidden="true" />
|
||||
<Button icon={Play} color="light" onClick={() => {}}>
|
||||
Start
|
||||
</Button>
|
||||
<Button color="primary" onClick={() => {}}>
|
||||
<i className="fa fa-sync space-right" aria-hidden="true" />
|
||||
<Button icon={RefreshCw} color="primary" onClick={() => {}}>
|
||||
Restart
|
||||
</Button>
|
||||
</ButtonGroup>
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { PropsWithChildren } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { Check, Copy } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
|
@ -33,7 +34,7 @@ export function CopyButton({
|
|||
title="Copy Value"
|
||||
type="button"
|
||||
>
|
||||
<Icon icon="copy" feather />
|
||||
<Icon icon={Copy} />
|
||||
{children}
|
||||
</Button>
|
||||
|
||||
|
@ -45,7 +46,7 @@ export function CopyButton({
|
|||
'vertical-center'
|
||||
)}
|
||||
>
|
||||
<Icon icon="check" feather />
|
||||
<Icon icon={Check} />
|
||||
{displayText && <span className="space-left">{displayText}</span>}
|
||||
</span>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Meta } from '@storybook/react';
|
||||
import { Download } from 'lucide-react';
|
||||
|
||||
import { LoadingButton } from './LoadingButton';
|
||||
|
||||
|
@ -14,8 +15,12 @@ interface Args {
|
|||
|
||||
function Template({ loadingText, isLoading }: Args) {
|
||||
return (
|
||||
<LoadingButton loadingText={loadingText} isLoading={isLoading}>
|
||||
<i className="fa fa-download" aria-hidden="true" /> Download
|
||||
<LoadingButton
|
||||
loadingText={loadingText}
|
||||
isLoading={isLoading}
|
||||
icon={Download}
|
||||
>
|
||||
Download
|
||||
</LoadingButton>
|
||||
);
|
||||
}
|
||||
|
@ -29,8 +34,8 @@ export const Example = Template.bind({});
|
|||
|
||||
export function IsLoading() {
|
||||
return (
|
||||
<LoadingButton loadingText="loading" isLoading>
|
||||
<i className="fa fa-download" aria-hidden="true" /> Download
|
||||
<LoadingButton loadingText="loading" isLoading icon={Download}>
|
||||
Download
|
||||
</LoadingButton>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -6,18 +6,18 @@ test('when isLoading is true should show spinner and loading text', async () =>
|
|||
const loadingText = 'loading';
|
||||
const children = 'not visible';
|
||||
|
||||
const { findByLabelText, queryByText, findByText } = render(
|
||||
const { queryByText, findByText, container } = render(
|
||||
<LoadingButton loadingText={loadingText} isLoading>
|
||||
{children}
|
||||
</LoadingButton>
|
||||
);
|
||||
|
||||
const spinner = container.querySelector('svg');
|
||||
expect(spinner).toBeVisible();
|
||||
|
||||
const buttonLabel = queryByText(children);
|
||||
expect(buttonLabel).toBeNull();
|
||||
|
||||
const spinner = await findByLabelText('loading');
|
||||
expect(spinner).toBeVisible();
|
||||
|
||||
const loadingTextElem = await findByText(loadingText);
|
||||
expect(loadingTextElem).toBeVisible();
|
||||
});
|
||||
|
@ -26,7 +26,7 @@ test('should show children when false', async () => {
|
|||
const loadingText = 'loading';
|
||||
const children = 'visible';
|
||||
|
||||
const { queryByLabelText, queryByText } = render(
|
||||
const { queryByText, container } = render(
|
||||
<LoadingButton loadingText={loadingText} isLoading={false}>
|
||||
{children}
|
||||
</LoadingButton>
|
||||
|
@ -35,7 +35,7 @@ test('should show children when false', async () => {
|
|||
const buttonLabel = queryByText(children);
|
||||
expect(buttonLabel).toBeVisible();
|
||||
|
||||
const spinner = queryByLabelText('loading');
|
||||
const spinner = container.querySelector('svg');
|
||||
expect(spinner).toBeNull();
|
||||
|
||||
const loadingTextElem = queryByText(loadingText);
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import { PropsWithChildren } from 'react';
|
||||
import { PropsWithChildren, ReactNode } from 'react';
|
||||
import { Loader2 } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import { type Props as ButtonProps, Button } from './Button';
|
||||
|
||||
|
@ -13,6 +16,7 @@ export function LoadingButton({
|
|||
disabled,
|
||||
type = 'submit',
|
||||
children,
|
||||
icon,
|
||||
...buttonProps
|
||||
}: PropsWithChildren<Props>) {
|
||||
return (
|
||||
|
@ -21,19 +25,22 @@ export function LoadingButton({
|
|||
{...buttonProps}
|
||||
type={type}
|
||||
disabled={disabled || isLoading}
|
||||
icon={loadingButtonIcon(isLoading, icon)}
|
||||
>
|
||||
{isLoading ? (
|
||||
<>
|
||||
<i
|
||||
className="fa fa-circle-notch fa-spin space-right"
|
||||
aria-label="loading"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
{loadingText}
|
||||
</>
|
||||
) : (
|
||||
children
|
||||
)}
|
||||
{isLoading ? loadingText : children}
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
||||
function loadingButtonIcon(isLoading: boolean, defaultIcon: ReactNode) {
|
||||
if (!isLoading) {
|
||||
return defaultIcon;
|
||||
}
|
||||
return (
|
||||
<Icon
|
||||
icon={Loader2}
|
||||
className="animate-spin-slow ml-1"
|
||||
aria-label="loading"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
import { ReactNode } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { Menu, MenuList, MenuButton } from '@reach/menu-button';
|
||||
import { MoreVertical } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import styles from './ActionsMenu.module.css';
|
||||
|
||||
|
@ -19,7 +22,7 @@ export function ActionsMenu({ children }: Props) {
|
|||
isExpanded && styles.actionsActive
|
||||
)}
|
||||
>
|
||||
<i className="fa fa-ellipsis-v" aria-hidden="true" />
|
||||
<Icon icon={MoreVertical} />
|
||||
</MenuButton>
|
||||
<MenuList>
|
||||
<div className={styles.tableActionsMenuList}>{children}</div>
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import clsx from 'clsx';
|
||||
import { Menu, MenuButton, MenuList } from '@reach/menu-button';
|
||||
import { ColumnInstance } from 'react-table';
|
||||
import { Columns } from 'react-feather';
|
||||
import { Columns } from 'lucide-react';
|
||||
|
||||
import { Checkbox } from '@@/form-components/Checkbox';
|
||||
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
import { PropsWithChildren } from 'react';
|
||||
import { Row } from 'react-table';
|
||||
import { ChevronDown, ChevronRight } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import styles from './ExpandingCell.module.css';
|
||||
|
||||
|
@ -15,22 +18,15 @@ export function ExpandingCell<
|
|||
<>
|
||||
{showExpandArrow && (
|
||||
<button type="button" className={styles.expandButton}>
|
||||
<i
|
||||
<Icon
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
{...row.getToggleRowExpandedProps()}
|
||||
className={`fas ${arrowClass(row.isExpanded)} space-right`}
|
||||
aria-hidden="true"
|
||||
icon={row.isExpanded ? ChevronDown : ChevronRight}
|
||||
className="mr-1"
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
{children}
|
||||
</>
|
||||
);
|
||||
|
||||
function arrowClass(isExpanded: boolean) {
|
||||
if (isExpanded) {
|
||||
return 'fa-angle-down';
|
||||
}
|
||||
return 'fa-angle-right';
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,9 @@ import clsx from 'clsx';
|
|||
import { useMemo } from 'react';
|
||||
import { Menu, MenuButton, MenuPopover } from '@reach/menu-button';
|
||||
import { ColumnInstance } from 'react-table';
|
||||
import { Check, Filter } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
export const DefaultFilter = filterHOC('Filter by state');
|
||||
|
||||
|
@ -25,17 +28,12 @@ export function MultipleSelectionFilter({
|
|||
<div>
|
||||
<Menu>
|
||||
<MenuButton
|
||||
className={clsx('table-filter', { 'filter-active': enabled })}
|
||||
className={clsx('table-filter flex items-center', {
|
||||
'filter-active': enabled,
|
||||
})}
|
||||
>
|
||||
Filter
|
||||
<i
|
||||
className={clsx(
|
||||
'fa',
|
||||
'space-left',
|
||||
enabled ? 'fa-check' : 'fa-filter'
|
||||
)}
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<Icon icon={enabled ? Check : Filter} className="!ml-1" />
|
||||
</MenuButton>
|
||||
<MenuPopover className="dropdown-menu">
|
||||
<div className="tableMenu">
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Search } from 'react-feather';
|
||||
import { Search } from 'lucide-react';
|
||||
|
||||
import { useLocalStorage } from '@/react/hooks/useLocalStorage';
|
||||
|
||||
|
@ -15,7 +15,7 @@ export function FilterSearchBar({
|
|||
}: Props) {
|
||||
return (
|
||||
<div className="searchBar items-center flex h-[34px]">
|
||||
<Search className="searchIcon feather" />
|
||||
<Search className="searchIcon lucide" />
|
||||
<input
|
||||
type="text"
|
||||
className="searchInput"
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Search } from 'react-feather';
|
||||
import { Search } from 'lucide-react';
|
||||
|
||||
import { useLocalStorage } from '@/react/hooks/useLocalStorage';
|
||||
import { AutomationTestingProps } from '@/types';
|
||||
|
@ -20,7 +20,7 @@ export function SearchBar({
|
|||
|
||||
return (
|
||||
<div className="searchBar items-center flex min-w-[90px]">
|
||||
<Search className="searchIcon feather shrink-0" />
|
||||
<Search className="searchIcon lucide shrink-0" />
|
||||
<input
|
||||
type="text"
|
||||
className="searchInput"
|
||||
|
|
|
@ -76,6 +76,7 @@ function SortWrapper({
|
|||
<TableHeaderSortIcons
|
||||
sorted={isSorted}
|
||||
descending={isSorted && !!isSortedDesc}
|
||||
className="ml-1"
|
||||
/>
|
||||
</div>
|
||||
</button>
|
||||
|
|
|
@ -7,14 +7,15 @@ import styles from './TableHeaderSortIcons.module.css';
|
|||
interface Props {
|
||||
sorted: boolean;
|
||||
descending: boolean;
|
||||
className?: string;
|
||||
}
|
||||
|
||||
export function TableHeaderSortIcons({ sorted, descending }: Props) {
|
||||
export function TableHeaderSortIcons({ sorted, descending, className }: Props) {
|
||||
return (
|
||||
<div className="flex flex-row no-wrap w-min-max">
|
||||
<div className="flex flex-row no-wrap w-min-max align-middle">
|
||||
<SortDownIcon
|
||||
className={clsx(
|
||||
'space-left',
|
||||
className,
|
||||
sorted && !descending && styles.activeSortIcon,
|
||||
styles.sortIcon
|
||||
)}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
import clsx from 'clsx';
|
||||
import { Menu, MenuButton, MenuList } from '@reach/menu-button';
|
||||
import { PropsWithChildren, ReactNode } from 'react';
|
||||
import { MoreVertical } from 'react-feather';
|
||||
import { MoreVertical } from 'lucide-react';
|
||||
|
||||
interface Props {
|
||||
quickActions?: ReactNode;
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
import clsx from 'clsx';
|
||||
import { useState } from 'react';
|
||||
import { Check } from 'lucide-react';
|
||||
|
||||
import { Checkbox } from '@@/form-components/Checkbox';
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import styles from './TableSettingsMenuAutoRefresh.module.css';
|
||||
|
||||
|
@ -46,12 +48,7 @@ export function TableSettingsMenuAutoRefresh({ onChange, value }: Props) {
|
|||
)}
|
||||
onTransitionEnd={() => setIsCheckVisible(false)}
|
||||
>
|
||||
<i
|
||||
id="refreshRateChange"
|
||||
className="fa fa-check green-icon"
|
||||
aria-hidden="true"
|
||||
style={{ marginTop: '7px' }}
|
||||
/>
|
||||
<Icon icon={Check} className="!ml-1" mode="success" />
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
@ -4,14 +4,12 @@ import { Icon } from '@@/Icon';
|
|||
|
||||
interface Props {
|
||||
icon?: ReactNode | ComponentType<unknown>;
|
||||
featherIcon?: boolean;
|
||||
label: string;
|
||||
description?: ReactNode;
|
||||
}
|
||||
|
||||
export function TableTitle({
|
||||
icon,
|
||||
featherIcon,
|
||||
label,
|
||||
children,
|
||||
description,
|
||||
|
@ -22,11 +20,7 @@ export function TableTitle({
|
|||
<div className="toolBarTitle">
|
||||
{icon && (
|
||||
<div className="widget-icon">
|
||||
<Icon
|
||||
icon={icon}
|
||||
feather={featherIcon}
|
||||
className="space-right feather"
|
||||
/>
|
||||
<Icon icon={icon} className="space-right" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { ChevronDown, ChevronUp } from 'react-feather';
|
||||
import { ChevronDown, ChevronUp } from 'lucide-react';
|
||||
import { CellProps, Column, HeaderProps } from 'react-table';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { ChangeEvent, createRef } from 'react';
|
||||
import { XCircle } from 'lucide-react';
|
||||
|
||||
import { Button } from '@@/buttons';
|
||||
import { Icon } from '@@/Icon';
|
||||
|
@ -46,7 +47,7 @@ export function FileUploadField({
|
|||
</Button>
|
||||
|
||||
<span className="vertical-center">
|
||||
{value ? value.name : <Icon icon="x-circle" feather mode="danger" />}
|
||||
{value ? value.name : <Icon icon={XCircle} mode="danger" />}
|
||||
</span>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -11,11 +11,11 @@ export type Size = 'xsmall' | 'small' | 'medium' | 'large';
|
|||
|
||||
export interface Props {
|
||||
inputId?: string;
|
||||
label: string | ReactNode;
|
||||
label: ReactNode;
|
||||
size?: Size;
|
||||
tooltip?: string;
|
||||
children: ReactNode;
|
||||
errors?: string | ReactNode;
|
||||
errors?: ReactNode;
|
||||
required?: boolean;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
import { PropsWithChildren } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { AlertTriangle } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
|
@ -10,7 +11,7 @@ interface Props {
|
|||
export function FormError({ children, className }: PropsWithChildren<Props>) {
|
||||
return (
|
||||
<p className={clsx(`text-muted small vertical-center`, className)}>
|
||||
<Icon icon="alert-triangle" className="icon-warning" feather />
|
||||
<Icon icon={AlertTriangle} className="icon-warning" />
|
||||
<span className="text-warning">{children}</span>
|
||||
</p>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { PropsWithChildren, useState } from 'react';
|
||||
import { ChevronDown, ChevronRight } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
|
@ -27,9 +28,8 @@ export function FormSection({
|
|||
className="border-0 mx-2 !ml-0 bg-transparent inline-flex justify-center items-center w-2"
|
||||
>
|
||||
<Icon
|
||||
icon={isExpanded ? 'chevron-down' : 'chevron-right'}
|
||||
icon={isExpanded ? ChevronDown : ChevronRight}
|
||||
className="shrink-0"
|
||||
feather
|
||||
/>
|
||||
</button>
|
||||
)}
|
||||
|
|
|
@ -1,9 +1,9 @@
|
|||
import { ComponentType } from 'react';
|
||||
import clsx from 'clsx';
|
||||
import { FormikErrors } from 'formik';
|
||||
import { ArrowDown, ArrowUp, Trash2 } from 'lucide-react';
|
||||
|
||||
import { AddButton, Button } from '@@/buttons';
|
||||
import { Icon } from '@@/Icon';
|
||||
import { Tooltip } from '@@/Tip/Tooltip';
|
||||
import { TextTip } from '@@/Tip/TextTip';
|
||||
|
||||
|
@ -136,18 +136,16 @@ export function InputList<T = DefaultType>({
|
|||
disabled={disabled || index === 0}
|
||||
onClick={() => handleMoveUp(index)}
|
||||
className="vertical-center btn-only-icon"
|
||||
>
|
||||
<Icon icon="arrow-up" feather />
|
||||
</Button>
|
||||
icon={ArrowUp}
|
||||
/>
|
||||
<Button
|
||||
size="medium"
|
||||
type="button"
|
||||
disabled={disabled || index === value.length - 1}
|
||||
onClick={() => handleMoveDown(index)}
|
||||
className="vertical-center btn-only-icon"
|
||||
>
|
||||
<Icon icon="arrow-down" feather />
|
||||
</Button>
|
||||
icon={ArrowDown}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{!readOnly && (
|
||||
|
@ -156,9 +154,8 @@ export function InputList<T = DefaultType>({
|
|||
size="medium"
|
||||
onClick={() => handleRemoveItem(key, item)}
|
||||
className="vertical-center btn-only-icon"
|
||||
>
|
||||
<Icon icon="trash-2" feather size="md" />
|
||||
</Button>
|
||||
icon={Trash2}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -32,6 +32,10 @@
|
|||
padding: 0 8px;
|
||||
}
|
||||
|
||||
.portainer-selector-root .portainer-selector__clear-indicator {
|
||||
padding: 6px;
|
||||
}
|
||||
|
||||
.portainer-selector-root .portainer-selector__multi-value__label {
|
||||
@apply text-black;
|
||||
@apply th-dark:text-white;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import clsx from 'clsx';
|
||||
import { Heart, Power } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@/react/components/Icon';
|
||||
|
||||
|
@ -17,37 +17,21 @@ export function ContainerStatus({ containers }: Props) {
|
|||
<div className="pull-right">
|
||||
<div>
|
||||
<div className="vertical-center space-right pr-5">
|
||||
<Icon
|
||||
icon="power"
|
||||
className={clsx('icon icon-sm icon-success')}
|
||||
feather
|
||||
/>
|
||||
<Icon icon={Power} mode="success" size="sm" />
|
||||
{runningContainersFilter(containers)} running
|
||||
</div>
|
||||
<div className="vertical-center space-right">
|
||||
<Icon
|
||||
icon="power"
|
||||
className={clsx('icon icon-sm icon-danger')}
|
||||
feather
|
||||
/>
|
||||
<Icon icon={Power} mode="danger" size="sm" />
|
||||
{stoppedContainersFilter(containers)} stopped
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="vertical-center space-right pr-5">
|
||||
<Icon
|
||||
icon="heart"
|
||||
className={clsx('icon icon-sm icon-success')}
|
||||
feather
|
||||
/>
|
||||
<Icon icon={Heart} mode="success" size="sm" />
|
||||
{healthyContainersFilter(containers)} healthy
|
||||
</div>
|
||||
<div className="vertical-center space-right">
|
||||
<Icon
|
||||
icon="heart"
|
||||
className={clsx('icon icon-sm icon-danger')}
|
||||
feather
|
||||
/>
|
||||
<Icon icon={Heart} mode="danger" size="sm" />
|
||||
{unhealthyContainersFilter(containers)} unhealthy
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import clsx from 'clsx';
|
||||
import { PieChart } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@/react/components/Icon';
|
||||
import { humanize } from '@/portainer/filters/filters';
|
||||
|
@ -14,7 +15,7 @@ export function useImagesTotalSizeComponent(imagesTotalSize: number) {
|
|||
export function ImagesTotalSize({ imagesTotalSize }: Props) {
|
||||
return (
|
||||
<div className="vertical-center">
|
||||
<Icon icon="pie-chart" className={clsx('space-right')} feather />
|
||||
<Icon icon={PieChart} className={clsx('space-right')} />
|
||||
{humanize(imagesTotalSize)}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { TableHeaderSortIcons } from '@@/datatables/TableHeaderSortIcons';
|
||||
|
||||
import { TemplateListDropdown } from '../TemplateListDropdown';
|
||||
|
||||
import styles from './TemplateListSort.module.css';
|
||||
|
@ -21,10 +25,6 @@ export function TemplateListSort({
|
|||
sortByButton,
|
||||
value,
|
||||
}: Props) {
|
||||
const upIcon = 'fa fa-sort-alpha-up';
|
||||
const downIcon = 'fa fa-sort-alpha-down';
|
||||
const iconStyle = sortByDescending ? upIcon : downIcon;
|
||||
|
||||
return (
|
||||
<div className={styles.sortByContainer}>
|
||||
<div className={styles.sortByElement}>
|
||||
|
@ -37,7 +37,7 @@ export function TemplateListSort({
|
|||
</div>
|
||||
<div className={styles.sortByElement}>
|
||||
<button
|
||||
className={styles.sortButton}
|
||||
className={clsx(styles.sortButton, 'h-[34px]')}
|
||||
type="button"
|
||||
disabled={!sortByButton || !value}
|
||||
onClick={(e) => {
|
||||
|
@ -45,7 +45,10 @@ export function TemplateListSort({
|
|||
onDescending();
|
||||
}}
|
||||
>
|
||||
<i className={iconStyle} />
|
||||
<TableHeaderSortIcons
|
||||
sorted={sortByButton && !!value}
|
||||
descending={sortByDescending}
|
||||
/>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { ComponentProps } from 'react';
|
||||
import { Server } from 'react-feather';
|
||||
import { HeartPulse, Server } from 'lucide-react';
|
||||
|
||||
import { TableContainer, TableTitle } from '@@/datatables';
|
||||
import { DetailsTable } from '@@/DetailsTable';
|
||||
|
@ -31,7 +31,7 @@ export function HealthStatus({ health }: Props) {
|
|||
<DetailsTable.Row label="Status">
|
||||
<div className="vertical-center">
|
||||
<Icon
|
||||
icon="fa fa-heartbeat"
|
||||
icon={HeartPulse}
|
||||
mode={StatusMode[health.Status]}
|
||||
className="space-right"
|
||||
/>
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import _ from 'lodash';
|
||||
import { useStore } from 'zustand';
|
||||
import { Box } from 'react-feather';
|
||||
import { Box } from 'lucide-react';
|
||||
|
||||
import { Environment } from '@/react/portainer/environments/types';
|
||||
import type { DockerContainer } from '@/react/docker/containers/types';
|
||||
|
|
|
@ -7,7 +7,7 @@ import {
|
|||
Slash,
|
||||
Square,
|
||||
Trash2,
|
||||
} from 'react-feather';
|
||||
} from 'lucide-react';
|
||||
|
||||
import * as notifications from '@/portainer/services/notifications';
|
||||
import { useAuthorizations, Authorized } from '@/react/hooks/useUser';
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { Column } from 'react-table';
|
||||
import _ from 'lodash';
|
||||
import { ExternalLink } from 'lucide-react';
|
||||
|
||||
import type { DockerContainer, Port } from '@/react/docker/containers/types';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import { useRowContext } from '../RowContext';
|
||||
|
||||
export const ports: Column<DockerContainer> = {
|
||||
|
@ -37,7 +40,7 @@ function PortsCell({ value: ports }: Props) {
|
|||
target="_blank"
|
||||
rel="noreferrer"
|
||||
>
|
||||
<i className="fa fa-external-link-alt" aria-hidden="true" />
|
||||
<Icon icon={ExternalLink} />
|
||||
{port.public}:{port.private}
|
||||
</a>
|
||||
));
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import clsx from 'clsx';
|
||||
import { BarChart, FileText, Info, Paperclip, Terminal } from 'lucide-react';
|
||||
|
||||
import { ContainerStatus } from '@/react/docker/containers/types';
|
||||
import { Authorized } from '@/react/hooks/useUser';
|
||||
|
@ -51,7 +52,7 @@ export function ContainerQuickActions({
|
|||
params={{ id: containerId, nodeName }}
|
||||
title="Logs"
|
||||
>
|
||||
<Icon icon="file-text" feather className="space-right" />
|
||||
<Icon icon={FileText} className="space-right" />
|
||||
</Link>
|
||||
</Authorized>
|
||||
)}
|
||||
|
@ -63,7 +64,7 @@ export function ContainerQuickActions({
|
|||
params={{ id: containerId, nodeName }}
|
||||
title="Inspect"
|
||||
>
|
||||
<Icon icon="info" feather className="space-right" />
|
||||
<Icon icon={Info} className="space-right" />
|
||||
</Link>
|
||||
</Authorized>
|
||||
)}
|
||||
|
@ -75,7 +76,7 @@ export function ContainerQuickActions({
|
|||
params={{ id: containerId, nodeName }}
|
||||
title="Stats"
|
||||
>
|
||||
<Icon icon="bar-chart" feather className="space-right" />
|
||||
<Icon icon={BarChart} className="space-right" />
|
||||
</Link>
|
||||
</Authorized>
|
||||
)}
|
||||
|
@ -87,7 +88,7 @@ export function ContainerQuickActions({
|
|||
params={{ id: containerId, nodeName }}
|
||||
title="Exec Console"
|
||||
>
|
||||
<Icon icon="terminal" feather className="space-right" />
|
||||
<Icon icon={Terminal} className="space-right" />
|
||||
</Link>
|
||||
</Authorized>
|
||||
)}
|
||||
|
@ -99,7 +100,7 @@ export function ContainerQuickActions({
|
|||
params={{ id: containerId, nodeName }}
|
||||
title="Attach Console"
|
||||
>
|
||||
<Icon icon="paperclip" feather className="space-right" />
|
||||
<Icon icon={Paperclip} className="space-right" />
|
||||
</Link>
|
||||
</Authorized>
|
||||
)}
|
||||
|
@ -122,7 +123,7 @@ function TaskQuickActions({ taskId, state }: TaskProps) {
|
|||
params={{ id: taskId }}
|
||||
title="Logs"
|
||||
>
|
||||
<Icon icon="file-text" feather className="space-right" />
|
||||
<Icon icon={FileText} className="space-right" />
|
||||
</Link>
|
||||
</Authorized>
|
||||
)}
|
||||
|
@ -130,7 +131,7 @@ function TaskQuickActions({ taskId, state }: TaskProps) {
|
|||
{state.showQuickActionInspect && (
|
||||
<Authorized authorizations="DockerTaskInspect">
|
||||
<Link to="docker.tasks.task" params={{ id: taskId }} title="Inspect">
|
||||
<Icon icon="info" feather className="space-right" />
|
||||
<Icon icon={Info} className="space-right" />
|
||||
</Link>
|
||||
</Authorized>
|
||||
)}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { List } from 'react-feather';
|
||||
import { List } from 'lucide-react';
|
||||
|
||||
import { joinCommand } from '@/docker/filters/utils';
|
||||
import { getPairKey, getPairValue } from '@/portainer/filters/filters';
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { Server, Trash2 } from 'lucide-react';
|
||||
|
||||
import { Authorized } from '@/react/hooks/useUser';
|
||||
import { EnvironmentId } from '@/react/portainer/environments/types';
|
||||
import { Icon } from '@/react/components/Icon';
|
||||
|
@ -39,7 +41,7 @@ export function NetworkContainersTable({
|
|||
|
||||
return (
|
||||
<TableContainer>
|
||||
<TableTitle label="Containers in network" icon="server" featherIcon />
|
||||
<TableTitle label="Containers in network" icon={Server} />
|
||||
<Table className="nopadding">
|
||||
<DetailsTable
|
||||
headers={tableHeaders}
|
||||
|
@ -78,11 +80,7 @@ export function NetworkContainersTable({
|
|||
}
|
||||
}}
|
||||
>
|
||||
<Icon
|
||||
icon="trash-2"
|
||||
feather
|
||||
class-name="icon-secondary icon-md"
|
||||
/>
|
||||
<Icon icon={Trash2} class-name="icon-secondary icon-md" />
|
||||
Leave Network
|
||||
</Button>
|
||||
</Authorized>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { Fragment } from 'react';
|
||||
import { Share2, Trash2 } from 'lucide-react';
|
||||
|
||||
import DockerNetworkHelper from '@/docker/helpers/networkHelper';
|
||||
import { Authorized } from '@/react/hooks/useUser';
|
||||
|
@ -30,7 +31,7 @@ export function NetworkDetailsTable({
|
|||
|
||||
return (
|
||||
<TableContainer>
|
||||
<TableTitle label="Network details" icon="share-2" featherIcon />
|
||||
<TableTitle label="Network details" icon={Share2} />
|
||||
<Table className="nopadding">
|
||||
<DetailsTable dataCy="networkDetails-detailsTable">
|
||||
{/* networkRowContent */}
|
||||
|
@ -46,8 +47,7 @@ export function NetworkDetailsTable({
|
|||
onClick={() => onRemoveNetworkClicked()}
|
||||
>
|
||||
<Icon
|
||||
icon="trash-2"
|
||||
feather
|
||||
icon={Trash2}
|
||||
className="space-right"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
import { Share2 } from 'lucide-react';
|
||||
|
||||
import { Table, TableContainer, TableTitle } from '@@/datatables';
|
||||
import { DetailsTable } from '@@/DetailsTable';
|
||||
|
||||
|
@ -16,7 +18,7 @@ export function NetworkOptionsTable({ options }: Props) {
|
|||
|
||||
return (
|
||||
<TableContainer>
|
||||
<TableTitle label="Network options" icon="share-2" featherIcon />
|
||||
<TableTitle label="Network options" icon={Share2} />
|
||||
<Table className="nopadding">
|
||||
<DetailsTable dataCy="networkDetails-networkOptionsTable">
|
||||
{networkEntries.map(([key, value]) => (
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import _ from 'lodash';
|
||||
import { useStore } from 'zustand';
|
||||
import { Box } from 'react-feather';
|
||||
import { Box } from 'lucide-react';
|
||||
|
||||
import { DockerContainer } from '@/react/docker/containers/types';
|
||||
import { Environment } from '@/react/portainer/environments/types';
|
||||
|
|
|
@ -1,4 +1,9 @@
|
|||
import { LayoutGrid } from 'lucide-react';
|
||||
|
||||
import Linux from '@/assets/ico/linux.svg?c';
|
||||
|
||||
import { ButtonSelector } from '@@/form-components/ButtonSelector/ButtonSelector';
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import { OS } from './types';
|
||||
|
||||
|
@ -20,7 +25,7 @@ export function OsSelector({ onChange, value }: Props) {
|
|||
value: 'linux',
|
||||
label: (
|
||||
<>
|
||||
<i className="fab fa-linux space-right" aria-hidden="true" />
|
||||
<Icon icon={Linux} className="mr-1" />
|
||||
Linux
|
||||
</>
|
||||
),
|
||||
|
@ -29,10 +34,7 @@ export function OsSelector({ onChange, value }: Props) {
|
|||
value: 'win',
|
||||
label: (
|
||||
<>
|
||||
<i
|
||||
className="fab fa-windows space-right"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<Icon icon={LayoutGrid} className="mr-1" />
|
||||
Windows
|
||||
</>
|
||||
),
|
||||
|
|
|
@ -1,8 +1,11 @@
|
|||
import { CellProps, Column } from 'react-table';
|
||||
import clsx from 'clsx';
|
||||
import { Settings } from 'lucide-react';
|
||||
|
||||
import { Device } from '@/portainer/hostmanagement/open-amt/model';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import { useRowContext } from './RowContext';
|
||||
|
||||
enum PowerState {
|
||||
|
@ -48,7 +51,11 @@ export function PowerStateCell({
|
|||
>
|
||||
{parsePowerState(device.powerState)}
|
||||
</span>
|
||||
<span>{isLoading && <i className="fa fa-cog fa-spin space-left" />}</span>
|
||||
<span>
|
||||
{isLoading && (
|
||||
<Icon icon={Settings} className="animate-spin-slow !ml-1" />
|
||||
)}
|
||||
</span>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import _ from 'lodash';
|
||||
import { useStore } from 'zustand';
|
||||
import { Box } from 'react-feather';
|
||||
import { Box } from 'lucide-react';
|
||||
import { useState } from 'react';
|
||||
|
||||
import { EdgeTypes, Environment } from '@/react/portainer/environments/types';
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useRouter } from '@uirouter/react';
|
||||
import { Plus, Trash2, Link as LinkIcon } from 'lucide-react';
|
||||
|
||||
import type { Environment } from '@/react/portainer/environments/types';
|
||||
import {
|
||||
|
@ -40,13 +41,12 @@ export function EdgeDevicesDatatableActions({
|
|||
disabled={selectedItems.length < 1}
|
||||
color="danger"
|
||||
onClick={() => onDeleteEdgeDeviceClick()}
|
||||
icon="trash-2"
|
||||
featherIcon
|
||||
icon={Trash2}
|
||||
>
|
||||
Remove
|
||||
</Button>
|
||||
|
||||
<Button onClick={() => onAddNewDeviceClick()} icon="plus" featherIcon>
|
||||
<Button onClick={() => onAddNewDeviceClick()} icon={Plus}>
|
||||
Add Device
|
||||
</Button>
|
||||
|
||||
|
@ -54,8 +54,7 @@ export function EdgeDevicesDatatableActions({
|
|||
<Button
|
||||
disabled={selectedItems.length !== 1}
|
||||
onClick={() => onAssociateOpenAMTClick(selectedItems)}
|
||||
icon="link"
|
||||
featherIcon
|
||||
icon={LinkIcon}
|
||||
>
|
||||
Associate with OpenAMT
|
||||
</Button>
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useEffect, useState } from 'react';
|
||||
import { Database } from 'react-feather';
|
||||
import { AlertTriangle, Database } from 'lucide-react';
|
||||
import { useStore } from 'zustand';
|
||||
|
||||
import { confirmWarn } from '@/portainer/services/modal.service/confirm';
|
||||
|
@ -150,7 +150,7 @@ export function IngressClassDatatable({
|
|||
ingControllerFormValues &&
|
||||
isUnsavedChanges(ingressControllers, ingControllerFormValues) && (
|
||||
<span className="flex items-center text-warning mt-1">
|
||||
<Icon icon="alert-triangle" feather className="!mr-1" />
|
||||
<Icon icon={AlertTriangle} className="!mr-1" />
|
||||
<span className="text-warning">Unsaved changes.</span>
|
||||
</span>
|
||||
)}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { CellProps, Column } from 'react-table';
|
||||
import { Check, X } from 'lucide-react';
|
||||
|
||||
import { Badge } from '@@/Badge';
|
||||
import { Icon } from '@@/Icon';
|
||||
|
@ -20,7 +21,7 @@ export const availability: Column<IngressControllerClassMap> = {
|
|||
function AvailailityCell({ value }: CellProps<IngressControllerClassMap>) {
|
||||
return (
|
||||
<Badge type={value ? 'success' : 'danger'}>
|
||||
<Icon icon={value ? 'check' : 'x'} feather className="!mr-1" />
|
||||
<Icon icon={value ? Check : X} className="!mr-1" />
|
||||
{value ? 'Allowed' : 'Disallowed'}
|
||||
</Badge>
|
||||
);
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { ChangeEvent, ReactNode } from 'react';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
import { FormError } from '@@/form-components/FormError';
|
||||
import { Button } from '@@/buttons';
|
||||
|
||||
import { Annotation } from './types';
|
||||
|
||||
|
@ -69,13 +70,14 @@ export function Annotations({
|
|||
)}
|
||||
</div>
|
||||
<div className="col-sm-3 !pl-0 !m-0">
|
||||
<button
|
||||
className="btn btn-sm btn-dangerlight btn-only-icon !ml-0"
|
||||
<Button
|
||||
size="small"
|
||||
color="dangerlight"
|
||||
className="btn-only-icon !ml-0"
|
||||
type="button"
|
||||
onClick={() => removeAnnotation(i)}
|
||||
>
|
||||
<Icon icon="trash-2" size="md" feather />
|
||||
</button>
|
||||
icon={Trash2}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
import { ChangeEvent, ReactNode } from 'react';
|
||||
import { Plus, RefreshCw, Trash2 } from 'react-feather';
|
||||
import { Info, Plus, RefreshCw, Trash2 } from 'lucide-react';
|
||||
|
||||
import Route from '@/assets/ico/route.svg?c';
|
||||
|
||||
import { Link } from '@@/Link';
|
||||
import { Icon } from '@@/Icon';
|
||||
|
@ -107,7 +109,7 @@ export function IngressForm({
|
|||
|
||||
return (
|
||||
<Widget>
|
||||
<WidgetTitle icon="svg-route" title="Ingress" />
|
||||
<WidgetTitle icon={Route} title="Ingress" />
|
||||
<WidgetBody key={rule.Key + rule.Namespace}>
|
||||
<div className="row">
|
||||
<div className="form-horizontal">
|
||||
|
@ -199,7 +201,7 @@ export function IngressForm({
|
|||
<div className="col-sm-12 px-0 text-muted !mb-0">
|
||||
<div className="mb-2">Annotations</div>
|
||||
<p className="vertical-center text-muted small">
|
||||
<Icon icon="info" mode="primary" feather />
|
||||
<Icon icon={Info} mode="primary" />
|
||||
<span>
|
||||
You can specify{' '}
|
||||
<a
|
||||
|
@ -355,7 +357,7 @@ export function IngressForm({
|
|||
</div>
|
||||
|
||||
<p className="vertical-center text-muted small whitespace-nowrap col-sm-12 !p-0">
|
||||
<Icon icon="info" mode="primary" size="md" feather />
|
||||
<Icon icon={Info} mode="primary" size="md" />
|
||||
<span>
|
||||
Add a secret via{' '}
|
||||
<Link
|
||||
|
@ -375,10 +377,10 @@ export function IngressForm({
|
|||
)}
|
||||
{host.NoHost && (
|
||||
<p className="vertical-center text-muted small whitespace-nowrap col-sm-12 !p-0">
|
||||
<Icon icon="info" mode="primary" size="md" feather />A
|
||||
fallback rule has no host specified. This rule only applies
|
||||
when an inbound request has a hostname that does not match
|
||||
with any of your other rules.
|
||||
<Icon icon={Info} mode="primary" size="md" />A fallback rule
|
||||
has no host specified. This rule only applies when an
|
||||
inbound request has a hostname that does not match with any
|
||||
of your other rules.
|
||||
</p>
|
||||
)}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Plus, Trash2 } from 'react-feather';
|
||||
import { Plus, Trash2 } from 'lucide-react';
|
||||
import { useRouter } from '@uirouter/react';
|
||||
import { useStore } from 'zustand';
|
||||
|
||||
|
@ -6,6 +6,7 @@ import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
|||
import { useNamespaces } from '@/react/kubernetes/namespaces/queries';
|
||||
import { useAuthorizations, Authorized } from '@/react/hooks/useUser';
|
||||
import { confirmDeletionAsync } from '@/portainer/services/modal.service/confirm';
|
||||
import Route from '@/assets/ico/route.svg?c';
|
||||
|
||||
import { Datatable } from '@@/datatables';
|
||||
import { Button } from '@@/buttons';
|
||||
|
@ -51,7 +52,7 @@ export function IngressDatatable() {
|
|||
isLoading={ingressesQuery.isLoading}
|
||||
emptyContentLabel="No supported ingresses found"
|
||||
title="Ingresses"
|
||||
titleIcon="svg-route"
|
||||
titleIcon={Route}
|
||||
getRowId={(row) => row.Name + row.Type + row.Namespace}
|
||||
renderTableActions={tableActions}
|
||||
disableSelect={useCheckboxes()}
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { CellProps, Column } from 'react-table';
|
||||
import { AlertTriangle, ArrowRight } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
import { Badge } from '@@/Badge';
|
||||
|
@ -34,11 +35,11 @@ export const ingressRules: Column<Ingress> = {
|
|||
<div key={`${path.Host}${path.Path}${path.ServiceName}:${path.Port}`}>
|
||||
<span className="flex px-2 flex-nowrap items-center gap-1">
|
||||
{link(path.Host, path.Path, isHttp)}
|
||||
<Icon icon="arrow-right" feather />
|
||||
<Icon icon={ArrowRight} />
|
||||
{`${path.ServiceName}:${path.Port}`}
|
||||
{!path.HasService && (
|
||||
<Badge type="warn" className="ml-1 gap-1">
|
||||
<Icon icon="alert-triangle" feather />
|
||||
<Icon icon={AlertTriangle} />
|
||||
Service doesn't exist
|
||||
</Badge>
|
||||
)}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { User as UserIcon, Users as TeamIcon } from 'react-feather';
|
||||
import { User as UserIcon, Users as TeamIcon } from 'lucide-react';
|
||||
import { OptionProps, components, MultiValueGenericProps } from 'react-select';
|
||||
|
||||
import { Select } from '@@/form-components/ReactSelect';
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
import { List, Settings, Boxes, Gauge } from 'lucide-react';
|
||||
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
|
||||
import { DashboardItem } from '@@/DashboardItem';
|
||||
import { Widget, WidgetTitle, WidgetBody } from '@@/Widget';
|
||||
import { PageHeader } from '@@/PageHeader';
|
||||
import { DashboardGrid } from '@@/DashboardItem/DashboardGrid';
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import { useDashboard } from './useDashboard';
|
||||
import { RunningStatus } from './RunningStatus';
|
||||
|
@ -25,7 +28,7 @@ export function DashboardView() {
|
|||
{dashboardQuery.isLoading ? (
|
||||
<div className="text-center" style={{ marginTop: '30%' }}>
|
||||
Connecting to the Edge environment...
|
||||
<i className="fa fa-cog fa-spin space-left" />
|
||||
<Icon icon={Settings} className="animate-spin-slow !ml-1" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
|
@ -33,10 +36,7 @@ export function DashboardView() {
|
|||
<div className="col-sm-12">
|
||||
{/* cluster info */}
|
||||
<Widget>
|
||||
<WidgetTitle
|
||||
icon="fa-tachometer-alt"
|
||||
title="Cluster information"
|
||||
/>
|
||||
<WidgetTitle icon={Gauge} title="Cluster information" />
|
||||
<WidgetBody className="no-padding">
|
||||
<table className="table">
|
||||
<tbody>
|
||||
|
@ -56,19 +56,19 @@ export function DashboardView() {
|
|||
{/* jobs */}
|
||||
<DashboardItem
|
||||
value={dashboardQuery.data?.JobCount}
|
||||
icon="fa fa-th-list"
|
||||
icon={List}
|
||||
type="Nomad Job"
|
||||
/>
|
||||
{/* groups */}
|
||||
<DashboardItem
|
||||
value={dashboardQuery.data?.GroupCount}
|
||||
icon="fa fa-list-alt"
|
||||
icon={List}
|
||||
type="Group"
|
||||
/>
|
||||
{/* tasks */}
|
||||
<DashboardItem
|
||||
value={dashboardQuery.data?.TaskCount}
|
||||
icon="fa fa-cubes"
|
||||
icon={Boxes}
|
||||
type="Task"
|
||||
>
|
||||
{/* running status of tasks */}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
import { Power } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
interface Props {
|
||||
running: number;
|
||||
stopped: number;
|
||||
|
@ -7,17 +11,11 @@ export function RunningStatus({ running, stopped }: Props) {
|
|||
return (
|
||||
<div>
|
||||
<div>
|
||||
<i
|
||||
className="fa fa-power-off green-icon space-right"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<Icon icon={Power} mode="success" />
|
||||
{`${running || '-'} running`}
|
||||
</div>
|
||||
<div>
|
||||
<i
|
||||
className="fa fa-power-off red-icon space-right"
|
||||
aria-hidden="true"
|
||||
/>
|
||||
<Icon icon={Power} mode="danger" />
|
||||
{`${stopped || '-'} stopped`}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -1,3 +1,4 @@
|
|||
import { History } from 'lucide-react';
|
||||
import { useStore } from 'zustand';
|
||||
|
||||
import { NomadEvent } from '@/react/nomad/types';
|
||||
|
@ -33,7 +34,7 @@ export function EventsDatatable({ data, isLoading }: EventsDatatableProps) {
|
|||
onSortByChange={settings.setSortBy}
|
||||
searchValue={search}
|
||||
onSearchChange={setSearch}
|
||||
titleIcon="fa-history"
|
||||
titleIcon={History}
|
||||
title="Events"
|
||||
totalCount={data.length}
|
||||
getRowId={(row) => `${row.Date}-${row.Message}-${row.Type}`}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { useStore } from 'zustand';
|
||||
import { Clock } from 'react-feather';
|
||||
import { Clock } from 'lucide-react';
|
||||
|
||||
import { Job } from '@/react/nomad/types';
|
||||
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { CellProps, Column } from 'react-table';
|
||||
import { Clock, FileText } from 'lucide-react';
|
||||
|
||||
import { Task } from '@/react/nomad/types';
|
||||
|
||||
|
@ -34,12 +35,12 @@ export function ActionsCell({ row }: CellProps<Task>) {
|
|||
title="Events"
|
||||
className="space-right"
|
||||
>
|
||||
<Icon icon="clock" feather className="space-right icon" />
|
||||
<Icon icon={Clock} className="space-right" />
|
||||
</Link>
|
||||
|
||||
{/* logs */}
|
||||
<Link to="nomad.logs" params={params} title="Logs">
|
||||
<Icon icon="file-text" feather className="space-right icon" />
|
||||
<Icon icon={FileText} className="space-right" />
|
||||
</Link>
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import { useMutation } from 'react-query';
|
||||
import { Trash2 } from 'lucide-react';
|
||||
|
||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||
import { Job } from '@/react/nomad/types';
|
||||
|
@ -25,8 +26,8 @@ export function JobActions({ selectedItems, refreshData }: Props) {
|
|||
disabled={selectedItems.length < 1 || mutation.isLoading}
|
||||
color="danger"
|
||||
onClick={handleDeleteClicked}
|
||||
icon={Trash2}
|
||||
>
|
||||
<i className="fa fa-trash-alt space-right" aria-hidden="true" />
|
||||
Remove
|
||||
</LoadingButton>
|
||||
);
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import { CellProps, Column } from 'react-table';
|
||||
import { Clock } from 'react-feather';
|
||||
import { Clock } from 'lucide-react';
|
||||
|
||||
import { Job } from '@/react/nomad/types';
|
||||
|
||||
|
@ -19,7 +19,7 @@ export function ActionsCell({ row }: CellProps<Job>) {
|
|||
return (
|
||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||
<div className="text-center" {...row.getToggleRowExpandedProps()}>
|
||||
<Clock className="feather" />
|
||||
<Clock className="lucide" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,7 @@
|
|||
import clsx from 'clsx';
|
||||
import { Settings } from 'lucide-react';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
import styles from './EdgeLoadingSpinner.module.css';
|
||||
|
||||
|
@ -6,7 +9,7 @@ export function EdgeLoadingSpinner() {
|
|||
return (
|
||||
<div className={clsx('row', styles.root)}>
|
||||
Connecting to the Edge environment...
|
||||
<i className="fa fa-cog fa-spin space-left" />
|
||||
<Icon icon={Settings} className="animate-spin-slow !ml-1" />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
import { Zap } from 'react-feather';
|
||||
import { Zap } from 'lucide-react';
|
||||
|
||||
import { EnvironmentType } from '@/react/portainer/environments/types';
|
||||
import {
|
||||
|
@ -19,7 +19,7 @@ export function AgentVersionTag({ type, version }: Props) {
|
|||
return (
|
||||
<span className="space-x-1">
|
||||
<span>
|
||||
+ <Zap className="icon icon-xs vertical-center" aria-hidden="true" />
|
||||
<Zap className="icon icon-xs vertical-center" aria-hidden="true" />
|
||||
</span>
|
||||
<span>{isEdgeEnvironment(type) ? 'Edge Agent' : 'Agent'}</span>
|
||||
|
||||
|
|
|
@ -1,5 +1,3 @@
|
|||
import clsx from 'clsx';
|
||||
|
||||
import { environmentTypeIcon } from '@/portainer/filters/filters';
|
||||
import dockerEdge from '@/assets/images/edge_endpoint.png';
|
||||
import kube from '@/assets/images/kubernetes_endpoint.png';
|
||||
|
@ -8,6 +6,8 @@ import { EnvironmentType } from '@/react/portainer/environments/types';
|
|||
import azure from '@/assets/ico/vendor/azure.svg';
|
||||
import docker from '@/assets/ico/vendor/docker.svg';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
|
||||
interface Props {
|
||||
type: EnvironmentType;
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ export function EnvironmentIcon({ type }: Props) {
|
|||
case EnvironmentType.AgentOnDocker:
|
||||
case EnvironmentType.Docker:
|
||||
return (
|
||||
<img src={docker} width="60" alt="azure endpoint" aria-hidden="true" />
|
||||
<img src={docker} width="60" alt="docker endpoint" aria-hidden="true" />
|
||||
);
|
||||
case EnvironmentType.Azure:
|
||||
return (
|
||||
|
@ -36,9 +36,9 @@ export function EnvironmentIcon({ type }: Props) {
|
|||
);
|
||||
default:
|
||||
return (
|
||||
<i
|
||||
className={clsx('fa-4x', 'blue-icon', environmentTypeIcon(type))}
|
||||
aria-hidden="true"
|
||||
<Icon
|
||||
icon={environmentTypeIcon(type)}
|
||||
className="blue-icon !h-16 !w-16"
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import clsx from 'clsx';
|
||||
import _ from 'lodash';
|
||||
import { Edit2, Tag, Cpu } from 'react-feather';
|
||||
import { Edit2, Tag, Cpu } from 'lucide-react';
|
||||
|
||||
import {
|
||||
isoDateFromTimestamp,
|
||||
|
@ -19,6 +19,7 @@ import {
|
|||
import type { TagId } from '@/portainer/tags/types';
|
||||
import { useTags } from '@/portainer/tags/queries';
|
||||
import { useUser } from '@/react/hooks/useUser';
|
||||
import Memory from '@/assets/ico/memory.svg?c';
|
||||
|
||||
import { Icon } from '@@/Icon';
|
||||
import { Link } from '@@/Link';
|
||||
|
@ -106,7 +107,7 @@ export function EnvironmentItem({ environment, onClick, groupName }: Props) {
|
|||
/>
|
||||
{environment.Snapshots[0].TotalCPU} CPU
|
||||
<Icon
|
||||
icon="svg-memory"
|
||||
icon={Memory}
|
||||
className="icon icon-sm space-right"
|
||||
/>
|
||||
{humanize(environment.Snapshots[0].TotalMemory)} RAM
|
||||
|
|
|
@ -1,3 +1,14 @@
|
|||
import {
|
||||
Layers,
|
||||
Shuffle,
|
||||
Database,
|
||||
List,
|
||||
HardDrive,
|
||||
Box,
|
||||
Power,
|
||||
Heart,
|
||||
} from 'lucide-react';
|
||||
|
||||
import {
|
||||
DockerSnapshot,
|
||||
EnvironmentType,
|
||||
|
@ -5,7 +16,7 @@ import {
|
|||
import { addPlural } from '@/portainer/helpers/strings';
|
||||
|
||||
import { AgentVersionTag } from './AgentVersionTag';
|
||||
import { Stat } from './EnvironmentStatsItem';
|
||||
import { EnvironmentStatsItem } from './EnvironmentStatsItem';
|
||||
|
||||
interface Props {
|
||||
snapshots: DockerSnapshot[];
|
||||
|
@ -31,17 +42,15 @@ export function EnvironmentStatsDocker({
|
|||
return (
|
||||
<div className="blocklist-item-line endpoint-item">
|
||||
<span className="blocklist-item-desc">
|
||||
<Stat
|
||||
<EnvironmentStatsItem
|
||||
value={addPlural(snapshot.StackCount, 'stack')}
|
||||
icon="layers"
|
||||
featherIcon
|
||||
icon={Layers}
|
||||
/>
|
||||
|
||||
{!!snapshot.Swarm && (
|
||||
<Stat
|
||||
<EnvironmentStatsItem
|
||||
value={addPlural(snapshot.ServiceCount, 'service')}
|
||||
icon="shuffle"
|
||||
featherIcon
|
||||
icon={Shuffle}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
@ -51,15 +60,13 @@ export function EnvironmentStatsDocker({
|
|||
healthy={snapshot.HealthyContainerCount}
|
||||
unhealthy={snapshot.UnhealthyContainerCount}
|
||||
/>
|
||||
<Stat
|
||||
<EnvironmentStatsItem
|
||||
value={addPlural(snapshot.VolumeCount, 'volume')}
|
||||
icon="database"
|
||||
featherIcon
|
||||
icon={Database}
|
||||
/>
|
||||
<Stat
|
||||
<EnvironmentStatsItem
|
||||
value={addPlural(snapshot.ImageCount, 'image')}
|
||||
icon="list"
|
||||
featherIcon
|
||||
icon={List}
|
||||
/>
|
||||
</span>
|
||||
|
||||
|
@ -68,10 +75,9 @@ export function EnvironmentStatsDocker({
|
|||
{snapshot.Swarm ? 'Swarm' : 'Standalone'} {snapshot.DockerVersion}
|
||||
</span>
|
||||
{snapshot.Swarm && (
|
||||
<Stat
|
||||
<EnvironmentStatsItem
|
||||
value={addPlural(snapshot.NodeCount, 'node')}
|
||||
icon="hard-drive"
|
||||
featherIcon
|
||||
icon={HardDrive}
|
||||
/>
|
||||
)}
|
||||
<AgentVersionTag version={agentVersion} type={type} />
|
||||
|
@ -96,39 +102,34 @@ function ContainerStats({
|
|||
const containersCount = running + stopped;
|
||||
|
||||
return (
|
||||
<Stat
|
||||
<EnvironmentStatsItem
|
||||
value={addPlural(containersCount, 'container')}
|
||||
icon="box"
|
||||
featherIcon
|
||||
icon={Box}
|
||||
>
|
||||
{containersCount > 0 && (
|
||||
<span className="space-x-2 space-right">
|
||||
<Stat
|
||||
<EnvironmentStatsItem
|
||||
value={running}
|
||||
icon="power"
|
||||
featherIcon
|
||||
icon={Power}
|
||||
iconClass="icon-success"
|
||||
/>
|
||||
<Stat
|
||||
<EnvironmentStatsItem
|
||||
value={stopped}
|
||||
icon="power"
|
||||
featherIcon
|
||||
icon={Power}
|
||||
iconClass="icon-danger"
|
||||
/>
|
||||
<Stat
|
||||
<EnvironmentStatsItem
|
||||
value={healthy}
|
||||
icon="heart"
|
||||
featherIcon
|
||||
icon={Heart}
|
||||
iconClass="icon-success"
|
||||
/>
|
||||
<Stat
|
||||
<EnvironmentStatsItem
|
||||
value={unhealthy}
|
||||
icon="heart"
|
||||
featherIcon
|
||||
icon={Heart}
|
||||
iconClass="icon-warning"
|
||||
/>
|
||||
</span>
|
||||
)}
|
||||
</Stat>
|
||||
</EnvironmentStatsItem>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,15 +5,14 @@ import { Icon, IconProps } from '@/react/components/Icon';
|
|||
|
||||
interface Props extends IconProps {
|
||||
value: string | number;
|
||||
icon: string;
|
||||
icon: IconProps['icon'];
|
||||
iconClass?: string;
|
||||
}
|
||||
|
||||
export function Stat({
|
||||
export function EnvironmentStatsItem({
|
||||
value,
|
||||
icon,
|
||||
children,
|
||||
featherIcon,
|
||||
iconClass,
|
||||
}: PropsWithChildren<Props>) {
|
||||
return (
|
||||
|
@ -21,7 +20,6 @@ export function Stat({
|
|||
<Icon
|
||||
className={clsx('icon icon-sm space-right', iconClass)}
|
||||
icon={icon}
|
||||
feather={featherIcon}
|
||||
/>
|
||||
<span>{value}</span>
|
||||
{children && <span className="space-left">{children}</span>}
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue