Add NotificationDecoration component
Add the NotificationDecoration component to shared-components. This is a leaf component that renders notification badges and indicators for rooms/items including mentions, unread counts, call indicators, etc.
|
After Width: | Height: | Size: 3.7 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 4.5 KiB |
|
After Width: | Height: | Size: 4.7 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.4 KiB |
|
After Width: | Height: | Size: 4.0 KiB |
|
After Width: | Height: | Size: 3.9 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
|
After Width: | Height: | Size: 3.8 KiB |
@ -0,0 +1,120 @@
|
||||
/*
|
||||
* 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 from "react";
|
||||
|
||||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { NotificationDecoration, type NotificationDecorationProps } from "./NotificationDecoration";
|
||||
|
||||
const defaultProps: NotificationDecorationProps = {
|
||||
hasAnyNotificationOrActivity: false,
|
||||
isUnsentMessage: false,
|
||||
invited: false,
|
||||
isMention: false,
|
||||
isActivityNotification: false,
|
||||
isNotification: false,
|
||||
hasUnreadCount: false,
|
||||
count: 0,
|
||||
muted: false,
|
||||
};
|
||||
|
||||
const meta = {
|
||||
title: "Room List/NotificationDecoration",
|
||||
component: NotificationDecoration,
|
||||
tags: ["autodocs"],
|
||||
decorators: [
|
||||
(Story) => (
|
||||
<div style={{ padding: "16px", backgroundColor: "var(--cpd-color-bg-canvas-default)" }}>
|
||||
<Story />
|
||||
</div>
|
||||
),
|
||||
],
|
||||
args: defaultProps,
|
||||
} satisfies Meta<typeof NotificationDecoration>;
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof meta>;
|
||||
|
||||
export const NoNotification: Story = {};
|
||||
|
||||
export const UnsentMessage: Story = {
|
||||
args: {
|
||||
hasAnyNotificationOrActivity: true,
|
||||
isUnsentMessage: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const VideoCall: Story = {
|
||||
args: {
|
||||
hasAnyNotificationOrActivity: true,
|
||||
callType: "video",
|
||||
},
|
||||
};
|
||||
|
||||
export const VoiceCall: Story = {
|
||||
args: {
|
||||
hasAnyNotificationOrActivity: true,
|
||||
callType: "voice",
|
||||
},
|
||||
};
|
||||
|
||||
export const Invited: Story = {
|
||||
args: {
|
||||
hasAnyNotificationOrActivity: true,
|
||||
invited: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Mention: Story = {
|
||||
args: {
|
||||
hasAnyNotificationOrActivity: true,
|
||||
isMention: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const MentionWithCount: Story = {
|
||||
args: {
|
||||
hasAnyNotificationOrActivity: true,
|
||||
isMention: true,
|
||||
count: 5,
|
||||
},
|
||||
};
|
||||
|
||||
export const NotificationWithCount: Story = {
|
||||
args: {
|
||||
hasAnyNotificationOrActivity: true,
|
||||
isNotification: true,
|
||||
count: 3,
|
||||
},
|
||||
};
|
||||
|
||||
export const ActivityIndicator: Story = {
|
||||
args: {
|
||||
hasAnyNotificationOrActivity: true,
|
||||
isActivityNotification: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const Muted: Story = {
|
||||
args: {
|
||||
muted: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const MutedWithoutActivity: Story = {
|
||||
args: {
|
||||
hasAnyNotificationOrActivity: false,
|
||||
muted: true,
|
||||
},
|
||||
};
|
||||
|
||||
export const VideoCallWithoutActivity: Story = {
|
||||
args: {
|
||||
hasAnyNotificationOrActivity: false,
|
||||
callType: "video",
|
||||
},
|
||||
};
|
||||
@ -0,0 +1,80 @@
|
||||
/*
|
||||
* 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 from "react";
|
||||
import { render } from "@test-utils";
|
||||
import { composeStories } from "@storybook/react-vite";
|
||||
import { describe, it, expect } from "vitest";
|
||||
|
||||
import * as stories from "./NotificationDecoration.stories";
|
||||
|
||||
const {
|
||||
NoNotification,
|
||||
UnsentMessage,
|
||||
VideoCall,
|
||||
VoiceCall,
|
||||
Invited,
|
||||
Mention,
|
||||
MentionWithCount,
|
||||
NotificationWithCount,
|
||||
ActivityIndicator,
|
||||
Muted,
|
||||
} = composeStories(stories);
|
||||
|
||||
describe("<NotificationDecoration />", () => {
|
||||
describe("snapshots", () => {
|
||||
it("renders NoNotification story", () => {
|
||||
const { container } = render(<NoNotification />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders UnsentMessage story", () => {
|
||||
const { container } = render(<UnsentMessage />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders VideoCall story", () => {
|
||||
const { container } = render(<VideoCall />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders VoiceCall story", () => {
|
||||
const { container } = render(<VoiceCall />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders Invited story", () => {
|
||||
const { container } = render(<Invited />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders Mention story", () => {
|
||||
const { container } = render(<Mention />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders MentionWithCount story", () => {
|
||||
const { container } = render(<MentionWithCount />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders NotificationWithCount story", () => {
|
||||
const { container } = render(<NotificationWithCount />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders ActivityIndicator story", () => {
|
||||
const { container } = render(<ActivityIndicator />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it("renders Muted story", () => {
|
||||
const { container } = render(<Muted />);
|
||||
expect(container).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
@ -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 React from "react";
|
||||
import {
|
||||
MentionIcon,
|
||||
ErrorSolidIcon,
|
||||
NotificationsOffSolidIcon,
|
||||
VideoCallSolidIcon,
|
||||
EmailSolidIcon,
|
||||
VoiceCallSolidIcon,
|
||||
} from "@vector-im/compound-design-tokens/assets/web/icons";
|
||||
import { UnreadCounter, Unread } from "@vector-im/compound-web";
|
||||
|
||||
import { Flex } from "../../../utils/Flex";
|
||||
|
||||
/**
|
||||
* Data representing the notification state for a room or item.
|
||||
* Used in snapshots and passed to the NotificationDecoration component.
|
||||
*/
|
||||
export interface NotificationDecorationData {
|
||||
/** Whether there is any notification or activity to display */
|
||||
hasAnyNotificationOrActivity: boolean;
|
||||
/** Whether there's an unsent message */
|
||||
isUnsentMessage: boolean;
|
||||
/** Whether the user is invited to the room */
|
||||
invited: boolean;
|
||||
/** Whether the notification is a mention */
|
||||
isMention: boolean;
|
||||
/** Whether there's activity (not a full notification) */
|
||||
isActivityNotification: boolean;
|
||||
/** Whether there's a notification (not just activity) */
|
||||
isNotification: boolean;
|
||||
/** Whether there are unread messages with a count */
|
||||
hasUnreadCount: boolean;
|
||||
/** Notification count */
|
||||
count: number;
|
||||
/** Whether notifications are muted */
|
||||
muted: boolean;
|
||||
/** Optional call type indicator */
|
||||
callType?: "video" | "voice";
|
||||
}
|
||||
|
||||
/**
|
||||
* Props for the NotificationDecoration component.
|
||||
*/
|
||||
export interface NotificationDecorationProps extends NotificationDecorationData {}
|
||||
|
||||
/**
|
||||
* Renders notification badges and indicators for rooms/items
|
||||
*/
|
||||
export const NotificationDecoration: React.FC<NotificationDecorationProps> = ({
|
||||
hasAnyNotificationOrActivity,
|
||||
muted,
|
||||
callType,
|
||||
isUnsentMessage,
|
||||
invited,
|
||||
isMention,
|
||||
isNotification,
|
||||
isActivityNotification,
|
||||
count,
|
||||
}) => {
|
||||
// Don't render anything if there's nothing to show
|
||||
if (!hasAnyNotificationOrActivity && !muted && !callType) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex align="center" justify="center" gap="var(--cpd-space-1x)" data-testid="notification-decoration">
|
||||
{isUnsentMessage && (
|
||||
<ErrorSolidIcon width="20px" height="20px" fill="var(--cpd-color-icon-critical-primary)" />
|
||||
)}
|
||||
{callType === "video" && (
|
||||
<VideoCallSolidIcon width="20px" height="20px" fill="var(--cpd-color-icon-accent-primary)" />
|
||||
)}
|
||||
{callType === "voice" && (
|
||||
<VoiceCallSolidIcon width="20px" height="20px" fill="var(--cpd-color-icon-accent-primary)" />
|
||||
)}
|
||||
{invited && <EmailSolidIcon width="20px" height="20px" fill="var(--cpd-color-icon-accent-primary)" />}
|
||||
{isMention && <MentionIcon width="20px" height="20px" fill="var(--cpd-color-icon-accent-primary)" />}
|
||||
{(isMention || isNotification) && <UnreadCounter count={count || null} />}
|
||||
{isActivityNotification && <Unread />}
|
||||
{muted && <NotificationsOffSolidIcon width="20px" height="20px" fill="var(--cpd-color-icon-tertiary)" />}
|
||||
</Flex>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,242 @@
|
||||
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
|
||||
|
||||
exports[`<NotificationDecoration /> > snapshots > renders ActivityIndicator story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style="padding: 16px; background-color: var(--cpd-color-bg-canvas-default);"
|
||||
>
|
||||
<div
|
||||
class="flex"
|
||||
data-testid="notification-decoration"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<div
|
||||
class="_unread_cti0f_8"
|
||||
>
|
||||
<div />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<NotificationDecoration /> > snapshots > renders Invited story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style="padding: 16px; background-color: var(--cpd-color-bg-canvas-default);"
|
||||
>
|
||||
<div
|
||||
class="flex"
|
||||
data-testid="notification-decoration"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<svg
|
||||
fill="var(--cpd-color-icon-accent-primary)"
|
||||
height="20px"
|
||||
viewBox="0 0 24 24"
|
||||
width="20px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M4 4h16a2 2 0 0 1 2 2v12a2 2 0 0 1-2 2H4a2 2 0 0 1-2-2V6a2 2 0 0 1 2-2m0 5.111a1 1 0 0 0 .514.874l7 3.89a1 1 0 0 0 .972 0l7-3.89a1 1 0 1 0-.972-1.748L12 11.856 5.486 8.237A1 1 0 0 0 4 9.111"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<NotificationDecoration /> > snapshots > renders Mention story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style="padding: 16px; background-color: var(--cpd-color-bg-canvas-default);"
|
||||
>
|
||||
<div
|
||||
class="flex"
|
||||
data-testid="notification-decoration"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<svg
|
||||
fill="var(--cpd-color-icon-accent-primary)"
|
||||
height="20px"
|
||||
viewBox="0 0 24 24"
|
||||
width="20px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 4a8 8 0 1 0 0 16 1 1 0 1 1 0 2C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10v1.5a3.5 3.5 0 0 1-6.396 1.966A5 5 0 1 1 17 12v1.5a1.5 1.5 0 0 0 3 0V12a8 8 0 0 0-8-8m3 8a3 3 0 1 0-6 0 3 3 0 0 0 6 0"
|
||||
/>
|
||||
</svg>
|
||||
<div
|
||||
class="_unread-counter_1147r_8"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<NotificationDecoration /> > snapshots > renders MentionWithCount story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style="padding: 16px; background-color: var(--cpd-color-bg-canvas-default);"
|
||||
>
|
||||
<div
|
||||
class="flex"
|
||||
data-testid="notification-decoration"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<svg
|
||||
fill="var(--cpd-color-icon-accent-primary)"
|
||||
height="20px"
|
||||
viewBox="0 0 24 24"
|
||||
width="20px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 4a8 8 0 1 0 0 16 1 1 0 1 1 0 2C6.477 22 2 17.523 2 12S6.477 2 12 2s10 4.477 10 10v1.5a3.5 3.5 0 0 1-6.396 1.966A5 5 0 1 1 17 12v1.5a1.5 1.5 0 0 0 3 0V12a8 8 0 0 0-8-8m3 8a3 3 0 1 0-6 0 3 3 0 0 0 6 0"
|
||||
/>
|
||||
</svg>
|
||||
<span
|
||||
class="_unread-counter_1147r_8"
|
||||
>
|
||||
5
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<NotificationDecoration /> > snapshots > renders Muted story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style="padding: 16px; background-color: var(--cpd-color-bg-canvas-default);"
|
||||
>
|
||||
<div
|
||||
class="flex"
|
||||
data-testid="notification-decoration"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<svg
|
||||
fill="var(--cpd-color-icon-tertiary)"
|
||||
height="20px"
|
||||
viewBox="0 0 24 24"
|
||||
width="20px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m4.917 2.083 17 17a1 1 0 0 1-1.414 1.414L19.006 19H4.414c-.89 0-1.337-1.077-.707-1.707L5 16v-6s0-2.034 1.096-3.91L3.504 3.498a1 1 0 0 1 1.414-1.414M19 13.35 9.136 3.484C9.93 3.181 10.874 3 12 3c7 0 7 7 7 7z"
|
||||
/>
|
||||
<path
|
||||
d="M10 20h4a2 2 0 0 1-4 0"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<NotificationDecoration /> > snapshots > renders NoNotification story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style="padding: 16px; background-color: var(--cpd-color-bg-canvas-default);"
|
||||
/>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<NotificationDecoration /> > snapshots > renders NotificationWithCount story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style="padding: 16px; background-color: var(--cpd-color-bg-canvas-default);"
|
||||
>
|
||||
<div
|
||||
class="flex"
|
||||
data-testid="notification-decoration"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<span
|
||||
class="_unread-counter_1147r_8"
|
||||
>
|
||||
3
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<NotificationDecoration /> > snapshots > renders UnsentMessage story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style="padding: 16px; background-color: var(--cpd-color-bg-canvas-default);"
|
||||
>
|
||||
<div
|
||||
class="flex"
|
||||
data-testid="notification-decoration"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<svg
|
||||
fill="var(--cpd-color-icon-critical-primary)"
|
||||
height="20px"
|
||||
viewBox="0 0 24 24"
|
||||
width="20px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M12 17q.424 0 .713-.288A.97.97 0 0 0 13 16a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 15a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 16q0 .424.287.712.288.288.713.288m0-4q.424 0 .713-.287A.97.97 0 0 0 13 12V8a.97.97 0 0 0-.287-.713A.97.97 0 0 0 12 7a.97.97 0 0 0-.713.287A.97.97 0 0 0 11 8v4q0 .424.287.713.288.287.713.287m0 9a9.7 9.7 0 0 1-3.9-.788 10.1 10.1 0 0 1-3.175-2.137q-1.35-1.35-2.137-3.175A9.7 9.7 0 0 1 2 12q0-2.075.788-3.9a10.1 10.1 0 0 1 2.137-3.175q1.35-1.35 3.175-2.137A9.7 9.7 0 0 1 12 2q2.075 0 3.9.788a10.1 10.1 0 0 1 3.175 2.137q1.35 1.35 2.137 3.175A9.7 9.7 0 0 1 22 12a9.7 9.7 0 0 1-.788 3.9 10.1 10.1 0 0 1-2.137 3.175q-1.35 1.35-3.175 2.137A9.7 9.7 0 0 1 12 22"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<NotificationDecoration /> > snapshots > renders VideoCall story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style="padding: 16px; background-color: var(--cpd-color-bg-canvas-default);"
|
||||
>
|
||||
<div
|
||||
class="flex"
|
||||
data-testid="notification-decoration"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<svg
|
||||
fill="var(--cpd-color-icon-accent-primary)"
|
||||
height="20px"
|
||||
viewBox="0 0 24 24"
|
||||
width="20px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="M6 4h10a2 2 0 0 1 2 2v4.286l3.35-2.871a1 1 0 0 1 1.65.76v7.65a1 1 0 0 1-1.65.76L18 13.715V18a2 2 0 0 1-2 2H6a4 4 0 0 1-4-4V8a4 4 0 0 1 4-4"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
exports[`<NotificationDecoration /> > snapshots > renders VoiceCall story 1`] = `
|
||||
<div>
|
||||
<div
|
||||
style="padding: 16px; background-color: var(--cpd-color-bg-canvas-default);"
|
||||
>
|
||||
<div
|
||||
class="flex"
|
||||
data-testid="notification-decoration"
|
||||
style="--mx-flex-display: flex; --mx-flex-direction: row; --mx-flex-align: center; --mx-flex-justify: center; --mx-flex-gap: var(--cpd-space-1x); --mx-flex-wrap: nowrap;"
|
||||
>
|
||||
<svg
|
||||
fill="var(--cpd-color-icon-accent-primary)"
|
||||
height="20px"
|
||||
viewBox="0 0 24 24"
|
||||
width="20px"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<path
|
||||
d="m20.958 16.374.039 3.527q0 .427-.33.756-.33.33-.756.33a16 16 0 0 1-6.57-1.105 16.2 16.2 0 0 1-5.563-3.663 16.1 16.1 0 0 1-3.653-5.573 16.3 16.3 0 0 1-1.115-6.56q0-.427.33-.757T4.095 3l3.528.039a1.07 1.07 0 0 1 1.085.93l.543 3.954q.039.271-.039.504a1.1 1.1 0 0 1-.271.426l-1.64 1.64q.505 1.008 1.154 1.909c.433.6 1.444 1.696 1.444 1.696s1.095 1.01 1.696 1.444q.9.65 1.909 1.153l1.64-1.64q.193-.193.426-.27t.504-.04l3.954.543q.406.059.668.359t.262.727"
|
||||
/>
|
||||
</svg>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
@ -0,0 +1,9 @@
|
||||
/*
|
||||
* 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 { NotificationDecoration } from "./NotificationDecoration";
|
||||
export type { NotificationDecorationProps, NotificationDecorationData } from "./NotificationDecoration";
|
||||