Dashboards redesign - improvments (#2313)
* Various minor style updates * Various style improvements * Update ExampleCards.tsx * various minor improvements in dashbaords. * revised dashboard widget header
This commit is contained in:
parent
9be31d80db
commit
8b49be81b0
20 changed files with 156 additions and 129 deletions
|
|
@ -1,5 +1,7 @@
|
|||
import React from "react";
|
||||
import {Button, Tooltip} from "UI";
|
||||
import {Tooltip} from "UI";
|
||||
import {Button} from "antd";
|
||||
import { PlusOutlined } from '@ant-design/icons';
|
||||
import AddCardSelectionModal from "Components/Dashboard/components/AddCardSelectionModal";
|
||||
import {useStore} from "App/mstore";
|
||||
|
||||
|
|
@ -15,11 +17,10 @@ function CreateCardButton() {
|
|||
<Tooltip delay={0} disabled={canAddMore}
|
||||
title="The number of cards in one dashboard is limited to 30.">
|
||||
<Button
|
||||
type="primary"
|
||||
disabled={!canAddMore}
|
||||
variant="primary"
|
||||
onClick={() => setOpen(true)}
|
||||
icon="plus"
|
||||
iconSize={24}
|
||||
icon={<PlusOutlined />}
|
||||
>
|
||||
Add Card
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ function DashboardEditModal(props: Props) {
|
|||
/>
|
||||
</Form.Field>
|
||||
|
||||
<Form.Field>
|
||||
{/* <Form.Field>
|
||||
<label>{'Description:'}</label>
|
||||
<Input
|
||||
className=""
|
||||
|
|
@ -69,7 +69,7 @@ function DashboardEditModal(props: Props) {
|
|||
maxLength={300}
|
||||
autoFocus={!focusTitle}
|
||||
/>
|
||||
</Form.Field>
|
||||
</Form.Field> */}
|
||||
|
||||
<Form.Field>
|
||||
<div className="flex items-center">
|
||||
|
|
|
|||
|
|
@ -20,6 +20,7 @@ interface IProps {
|
|||
renderReport?: any;
|
||||
}
|
||||
|
||||
|
||||
type Props = IProps & RouteComponentProps;
|
||||
const MAX_CARDS = 29;
|
||||
|
||||
|
|
@ -43,7 +44,7 @@ function DashboardHeader(props: Props) {
|
|||
const onDelete = async () => {
|
||||
if (
|
||||
await confirm({
|
||||
header: 'Confirm',
|
||||
header: 'Delete Dashboard',
|
||||
confirmButton: 'Yes, delete',
|
||||
confirmation: `Are you sure you want to permanently delete this Dashboard?`,
|
||||
})
|
||||
|
|
@ -82,11 +83,11 @@ function DashboardHeader(props: Props) {
|
|||
className="mr-3 select-none border-b border-b-borderColor-transparent hover:border-dotted hover:border-gray-medium cursor-pointer"
|
||||
/>
|
||||
</div>
|
||||
<div className="flex items-center" style={{flex: 1, justifyContent: 'end'}}>
|
||||
<div className="flex items-center gap-2" style={{flex: 1, justifyContent: 'end'}}>
|
||||
<CreateCardButton disabled={canAddMore} />
|
||||
<div className="mx-4"></div>
|
||||
|
||||
<div
|
||||
className="flex items-center flex-shrink-0 justify-end"
|
||||
className="flex items-center flex-shrink-0 justify-end dashboardDataPeriodSelector"
|
||||
style={{width: 'fit-content'}}
|
||||
>
|
||||
<SelectDateRange
|
||||
|
|
@ -94,9 +95,11 @@ function DashboardHeader(props: Props) {
|
|||
period={period}
|
||||
onChange={(period: any) => dashboardStore.setPeriod(period)}
|
||||
right={true}
|
||||
isAnt={true}
|
||||
useButtonStyle={true}
|
||||
/>
|
||||
</div>
|
||||
<div className="mx-4"/>
|
||||
|
||||
<div className="flex items-center flex-shrink-0">
|
||||
<DashboardOptions
|
||||
editHandler={onEdit}
|
||||
|
|
@ -114,7 +117,7 @@ function DashboardHeader(props: Props) {
|
|||
className="my-2 font-normal w-fit text-disabled-text border-b border-b-borderColor-transparent hover:border-dotted hover:border-gray-medium cursor-pointer"
|
||||
onDoubleClick={() => onEdit(false)}
|
||||
>
|
||||
{dashboard?.description || 'Describe the purpose of this dashboard'}
|
||||
{/* {dashboard?.description || 'Describe the purpose of this dashboard'} */}
|
||||
</h2>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -19,9 +19,9 @@ function ExCard({
|
|||
style={{width: '100%', height: height || 286}}
|
||||
>
|
||||
<div className={'font-medium text-lg'}>{title}</div>
|
||||
<div className={'flex flex-col gap-2 mt-2 cursor-pointer'} style={{
|
||||
height: height ? height - 50 : 236,
|
||||
}} onClick={() => onCard(type)}>{children}</div>
|
||||
<div className={'flex flex-col gap-2 mt-2 cursor-pointer'}
|
||||
style={{height: height ? height - 50 : 236,}}
|
||||
onClick={() => onCard(type)}>{children}</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ function DashboardOptions(props: Props) {
|
|||
const { editHandler, deleteHandler, renderReport, isEnterprise, isTitlePresent } = props;
|
||||
const menuItems = [
|
||||
{ icon: 'pencil', text: 'Rename', onClick: () => editHandler(true) },
|
||||
{ icon: 'text-paragraph', text: `${!isTitlePresent ? 'Add' : 'Edit'} Description`, onClick: () => editHandler(false) },
|
||||
// { icon: 'text-paragraph', text: `${!isTitlePresent ? 'Add' : 'Edit'} Description`, onClick: () => editHandler(false) },
|
||||
{ icon: 'users', text: 'Visibility & Access', onClick: editHandler },
|
||||
{ icon: 'trash', text: 'Delete', onClick: deleteHandler },
|
||||
{ icon: 'pdf-download', text: 'Download Report', onClick: renderReport, disabled: !isEnterprise, tooltipTitle: ENTERPRISE_REQUEIRED }
|
||||
|
|
@ -23,7 +23,6 @@ function DashboardOptions(props: Props) {
|
|||
return (
|
||||
<ItemMenu
|
||||
bold
|
||||
label="More Options"
|
||||
items={menuItems}
|
||||
/>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ function DashboardWidgetGrid(props: Props) {
|
|||
show={list?.length === 0}
|
||||
icon="no-metrics-chart"
|
||||
title={
|
||||
<div className="bg-white rounded">
|
||||
<div className="bg-white rounded-lg">
|
||||
<div className="border-b p-5">
|
||||
<div className="text-2xl font-normal">
|
||||
There are no cards in this dashboard
|
||||
|
|
|
|||
|
|
@ -2,7 +2,8 @@ import React from 'react';
|
|||
import SessionSearchField from 'Shared/SessionSearchField';
|
||||
import AiSessionSearchField from 'Shared/SessionSearchField/AiSessionSearchField';
|
||||
import SavedSearch from 'Shared/SavedSearch';
|
||||
import { Button } from 'UI';
|
||||
// import { Button } from 'UI';
|
||||
import { Button } from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import { clearSearch } from 'Duck/search';
|
||||
import TagList from './components/TagList';
|
||||
|
|
@ -41,10 +42,12 @@ const MainSearchBar = (props: Props) => {
|
|||
<TagList />
|
||||
<SavedSearch />
|
||||
<Button
|
||||
variant={hasSearch ? 'text-primary' : 'text'}
|
||||
className="ml-auto font-medium"
|
||||
// variant={hasSearch ? 'text-primary' : 'text'}
|
||||
// className="ml-auto font-medium"
|
||||
type='link'
|
||||
disabled={!hasSearch}
|
||||
onClick={() => props.clearSearch()}
|
||||
className='ml-auto font-medium'
|
||||
>
|
||||
Clear Search
|
||||
</Button>
|
||||
|
|
|
|||
|
|
@ -7,8 +7,8 @@ import { useStore } from 'App/mstore';
|
|||
import { observer } from 'mobx-react-lite';
|
||||
import { FilterKey } from 'Types/filter/filterType';
|
||||
import { addOptionsToFilter } from 'Types/filter/newFilter';
|
||||
import { Button, Icon, confirm } from 'UI';
|
||||
import { Typography } from 'antd';
|
||||
import { Icon, confirm } from 'UI';
|
||||
import { Button, Typography } from 'antd';
|
||||
import { toast } from 'react-toastify';
|
||||
|
||||
function TagList(props: {
|
||||
|
|
@ -44,9 +44,15 @@ function TagList(props: {
|
|||
});
|
||||
};
|
||||
return (
|
||||
<Button variant={'outline'} disabled={!tagWatchStore.tags.length} onClick={openModal}>
|
||||
<Button
|
||||
// variant={'outline'}
|
||||
type='primary'
|
||||
ghost
|
||||
className='gap-1'
|
||||
disabled={!tagWatchStore.tags.length}
|
||||
onClick={openModal}>
|
||||
<span>Tags</span>
|
||||
<span className={'font-bold ml-1'}>{tagWatchStore.tags.length}</span>
|
||||
<span className={'font-medium ml-1'}>{tagWatchStore.tags.length}</span>
|
||||
</Button>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
import React, { useEffect } from 'react';
|
||||
import { Button, Icon } from 'UI';
|
||||
import { Icon } from 'UI';
|
||||
import { Button } from 'antd';
|
||||
import { connect } from 'react-redux';
|
||||
import { fetchList as fetchListSavedSearch } from 'Duck/search';
|
||||
import cn from 'classnames';
|
||||
|
|
@ -27,11 +28,14 @@ function SavedSearch(props: Props) {
|
|||
return (
|
||||
<div className={cn("flex items-center", { [stl.disabled] : list.size === 0})}>
|
||||
<Button
|
||||
variant="outline"
|
||||
// variant="outline"
|
||||
type='primary'
|
||||
ghost
|
||||
onClick={() => showModal(<SavedSearchModal />, { right: true, width: 450 })}
|
||||
className='flex gap-1'
|
||||
>
|
||||
<span className="mr-1">Saved Search</span>
|
||||
<span className="font-bold mr-2">{list.size}</span>
|
||||
<span className="font-meidum">{list.size}</span>
|
||||
<Icon name="ellipsis-v" color="teal" size="14" />
|
||||
</Button>
|
||||
{ savedSearch.exists() && (
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { DownOutlined } from '@ant-design/icons';
|
||||
import Period from 'Types/app/period';
|
||||
import { Dropdown, DatePicker } from 'antd';
|
||||
import { Dropdown, Button } from 'antd';
|
||||
import cn from 'classnames';
|
||||
import { observer } from 'mobx-react-lite';
|
||||
import React from 'react';
|
||||
|
|
@ -20,14 +20,14 @@ interface Props {
|
|||
timezone?: string;
|
||||
isAnt?: boolean;
|
||||
small?: boolean;
|
||||
useButtonStyle?: boolean; // New prop to control button style
|
||||
|
||||
[x: string]: any;
|
||||
}
|
||||
|
||||
function SelectDateRange(props: Props) {
|
||||
const [isCustom, setIsCustom] = React.useState(false);
|
||||
const [isCustomOpen, setIsCustomOpen] = React.useState(false);
|
||||
const { right = false, period, disableCustom = false, timezone } = props;
|
||||
const { right = false, period, disableCustom = false, timezone, useButtonStyle = false } = props;
|
||||
let selectedValue = DATE_RANGE_OPTIONS.find(
|
||||
(obj: any) => obj.value === period.rangeName
|
||||
);
|
||||
|
|
@ -41,13 +41,11 @@ function SelectDateRange(props: Props) {
|
|||
setIsCustom(true);
|
||||
}, 1);
|
||||
} else {
|
||||
// @ts-ignore
|
||||
props.onChange(new Period({ rangeName: value }));
|
||||
}
|
||||
};
|
||||
|
||||
const onApplyDateRange = (value: any) => {
|
||||
// @ts-ignore
|
||||
const range = new Period({
|
||||
rangeName: CUSTOM_RANGE,
|
||||
start: value.start,
|
||||
|
|
@ -61,34 +59,31 @@ function SelectDateRange(props: Props) {
|
|||
const customRange = isCustomRange ? period.rangeFormatted() : '';
|
||||
|
||||
if (props.isAnt) {
|
||||
const onAntUpdate = (val: any) => {
|
||||
onChange(val);
|
||||
const menuProps = {
|
||||
items: options.map((opt) => ({
|
||||
label: opt.label,
|
||||
key: opt.value,
|
||||
})),
|
||||
defaultSelectedKeys: selectedValue?.value ? [selectedValue.value] : undefined,
|
||||
onClick: (e: any) => {
|
||||
onChange(e.key);
|
||||
},
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'relative'}>
|
||||
<Dropdown
|
||||
menu={{
|
||||
items: options.map((opt) => ({
|
||||
label: opt.label,
|
||||
key: opt.value,
|
||||
})),
|
||||
defaultSelectedKeys: selectedValue?.value
|
||||
? [selectedValue.value]
|
||||
: undefined,
|
||||
onClick: (e: any) => {
|
||||
onChange(e.key);
|
||||
},
|
||||
}}
|
||||
onChange={onAntUpdate}
|
||||
style={{ width: 170 }}
|
||||
defaultValue={selectedValue?.value ?? undefined}
|
||||
>
|
||||
<div
|
||||
className={'cursor-pointer flex items-center gap-2'}
|
||||
>
|
||||
<div>{isCustomRange ? customRange : selectedValue?.label}</div>
|
||||
<DownOutlined />
|
||||
</div>
|
||||
<Dropdown menu={menuProps}>
|
||||
{useButtonStyle ? (
|
||||
<Button>
|
||||
<span>{isCustomRange ? customRange : selectedValue?.label}</span>
|
||||
<DownOutlined />
|
||||
</Button>
|
||||
) : (
|
||||
<div className={'cursor-pointer flex items-center gap-2'}>
|
||||
<span>{isCustomRange ? customRange : selectedValue?.label}</span>
|
||||
<DownOutlined />
|
||||
</div>
|
||||
)}
|
||||
</Dropdown>
|
||||
{isCustom && (
|
||||
<OutsideClickDetectingDiv
|
||||
|
|
@ -99,10 +94,8 @@ function SelectDateRange(props: Props) {
|
|||
) ||
|
||||
e.target.parentElement.parentElement.classList[0]?.includes(
|
||||
'-menu'
|
||||
)
|
||||
|| e.target.className.includes(
|
||||
'ant-picker'
|
||||
)
|
||||
) ||
|
||||
e.target.className.includes('ant-picker')
|
||||
) {
|
||||
return false;
|
||||
}
|
||||
|
|
@ -110,10 +103,7 @@ function SelectDateRange(props: Props) {
|
|||
}}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
'absolute top-0 mt-10 z-40',
|
||||
{ 'right-0': right }
|
||||
)}
|
||||
className={cn('absolute top-0 mt-10 z-40', { 'right-0': right })}
|
||||
style={{
|
||||
width: '770px',
|
||||
fontSize: '14px',
|
||||
|
|
@ -132,6 +122,7 @@ function SelectDateRange(props: Props) {
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<Select
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ interface Props {
|
|||
function Pdf_download(props: Props) {
|
||||
const { size = 14, width = size, height = size, fill = '' } = props;
|
||||
return (
|
||||
<svg viewBox="0 0 19 19" width={ `${ width }px` } height={ `${ height }px` } ><path d="M10.094 5.249a.594.594 0 0 0-1.188 0v4.504l-1.36-1.362a.595.595 0 0 0-.841.84l2.375 2.376a.596.596 0 0 0 .84 0l2.375-2.375a.595.595 0 0 0-.84-.841l-1.361 1.362V5.249Z"/><path d="M16.625 16.625V5.344L11.281 0H4.75a2.375 2.375 0 0 0-2.375 2.375v14.25A2.375 2.375 0 0 0 4.75 19h9.5a2.375 2.375 0 0 0 2.375-2.375ZM11.281 3.562a1.781 1.781 0 0 0 1.781 1.782h2.376v11.281a1.188 1.188 0 0 1-1.188 1.188h-9.5a1.187 1.187 0 0 1-1.188-1.188V2.375A1.188 1.188 0 0 1 4.75 1.187h6.531v2.375Z"/><path clipRule="evenodd" d="M15.58 13.49H3.42v4.37h1.789v-3.512H6.61c.282 0 .524.051.726.154.205.103.361.245.47.425.11.178.165.383.165.613 0 .226-.055.424-.164.593-.11.169-.266.3-.47.396-.203.093-.445.14-.727.14h-.554v1.191h2.37v-3.512h1.13c.238 0 .455.041.653.123a1.537 1.537 0 0 1 .854.88c.08.205.12.431.12.68v.148c0 .247-.04.474-.12.68a1.512 1.512 0 0 1-.849.88c-.193.08-.404.12-.635.121h2.046v-3.512h2.35v.654h-1.503v.808h1.365v.651h-1.365v1.399h3.108v-4.37Zm-9.524 1.512v1.013h.554c.12 0 .216-.02.29-.06a.367.367 0 0 0 .161-.167.547.547 0 0 0 .054-.244.668.668 0 0 0-.054-.267.431.431 0 0 0-.161-.198.496.496 0 0 0-.29-.077h-.554Zm3.512 2.207h-.295v-2.207h.283c.123 0 .233.021.328.065a.604.604 0 0 1 .24.195.88.88 0 0 1 .146.321c.033.127.05.275.05.444v.152c0 .225-.03.415-.089.569a.707.707 0 0 1-.256.345.696.696 0 0 1-.407.116Z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={ `${ width }px` } height={ `${ height }px` } viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-down"><path d="M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z"/><path d="M14 2v4a2 2 0 0 0 2 2h4"/><path d="M12 18v-6"/><path d="m9 15 3 3 3-3"/></svg>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ interface Props {
|
|||
function Pencil(props: Props) {
|
||||
const { size = 14, width = size, height = size, fill = '' } = props;
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" width={ `${ width }px` } height={ `${ height }px` } ><path d="M12.146.146a.5.5 0 0 1 .708 0l3 3a.5.5 0 0 1 0 .708l-10 10a.5.5 0 0 1-.168.11l-5 2a.5.5 0 0 1-.65-.65l2-5a.5.5 0 0 1 .11-.168l10-10zM11.207 2.5 13.5 4.793 14.793 3.5 12.5 1.207 11.207 2.5zm1.586 3L10.5 3.207 4 9.707V10h.5a.5.5 0 0 1 .5.5v.5h.5a.5.5 0 0 1 .5.5v.5h.293l6.5-6.5zm-9.761 5.175-.106.106-1.528 3.821 3.821-1.528.106-.106A.5.5 0 0 1 5 12.5V12h-.5a.5.5 0 0 1-.5-.5V11h-.5a.5.5 0 0 1-.468-.325z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={ `${ width }px` } height={ `${ height }px` } viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-file-pen-line"><path d="m18 5-2.414-2.414A2 2 0 0 0 14.172 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2"/><path d="M21.378 12.626a1 1 0 0 0-3.004-3.004l-4.01 4.012a2 2 0 0 0-.506.854l-.837 2.87a.5.5 0 0 0 .62.62l2.87-.837a2 2 0 0 0 .854-.506z"/><path d="M8 18h1"/></svg>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ interface Props {
|
|||
function Trash(props: Props) {
|
||||
const { size = 14, width = size, height = size, fill = '' } = props;
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" width={ `${ width }px` } height={ `${ height }px` } ><path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/><path d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4 4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/></svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={ `${ width }px` } height={ `${ height }px` } viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trash"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,7 +12,8 @@ interface Props {
|
|||
function Users(props: Props) {
|
||||
const { size = 14, width = size, height = size, fill = '' } = props;
|
||||
return (
|
||||
<svg viewBox="0 0 16 16" width={ `${ width }px` } height={ `${ height }px` } ><path d="M15 14s1 0 1-1-1-4-5-4-5 3-5 4 1 1 1 1h8zm-7.978-1A.261.261 0 0 1 7 12.996c.001-.264.167-1.03.76-1.72C8.312 10.629 9.282 10 11 10c1.717 0 2.687.63 3.24 1.276.593.69.758 1.457.76 1.72l-.008.002a.274.274 0 0 1-.014.002H7.022zM11 7a2 2 0 1 0 0-4 2 2 0 0 0 0 4zm3-2a3 3 0 1 1-6 0 3 3 0 0 1 6 0zM6.936 9.28a5.88 5.88 0 0 0-1.23-.247A7.35 7.35 0 0 0 5 9c-4 0-5 3-5 4 0 .667.333 1 1 1h4.216A2.238 2.238 0 0 1 5 13c0-1.01.377-2.042 1.09-2.904.243-.294.526-.569.846-.816zM4.92 10A5.493 5.493 0 0 0 4 13H1c0-.26.164-1.03.76-1.724.545-.636 1.492-1.256 3.16-1.275zM1.5 5.5a3 3 0 1 1 6 0 3 3 0 0 1-6 0zm3-2a2 2 0 1 0 0 4 2 2 0 0 0 0-4z"/></svg>
|
||||
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width={ `${ width }px` } height={ `${ height }px` } viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-users-round"><path d="M18 21a8 8 0 0 0-16 0"/><circle cx="10" cy="8" r="5"/><path d="M22 20c0-3.37-2-6.5-4-8a5 5 0 0 0-.45-8.3"/></svg>
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import React from 'react';
|
||||
import { Icon, Popover, Tooltip } from 'UI';
|
||||
import styles from './itemMenu.module.css';
|
||||
import cn from 'classnames';
|
||||
import React from "react";
|
||||
import { Icon, Popover, Tooltip } from "UI";
|
||||
import { Dropdown, Menu, Button } from "antd";
|
||||
import { MoreOutlined } from "@ant-design/icons";
|
||||
import styles from "./itemMenu.module.css";
|
||||
import cn from "classnames";
|
||||
|
||||
interface Item {
|
||||
icon?: string;
|
||||
|
|
@ -28,13 +30,13 @@ export default class ItemMenu extends React.PureComponent<Props> {
|
|||
displayed: false,
|
||||
};
|
||||
|
||||
handleEsc = (e: KeyboardEvent) => e.key === 'Escape' && this.closeMenu();
|
||||
handleEsc = (e: KeyboardEvent) => e.key === "Escape" && this.closeMenu();
|
||||
|
||||
componentDidMount() {
|
||||
document.addEventListener('keydown', this.handleEsc, false);
|
||||
document.addEventListener("keydown", this.handleEsc, false);
|
||||
}
|
||||
componentWillUnmount() {
|
||||
document.removeEventListener('keydown', this.handleEsc, false);
|
||||
document.removeEventListener("keydown", this.handleEsc, false);
|
||||
}
|
||||
|
||||
onClick = (callback: Function) => (e: React.MouseEvent<HTMLDivElement>) => {
|
||||
|
|
@ -54,74 +56,69 @@ export default class ItemMenu extends React.PureComponent<Props> {
|
|||
};
|
||||
|
||||
render() {
|
||||
const { items, label = '', bold, sm } = this.props;
|
||||
const { items, label, bold, sm } = this.props;
|
||||
const { displayed } = this.state;
|
||||
const parentStyles = label ? 'rounded px-2 py-2 hover:bg-gray-light' : '';
|
||||
const parentStyles = label ? "hover:bg-gray-light" : "";
|
||||
|
||||
return (
|
||||
<Popover
|
||||
placement="bottom-end" // Set the placement to bottom-end for right alignment
|
||||
render={() => (
|
||||
<div
|
||||
className={cn(styles.menu, { [styles.menuDim]: !bold })}
|
||||
// data-displayed={displayed}
|
||||
>
|
||||
<div className={cn(styles.menu, 'rounded-lg', { [styles.menuDim]: !bold })}>
|
||||
{items
|
||||
.filter(({ hidden }) => !hidden)
|
||||
.map(({ onClick, text, icon, disabled = false, tooltipTitle = '' }) => (
|
||||
<Tooltip disabled={!disabled} title={tooltipTitle} delay={0}>
|
||||
<div
|
||||
key={text}
|
||||
onClick={!disabled ? this.onClick(onClick) : () => {}}
|
||||
className={disabled ? 'cursor-not-allowed' : ''}
|
||||
role="menuitem"
|
||||
>
|
||||
<div className={cn(styles.menuItem, 'text-neutral-700', { disabled: disabled })}>
|
||||
{icon && (
|
||||
<div className={styles.iconWrapper}>
|
||||
{/* @ts-ignore */}
|
||||
<Icon name={icon} size="13" color="gray-dark" />
|
||||
</div>
|
||||
)}
|
||||
<div>{text}</div>
|
||||
.map(
|
||||
({
|
||||
onClick,
|
||||
text,
|
||||
icon,
|
||||
disabled = false,
|
||||
tooltipTitle = "",
|
||||
}) => (
|
||||
<Tooltip key={text} disabled={!disabled} title={tooltipTitle} delay={0}>
|
||||
<div
|
||||
onClick={!disabled ? this.onClick(onClick) : () => {}}
|
||||
className={`${disabled ? "cursor-not-allowed" : ""}`}
|
||||
role="menuitem"
|
||||
>
|
||||
<div className={cn(styles.menuItem, { disabled: disabled })}>
|
||||
{icon && (
|
||||
<div className={styles.iconWrapper}>
|
||||
<Icon name={icon} size="13" color="gray-dark" />
|
||||
</div>
|
||||
)}
|
||||
<div>{text}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Tooltip>
|
||||
))}
|
||||
</Tooltip>
|
||||
)
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
>
|
||||
<div
|
||||
// onClick={this.toggleMenu}
|
||||
className={cn(
|
||||
'flex items-center cursor-pointer select-none',
|
||||
!this.props.flat ? parentStyles : '',
|
||||
{ 'bg-gray-light': !this.props.flat && displayed && label }
|
||||
)}
|
||||
<Button
|
||||
className={cn("select-none", !this.props.flat ? parentStyles : "", {
|
||||
"": !this.props.flat && displayed && label,
|
||||
})}
|
||||
>
|
||||
{label && (
|
||||
<span
|
||||
className={cn('mr-1', bold ? 'font-medium color-gray-darkest' : 'color-gray-medium')}
|
||||
>
|
||||
<span className={cn("font-medium")}>
|
||||
{label}
|
||||
</span>
|
||||
)}
|
||||
{this.props.flat ? null : (
|
||||
{!this.props.flat && (
|
||||
<div
|
||||
ref={(ref) => {
|
||||
this.menuBtnRef = ref;
|
||||
}}
|
||||
className={cn('rounded-full flex items-center justify-center', {
|
||||
'bg-gray-light': displayed,
|
||||
'w-10 h-10': !label && !sm,
|
||||
'w-8 h-8': sm,
|
||||
})}
|
||||
className={cn("rounded-full flex items-center justify-center")}
|
||||
role="button"
|
||||
>
|
||||
<Icon name="ellipsis-v" size="16" />
|
||||
<MoreOutlined />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</Button>
|
||||
</Popover>
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -45,9 +45,10 @@
|
|||
/* top: 37px; */
|
||||
min-width: 150px;
|
||||
background-color: $white;
|
||||
border-radius: 3px;
|
||||
border: 1px solid rgba(34,36,38,.15);
|
||||
box-shadow: 0 2px 3px 0 rgb(34 36 38 / 15%);
|
||||
border-radius: 0.5rem;
|
||||
|
||||
|
||||
|
||||
& .menuItem {
|
||||
|
|
@ -57,6 +58,7 @@
|
|||
display: flex;
|
||||
align-items: center;
|
||||
border-bottom: 1px solid $gray-light;
|
||||
|
||||
|
||||
& .iconWrapper {
|
||||
width: 13px;
|
||||
|
|
@ -66,6 +68,7 @@
|
|||
|
||||
&:hover {
|
||||
background-color: $active-blue;
|
||||
color: $teal;
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ interface Props {
|
|||
onClose?: () => void;
|
||||
}
|
||||
|
||||
const Popover = ({ children, render, placement, onOpen, onClose }: Props) => {
|
||||
const Popover = ({ children, render, placement = 'bottom-end', onOpen, onClose }: Props) => {
|
||||
const [open, setOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
|
|
@ -33,7 +33,7 @@ const Popover = ({ children, render, placement, onOpen, onClose }: Props) => {
|
|||
} else {
|
||||
onClose?.();
|
||||
}
|
||||
}, [open]);
|
||||
}, [open, onOpen, onClose]);
|
||||
|
||||
const { x, y, reference, floating, strategy, context } = useFloating({
|
||||
open,
|
||||
|
|
@ -68,7 +68,7 @@ const Popover = ({ children, render, placement, onOpen, onClose }: Props) => {
|
|||
>
|
||||
<div
|
||||
ref={floating}
|
||||
className="rounded border shadow"
|
||||
className="rounded-lg"
|
||||
style={{
|
||||
position: strategy,
|
||||
top: y ?? 0,
|
||||
|
|
|
|||
|
|
@ -10,6 +10,18 @@
|
|||
background-color: var(--bg-teal);
|
||||
}
|
||||
|
||||
:root{
|
||||
--bg-teal: #394dfe;
|
||||
}
|
||||
|
||||
.ant-btn{
|
||||
border-radius: .5rem;
|
||||
}
|
||||
|
||||
.ant-btn-primary{
|
||||
background-color: var(--bg-teal);
|
||||
}
|
||||
|
||||
.ml-15 { margin-left: 15px; }
|
||||
|
||||
.ph-10 { padding-left: 10px; padding-right: 10px; }
|
||||
|
|
@ -404,4 +416,10 @@ p {
|
|||
.chooseDashboardCards .ant-modal-content{
|
||||
background-color: #efefef;
|
||||
border-radius: 0.75rem;
|
||||
max-height: 700px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.dashboardDataPeriodSelector .dashboardMoreOptionsLabel{
|
||||
display: none;
|
||||
}
|
||||
|
|
@ -1,4 +1 @@
|
|||
<svg xmlns="http://www.w3.org/2000/svg" class="bi bi-trash" viewBox="0 0 16 16">
|
||||
<path d="M5.5 5.5A.5.5 0 0 1 6 6v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm2.5 0a.5.5 0 0 1 .5.5v6a.5.5 0 0 1-1 0V6a.5.5 0 0 1 .5-.5zm3 .5a.5.5 0 0 0-1 0v6a.5.5 0 0 0 1 0V6z"/>
|
||||
<path fill-rule="evenodd" d="M14.5 3a1 1 0 0 1-1 1H13v9a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V4h-.5a1 1 0 0 1-1-1V2a1 1 0 0 1 1-1H6a1 1 0 0 1 1-1h2a1 1 0 0 1 1 1h3.5a1 1 0 0 1 1 1v1zM4.118 4L4 4.059V13a1 1 0 0 0 1 1h6a1 1 0 0 0 1-1V4.059L11.882 4H4.118zM2.5 3V2h11v1h-11z"/>
|
||||
</svg>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-trash"><path d="M3 6h18"/><path d="M19 6v14c0 1-1 2-2 2H7c-1 0-2-1-2-2V6"/><path d="M8 6V4c0-1 1-2 2-2h4c1 0 2 1 2 2v2"/></svg>
|
||||
|
Before Width: | Height: | Size: 531 B After Width: | Height: | Size: 331 B |
4
yarn.lock
Normal file
4
yarn.lock
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
|
||||
# yarn lockfile v1
|
||||
|
||||
|
||||
Loading…
Add table
Reference in a new issue