@usefui/components 1.5.2 → 1.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +12 -0
- package/dist/index.d.mts +246 -3
- package/dist/index.d.ts +246 -3
- package/dist/index.js +740 -307
- package/dist/index.mjs +708 -288
- package/package.json +12 -12
- package/src/__tests__/MessageBubble.test.tsx +179 -0
- package/src/__tests__/Shimmer.test.tsx +122 -0
- package/src/__tests__/Tree.test.tsx +275 -0
- package/src/accordion/hooks/index.tsx +3 -1
- package/src/badge/index.tsx +2 -3
- package/src/checkbox/hooks/index.tsx +5 -1
- package/src/collapsible/hooks/index.tsx +3 -1
- package/src/dialog/hooks/index.tsx +5 -1
- package/src/dropdown/hooks/index.tsx +3 -1
- package/src/dropdown/index.tsx +9 -9
- package/src/field/hooks/index.tsx +5 -1
- package/src/field/styles/index.ts +1 -0
- package/src/index.ts +6 -0
- package/src/message-bubble/MessageBubble.stories.tsx +91 -0
- package/src/message-bubble/hooks/index.tsx +41 -0
- package/src/message-bubble/index.tsx +153 -0
- package/src/message-bubble/styles/index.ts +61 -0
- package/src/otp-field/hooks/index.tsx +3 -1
- package/src/otp-field/index.tsx +5 -3
- package/src/sheet/hooks/index.tsx +5 -1
- package/src/shimmer/Shimmer.stories.tsx +95 -0
- package/src/shimmer/index.tsx +64 -0
- package/src/shimmer/styles/index.ts +33 -0
- package/src/switch/hooks/index.tsx +5 -1
- package/src/tabs/hooks/index.tsx +5 -1
- package/src/text-area/Textarea.stories.tsx +7 -2
- package/src/text-area/index.tsx +30 -14
- package/src/text-area/styles/index.ts +32 -72
- package/src/toolbar/hooks/index.tsx +5 -1
- package/src/tooltip/index.tsx +4 -3
- package/src/tree/Tree.stories.tsx +139 -0
- package/src/tree/hooks/tree-node-provider.tsx +50 -0
- package/src/tree/hooks/tree-provider.tsx +75 -0
- package/src/tree/index.tsx +231 -0
- package/src/tree/styles/index.ts +23 -0
- package/tsconfig.build.json +20 -0
- package/tsconfig.json +1 -3
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import React, { useState, createContext, useContext } from "react";
|
|
2
4
|
import { IReactChildren, IComponentAPI } from "../../../../../types";
|
|
3
5
|
|
|
@@ -9,7 +11,9 @@ const defaultComponentAPI = {
|
|
|
9
11
|
const DialogContext = createContext<IComponentAPI>(defaultComponentAPI);
|
|
10
12
|
export const useDialog = () => useContext(DialogContext);
|
|
11
13
|
|
|
12
|
-
export const DialogProvider = ({
|
|
14
|
+
export const DialogProvider = ({
|
|
15
|
+
children,
|
|
16
|
+
}: IReactChildren): React.JSX.Element => {
|
|
13
17
|
const context = useDialogProvider();
|
|
14
18
|
|
|
15
19
|
return (
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import React from "react";
|
|
2
4
|
import { IReactChildren, IComponentAPI } from "../../../../../types";
|
|
3
5
|
|
|
@@ -22,7 +24,7 @@ export const useDropdownMenu = () => React.useContext(DropdownMenuContext);
|
|
|
22
24
|
|
|
23
25
|
export const DropdownMenuProvider = ({
|
|
24
26
|
children,
|
|
25
|
-
}: IReactChildren): JSX.Element => {
|
|
27
|
+
}: IReactChildren): React.JSX.Element => {
|
|
26
28
|
const context = useDropdownMenuProvider();
|
|
27
29
|
|
|
28
30
|
return (
|
package/src/dropdown/index.tsx
CHANGED
|
@@ -14,18 +14,15 @@ import {
|
|
|
14
14
|
} from "../../../../types";
|
|
15
15
|
|
|
16
16
|
export interface IDropdownContentProperties
|
|
17
|
-
extends IComponentStyling,
|
|
18
|
-
IComponentSize,
|
|
19
|
-
React.ComponentPropsWithRef<"ul"> {
|
|
17
|
+
extends IComponentStyling, IComponentSize, React.ComponentPropsWithRef<"ul"> {
|
|
20
18
|
defaultOpen?: boolean;
|
|
21
19
|
}
|
|
22
20
|
export interface IDropdownItemProperties
|
|
23
|
-
extends IComponentStyling,
|
|
24
|
-
React.ComponentProps<"li"> {
|
|
21
|
+
extends IComponentStyling, React.ComponentProps<"li"> {
|
|
25
22
|
radio?: boolean;
|
|
26
23
|
disabled?: boolean;
|
|
27
24
|
onClick?: (
|
|
28
|
-
event: React.MouseEvent<HTMLLIElement> | React.KeyboardEvent<HTMLLIElement
|
|
25
|
+
event: React.MouseEvent<HTMLLIElement> | React.KeyboardEvent<HTMLLIElement>,
|
|
29
26
|
) => void;
|
|
30
27
|
onKeyDown?: (event: React.KeyboardEvent<HTMLLIElement>) => void;
|
|
31
28
|
}
|
|
@@ -52,7 +49,7 @@ export interface IDropdownComposition {
|
|
|
52
49
|
* @returns {ReactElement} The DropdownMenu component.
|
|
53
50
|
*/
|
|
54
51
|
const DropdownMenu = ({ children }: React.ComponentProps<"div">) => {
|
|
55
|
-
const DropdownContentRef = React.useRef(null);
|
|
52
|
+
const DropdownContentRef = React.useRef<HTMLDivElement | null>(null);
|
|
56
53
|
const { states, methods } = useDropdownMenu();
|
|
57
54
|
const { toggleOpen } = methods;
|
|
58
55
|
|
|
@@ -60,7 +57,10 @@ const DropdownMenu = ({ children }: React.ComponentProps<"div">) => {
|
|
|
60
57
|
if (states.open && toggleOpen) toggleOpen();
|
|
61
58
|
};
|
|
62
59
|
|
|
63
|
-
useClickOutside(
|
|
60
|
+
useClickOutside(
|
|
61
|
+
DropdownContentRef as React.RefObject<HTMLElement>,
|
|
62
|
+
handleClickOutside,
|
|
63
|
+
);
|
|
64
64
|
useDisabledScroll(Boolean(states.open));
|
|
65
65
|
return <RootWrapper ref={DropdownContentRef}>{children}</RootWrapper>;
|
|
66
66
|
};
|
|
@@ -266,7 +266,7 @@ const DropdownMenuItem = (props: IDropdownItemProperties) => {
|
|
|
266
266
|
click: (
|
|
267
267
|
event:
|
|
268
268
|
| React.MouseEvent<HTMLLIElement>
|
|
269
|
-
| React.KeyboardEvent<HTMLLIElement
|
|
269
|
+
| React.KeyboardEvent<HTMLLIElement>,
|
|
270
270
|
) => {
|
|
271
271
|
if (onClick) onClick(event);
|
|
272
272
|
},
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import React, { createContext, useContext } from "react";
|
|
2
4
|
import { IReactChildren, IComponentAPI } from "../../../../../types";
|
|
3
5
|
|
|
@@ -9,7 +11,9 @@ const defaultComponentAPI = {
|
|
|
9
11
|
const FieldContext = createContext<IComponentAPI>(defaultComponentAPI);
|
|
10
12
|
export const useField = () => useContext(FieldContext);
|
|
11
13
|
|
|
12
|
-
export const FieldProvider = ({
|
|
14
|
+
export const FieldProvider = ({
|
|
15
|
+
children,
|
|
16
|
+
}: IReactChildren): React.JSX.Element => {
|
|
13
17
|
const context = useFieldProvider();
|
|
14
18
|
|
|
15
19
|
return (
|
package/src/index.ts
CHANGED
|
@@ -11,6 +11,7 @@ export * from "./dialog";
|
|
|
11
11
|
export * from "./divider";
|
|
12
12
|
export * from "./dropdown";
|
|
13
13
|
export * from "./field";
|
|
14
|
+
export * from "./message-bubble";
|
|
14
15
|
export * from "./otp-field";
|
|
15
16
|
export * from "./overlay";
|
|
16
17
|
export * from "./page";
|
|
@@ -18,6 +19,7 @@ export * from "./portal";
|
|
|
18
19
|
export * from "./privacy-field";
|
|
19
20
|
export * from "./resizable";
|
|
20
21
|
export * from "./sheet";
|
|
22
|
+
export * from "./shimmer";
|
|
21
23
|
export * from "./scrollarea";
|
|
22
24
|
export * from "./spinner";
|
|
23
25
|
export * from "./skeleton";
|
|
@@ -28,6 +30,7 @@ export * from "./text-area";
|
|
|
28
30
|
export * from "./toggle";
|
|
29
31
|
export * from "./toolbar";
|
|
30
32
|
export * from "./tooltip";
|
|
33
|
+
export * from "./tree";
|
|
31
34
|
|
|
32
35
|
export { useAccordion } from "./accordion/hooks";
|
|
33
36
|
export { useCheckbox } from "./checkbox/hooks";
|
|
@@ -39,3 +42,6 @@ export { useSheet } from "./sheet/hooks";
|
|
|
39
42
|
export { useSwitch } from "./switch/hooks";
|
|
40
43
|
export { useTabs } from "./tabs/hooks";
|
|
41
44
|
export { useToolbar } from "./toolbar/hooks";
|
|
45
|
+
export { useMessageBubble } from "./message-bubble/hooks";
|
|
46
|
+
export { useTree } from "./tree/hooks/tree-provider";
|
|
47
|
+
export { useTreeNode } from "./tree/hooks/tree-node-provider";
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
|
|
4
|
+
import { MessageBubble } from "..";
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: "Components/MessageBubble",
|
|
8
|
+
component: MessageBubble,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
decorators: [
|
|
11
|
+
(Story) => (
|
|
12
|
+
<div className="m-medium-30">
|
|
13
|
+
<Story />
|
|
14
|
+
</div>
|
|
15
|
+
),
|
|
16
|
+
],
|
|
17
|
+
} satisfies Meta<typeof MessageBubble>;
|
|
18
|
+
export default meta;
|
|
19
|
+
|
|
20
|
+
type Story = StoryObj<typeof meta>;
|
|
21
|
+
|
|
22
|
+
const MOCK_DATE = new Date("2026-03-17T13:00:00Z");
|
|
23
|
+
const MOCK_MESSAGE = "Hey, how are you doing?";
|
|
24
|
+
|
|
25
|
+
export const Default: Story = {
|
|
26
|
+
args: {
|
|
27
|
+
side: "left",
|
|
28
|
+
raw: false,
|
|
29
|
+
},
|
|
30
|
+
argTypes: {
|
|
31
|
+
side: {
|
|
32
|
+
options: ["left", "right"],
|
|
33
|
+
control: { type: "radio" },
|
|
34
|
+
},
|
|
35
|
+
raw: {
|
|
36
|
+
control: { type: "boolean" },
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
render: ({ ...args }) => (
|
|
40
|
+
<MessageBubble.Root>
|
|
41
|
+
<MessageBubble {...args}>
|
|
42
|
+
<MessageBubble.Content>{MOCK_MESSAGE}</MessageBubble.Content>
|
|
43
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} />
|
|
44
|
+
</MessageBubble>
|
|
45
|
+
</MessageBubble.Root>
|
|
46
|
+
),
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
export const Left: Story = {
|
|
50
|
+
render: () => (
|
|
51
|
+
<MessageBubble.Root>
|
|
52
|
+
<MessageBubble side="left">
|
|
53
|
+
<MessageBubble.Content>{MOCK_MESSAGE}</MessageBubble.Content>
|
|
54
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} />
|
|
55
|
+
</MessageBubble>
|
|
56
|
+
</MessageBubble.Root>
|
|
57
|
+
),
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
export const Right: Story = {
|
|
61
|
+
render: () => (
|
|
62
|
+
<MessageBubble.Root>
|
|
63
|
+
<MessageBubble side="right">
|
|
64
|
+
<MessageBubble.Content>{MOCK_MESSAGE}</MessageBubble.Content>
|
|
65
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} />
|
|
66
|
+
</MessageBubble>
|
|
67
|
+
</MessageBubble.Root>
|
|
68
|
+
),
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const Conversation: Story = {
|
|
72
|
+
render: () => (
|
|
73
|
+
<React.Fragment>
|
|
74
|
+
{(
|
|
75
|
+
[
|
|
76
|
+
{ side: "left", message: "Hey, how are you doing?" },
|
|
77
|
+
{ side: "right", message: "All good! What about you?" },
|
|
78
|
+
{ side: "left", message: "Pretty great, thanks for asking 🐻" },
|
|
79
|
+
{ side: "right", message: "Glad to hear it! 🐻❄️" },
|
|
80
|
+
] as const
|
|
81
|
+
).map(({ side, message }, index) => (
|
|
82
|
+
<MessageBubble.Root key={index}>
|
|
83
|
+
<MessageBubble side={side}>
|
|
84
|
+
<MessageBubble.Content>{message}</MessageBubble.Content>
|
|
85
|
+
<MessageBubble.Meta createdAt={MOCK_DATE} />
|
|
86
|
+
</MessageBubble>
|
|
87
|
+
</MessageBubble.Root>
|
|
88
|
+
))}
|
|
89
|
+
</React.Fragment>
|
|
90
|
+
),
|
|
91
|
+
};
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState, createContext, useContext } from "react";
|
|
4
|
+
|
|
5
|
+
import { IReactChildren, IComponentAPI } from "../../../../../types";
|
|
6
|
+
import { MessageBubbleSide } from "../";
|
|
7
|
+
|
|
8
|
+
const defaultComponentAPI = {
|
|
9
|
+
id: "",
|
|
10
|
+
states: {},
|
|
11
|
+
methods: {},
|
|
12
|
+
};
|
|
13
|
+
const MessageBubbleContext = createContext<IComponentAPI>(defaultComponentAPI);
|
|
14
|
+
export const useMessageBubble = () => useContext(MessageBubbleContext);
|
|
15
|
+
|
|
16
|
+
export const MessageBubbleProvider = ({
|
|
17
|
+
children,
|
|
18
|
+
}: IReactChildren): React.JSX.Element => {
|
|
19
|
+
const context = useMessageBubbleProvider();
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<MessageBubbleContext.Provider value={context}>
|
|
23
|
+
{children}
|
|
24
|
+
</MessageBubbleContext.Provider>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
function useMessageBubbleProvider(): IComponentAPI {
|
|
29
|
+
const [side, setSide] = useState<MessageBubbleSide | null>(null);
|
|
30
|
+
const MessageBubbleId = React.useId();
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
id: MessageBubbleId,
|
|
34
|
+
states: {
|
|
35
|
+
side,
|
|
36
|
+
},
|
|
37
|
+
methods: {
|
|
38
|
+
applySide: (side: MessageBubbleSide): string | void => setSide(side),
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { useMessageBubble, MessageBubbleProvider } from "./hooks";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
MessageBubbleWrapper,
|
|
8
|
+
MessageBubbleBadge,
|
|
9
|
+
MessageBubbleContentWrapper,
|
|
10
|
+
MessageBubbleMetaWrapper,
|
|
11
|
+
} from "./styles";
|
|
12
|
+
|
|
13
|
+
import { IComponentStyling, IReactChildren } from "../../../../types";
|
|
14
|
+
|
|
15
|
+
export type MessageBubbleSide = "left" | "right";
|
|
16
|
+
|
|
17
|
+
export interface IMessageBubbleContext {
|
|
18
|
+
side: MessageBubbleSide;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface IMessageBubbleProperties
|
|
22
|
+
extends IComponentStyling, React.HTMLAttributes<HTMLDivElement> {
|
|
23
|
+
side: MessageBubbleSide;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface IMessageBubbleContentProperties
|
|
27
|
+
extends IComponentStyling, React.HTMLAttributes<HTMLDivElement> {
|
|
28
|
+
children: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface IMessageBubbleMetaProperties
|
|
32
|
+
extends IComponentStyling, React.HTMLAttributes<HTMLDivElement> {
|
|
33
|
+
createdAt: Date;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const MessageBubbleRoot = ({ children }: IReactChildren) => {
|
|
37
|
+
return <MessageBubbleProvider>{children}</MessageBubbleProvider>;
|
|
38
|
+
};
|
|
39
|
+
MessageBubbleRoot.displayName = "MessageBubble.Root";
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* MessageBubble is used to display a chat message with an optional side and raw layout.
|
|
43
|
+
*
|
|
44
|
+
* **Best practices:**
|
|
45
|
+
*
|
|
46
|
+
* - Always wrap MessageBubble inside a MessageBubble.Root to provide the necessary context.
|
|
47
|
+
* - Use `side` to visually distinguish between sent and received messages.
|
|
48
|
+
* - Pair with MessageBubble.Content and MessageBubble.Meta for a complete message layout.
|
|
49
|
+
*
|
|
50
|
+
* @param {IMessageBubbleProperties} props - The props for the MessageBubble component.
|
|
51
|
+
* @param {"left" | "right"} props.side - The side the bubble is aligned to. Propagated to all child compounds via context.
|
|
52
|
+
* @param {boolean} props.raw - When true, removes default styling for custom layouts.
|
|
53
|
+
* @param {ReactNode} props.children - The content to be rendered inside the bubble.
|
|
54
|
+
* @returns {ReactElement} The MessageBubble component.
|
|
55
|
+
*/
|
|
56
|
+
const MessageBubble = (props: IMessageBubbleProperties) => {
|
|
57
|
+
const { side, raw, children, ...restProps } = props;
|
|
58
|
+
const { methods } = useMessageBubble();
|
|
59
|
+
|
|
60
|
+
React.useEffect(() => {
|
|
61
|
+
if (side && methods?.applySide) methods.applySide(side);
|
|
62
|
+
}, [side]);
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<MessageBubbleWrapper
|
|
66
|
+
data-raw={Boolean(raw)}
|
|
67
|
+
data-side={side}
|
|
68
|
+
aria-label={restProps["aria-label"] ?? `message-bubble-${side}`}
|
|
69
|
+
{...restProps}
|
|
70
|
+
>
|
|
71
|
+
{children}
|
|
72
|
+
</MessageBubbleWrapper>
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
MessageBubble.displayName = "MessageBubble";
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* MessageBubble.Content is used to display the text or rich content of the message.
|
|
79
|
+
*
|
|
80
|
+
* **Best practices:**
|
|
81
|
+
*
|
|
82
|
+
* - Place MessageBubble.Content inside a MessageBubble to inherit the correct side context.
|
|
83
|
+
* - Avoid nesting interactive elements inside the content that may conflict with bubble focus management.
|
|
84
|
+
*
|
|
85
|
+
* @param {IMessageBubbleContentProperties} props - The props for the MessageBubble.Content component.
|
|
86
|
+
* @param {boolean} props.raw - When true, removes default styling for custom layouts.
|
|
87
|
+
* @param {ReactNode} props.children - The message text or rich content to render.
|
|
88
|
+
* @returns {ReactElement} The MessageBubble.Content component.
|
|
89
|
+
*/
|
|
90
|
+
const MessageBubbleContent = (props: IMessageBubbleContentProperties) => {
|
|
91
|
+
const { children, raw, ...restProps } = props;
|
|
92
|
+
const { id, states } = useMessageBubble();
|
|
93
|
+
|
|
94
|
+
return (
|
|
95
|
+
<MessageBubbleBadge
|
|
96
|
+
variant="secondary"
|
|
97
|
+
data-raw={Boolean(raw)}
|
|
98
|
+
data-side={states?.side}
|
|
99
|
+
aria-label={`message-bubble-content-${id}`}
|
|
100
|
+
{...restProps}
|
|
101
|
+
>
|
|
102
|
+
<MessageBubbleContentWrapper>{children}</MessageBubbleContentWrapper>
|
|
103
|
+
</MessageBubbleBadge>
|
|
104
|
+
);
|
|
105
|
+
};
|
|
106
|
+
MessageBubbleContent.displayName = "MessageBubble.Content";
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* MessageBubble.Meta is used to display metadata associated with the message, such as its timestamp.
|
|
110
|
+
*
|
|
111
|
+
* **Best practices:**
|
|
112
|
+
*
|
|
113
|
+
* - Always provide a valid `createdAt` date for accurate timestamp display.
|
|
114
|
+
* - Place MessageBubble.Meta after MessageBubble.Content for a natural reading flow.
|
|
115
|
+
* - The side is automatically inherited from context — do not pass it manually.
|
|
116
|
+
*
|
|
117
|
+
* @param {IMessageBubbleMetaProperties} props - The props for the MessageBubble.Meta component.
|
|
118
|
+
* @param {Date} props.createdAt - The date the message was created. Formatted as medium date and short time.
|
|
119
|
+
* @param {boolean} props.raw - When true, removes default styling for custom layouts.
|
|
120
|
+
* @returns {ReactElement} The MessageBubble.Meta component.
|
|
121
|
+
*/
|
|
122
|
+
const MessageBubbleMeta = (props: IMessageBubbleMetaProperties) => {
|
|
123
|
+
const { createdAt, raw, ...restProps } = props;
|
|
124
|
+
const { states } = useMessageBubble();
|
|
125
|
+
|
|
126
|
+
const formattedDate = new Intl.DateTimeFormat("en-US", {
|
|
127
|
+
dateStyle: "medium",
|
|
128
|
+
timeStyle: "short",
|
|
129
|
+
}).format(createdAt);
|
|
130
|
+
|
|
131
|
+
return (
|
|
132
|
+
<MessageBubbleMetaWrapper
|
|
133
|
+
data-raw={Boolean(raw)}
|
|
134
|
+
data-side={states?.side}
|
|
135
|
+
aria-label={`message-bubble-meta-${states?.side}`}
|
|
136
|
+
{...restProps}
|
|
137
|
+
>
|
|
138
|
+
{formattedDate}
|
|
139
|
+
</MessageBubbleMetaWrapper>
|
|
140
|
+
);
|
|
141
|
+
};
|
|
142
|
+
MessageBubbleMeta.displayName = "MessageBubble.Meta";
|
|
143
|
+
|
|
144
|
+
MessageBubble.Root = MessageBubbleRoot;
|
|
145
|
+
MessageBubble.Content = MessageBubbleContent;
|
|
146
|
+
MessageBubble.Meta = MessageBubbleMeta;
|
|
147
|
+
|
|
148
|
+
export {
|
|
149
|
+
MessageBubble,
|
|
150
|
+
MessageBubbleRoot,
|
|
151
|
+
MessageBubbleContent,
|
|
152
|
+
MessageBubbleMeta,
|
|
153
|
+
};
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import styled from "styled-components";
|
|
2
|
+
import { Badge, IBadgeProperties } from "../../";
|
|
3
|
+
|
|
4
|
+
export const MessageBubbleWrapper = styled.div`
|
|
5
|
+
display: flex;
|
|
6
|
+
flex-direction: column;
|
|
7
|
+
gap: var(--measurement-medium-10);
|
|
8
|
+
|
|
9
|
+
&[data-side="right"] {
|
|
10
|
+
align-items: flex-end;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
&[data-side="left"] {
|
|
14
|
+
align-items: flex-start;
|
|
15
|
+
}
|
|
16
|
+
`;
|
|
17
|
+
|
|
18
|
+
export const MessageBubbleBadge: React.FC<IBadgeProperties> = styled(Badge)`
|
|
19
|
+
position: relative;
|
|
20
|
+
max-width: var(--measurement-large-90);
|
|
21
|
+
width: 100%;
|
|
22
|
+
justify-self: flex-end;
|
|
23
|
+
padding: var(--measurement-medium-30) var(--measurement-medium-50) !important;
|
|
24
|
+
border-radius: var(--measurement-medium-60) !important;
|
|
25
|
+
|
|
26
|
+
&[data-side="left"] {
|
|
27
|
+
background-color: var(--contrast-color) !important;
|
|
28
|
+
border-bottom-left-radius: 0 !important;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
&[data-side="right"] {
|
|
32
|
+
background-color: var(--font-color-alpha-10) !important;
|
|
33
|
+
border-bottom-right-radius: 0 !important;
|
|
34
|
+
}
|
|
35
|
+
`;
|
|
36
|
+
|
|
37
|
+
export const MessageBubbleContentWrapper = styled.div`
|
|
38
|
+
line-height: 1.5;
|
|
39
|
+
font-weight: 500;
|
|
40
|
+
word-break: keep-all;
|
|
41
|
+
width: 100%;
|
|
42
|
+
|
|
43
|
+
* {
|
|
44
|
+
font-size: var(--fontsize-medium-10) !important;
|
|
45
|
+
}
|
|
46
|
+
`;
|
|
47
|
+
|
|
48
|
+
export const MessageBubbleMetaWrapper = styled.div`
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
gap: var(--measurement-medium-10);
|
|
52
|
+
width: 100%;
|
|
53
|
+
|
|
54
|
+
&[data-side="right"] {
|
|
55
|
+
justify-content: flex-end;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
&[data-side="left"] {
|
|
59
|
+
justify-content: flex-start;
|
|
60
|
+
}
|
|
61
|
+
`;
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import React from "react";
|
|
2
4
|
|
|
3
5
|
import type { OTPFieldContextType } from "../types";
|
|
4
6
|
|
|
5
7
|
export const OTPFieldContext = React.createContext<OTPFieldContextType | null>(
|
|
6
|
-
null
|
|
8
|
+
null,
|
|
7
9
|
);
|
|
8
10
|
export const useOTPField = () => {
|
|
9
11
|
const context = React.useContext(OTPFieldContext);
|
package/src/otp-field/index.tsx
CHANGED
|
@@ -26,7 +26,7 @@ const OTPField = ({ children, length, onComplete }: OTPFieldProps) => {
|
|
|
26
26
|
|
|
27
27
|
const inputRefs = React.useRef<(HTMLInputElement | null)[]>([]);
|
|
28
28
|
const [otp, setOtp] = React.useState<string[]>(
|
|
29
|
-
Array.from({ length: defaultLength }, () => "")
|
|
29
|
+
Array.from({ length: defaultLength }, () => ""),
|
|
30
30
|
);
|
|
31
31
|
const [activeIndex, setActiveIndex] = React.useState<number>(0);
|
|
32
32
|
|
|
@@ -121,7 +121,7 @@ const OTPField = ({ children, length, onComplete }: OTPFieldProps) => {
|
|
|
121
121
|
*/
|
|
122
122
|
const timeout = setTimeout(
|
|
123
123
|
() => inputRefs.current[targetIndex]?.select(),
|
|
124
|
-
0
|
|
124
|
+
0,
|
|
125
125
|
);
|
|
126
126
|
|
|
127
127
|
return () => clearTimeout(timeout);
|
|
@@ -209,7 +209,9 @@ const OTPFieldSlot = ({
|
|
|
209
209
|
|
|
210
210
|
return (
|
|
211
211
|
<OTPCell
|
|
212
|
-
ref={(el) =>
|
|
212
|
+
ref={(el) => {
|
|
213
|
+
inputRefs.current[index] = el;
|
|
214
|
+
}}
|
|
213
215
|
type="text"
|
|
214
216
|
data-testid="otp-field-slot"
|
|
215
217
|
data-active={activeIndex === index}
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
1
3
|
import React from "react";
|
|
2
4
|
import { IReactChildren } from "../../../../../types";
|
|
3
5
|
|
|
@@ -15,7 +17,9 @@ const SheetContext = React.createContext<IContextProperties>({
|
|
|
15
17
|
});
|
|
16
18
|
export const useSheet = () => React.useContext(SheetContext);
|
|
17
19
|
|
|
18
|
-
export const SheetProvider = ({
|
|
20
|
+
export const SheetProvider = ({
|
|
21
|
+
children,
|
|
22
|
+
}: IReactChildren): React.JSX.Element => {
|
|
19
23
|
const context = useSheetProvider();
|
|
20
24
|
|
|
21
25
|
return (
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import type { Meta, StoryObj } from "@storybook/react";
|
|
3
|
+
|
|
4
|
+
import { Shimmer } from "..";
|
|
5
|
+
|
|
6
|
+
const meta = {
|
|
7
|
+
title: "Components/Shimmer",
|
|
8
|
+
component: Shimmer,
|
|
9
|
+
tags: ["autodocs"],
|
|
10
|
+
decorators: [
|
|
11
|
+
(Story) => (
|
|
12
|
+
<div className="m-medium-30">
|
|
13
|
+
<Story />
|
|
14
|
+
</div>
|
|
15
|
+
),
|
|
16
|
+
],
|
|
17
|
+
} satisfies Meta<typeof Shimmer>;
|
|
18
|
+
export default meta;
|
|
19
|
+
|
|
20
|
+
type Story = StoryObj<typeof meta>;
|
|
21
|
+
|
|
22
|
+
export const Default: Story = {
|
|
23
|
+
args: {
|
|
24
|
+
children: "Loading your content…",
|
|
25
|
+
duration: 2,
|
|
26
|
+
spread: 200,
|
|
27
|
+
shimmerColor: "var(--font-color-alpha-60)",
|
|
28
|
+
baseColor: "var(--font-color-alpha-30)",
|
|
29
|
+
},
|
|
30
|
+
argTypes: {
|
|
31
|
+
duration: {
|
|
32
|
+
control: { type: "number", min: 0.5, max: 10, step: 0.5 },
|
|
33
|
+
},
|
|
34
|
+
spread: {
|
|
35
|
+
control: { type: "number", min: 100, max: 500, step: 50 },
|
|
36
|
+
},
|
|
37
|
+
shimmerColor: {
|
|
38
|
+
control: { type: "color" },
|
|
39
|
+
},
|
|
40
|
+
baseColor: {
|
|
41
|
+
control: { type: "color" },
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
render: ({ ...args }) => <Shimmer {...args} />,
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
export const SlowAnimation: Story = {
|
|
48
|
+
render: ({ ...args }) => (
|
|
49
|
+
<Shimmer duration={6} spread={200}>
|
|
50
|
+
Slowly shimmering text…
|
|
51
|
+
</Shimmer>
|
|
52
|
+
),
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
export const FastAnimation: Story = {
|
|
56
|
+
render: ({ ...args }) => (
|
|
57
|
+
<Shimmer duration={0.8} spread={200}>
|
|
58
|
+
Rapidly shimmering text…
|
|
59
|
+
</Shimmer>
|
|
60
|
+
),
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const WideSpread: Story = {
|
|
64
|
+
render: ({ ...args }) => (
|
|
65
|
+
<Shimmer spread={400}>Wide spread shimmer effect</Shimmer>
|
|
66
|
+
),
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
export const CustomColors: Story = {
|
|
70
|
+
render: ({ ...args }) => (
|
|
71
|
+
<Shimmer
|
|
72
|
+
shimmerColor="var(--alpha-blue-80)"
|
|
73
|
+
baseColor="var(--alpha-blue-30)"
|
|
74
|
+
>
|
|
75
|
+
Custom branded shimmer
|
|
76
|
+
</Shimmer>
|
|
77
|
+
),
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export const Group: Story = {
|
|
81
|
+
render: ({ ...args }) => (
|
|
82
|
+
<React.Fragment>
|
|
83
|
+
{[
|
|
84
|
+
"Fetching user profile…",
|
|
85
|
+
"Loading dashboard…",
|
|
86
|
+
"Syncing your data…",
|
|
87
|
+
"Preparing your workspace…",
|
|
88
|
+
].map((label) => (
|
|
89
|
+
<div key={label} className="m-b-medium-30">
|
|
90
|
+
<Shimmer>{label}</Shimmer>
|
|
91
|
+
</div>
|
|
92
|
+
))}
|
|
93
|
+
</React.Fragment>
|
|
94
|
+
),
|
|
95
|
+
};
|