Move ReactionRow To Shared Components MVVM (#32634)
* Init shared component structure * Storybook implementation * Add snapshots of storybook examples * ViewModel Creation + Implementation In EventTile.tsx * Prettier * Update HTML snapshot * Add onhover pointer on bottons * Added compound web tooltip * Removed possible of undefined on label * Update snapshots * Update setters to use merge instead of updating full snapshot * adapt view model test for setters change * Actions should be passed to viewmodel fix * replace ReactionsRowWrapper forceRender with explicit reaction state * Update snapshot
This commit is contained in:
@@ -21,6 +21,7 @@ export * from "./message-body/MessageTimestampView";
|
||||
export * from "./message-body/DecryptionFailureBodyView";
|
||||
export * from "./message-body/ReactionsRowButtonTooltip";
|
||||
export * from "./message-body/ReactionsRowButton";
|
||||
export * from "./message-body/ReactionRow";
|
||||
export * from "./message-body/TimelineSeparator/";
|
||||
export * from "./pill-input/Pill";
|
||||
export * from "./pill-input/PillInput";
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
.reactionsRow {
|
||||
color: var(--cpd-color-text-primary);
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
gap: var(--cpd-space-1x);
|
||||
}
|
||||
|
||||
.showAllButton {
|
||||
all: unset;
|
||||
color: var(--cpd-color-text-secondary);
|
||||
font-size: var(--cpd-font-size-body-xs);
|
||||
line-height: 20px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.showAllButton:hover,
|
||||
.showAllButton:focus-visible {
|
||||
color: var(--cpd-color-text-primary);
|
||||
}
|
||||
|
||||
.addReactionButton {
|
||||
all: unset;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
padding: var(--cpd-space-1x);
|
||||
margin-inline: var(--cpd-space-1x);
|
||||
border-radius: var(--cpd-space-1x);
|
||||
color: var(--cpd-color-icon-secondary);
|
||||
visibility: hidden;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.reactionsRow:hover .addReactionButton {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.addReactionButtonVisible {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.addReactionButtonActive {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
.addReactionButtonDisabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.addReactionButton:hover,
|
||||
.addReactionButton:focus-visible,
|
||||
.addReactionButtonActive {
|
||||
color: var(--cpd-color-icon-primary);
|
||||
}
|
||||
|
||||
.addReactionButton svg {
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type JSX } from "react";
|
||||
import { fn } from "storybook/test";
|
||||
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { ReactionsRowButtonView } from "../ReactionsRowButton";
|
||||
import { useMockedViewModel } from "../../viewmodel";
|
||||
import { ReactionsRowView, type ReactionsRowViewActions, type ReactionsRowViewSnapshot } from "./ReactionsRowView";
|
||||
|
||||
interface MockReactionButtonProps {
|
||||
content: string;
|
||||
count: number;
|
||||
isSelected?: boolean;
|
||||
}
|
||||
|
||||
const MockReactionButton = ({ content, count, isSelected }: Readonly<MockReactionButtonProps>): JSX.Element => {
|
||||
const tooltipVm = useMockedViewModel(
|
||||
{
|
||||
formattedSenders: "Alice and Bob",
|
||||
caption: undefined,
|
||||
tooltipOpen: false,
|
||||
},
|
||||
{},
|
||||
);
|
||||
|
||||
const vm = useMockedViewModel(
|
||||
{
|
||||
content,
|
||||
count,
|
||||
"isSelected": !!isSelected,
|
||||
"isDisabled": false,
|
||||
tooltipVm,
|
||||
"aria-label": `${count} reactions for ${content}`,
|
||||
},
|
||||
{
|
||||
onClick: fn(),
|
||||
},
|
||||
);
|
||||
|
||||
return <ReactionsRowButtonView vm={vm} />;
|
||||
};
|
||||
|
||||
const DefaultReactionButtons = (): JSX.Element => (
|
||||
<>
|
||||
<MockReactionButton content="👍" count={4} isSelected />
|
||||
<MockReactionButton content="🎉" count={2} />
|
||||
<MockReactionButton content="👀" count={1} />
|
||||
</>
|
||||
);
|
||||
|
||||
type WrapperProps = ReactionsRowViewSnapshot & Partial<ReactionsRowViewActions>;
|
||||
|
||||
const ReactionsRowViewWrapper = ({
|
||||
onShowAllClick,
|
||||
onAddReactionClick,
|
||||
onAddReactionContextMenu,
|
||||
...snapshotProps
|
||||
}: WrapperProps): JSX.Element => {
|
||||
const vm = useMockedViewModel(snapshotProps, {
|
||||
onShowAllClick: onShowAllClick ?? fn(),
|
||||
onAddReactionClick: onAddReactionClick ?? fn(),
|
||||
onAddReactionContextMenu: onAddReactionContextMenu ?? fn(),
|
||||
});
|
||||
|
||||
return <ReactionsRowView vm={vm} />;
|
||||
};
|
||||
|
||||
const meta = {
|
||||
title: "MessageBody/ReactionsRow",
|
||||
component: ReactionsRowViewWrapper,
|
||||
tags: ["autodocs"],
|
||||
args: {
|
||||
ariaLabel: "Reactions",
|
||||
isVisible: true,
|
||||
children: <DefaultReactionButtons />,
|
||||
showAllButtonVisible: false,
|
||||
showAllButtonLabel: "Show all",
|
||||
showAddReactionButton: true,
|
||||
addReactionButtonLabel: "Add reaction",
|
||||
addReactionButtonVisible: true,
|
||||
addReactionButtonActive: false,
|
||||
addReactionButtonDisabled: false,
|
||||
},
|
||||
} satisfies Meta<typeof ReactionsRowViewWrapper>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const Default: Story = {};
|
||||
|
||||
export const WithShowAllButton: Story = {
|
||||
args: {
|
||||
showAllButtonVisible: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const AddReactionButtonActive: Story = {
|
||||
args: {
|
||||
addReactionButtonActive: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const AddReactionButtonHiddenUntilHover: Story = {
|
||||
args: {
|
||||
addReactionButtonVisible: false,
|
||||
},
|
||||
};
|
||||
|
||||
export const Hidden: Story = {
|
||||
args: {
|
||||
isVisible: false,
|
||||
},
|
||||
};
|
||||
@@ -0,0 +1,90 @@
|
||||
/*
|
||||
* Copyright 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import { composeStories } from "@storybook/react-vite";
|
||||
import { render, screen } from "@test-utils";
|
||||
import React, { type MouseEventHandler } from "react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import { MockViewModel } from "../../viewmodel";
|
||||
import {
|
||||
ReactionsRowView,
|
||||
type ReactionsRowViewActions,
|
||||
type ReactionsRowViewModel,
|
||||
type ReactionsRowViewSnapshot,
|
||||
} from "./ReactionsRowView";
|
||||
import * as stories from "./ReactionsRow.stories";
|
||||
|
||||
const { Default, WithShowAllButton, Hidden } = composeStories(stories);
|
||||
|
||||
describe("ReactionsRowView", () => {
|
||||
it("renders the default reactions row", () => {
|
||||
const { container } = render(<Default />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders the row with a show-all button", () => {
|
||||
const { container } = render(<WithShowAllButton />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("does not render the row when hidden", () => {
|
||||
render(<Hidden />);
|
||||
expect(screen.queryByRole("toolbar")).not.toBeInTheDocument();
|
||||
});
|
||||
|
||||
it("invokes show-all and add-reaction actions", async () => {
|
||||
const user = userEvent.setup();
|
||||
|
||||
const onShowAllClick = vi.fn();
|
||||
const onAddReactionClick = vi.fn();
|
||||
const onAddReactionContextMenu = vi.fn();
|
||||
|
||||
class TestReactionsRowViewModel
|
||||
extends MockViewModel<ReactionsRowViewSnapshot>
|
||||
implements ReactionsRowViewActions
|
||||
{
|
||||
public onShowAllClick?: () => void;
|
||||
public onAddReactionClick?: MouseEventHandler<HTMLButtonElement>;
|
||||
public onAddReactionContextMenu?: MouseEventHandler<HTMLButtonElement>;
|
||||
|
||||
public constructor(snapshot: ReactionsRowViewSnapshot, actions: ReactionsRowViewActions) {
|
||||
super(snapshot);
|
||||
Object.assign(this, actions);
|
||||
}
|
||||
}
|
||||
|
||||
const vm = new TestReactionsRowViewModel(
|
||||
{
|
||||
ariaLabel: "Reactions",
|
||||
isVisible: true,
|
||||
showAllButtonVisible: true,
|
||||
showAllButtonLabel: "Show all",
|
||||
showAddReactionButton: true,
|
||||
addReactionButtonLabel: "Add reaction",
|
||||
addReactionButtonVisible: true,
|
||||
children: <span>👍</span>,
|
||||
},
|
||||
{
|
||||
onShowAllClick,
|
||||
onAddReactionClick,
|
||||
onAddReactionContextMenu,
|
||||
},
|
||||
) as ReactionsRowViewModel;
|
||||
|
||||
render(<ReactionsRowView vm={vm} />);
|
||||
|
||||
await user.click(screen.getByRole("button", { name: "Show all" }));
|
||||
await user.click(screen.getByRole("button", { name: "Add reaction" }));
|
||||
await user.pointer({ target: screen.getByRole("button", { name: "Add reaction" }), keys: "[MouseRight]" });
|
||||
|
||||
expect(onShowAllClick).toHaveBeenCalledTimes(1);
|
||||
expect(onAddReactionClick).toHaveBeenCalledTimes(1);
|
||||
expect(onAddReactionContextMenu).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,144 @@
|
||||
/*
|
||||
* Copyright 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
import React, { type JSX, type MouseEventHandler, type ReactNode } from "react";
|
||||
import classNames from "classnames";
|
||||
import { ReactionAddIcon } from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
import { Tooltip } from "@vector-im/compound-web";
|
||||
|
||||
import { type ViewModel, useViewModel } from "../../viewmodel";
|
||||
import styles from "./ReactionsRow.module.css";
|
||||
|
||||
export interface ReactionsRowViewSnapshot {
|
||||
/**
|
||||
* Toolbar label announced by assistive technologies.
|
||||
*/
|
||||
ariaLabel: string;
|
||||
/**
|
||||
* Controls whether the row should render at all.
|
||||
*/
|
||||
isVisible: boolean;
|
||||
/**
|
||||
* Reaction button elements to render in the row.
|
||||
*/
|
||||
children?: ReactNode;
|
||||
/**
|
||||
* Optional CSS className for the row container.
|
||||
*/
|
||||
className?: string;
|
||||
/**
|
||||
* Whether to render the "show all" button.
|
||||
*/
|
||||
showAllButtonVisible?: boolean;
|
||||
/**
|
||||
* Label shown for the "show all" button.
|
||||
*/
|
||||
showAllButtonLabel?: string;
|
||||
/**
|
||||
* Whether to render the add-reaction button.
|
||||
*/
|
||||
showAddReactionButton?: boolean;
|
||||
/**
|
||||
* Accessible label for the add-reaction button.
|
||||
*/
|
||||
addReactionButtonLabel: string;
|
||||
/**
|
||||
* Force the add-reaction button to be visible.
|
||||
*/
|
||||
addReactionButtonVisible?: boolean;
|
||||
/**
|
||||
* Marks the add-reaction button as active.
|
||||
*/
|
||||
addReactionButtonActive?: boolean;
|
||||
/**
|
||||
* Disables the add-reaction button.
|
||||
*/
|
||||
addReactionButtonDisabled?: boolean;
|
||||
}
|
||||
|
||||
export interface ReactionsRowViewActions {
|
||||
/**
|
||||
* Invoked when the user clicks the "show all" button.
|
||||
*/
|
||||
onShowAllClick?: () => void;
|
||||
/**
|
||||
* Invoked when the user clicks the add-reaction button.
|
||||
*/
|
||||
onAddReactionClick?: MouseEventHandler<HTMLButtonElement>;
|
||||
/**
|
||||
* Invoked on right-click/context-menu for the add-reaction button.
|
||||
*/
|
||||
onAddReactionContextMenu?: MouseEventHandler<HTMLButtonElement>;
|
||||
}
|
||||
|
||||
export type ReactionsRowViewModel = ViewModel<ReactionsRowViewSnapshot, ReactionsRowViewActions>;
|
||||
|
||||
interface ReactionsRowViewProps {
|
||||
vm: ReactionsRowViewModel;
|
||||
}
|
||||
|
||||
export function ReactionsRowView({ vm }: Readonly<ReactionsRowViewProps>): JSX.Element {
|
||||
const {
|
||||
ariaLabel,
|
||||
isVisible,
|
||||
children,
|
||||
className,
|
||||
showAllButtonVisible,
|
||||
showAllButtonLabel,
|
||||
showAddReactionButton,
|
||||
addReactionButtonLabel,
|
||||
addReactionButtonVisible,
|
||||
addReactionButtonActive,
|
||||
addReactionButtonDisabled,
|
||||
} = useViewModel(vm);
|
||||
|
||||
if (!isVisible) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
const addReactionButtonClasses = classNames(styles.addReactionButton, {
|
||||
[styles.addReactionButtonVisible]: addReactionButtonVisible,
|
||||
[styles.addReactionButtonActive]: addReactionButtonActive,
|
||||
[styles.addReactionButtonDisabled]: addReactionButtonDisabled,
|
||||
});
|
||||
|
||||
const onAddReactionContextMenu: MouseEventHandler<HTMLButtonElement> | undefined = vm.onAddReactionContextMenu
|
||||
? (event): void => {
|
||||
event.preventDefault();
|
||||
vm.onAddReactionContextMenu?.(event);
|
||||
}
|
||||
: undefined;
|
||||
|
||||
const addReactionButton = (
|
||||
<button
|
||||
type="button"
|
||||
className={addReactionButtonClasses}
|
||||
aria-label={addReactionButtonLabel}
|
||||
disabled={addReactionButtonDisabled}
|
||||
onClick={vm.onAddReactionClick}
|
||||
onContextMenu={onAddReactionContextMenu}
|
||||
>
|
||||
<ReactionAddIcon />
|
||||
</button>
|
||||
);
|
||||
|
||||
return (
|
||||
<div className={classNames(className, styles.reactionsRow)} role="toolbar" aria-label={ariaLabel}>
|
||||
{children}
|
||||
{showAllButtonVisible && (
|
||||
<button type="button" className={styles.showAllButton} onClick={vm.onShowAllClick}>
|
||||
{showAllButtonLabel}
|
||||
</button>
|
||||
)}
|
||||
{showAddReactionButton && (
|
||||
<Tooltip description={addReactionButtonLabel} placement="right">
|
||||
{addReactionButton}
|
||||
</Tooltip>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
+183
@@ -0,0 +1,183 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`ReactionsRowView > renders the default reactions row 1`] = `
|
||||
<div>
|
||||
<div
|
||||
aria-label="Reactions"
|
||||
class="reactionsRow"
|
||||
role="toolbar"
|
||||
>
|
||||
<button
|
||||
aria-label="4 reactions for 👍"
|
||||
class="reactionsRowButton reactionsRowButtonSelected"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="reactionsRowButtonContent"
|
||||
>
|
||||
👍
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="reactionsRowButtonCount"
|
||||
>
|
||||
4
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="2 reactions for 🎉"
|
||||
class="reactionsRowButton"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="reactionsRowButtonContent"
|
||||
>
|
||||
🎉
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="reactionsRowButtonCount"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="1 reactions for 👀"
|
||||
class="reactionsRowButton"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="reactionsRowButtonContent"
|
||||
>
|
||||
👀
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="reactionsRowButtonCount"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="Add reaction"
|
||||
class="addReactionButton addReactionButtonVisible"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14.74 2.38C13.87 2.133 12.95 2 12 2 6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10c0-.95-.133-1.87-.38-2.74a5 5 0 0 1-1.886.687 8 8 0 1 1-5.68-5.68c.1-.684.339-1.323.687-1.887"
|
||||
/>
|
||||
<path
|
||||
d="M15.536 14.121a1 1 0 0 1 0 1.415A5 5 0 0 1 12 17c-1.38 0-2.632-.56-3.535-1.464a1 1 0 1 1 1.414-1.415A3 3 0 0 0 12 15c.829 0 1.577-.335 2.121-.879a1 1 0 0 1 1.415 0M8.5 12a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3m8.5-1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0M18 6h-1a.97.97 0 0 1-.712-.287A.97.97 0 0 1 16 5q0-.424.288-.713A.97.97 0 0 1 17 4h1V3q0-.424.288-.712A.97.97 0 0 1 19 2q.424 0 .712.288Q20 2.575 20 3v1h1q.424 0 .712.287Q22 4.576 22 5t-.288.713A.97.97 0 0 1 21 6h-1v1q0 .424-.288.713A.97.97 0 0 1 19 8a.97.97 0 0 1-.712-.287A.97.97 0 0 1 18 7z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`ReactionsRowView > renders the row with a show-all button 1`] = `
|
||||
<div>
|
||||
<div
|
||||
aria-label="Reactions"
|
||||
class="reactionsRow"
|
||||
role="toolbar"
|
||||
>
|
||||
<button
|
||||
aria-label="4 reactions for 👍"
|
||||
class="reactionsRowButton reactionsRowButtonSelected"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="reactionsRowButtonContent"
|
||||
>
|
||||
👍
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="reactionsRowButtonCount"
|
||||
>
|
||||
4
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="2 reactions for 🎉"
|
||||
class="reactionsRowButton"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="reactionsRowButtonContent"
|
||||
>
|
||||
🎉
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="reactionsRowButtonCount"
|
||||
>
|
||||
2
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
aria-label="1 reactions for 👀"
|
||||
class="reactionsRowButton"
|
||||
tabindex="0"
|
||||
type="button"
|
||||
>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="reactionsRowButtonContent"
|
||||
>
|
||||
👀
|
||||
</span>
|
||||
<span
|
||||
aria-hidden="true"
|
||||
class="reactionsRowButtonCount"
|
||||
>
|
||||
1
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
class="showAllButton"
|
||||
type="button"
|
||||
>
|
||||
Show all
|
||||
</button>
|
||||
<button
|
||||
aria-label="Add reaction"
|
||||
class="addReactionButton addReactionButtonVisible"
|
||||
type="button"
|
||||
>
|
||||
<svg
|
||||
fill="currentColor"
|
||||
height="1em"
|
||||
viewBox="0 0 24 24"
|
||||
width="1em"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M14.74 2.38C13.87 2.133 12.95 2 12 2 6.477 2 2 6.477 2 12s4.477 10 10 10 10-4.477 10-10c0-.95-.133-1.87-.38-2.74a5 5 0 0 1-1.886.687 8 8 0 1 1-5.68-5.68c.1-.684.339-1.323.687-1.887"
|
||||
/>
|
||||
<path
|
||||
d="M15.536 14.121a1 1 0 0 1 0 1.415A5 5 0 0 1 12 17c-1.38 0-2.632-.56-3.535-1.464a1 1 0 1 1 1.414-1.415A3 3 0 0 0 12 15c.829 0 1.577-.335 2.121-.879a1 1 0 0 1 1.415 0M8.5 12a1.5 1.5 0 1 0 0-3 1.5 1.5 0 0 0 0 3m8.5-1.5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0M18 6h-1a.97.97 0 0 1-.712-.287A.97.97 0 0 1 16 5q0-.424.288-.713A.97.97 0 0 1 17 4h1V3q0-.424.288-.712A.97.97 0 0 1 19 2q.424 0 .712.288Q20 2.575 20 3v1h1q.424 0 .712.287Q22 4.576 22 5t-.288.713A.97.97 0 0 1 21 6h-1v1q0 .424-.288.713A.97.97 0 0 1 19 8a.97.97 0 0 1-.712-.287A.97.97 0 0 1 18 7z"
|
||||
/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@@ -0,0 +1,13 @@
|
||||
/*
|
||||
* Copyright 2026 Element Creations Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR GPL-3.0-only OR LicenseRef-Element-Commercial
|
||||
* Please see LICENSE files in the repository root for full details.
|
||||
*/
|
||||
|
||||
export {
|
||||
ReactionsRowView,
|
||||
type ReactionsRowViewSnapshot,
|
||||
type ReactionsRowViewModel,
|
||||
type ReactionsRowViewActions,
|
||||
} from "./ReactionsRowView";
|
||||
+1
@@ -8,6 +8,7 @@
|
||||
.reactionsRowButton {
|
||||
display: inline-flex;
|
||||
all: unset;
|
||||
cursor: pointer;
|
||||
line-height: var(--cpd-font-size-heading-sm);
|
||||
padding: 1px var(--cpd-space-1-5x);
|
||||
border: 1px solid var(--cpd-color-gray-400);
|
||||
|
||||
Reference in New Issue
Block a user