Skip to main content

Common Components

The project includes some built-in common components that developers can use directly and extend as needed. Below is a brief introduction to the API of some common components. For detailed information, please refer to the component source code.

The dialog component, implemented based on @mui/material, supports custom content.

Example

import { Modal } from "@milesight/shared/src/components";

const Service: React.FC<Props> = () => {
// ...

return (
<div className="ms-tab-panel-service">
<Modal
visible={!!targetService}
title={targetService?.name}
onCancel={() => setTargetService(undefined)}
onOk={handleSubmit(onSubmit)}
>
{formItems.map((props) => (
<Controller<EntityFormDataProps>
{...props}
key={props.name}
control={control}
/>
))}
</Modal>
</div>
);
};

API

ParameterDescriptionTypeDefault
titleTitlestring-
visibleWhether the dialog is visiblebooleanfalse
sizeDialog size'sm' | 'md' | 'lg' | 'xl' | 'full'md
widthDialog width (if set, size is ignored)string-
classNameCustom class namestring-
disabledBackdropCloseWhether to disable closing the dialog by clicking the backdropbooleanfalse
onOkTextConfirm button textstringConfirm
onCancelTextCancel button textstringCancel
containerDialog mount nodeHTMLElement-
childrenDialog contentReactNode-
onOkConfirm callback() => void-
onCancelCancel callback() => void-

Confirm

The confirmation dialog component, implemented based on Modal, supports imperative calling.

Example

import { Breadcrumbs } from "@/components";

// ...

const confirm = useConfirm();
const handleDeleteConfirm = useCallback(
(ids?: ApiKey[]) => {
const idsToDelete = ids || [...selectedIds];

// Note: The `confirm()` call returns a Promise, you can use `await` to wait for the confirmation callback to complete before executing subsequent logic.
confirm({
title: getIntlText("common.label.delete"),
description: getIntlText("device.message.delete_tip"),
confirmButtonText: getIntlText("common.label.delete"),
confirmButtonProps: {
color: "error",
},
onConfirm: async () => {
const [error, resp] = await awaitWrap(
deviceAPI.deleteDevices({ device_id_list: idsToDelete })
);

if (error || !isRequestSuccess(resp)) return;

getDeviceList();
setSelectedIds([]);
toast.success(getIntlText("common.message.delete_success"));
},
});
},
[confirm, getIntlText, getDeviceList, selectedIds]
);

API

ParameterDescriptionTypeDefault
titleTitlestring-
descriptionDescriptionstring-
iconIconReactNode-
cancelButtonTextCancel button textstringCancel
confirmButtonTextConfirm button textstringConfirm
rejectOnCancelWhether to reject on cancel button clickbooleanfalse
confirmTextConfirmation text input, when not empty, confirmation text input mode is enabled automaticallystring-
timerAuto-close countdown in ms, when not empty, countdown mode is enabled automaticallynumber-
disabledBackdropCloseWhether to disable closing by clicking the backdropbooleanfalse
dialogPropsMUI Dialog component propertiesDialogProps-
dialogTitlePropsMUI DialogTitle component propertiesDialogTitleProps-
dialogContentPropsMUI DialogContent component propertiesDialogContentProps-
dialogContentTextPropsMUI DialogContentText component propertiesDialogContentTextProps-
dialogActionsPropsMUI DialogActions component propertiesDialogActionsProps-
confirmTextFieldPropsMUI TextField component propertiesTextFieldProps-
timerProgressPropsMUI LinearProgress component propertiesLinearProgressProps-

Toast

The global notification component, implemented based on @mui/material, can be used for success, warning, error, and informational messages. It is displayed in the center of the page, and automatically closes after 3 seconds by default, providing a lightweight way to notify users without interrupting their operations.

Example

// web/src/services/http/client/error-handler.ts
import { toast } from "@milesight/shared/src/components";

//...

const handlerConfigs: ErrorHandlerConfig[] = [
// Unified Message popup notification
{
errCodes: ["authentication_failed"],
handler(errCode, resp) {
const intlKey = getHttpErrorKey(errCode);
const message = intl.get(intlKey) || intl.get(serverErrorKey);
const target = iotLocalStorage.getItem(REGISTERED_KEY)
? "/auth/login"
: "/auth/register";

toast.error({
key: errCode,
content: message,
duration: 1000,
onClose: () => {
const { pathname } = window.location;

if (target === pathname) return;
location.replace(target);
},
});

iotLocalStorage.removeItem(TOKEN_CACHE_KEY);
},
},
];

API

The component provides some static methods with the following usage and parameters:

  • toast.info(options): Informational notification
  • toast.success(options): Success notification
  • toast.error(options): Error notification
  • toast.warning(options): Warning notification

The options parameter type is as follows:

ParameterDescriptionTypeDefault
keyUnique identifier, only one toast with the same identifier will be rendered on the pagestring-
contentNotification contentReactNode-
durationAuto-close time in msnumber3000
onCloseClose callback() => void-

Empty

The empty state placeholder component is used to explicitly notify users when there is no data.

Example

import { Empty, Descriptions, Tooltip } from "@/components";

const Property: React.FC<Props> = ({ loading, entities, onUpdateSuccess }) => {
// ...

return !readOnlyProps?.length && !writableProps?.length ? (
<Empty
loading={loading}
type="nodata"
text={getIntlText("common.label.empty")}
className="ms-empty"
/>
) : (
<div
className={cls("ms-entity-property", { loading: formState.isSubmitting })}
>
{/* ... */}
</div>
);
};

export default Property;

API

ParameterDescriptionTypeRequiredDefault
typeEmpty state type'nodata'Nonodata
sizeEmpty state size'small' | 'middle' | 'large'Nomiddle
textEmpty state textReact.ReactNodeNo-
imageEmpty state placeholder image, if set, type attribute is ignoredReact.ReactNodeNo-
extraExtra content for the empty stateReact.ReactNodeNo-
loadingWhether to show the loading statebooleanNo-
classNameCustom class namestringNo-

Tooltip

The tooltip component, implemented based on the @mui/material Tooltip component, supports automatic handling of text ellipsis and tooltip activation based on content and container width while retaining the original functionality.

Example

import { Tooltip } from "@/components";

// ...

const Integration = () => {
// ...
return (
<>
<Grid2 container spacing={2}>
{intList?.map((item) => (
<Grid2 key={item.id} size={{ xs: 12, sm: 6, md: 4, xl: 3 }}>
<div
className="ms-int-card"
onClick={() => handleCardClick(item.id, item)}
>
<div className="icon">
{!!item.icon && (
<img src={genInteIconUrl(item.icon)} alt={item.name} />
)}
</div>
<Tooltip autoEllipsis className="title" title={item.name} />
<Tooltip autoEllipsis className="desc" title={item.description} />
<div className="meta">
<span className="meta-item">
<DevicesOtherIcon />
<span>{thousandSeparate(item.deviceCount) || "-"}</span>
</span>
<span className="meta-item">
<EntityIcon />
<span>{thousandSeparate(item.entityCount) || "-"}</span>
</span>
</div>
</div>
</Grid2>
))}
</Grid2>
</>
);
};

export default Integration;

API

The Tooltip component supports all @mui/material Tooltip component API and adds the following attribute:

ParameterDescriptionTypeRequiredDefault
autoEllipsisWhether to automatically add text ellipsis based on content and container widthbooleanNofalse

The breadcrumbs navigation component, implemented based on the @mui/material Breadcrumbs component, supports automatic generation of breadcrumb navigation based on the routing hierarchy and allows customization of the navigation menu.

Example

import { Breadcrumbs } from "@/components";
// ...

function Setting() {
// ...

return (
<div className="ms-main">
<Breadcrumbs />
{/* ... */}
</div>
);
}

export default Setting;

API

type NavsType = {
path?: string;
title: string;
state?: any;
}[];
ParameterDescriptionTypeRequiredDefault
navsCustom navigation set, if not provided, the navigation menu is calculated automatically based on the current routeNavsTypeNo-
rewriteNavigation rewrite function(navs: NavsType) => NavsTypeNo-

Descriptions

The description list component, implemented based on the @mui/material Table component, is often used to display detailed information of data.

Example

import { Descriptions, Tooltip } from "@/components";

const BasicTable = (
{ data, loading, onEditSuccess }: Props,
ref?: React.ForwardedRef<BasicTableInstance>
) => {
const { getIntlText } = useI18n();
const { getTimeFormat } = useTime();
const descList = useMemo(() => {
return [
{
key: "name",
label: getIntlText("common.label.name"),
content: (
<Stack
direction="row"
sx={{ alignItems: "center", justifyContent: "space-between" }}
>
<Tooltip autoEllipsis title={data?.name} />
<IconButton
sx={{ width: 22, height: 22 }}
onClick={() => {
setDialogOpen(true);
}}
>
<EditIcon sx={{ fontSize: 16 }} />
</IconButton>
</Stack>
),
},
{
key: "externalId",
label: getIntlText("device.label.param_external_id"),
content: data?.identifier,
},
{
key: "source",
label: getIntlText("device.label.param_source"),
content: <Tooltip autoEllipsis title={data?.integrationName} />,
},
{
key: "createTime",
label: getIntlText("common.label.create_time"),
content: getTimeFormat(data?.createdAt),
},
{
key: "founder",
label: getIntlText("device.label.param_founder"),
content: data?.integrationName,
},
{
key: "id",
label: getIntlText("device.label.param_device_id"),
content: data?.id,
},
];
}, [data, getIntlText, getTimeFormat]);

return (
<div className="ms-com-device-basic">
<Descriptions data={descList} loading={loading} />
{/* ... */}
</div>
);
};

export default BasicTable;

API

interface DescriptionDataType {
key: ApiKey;
label: React.ReactNode;
content: React.ReactNode;
}
ParameterDescriptionTypeRequiredDefault
dataDescription list dataDescriptionDataType[]No-
loadingWhether to show the loading statebooleanNo-
columnsNumber of data pairs rendered per rownumberNo2

TablePro

The table component, implemented based on the @mui/x-data-grid DataGrid component, comes with a lot of common configurations built-in. It retains the original functionality while allowing customization of the table.

Example

import { TablePro, type ColumnType } from "@/components";
// ...

const EntityTable: React.FC<Props> = ({ data, onRefresh }) => {
const { getIntlText } = useI18n();
const columns = useMemo(() => {
const entityTypeFilterOptions: { label: EntityType; value: EntityType }[] =
[
{
label: "PROPERTY",
value: "PROPERTY",
},
{
label: "SERVICE",
value: "SERVICE",
},
{
label: "EVENT",
value: "EVENT",
},
];
const result: ColumnType<TableRowDataType>[] = [
{
field: "name",
headerName: getIntlText("device.label.param_entity_name"),
flex: 1,
minWidth: 150,
ellipsis: true,
filterable: true,
disableColumnMenu: false,
filterOperators: getGridStringOperators().filter(
(item) => item.value === "contains"
),
},
{
field: "id",
headerName: getIntlText("device.label.param_entity_id"),
flex: 1,
minWidth: 150,
ellipsis: true,
},
{
field: "type",
headerName: getIntlText("common.label.type"),
flex: 1,
minWidth: 150,
filterable: true,
disableColumnMenu: false,
type: "singleSelect",
valueOptions: entityTypeFilterOptions,
filterOperators: getGridSingleSelectOperators().filter(
(item) => item.value === "is"
),
renderCell({ value }) {
return (
<Chip
size="small"
color={entityTypeColorMap[(value || "").toLocaleLowerCase()]}
label={value}
sx={{ borderRadius: 1, lineHeight: "24px" }}
/>
);
},
},
{
field: "valueType",
headerName: getIntlText("common.label.data_type"),
align: "left",
headerAlign: "left",
flex: 1,
minWidth: 150,
ellipsis: true,
},
];

return result;
}, [getIntlText]);

return (
<Stack className="ms-com-device-entity" sx={{ height: "100%" }}>
<TablePro<TableRowDataType>
paginationMode="client"
loading={false}
columns={columns}
rows={data?.entities}
onRefreshButtonClick={onRefresh}
/>
</Stack>
);
};

export default EntityTable;

API

The TablePro component supports all @mui/x-data-grid DataGrid component API and adds the following attributes:

ParameterDescriptionTypeRequiredDefault
toolbarRenderCustom rendering on the left side of the toolbarReact.ReactNodeNo-
onSearchSearch callback on the right side of the toolbar (if not provided, the search box is hidden)(value: string) => voidNo-
onRefreshButtonClickRefresh button click callback() => voidNo-

Additional Column Props:

ParameterDescriptionTypeRequiredDefault
ellipsisWhether to enable automatic text omission during development and debuggingbooleanNo-