跳到主要内容

RoomIcon

Square room tile that shows the room's logo, or its initials on a colour when there is none. It is the thing at the left of a room row and at the top of a room tile: a logo image, an inlined cover glyph, or two letters cut from the room's name, with an optional corner badge and an optional pencil that opens the logo menu.

RoomIconRoomIcon

Use this when / not when​

  • Use wherever a specific room is shown by name — a row, a tile, a breadcrumb, a dialog header.
  • Not for the icon that says what kind of room it is — that is RoomLogo, which picks a fixed glyph from RoomsType and knows nothing about a logo of the room's own.
  • Not for a person — use Avatar, which has the role ring, the status dot and the person-shaped placeholder.
  • Not for a count or a marker on its own — that is Badge. The badge here is a decoration on the tile, not a counter.

It is a square with no built-in spacing and no label. The room's name is drawn only as two initials, and only when there is no logo; the readable name next to the tile is yours.

Import​

import { RoomIcon } from "@onlyoffice/apps-ui-kit/components/room-icon";

Also exported from the root barrel @onlyoffice/apps-ui-kit.

Needs ThemeProvider above it in the tree, and unusually it reads the theme in JavaScript as well as in CSS: the colour of the initials is computed from isBase. Without a provider the context falls back to the light theme, so the tile keeps light-theme initials on a dark page — this is not something a stylesheet override can correct.

Stories​

Default​

A room with no logo is shown by its initials on its colour. Change the name, the colour or the size live in the Controls panel below.

DefaultDefault

Sizes​

The same tile at 32px, 48px and 96px (size). Any px value works; the initials stay 14px at every size, so a large tile needs its own text style for them.

SizesSizes

Colors​

Each room gets its own colour (color, six hex digits without #), and the initials turn white or black to stay readable on it.

ColorsColors

With Editing​

Lets the reader change the logo: click the pencil in the corner, or anywhere on the tile, to open the logo menu (withEditing, model); picking an entry shows up in the Actions panel.

With EditingWith Editing

Empty State​

For a room that has no logo yet: a dashed frame with a camera glyph, and a plus button in the bottom-right corner that opens the logo menu (isEmptyIcon, model). The button takes its background from the host's accent colour, which Storybook does not define, so here only a click on that corner finds it.

Empty StateEmpty State

Archive​

An archived room is greyed out whatever its colour: this tile is given the same blue as the others (isArchive).

ArchiveArchive

With Badge​

Marks something about the room with a glyph in the bottom corner (badgeUrl); clicking it calls onBadgeClick, shown in the Actions panel.

With BadgeWith Badge

With Tooltip​

Explains the badge in words: hover it to read the text (tooltipContent), which the badge finds through tooltipId.

With TooltipWith Tooltip

Template​

Tells a template apart from a room: an outline in the tile colour instead of a filled square, with the initials inside (isTemplate).

TemplateTemplate

With Hover​

Hints that the tile can be clicked: hover it, and the initials slide away while a second image fades in (hoverSrc); a click opens the logo menu (model).

With HoverWith Hover

Long Title​

However long the name, the tile shows two letters: the first of its first word and the first of its last (title).

Long TitleLong Title

A room with a logo of its own shows it instead of the initials (logo):

  • Image — a URL, drawn as it is
  • Cover — an object with a cover SVG, inlined and painted in the initials' colour on the tile
  • Broken URL — the image fails to load, so the tile falls back to the initials
With LogoWith Logo

Right To Left​

The tile under a right-to-left interface: the pencil button and the badge move to the bottom-left corner. The direction comes from the theme's interfaceDirection (the Direction toolbar); the wrapper also carries dir="rtl" for the rules that read the DOM direction.

Right To LeftRight To Left

Css Customization​

Every overridable variable set on one wrapper -- the variables are listed under CSS variables on this page. It covers two instances:

  • Design review — an editable tile, for --room-icon-bg-opacity on the tile and --room-icon-edit-bg on the pencil
  • Empty frame — for --room-icon-bg on the plus glyph, --room-icon-button-icon-color on the camera, and the frame's --room-icon-dashed-border and --room-icon-empty-radius
Css CustomizationCss Customization

Minimal example​

import { RoomIcon } from "@onlyoffice/apps-ui-kit/components/room-icon";

export function RoomRow({ name }: { name: string }) {
return (
<div style={{ display: "flex", alignItems: "center", gap: 12 }}>
<RoomIcon title={name} color="4781D1" showDefault />
<span>{name}</span>
</div>
);
}

Props​

The props type is an intersection of two unions. The first says a tile is either coloured (color required, imgClassName refused) or image-backed (imgClassName allowed, color optional); the second makes the four badge props all-or-nothing. The table below merges them.

PropertyTypeDescription
titlestringRoom name. Only its initials are drawn — the first letter of the first word and of the last.
badgeIconColor?stringKeeps the glyph's own colours instead of filling it with the tile's background colour.
badgeIconNode?ReactNodeBadge glyph as a node, used instead of badgeUrl.
badgeUrl?stringURL of the badge glyph, drawn in the corner. Either this or badgeIconNode renders the badge; withEditing suppresses it.
className?stringAdded to the outer element, before the component's own classes.
color?stringBackground of the tile and the source of the initials' colour, as six hex digits without a leading #. Required unless you pass imgClassName instead.
dataTestId?stringValue of data-testid on the outer element. Default: "room-icon".
hoverSrc?stringImage faded in over the tile while the pointer is on it. Ignored while isArchive is set.
imgClassName?stringAdded to the <img> the logo renders into. The type refuses it alongside a required color: a coloured default or an image, not both.
isArchive?booleanPaints the tile in the archive grey instead of color, and turns off the hover overlay. Default: false.
isEmptyIcon?booleanDraws a dashed empty frame with a camera glyph and a plus button, for a room that has no logo yet. It replaces every other content, including the initials.
isTemplate?booleanDraws the template outline instead of the round tile, with the logo shrunk to 24px inside it. Default: false.
model?TModel[]Entries of the logo menu. Without it the menu opens empty.
onBadgeClick?() => voidCalled when the badge is clicked. The click also reaches the tile, which toggles the logo menu.
onChangeFile?(e: React.ChangeEvent<HTMLInputElement>) => voidCalled with the change event of the hidden file input. Passing it is what renders that input at all.
radius?stringCorner radius of the tile and of the image inside it. Default: "6px".
showDefault?booleanDraws the initials instead of the logo, whatever logo holds.
size?stringSide of the square, as a px string. It is also divided to scale the cover glyph, so a value in any other unit gives NaN. Default: "32px".
tooltipContent?stringText of the badge's tooltip. It also needs tooltipId, and it is what makes the badge show a pointer cursor.
tooltipId?stringId the badge's tooltip is registered under. Without it the tooltip has nothing to attach to.
withEditing?booleanAdds the pencil button and the menu it opens. It also makes the tile 64px wide at the least.

Recipes​

A room with a logo, falling back to initials​

The logo is prefetched into an Image on mount, and a load failure switches the tile to the initials on its own. You do not need to test the URL first.

import { RoomIcon } from "@onlyoffice/apps-ui-kit/components/room-icon";

export function RoomAvatar({
name,
logoUrl,
}: {
name: string;
logoUrl?: string;
}) {
return <RoomIcon title={name} color="4781D1" logo={logoUrl} size="48px" />;
}

The editable tile​

withEditing adds the pencil and the menu; model fills that menu; onChangeFile is what renders the hidden file input. Only the entry whose key is ROOM_ACTION_KEYS.CREATE_EDIT_ROOM_UPLOAD ("create_edit_room_upload", exported from the root barrel) is handed that input's ref; every other entry's onClick is called with no argument.

import { RoomIcon } from "@onlyoffice/apps-ui-kit/components/room-icon";
import type { TModel } from "@onlyoffice/apps-ui-kit/components/room-icon";

export function LogoPicker({
name,
onUpload,
onRemove,
}: {
name: string;
onUpload: (ref?: React.RefObject<HTMLInputElement | null>) => void;
onRemove: () => void;
}) {
const model: TModel[] = [
{
key: "create_edit_room_upload",
label: "Upload",
icon: "",
onClick: onUpload,
},
{ key: "remove", label: "Remove", icon: "", onClick: onRemove },
];

return (
<RoomIcon
title={name}
color="F97A0B"
size="96px"
withEditing
model={model}
onChangeFile={(e) => console.log(e.target.files)}
/>
);
}

A tile with a corner badge​

The badge renders when badgeUrl or badgeIconNode is set and withEditing is not — the two share the corner and editing wins.

import { RoomIcon } from "@onlyoffice/apps-ui-kit/components/room-icon";

export function PublicRoomIcon({ name }: { name: string }) {
return (
<RoomIcon
title={name}
color="2DB482"
size="48px"
showDefault
badgeIconNode={
<svg viewBox="0 0 12 12" aria-hidden="true">
<circle cx="6" cy="6" r="5" />
</svg>
}
onBadgeClick={() => console.log("badge")}
/>
);
}

Behaviour the types don't state​

  • color is six hex digits with no #. The component prepends it. Passing #4781D1 yields ##4781D1 and no background at all.
  • size must be a px string. It is parsed with parseFloat after stripping "px" to scale the cover glyph, so size="3rem" gives a NaN scale and the glyph disappears; the width and height themselves would still be honoured.
  • A click anywhere on the tile toggles the logo menu, not just on the pencil. The handler is on the outer element unconditionally, so a tile inside a clickable row opens the menu on every row click when withEditing, isEmptyIcon or hoverSrc is set — and silently flips an internal boolean the rest of the time. Clicking the badge does the same, because the badge's click bubbles.
  • The initials are the first letter of the first word and the first letter of the last, taken after -_[]{}()*+!?.,&\^$|#@% are stripped and runs of whitespace collapsed, then uppercased. A one-word name therefore gives one letter, and a name made only of punctuation gives none.
  • The "wrong image" state can never be reached. Its condition requires imgSrc to be something other than a string, and the only values imgSrc ever takes are a string or undefined — so the wrongImage class is dead and a failed load falls back to the plain initials.
  • The badge is painted outside the square. It is absolutely positioned and pushed 24px (80px at size="96px") from the centre, so it overflows the tile's own box; a container with overflow: hidden clips it.
  • The file input carries the literal id customFileInput. Two editable tiles on one page produce two elements with the same id.
  • logo with a cover is turned into a data:image/svg+xml;base64 URL and rendered through react-svg, which means the glyph's path fill is recoloured from the theme. A plain string, or an object with only medium, is used as an ordinary image URL and is not recoloured.
  • isTemplate, isEmptyIcon and showDefault are checked in that order and each replaces everything below it, so a tile that is both a template and empty renders the template.
  • The initials' colour depends on the theme. In the light theme they are black on a color whose brightness is above 202 (of 255) and white on anything darker; in the dark theme they are drawn in color itself, on the same colour at 0.1 opacity. An inlined cover glyph is painted the same way. An archived tile in the dark theme keeps the black-or-white rule.
  • hoverSrc animates the tile. On hover the initials or the cover glyph slide up and fade out, the background darkens to 80% brightness and the hover image fades in over it.
  • isArchive removes the pencil. An archived tile with withEditing keeps the 64px minimum width and the pointer cursor, but draws neither the pencil nor the menu behind it.
  • No logo means the initials, on color. showDefault is for forcing them while a logo exists; it is not needed to get them.

CSS variables​

All are read with a fallback, so setting any of them on an ancestor works.

VariableDefaultEffect
--room-icon-bgwhite, black in darkFill of the badge glyph and of the plus button
--room-icon-bg-opacity1, 0.1 in darkOpacity of the coloured background
--room-icon-button-icon-colorgreyFill of the empty-state camera and template glyph
--room-icon-edit-bglight grey, dark grey in darkBackground of the pencil button
--room-icon-dashed-border2px dashed greyBorder of the empty state
--room-icon-empty-radius10pxCorner radius of the empty state

--room-icon-bg fills the badge glyph only when badgeIconColor is unset; a badge that keeps its own colours ignores it. --room-icon-button-icon-color paints the template outline only when the tile has no color.

The tile's own size, radius and colour are written as inline custom properties from the props and cannot be overridden from a stylesheet.

Accessibility​

  • The outer element is a <div> with no role and no ARIA, and it carries a click handler. It is not reachable by keyboard; the room's name has to be a real link or button next to it.
  • The initials are rendered as text, so a screen reader reads out "TR" rather than the room name. Treat the tile as decorative and label the row.
  • Both images use fixed English alt text — "room icon" and "room icon hover" — which is not translated and cannot be changed through a prop.
  • The pencil, the plus and the badge are IconButtons and are focusable, but the menu they open is a DropDown that closes on an outside click and offers no roving focus of its own.

Test ids​

Elementdata-testid
Outer elementroom-icon, overridden by dataTestId
Initialsroom-title
Inlined cover glyphroom-icon-cover
Camera glyph, empty stateempty-icon
Hover overlayhover-container, hover-image
Badge wrapperbadge-container
Hidden file inputcustomFileInput

The outer element also carries data-is-archive, data-has-editing, data-is-template and data-is-empty.

  • RoomLogo — the glyph for a room type, with no per-room logo.
  • Avatar — the same job for a person, with the status and role rings.
  • Badge — a standalone counter or marker.