These are custom components specially-developed for NexusUI applications. They will make your life easier by giving you out-of-the-box implementations for various high-level UI elements that you can drop directly into your application.
The NexusUI components are split into two groups: 1) the core components requiring a minimum of peerDependencies and 2) the extended components which are more specialized and require the installation of additional peerDependencies to function (see readme for specific extended components for details).
Add the components package as a dependency to your project:
# With yarn
yarn add @nexusui/components
# With npm
npm install --save @nexusui/components
You will also need to install the required peer dependencies (additional peerDependencies will be required if you are using any of the extended components):
# With yarn
yarn add @mui/material @emotion/react @emotion/styled @mui/icons-material
# With npm
npm install --save @mui/material @emotion/react @emotion/styled @mui/icons-material
In your typescript file, import the component(s) you want to use:
Core components can be installed from the root of the package or from their full path:
// Replace ComponentName with the specific core component you want to use
import { ComponentName } from '@nexusui/components';
import { ComponentName } from '@nexusui/components/core/ComponentName';
Extended components must be installed from their full path in the extended subfolder:
// Replace ExtendedComponentName with the specific extended component you want to use
import { ExtendedComponentName } from '@nexusui/components/extended/ComponentName';
The ShareDialog
component has been refactored to facilitate managing share permissions, especially for the organization-level share permissions.
It also has been enhanced to auto-fill the invitees' email addresses.
- The following prop and type have been removed:
teamShareConfig
,ITeamShareConfig
. - Renamed type:
IGroupShareConfig
=>IOrgShareConfig
. - Added prop
availableUsers
,loading
andonSearch
to typeIInviteConfig
for supporting the email autocompletion.
// before
<ShareDialog
inviteConfig={{
initialPermissionLevel: 'readWrite',
permissionLevels,
canEditInvitePermission: true,
canInvite: editable,
...inviteConfig,
inviteMessage,
onInvite: handleInvite,
}}
{...otherProps}
/>
// after
<ShareDialog
inviteConfig={{
availableUsers: orgUsers,
loading: isLoading,
initialPermissionLevel: 'readWrite',
permissionLevels,
canEditInvitePermission: true,
canInvite: editable,
...inviteConfig,
inviteMessage,
onInvite: handleInvite,
}}
{...otherProps}
/>
In order to reduce the amount of peerDependencies required to use NexusUI components, we have split our components package into two groups: core and extended. The core components only require the minimum number of peerDependencies. You will only need to install additional dependencies if you are using one of the extended components that rely on them.
Because of the split, import paths have changes slightly.
For core components using full path imports:
// before
import { ComponentName } from '@nexusui/components/ComponentName';
// after
import { ComponentName } from '@nexusui/components/core/ComponentName';
For extended components (root imports):
// before
import { AssemblyTree } from '@nexusui/components'; // or
import { AssemblyTree } from '@nexusui/components/AssemblyTree';
// after
import { AssemblyTree } from '@nexusui/components/extended/AssemblyTree';
// before
import { FileManagement } from '@nexusui/components'; // or
import { FileManagement } from '@nexusui/components/FileManagement';
// after
import { FileManagement } from '@nexusui/components/extended/FileManagement';
The core components no longer have a peerDependency on MUI X packages, the MUI Lab, or the NexusUI theme package. Unless you are using the AssemblyTree or FileManagement components, you only need the set of core peerDependencies mentioned in the installation section of the Readme.
- peerDependency on @mui/material has been updated to v6. Follow the Material-ui Migration from MUI to update your code as need.
- peerDependency on @mui/icons-material has been updated to v6.
- peerDependency on @mui/x-data-grid has been updated to v7. Follow the Data Grid Migration from MUI to update your code if you are using any
DataGrid
components. - peerDependency on @mui/x-tree-view has been updated to v7. Follow the Tree View Migration from MUI to update your code if you are using any
TreeView
components.
- The
nodeId
field indata -> sectionData
has been renamed toitemId
. - The following props has been renamed to new props. Read the Tree View Migration for more details.
Old name | New name |
---|---|
onNodeSelect | onSelectedItemsChange |
selected | selectedItems |
defaultSelected | defaultSelectedItems |
onNodeToggle | onExpandedItemsChange |
expanded | expandedItems |
defaultExpanded | defaultExpandedItems |
onNodeFocus | onItemFocus |
// before
<AssemblyTree
data={
[
{
sectionLabel: 'SECTION #1',
sectionData: [
{
label: 'Item 1',
nodeId: `A1`,
},
{
label: 'Item 2',
nodeId: `A3`,
children: [{ label: 'Item 2-1', nodeId: `A4` }],
}
]
}
]
}
onNodeSelect={() => {}}
selected={['a', 'b']}
defaultSelected={['a']}
onNodeToggle={() => {}}
expanded={['a, b']}
defaultExpanded={['a']}
onNodeFocus={() => {}}
/>
// after
<AssemblyTree
data={
[
{
sectionLabel: 'SECTION #1',
sectionData: [
{
label: 'Item 1',
itemId: `A1`,
},
{
label: 'Item 2',
itemId: `A3`,
children: [{ label: 'Item 2-1', itemId: `A4` }],
}
]
}
]
}
onSelectedItemsChange={() => {}}
selectedItems={['a', 'b']}
defaultSelectedItems={['a']}
onExpandedItemsChange={() => {}}
expandedItems={['a, b']}
defaultExpandedItems={['a']}
onItemFocus={() => {}}
/>
- The
LegalMenu
component is built intoAccountDropdown
and is controlled by thelegalItems
props now. - The menu item icons are changed to use grey outline icons.
// before
<AccountDropdown
items={[
{
'data-testid': 'NexusAccountDropdown-appSettings',
name: 'Settings',
icon: <Settings color={'primary'} />,
onClick: () => {},
},
<LegalMenu key="legacyMenu" items={legalItems} onMenuItemClick={() => {}} />,
]}
/>
// after
<AccountDropdown
items={[
{
'data-testid': 'NexusAccountDropdown-appSettings',
name: 'Settings',
icon: <SettingsOutlined
sx={(th) => (
{
color: 'grey.600',
...th.applyStyles("dark", { color: 'grey.400' } )
}
)}
/>,
onClick: () => {},
}
]}
legalItems={legalItems}
/>
- The
primary
color variant has been removed, use theinfo
color instead.
// before
<StatusBadge label={'Text'} color="primary" />
// after
<StatusBadge label={'Text'} color="info" />
-
@mui/lab peerDependency has been removed and replaced by @mui/x-tree-view following the
TreeView
component promotion out of the lab. - peerDependency on @nexusui/theme has been updated to version 4.0.0 or higher.
The AccountDropdown
component has been refactored to be easier to use through composition, rather than providing excessive configuration props.
All of the items you want to appear in the menu are now passed through a single items
prop (items will be presented in the same order they appear in the provided array):
// before
<AccountDropdown
supportItems={[]}
userItems={[]}
footerItems={[]}
/>
// after
<AccountDropdown items={[]} />
The items
prop also supports JSX Element contents. This can be used to show things like a OrganizationSelector
or LegalMenu
submenus. You can also provide a Divider
to group menu items into sections.
The OrganizationSelector
and LegalMenu
submenus are now provided as separate components that can be included via composition.
// before
<AccountDropdown
orgItems={[]}
footerItems={[]}
/>
// after
<AccountDropdown
items={[
<OrganizationSelector {...props} />,
<LegalMenu {...props} />
]}
/>
-
onMenuClick
renamed toonMenuItemClick
- menu header can be made clickable now via
onUserClick
prop - removed support for inline actions on Organization Items
Consider using the ConnectedAccountDropdown
component to automatically configure the recommended menu items for you. Refer to the documentation for more usage details and examples.
Version 4.0.0 includes several breaking changes. If you plan to upgrade to 4.0.0, please follow the upgrade guide below carefully.
In version 4, some behaviors of the Drawer
component have changed slightly:
- new
expanded
prop to allow for controlled management of the expanded/collapsed nodes - new
onNavItemExpanded
callback prop that is called whenever a node is expanded / collapsed -
onNavItemClick
(specified at the Drawer-level) is now called whenever ANY drawer item is clicked (previously, elements that had children did not trigger this event) - Drawer items that have children AND a click event now have two separate click areas: clicking the expand icon will expand/collapse the item, clicking the item body will trigger the click handler
// controlled use of the Drawer expanded items
const [expanded, setExpanded] = useState<string[]>([]);
return (
<Drawer
expanded={expanded}
onNavItemExpanded={
(expandedNodes) => {
setExpanded(expandedNodes)
}
}
/>
)
The AddCard
has been renamed to EmptyCard
, including in type definitions. The API for this component is unchanged.
// before
import { AddCard } from '@nexusui/components';
const props: IAddCard = {};
<AddCard {...props} />
// after
import { EmptyCard } from '@nexusui/components';
const props: IEmptyCard = {};
<EmptyCard {...props} />
The GridCard
has been renamed to ComplexCard
, including in type definitions and internal class names.
// before
import { GridCard } from '@nexusui/components';
const props: IGridCard = {};
<GridCard {...props} sx={{'& .NexusGridCard-menuButton': {}}}/>
// after
import { ComplexCard } from '@nexusui/components';
const props: IComplexCard = {};
<ComplexCard {...props} sx={{'& .NexusComplexCard-menuButton': {}}}/>
To simplify usage and better align with components in Figma, the CommonCard
component has been removed and replaced with three new components: PlainCard
, ImageCard
, and LinkCard
.
// Before
<CommonCard cardType={'academy'} image={image} actionProps={actionProps} status={status} {...otherProps} />
// After
<ImageCard dense image={image || ''} actionButtons={[actionProps]} statusBadges={status} {...otherProps} />
// Before
<CommonCard cardType={'collection'} image={image} {...otherProps} />
// After
<ImageCard dense image={image || ''} {...otherProps} />
// Before
<CommonCard cardType={'news'} actionProps={actionProps} headline={headline} content={content} {...otherProps} />
// After
<LinkCard dense actionButtons={[actionProps]} label={headline} title={content} {...otherProps} />
// Before
<CommonCard cardType={'support'} actionProps={actionProps} {...otherProps} />
// After
<PlainCard dense actionButtons={actionProps ? [actionProps] : undefined} {...otherProps} />
// Before
<CommonCard cardType={'teaser'} actionProps={actionProps} {...otherProps} />
// After
<PlainCard actionButtons={[actionProps]} {...otherProps} />
The default card rendered in the CarouselCard
has been updated to align with the new ImageCard
component type. When using the items
prop, the title
key has changed to headline
and the description
key has changed to content
:
// before
import { CarouselCard } from '@nexusui/components';
<CarouselCard {...props}
items={[
{...oldItem1Props},
{...oldItem2Props},
<CustomCardComponent />
]}
/>
// after
import { CarouselCard } from '@nexusui/components';
<CarouselCard {...props}
items={[
{...oldItem1Props, headline: oldItem1Props.title, content: oldItem1Props.description},
{...oldItem2Props, headline: oldItem2Props.title, content: oldItem2Props.description},
<CustomCardComponent />
]}
/>
The CarouselCard
will support the use of the new Nexus UI Card components as well.
import { CarouselCard, PlainCard, ImageCard } from '@nexusui/components';
<CarouselCard
items={[
{...carouselCardProps},
<PlainCard {...plainCardProps}/>,
<ImageCard {...imageCardProps} />,
...otherCards
]}
/>
The OrgManagement
, UserManagement
, UserPreferences
and UserDetails
components have been removed. With the introduction of Account Management, there is no longer a need to implement account management within your own projects. You can facilitate navigation to Account Management directly from the account dropdown menu.
Version 3.0.0 includes several breaking changes. If you plan to upgrade to 3.0.0, please follow the upgrade guide below carefully.
A few peer dependency versions have been updated so we can take advantage of new features in these libraries. You'll need to update your dependencies to the following minimum versions:
- @nexusui/theme version 3.0.0 or higher.
-
@mui/x-data-grid version 6.5.0 or higher (this affects projects using
UserManagement
andOrgManagement
components).- upgrading from version 5 requires additional code changes in your application. Follow the Data Grid Migration from MUI to update your code if you are using any
DataGrid
components.
- upgrading from version 5 requires additional code changes in your application. Follow the Data Grid Migration from MUI to update your code if you are using any
// before
import { FormInputText, FormInputTextProps } from '@nexusui/components';
// after
import { FormInput, FormInputProps } from '@nexusui/components';
Additionally, the FormInputProps
now inherit from TextFieldProps
, resulting in the following type changes:
label
field was required before and is now optionalname
field was not present before and is now requiredcontrollerProps
field was required before and is now optional
// before
<FormInputText label={'label'} controllerProps={{ name: 'test' }}/>
// after
<FormInput name="test" label={'label'} />
// before
import { FormInputRadio, FormInputRadioProps } from '@nexusui/components';
// after
import { FormRadio, FormRadioProps } from '@nexusui/components';
// before
import { FormInputToggle, FormInputToggleProps } from '@nexusui/components';
// after
import { FormToggle, FormToggleProps } from '@nexusui/components';
Additionally, the following type changes apply to the FormToggleProps
prop:
name
field was not present before and is now requiredcontrollerProps
field was required before and is now optional
// before
<FormInputToggle controllerProps={{ name: 'test' }}/>
// after
<FormToggle name="test" />
This selected state of this component has changed from internally managed to user-controlled. We have added a prop selected
to control its selected state.
// before, the selection state is controlled inside this component, users don't and cannot control its selection state.
<GridCard {...otherProps}>{...children}</GridCard>
// after, users must control its selection state by the new 'selected' prop, and this prop is required.
<GridCard selected {...otherProps}>{...children}</GridCard>
This component has been updated to be a controlled component to allow for greater control over behavior and integration with 3rd-party form libraries. If you want to continue to have the component manage state internally, we have created a new SimpleUnitFormatter
with the legacy behavior.
// before
<UnitFormatter {...props} />
// after
<SimpleUnitFormatter {...props} />
In order to facilitate using this component in NextJs applications, we removed the automatic import of PrismJS css files. If you are using the CodeSnipper
component, you must now manually import these stylesheets in your root TS/JS file.
// before
<CodeSnippet previewCode={preview} code={code} />
// after
import 'prismjs/themes/prism-okaidia.css';
<CodeSnippet previewCode={preview} code={code} />
// after with show line numbers
import 'prismjs/themes/prism-okaidia.css';
import 'prismjs/plugins/line-numbers/prism-line-numbers.css';
<CodeSnippet previewCode={preview} code={code} showLineNumbers/>
You can now show replies to comments in the drawer using the replies
field in comments prop.
const comments = [{
{...otherProps}
replies: [
{
id: '3',
author: { id: '3', firstName: 'Arthur', lastName: 'Lutz', email: '', avatar: '' },
actions: [{id: 'edit',label: 'Edit',onClick: () => {}},
{id: 'delete',label: 'Delete',onClick: () => {}}],
dateModified: new Date(Date.now() - 3000),
message: 'Pellentesque ac risus non tortor convallis gravida in eget est. Aenean sed augue ullamcorper, mollis risus eu, venenatis lorem. Fusce pharetra dui ac massa commodo dapibus.',
},
]
}
]
In version 2, this component managed sorting and filtering internally. For greater flexibility in your application, these behaviors must now be controlled by the application code. To support this, we have introduced new props for onFilterChanged
and onSortChanged
. You can control which comment is active/selected using the new selectedCommentId
prop. Refer to the component readme for full API details and usage examples.
The loading
prop has been deprecated, and will no longer have any effect. You should instead use the new status
prop to control the file status.
// before
<FileItem loading={false} {..otherProps} />
// after
// `Uploading` status
import { UploadStatus } from '@nexusui/components';
<FileItem status={UploadStatus.Uploading} progress={10} {..otherProps} />
// `Succeeded` status
<FileItem {..otherProps} />
// `Failed` status
<FileItem status={UploadStatus.Failed} errorMessage={'Upload failed'} onRetry={() => {}} {..otherProps} />
react-swipeable-views
is no longer being actively maintained, so we have rewritten the ImageCarousel
component to use react-slick
instead.
To update to using the new component, you must make the following changes:
- Import the
react-slick
css styles in your root TS/JS file as follows:
import 'slick-carousel/slick/slick.css';
import 'slick-carousel/slick/slick-theme.css';
// ...rest of your code
- Replace autoplay HOC from
react-swipeable-views
with new propsautoplay
andautoplaySpeed
// before
import { ImageCarousel, autoPlay, SwipeableViews } from '@nexusui/components';
export const ImageCarousel = () => {
return (<ImageCarousel images={[/*images*/]} SwipeableViews={autoPlay(SwipeableViews)} swipeableViewsProps={{ enableMouseEvents: true, interval: 3000 }}/>);
};
// after
import { ImageCarousel } from '@nexusui/components';
export const ImageCarousel = () => {
return (<ImageCarousel images={[/*images*/]} autoplay={true} autoplaySpeed={3000} />);
};
-
Some higher-order component (HOC) functionality from react-swipeable-views is not supported in the new implementation. If you are using the following HOCs, you will need to remove them:
bindKeyboard
virtualize
-
Some prop names in
swipeableViewsProps
have changed:-
enableMouseEvents
=>swipe
-
axis
=>vertical
-
onChangeIndex
=>afterChange
-
slideRenderer
=>children
-
// before
<ImageCarousel images={[ /*Images*/]}
swipeableViewsProps={{
enableMouseEvents: true,
axis: "x",
onChangeIndex: (index: number) => {
console.log("onChangeIndex: ", index);
},
slideRenderer: (params: any) => {
return (<Box><img src={params.src} alt={params.alt} /></Box>);
},
}}
/>
// after
<ImageCarousel
images={[ /*Images*/ ]}
swipeableViewsProps={{
swipe: true,
vertical: false,
afterChange: (index: number) => {
console.log("afterChange: ", index);
}
// ...other props
}}
>
{
[/*Images*/].map((image, index) => {
return (<img src={image.src} alt={image.alt} key={index} />)
})
}
</ImageCarousel>
Version 2.0.0 includes several breaking changes. If you plan to upgrade to 2.0.0, please read the upgrade guide below carefully.
If you are currently using this component in your project, you can take the source code from the older version of the library and include it as a local component in your application.
This prop was deprecated in the latest 1.x version and has now been removed entirely.
// before
import { HexSearchComponent } from '@nexusui/components';
// after
import { SearchBar } from '@nexusui/components';
The withDebounce
prop has also been renamed to debounce
and now additionally accepts numeric values to customize the debounce delay.
// before
<HexSearchComponent withDebounce={true} {...others}/>
// after
<SearchBar debounce={500} {...others}/> // 500ms is the default
Several components in this library rely on a User object. There were some inconsistencies between components and how they defined these types. Version 2 updates these components to have a shared type definition for basic user information.
export type IBasicUser = {
id: string;
email: string;
firstName: string;
lastName: string;
avatar: string;
};
The following components are affected by this change:
AccountDropdown
, AudienceGroup
, CommentCard
, CommentDrawer
, CommentThread
, UserManagement
, UserDetail
, and UserPreference
.
AccountDropdown
In the userInfo
props:
-
name
has been split intofirstName
andlastName
-
avatarSrc
is renamed toavatar
-
email
was not present before and is now required
Examples:
// before
const userInfo: IUserInfo = { name: `Jon Snow`, avatarSrc: 'https://i2.wp.com/cdn.auth0.com/avatars/js.png' };
<AccountDropdown userInfo={userInfo} {...others}/>
// after
const userInfo: IUserInfo = { id: '1', firstName: 'Jon', lastName: 'Snow', avatar: 'https://i2.wp.com/cdn.auth0.com/avatars/js.png', email: 'jon.snow@hexagon.com' };
<AccountDropdown userInfo={userInfo} {...others}/>
AudienceGroup
In the users
prop:
-
userId
is renamed toid
. -
avatar
andemail
were previously optional and are now required
Examples:
// before
const users: IUserInfo[] = [{userId: '1', firstName: 'Jon', lastName: 'Snow'}];
<AudienceGroup users={users} {...others}/>
// after
const users: IUserInfo[] = [{id: '1', firstName: 'Jon', lastName: 'Snow', avatar: 'https://i2.wp.com/cdn.auth0.com/avatars/js.png', email: 'jon.snow@hexagon.com'}];
<AudienceGroup users={users} {...others}/>
CommentCard
In the author
props:
-
email
field was not present before and is now required -
avatar
field was previously optional and is now required
Examples:
// before
const author: ICommentAuthor = {id: '1', firstName: 'Jon', lastName: 'Snow'};
<CommentCard author={author} {...others}/>
// after
const author: ICommentAuthor = {id: '1', firstName: 'Jon', lastName: 'Snow', avatar: 'https://i2.wp.com/cdn.auth0.com/avatars/js.png', email: 'jon.snow@hexagon.com'};
<CommentCard author={author} {...others}/>
CommentDrawer
In the comments
-> author
& participants
props:
-
email
field was not present before and is now required -
avatar
field was previously optional and is now required
Examples:
// before
const author: ICommentAuthor = {id: '1', firstName: 'Jon', lastName: 'Snow'};
const participants: ICommentAuthor[] = [author];
const comments: IComment[] = [{author, participants, ...{others}}]
<CommentDrawer comments={comments} {...others}/>
// after
const author: ICommentAuthor = {id: '1', firstName: 'Jon', lastName: 'Snow', avatar: 'https://i2.wp.com/cdn.auth0.com/avatars/js.png', email: 'jon.snow@hexagon.com'}
const participants: ICommentAuthor[] = [author];
const comments: IComment[] = [{author, participants, ...{others}}]
<CommentDrawer comments={comments} {...others}/>
CommentThread
In the currentUser
prop:
-
email
field was not present before and is now required -
avatar
field was previously optional and is now required
In the comment
-> author
& participants
props:
-
email
field was not present before and is now required -
avatar
field was previously optional and is now required
In the replies
-> author
prop:
-
email
field was not present before and is now required -
avatar
field was previously optional and is now required
Examples:
// before
const user: ICommentAuthor = {id: '1', firstName: 'Jon', lastName: 'Snow'};
const participants: ICommentAuthor[] = [user];
const comment: IComment = {author: user, participants, ...{others}}
<CommentThread currentUser={currentUser} comment={comment} replies={[{author: user}]} {...others}/>
// after
const user: ICommentAuthor = {id: '1', firstName: 'Jon', lastName: 'Snow', avatar: 'https://i2.wp.com/cdn.auth0.com/avatars/js.png', email: 'jon.snow@hexagon.com'}
const participants: ICommentAuthor[] = [user];
const comment: IComment = {author: user, participants, ...{others}}
<CommentThread currentUser={currentUser} comment={comment} replies={[{author: user}]} {...others}/>
UserManagement
In the users
prop:
-
avatarSrc
field renamed toavatar
and is now required instead of optional
Examples:
// before
const users: UserItem[] = [{id: '1', firstName: 'Jon', lastName: 'Snow', email: 'jon.snow@hexagon.com', avatarSrc: 'https://i2.wp.com/cdn.auth0.com/avatars/js.png', role: '', status: ''}]
<UserManagement users={users} {...others}/>
// after
const users: UserItem[] = [{id: '1', firstName: 'Jon', lastName: 'Snow', email: 'jon.snow@hexagon.com', avatar: 'https://i2.wp.com/cdn.auth0.com/avatars/js.png', role: '', status: ''}]
<UserManagement users={users} {...others}/>
UserDetail
In the userInfo
prop:
-
avatarSrc
field renamed toavatar
and is now required instead of optional
Examples:
// before
const userInfo: UserItem = {id: '1', firstName: 'Jon', lastName: 'Snow', email: 'jon.snow@hexagon.com', avatarSrc: 'https://i2.wp.com/cdn.auth0.com/avatars/js.png', role: '', status: ''}
<UserDetail userInfo={userInfo} {...others}/>
// after
const userInfo: UserItem = {id: '1', firstName: 'Jon', lastName: 'Snow', email: 'jon.snow@hexagon.com', avatar: 'https://i2.wp.com/cdn.auth0.com/avatars/js.png', role: '', status: ''}
<UserDetail userInfo={userInfo} {...others}/>
UserPreference
In the userInfo
-> contact
prop:
-
id
field was not present before and is now required - redundant
name
field has been removed (usefirstName
andlastName
)
Examples:
// before
const contact: ContactInfo = {firstName: 'Jon', lastName: 'Snow', name: 'Jon Snow', email: 'jon.snow@hexagon.com', avatar: 'https://i2.wp.com/cdn.auth0.com/avatars/js.png', role: '', mobile: ''}
const userInfo = {contact, ...others}
<UserPreference userInfo={userInfo} {...others}/>
// after
const contact: ContactInfo = {id: '1', firstName: 'Jon', lastName: 'Snow', email: 'jon.snow@hexagon.com', avatar: 'https://i2.wp.com/cdn.auth0.com/avatars/js.png', role: '', mobile: ''}
const userInfo = {contact, ...others}
<UserPreference userInfo={userInfo} {...others}/>