mirror of
https://github.com/wavetermdev/waveterm.git
synced 2025-02-08 00:21:23 +01:00
menu stories
This commit is contained in:
parent
4b8520b9fa
commit
6a41e3c0a8
@ -56,7 +56,7 @@ const items = [
|
||||
];
|
||||
|
||||
const meta = {
|
||||
title: "Elements/Menu",
|
||||
title: "Elements/ContextMenu",
|
||||
component: ContextMenu,
|
||||
args: {
|
||||
items: [],
|
||||
|
@ -1,35 +1,35 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
.list {
|
||||
.menu {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.list-item {
|
||||
.menu-item {
|
||||
padding: 10px;
|
||||
cursor: pointer;
|
||||
user-select: none;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.list-item-button {
|
||||
.menu-item-button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.list-item-content {
|
||||
.menu-item-content {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.list-item-icon {
|
||||
.menu-item-icon {
|
||||
margin-right: 10px; /* Space between icon and text */
|
||||
}
|
||||
|
||||
.list-item-text {
|
||||
.menu-item-text {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
@ -46,7 +46,7 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.list-item-button {
|
||||
.menu-item-button {
|
||||
padding: 10px;
|
||||
color: var(--main-text-color);
|
||||
|
||||
@ -56,6 +56,6 @@
|
||||
}
|
||||
}
|
||||
|
||||
.list-item-button.clickable:hover {
|
||||
.menu-item-button.clickable:hover {
|
||||
background-color: #f0f0f0;
|
||||
}
|
@ -3,13 +3,13 @@
|
||||
|
||||
import { Meta, StoryObj } from "@storybook/react";
|
||||
import { Avatar } from "./avatar";
|
||||
import { List } from "./list";
|
||||
import { Menu } from "./menu";
|
||||
|
||||
import "./list.less";
|
||||
import "./menu.less";
|
||||
|
||||
const meta: Meta<typeof List> = {
|
||||
title: "Elements/List",
|
||||
component: List,
|
||||
const meta: Meta<typeof Menu> = {
|
||||
title: "Elements/Menu",
|
||||
component: Menu,
|
||||
argTypes: {
|
||||
items: { control: "object" },
|
||||
renderItem: { control: false },
|
||||
@ -145,7 +145,7 @@ export const Default: Story = {
|
||||
},
|
||||
render: (args) => (
|
||||
<Container>
|
||||
<List {...args} />
|
||||
<Menu {...args} />
|
||||
</Container>
|
||||
),
|
||||
};
|
||||
@ -156,7 +156,7 @@ export const NestedList: Story = {
|
||||
},
|
||||
render: (args) => (
|
||||
<Container>
|
||||
<List {...args} />
|
||||
<Menu {...args} />
|
||||
</Container>
|
||||
),
|
||||
};
|
||||
@ -168,7 +168,7 @@ export const CustomRender: Story = {
|
||||
},
|
||||
render: (args) => (
|
||||
<Container>
|
||||
<List {...args} />
|
||||
<Menu {...args} />
|
||||
</Container>
|
||||
),
|
||||
};
|
||||
@ -179,7 +179,7 @@ export const WithClickHandlers: Story = {
|
||||
},
|
||||
render: (args) => (
|
||||
<Container>
|
||||
<List {...args} />
|
||||
<Menu {...args} />
|
||||
</Container>
|
||||
),
|
||||
};
|
||||
@ -190,7 +190,7 @@ export const NestedWithClickHandlers: Story = {
|
||||
},
|
||||
render: (args) => (
|
||||
<Container>
|
||||
<List {...args} />
|
||||
<Menu {...args} />
|
||||
</Container>
|
||||
),
|
||||
};
|
||||
@ -224,7 +224,7 @@ export const WithAvatars: Story = {
|
||||
},
|
||||
render: (args) => (
|
||||
<Container>
|
||||
<List {...args} />
|
||||
<Menu {...args} />
|
||||
</Container>
|
||||
),
|
||||
};
|
@ -3,48 +3,48 @@
|
||||
|
||||
import clsx from "clsx";
|
||||
import React, { memo, useState } from "react";
|
||||
import "./list.less";
|
||||
import "./menu.less";
|
||||
|
||||
interface ListItem {
|
||||
interface MenuItem {
|
||||
text: string;
|
||||
icon?: React.ReactNode;
|
||||
children?: ListItem[];
|
||||
children?: MenuItem[];
|
||||
onClick?: () => void;
|
||||
}
|
||||
|
||||
interface ListProps {
|
||||
items: ListItem[];
|
||||
interface MenuProps {
|
||||
items: MenuItem[];
|
||||
className?: string;
|
||||
renderItem?: (item: ListItem, isOpen: boolean, handleClick: () => void) => React.ReactNode;
|
||||
renderItem?: (item: MenuItem, isOpen: boolean, handleClick: () => void) => React.ReactNode;
|
||||
}
|
||||
|
||||
const List = memo(({ items, className, renderItem }: ListProps) => {
|
||||
const Menu = memo(({ items, className, renderItem }: MenuProps) => {
|
||||
const [open, setOpen] = useState<{ [key: string]: boolean }>({});
|
||||
|
||||
// Helper function to generate a unique key for each item based on its path in the hierarchy
|
||||
const getItemKey = (item: ListItem, path: string) => `${path}-${item.text}`;
|
||||
const getItemKey = (item: MenuItem, path: string) => `${path}-${item.text}`;
|
||||
|
||||
const handleClick = (item: ListItem, itemKey: string) => {
|
||||
const handleClick = (item: MenuItem, itemKey: string) => {
|
||||
setOpen((prevState) => ({ ...prevState, [itemKey]: !prevState[itemKey] }));
|
||||
if (item.onClick) {
|
||||
item.onClick();
|
||||
}
|
||||
};
|
||||
|
||||
const renderListItem = (item: ListItem, index: number, path: string) => {
|
||||
const itemKey = getItemKey(item, path); // Generate unique key based on the path
|
||||
const renderListItem = (item: MenuItem, index: number, path: string) => {
|
||||
const itemKey = getItemKey(item, path);
|
||||
const isOpen = open[itemKey] === true;
|
||||
const hasChildren = item.children && item.children.length > 0;
|
||||
|
||||
return (
|
||||
<li key={itemKey} className={clsx("list-item", className)}>
|
||||
<li key={itemKey} className={clsx("menu-item", className)}>
|
||||
{renderItem ? (
|
||||
renderItem(item, isOpen, () => handleClick(item, itemKey))
|
||||
) : (
|
||||
<div className="list-item-button" onClick={() => handleClick(item, itemKey)}>
|
||||
<div className="list-item-content">
|
||||
<div className="list-item-icon">{item.icon}</div>
|
||||
<div className="list-item-text">{item.text}</div>
|
||||
<div className="menu-item-button" onClick={() => handleClick(item, itemKey)}>
|
||||
<div className="menu-item-content">
|
||||
<div className="menu-item-icon">{item.icon}</div>
|
||||
<div className="menu-item-text">{item.text}</div>
|
||||
</div>
|
||||
{hasChildren && (
|
||||
<i className={`fa-sharp fa-solid ${isOpen ? "fa-angle-up" : "fa-angle-down"}`}></i>
|
||||
@ -62,9 +62,9 @@ const List = memo(({ items, className, renderItem }: ListProps) => {
|
||||
);
|
||||
};
|
||||
|
||||
return <ul className="list">{items.map((item, index) => renderListItem(item, index, "root"))}</ul>;
|
||||
return <ul className="menu">{items.map((item, index) => renderListItem(item, index, "root"))}</ul>;
|
||||
});
|
||||
|
||||
List.displayName = "List";
|
||||
Menu.displayName = "Menu";
|
||||
|
||||
export { List };
|
||||
export { Menu };
|
@ -1,16 +1,16 @@
|
||||
// Copyright 2024, Command Line Inc.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
import { Avatar } from "@/app/element/avatar"
|
||||
import { List } from "@/app/element/list"
|
||||
import { List } from "@/app/element/menu"
|
||||
import { ChatItem } from "./chatitem"
|
||||
import type { Channel, Message, User } from "./data"
|
||||
import type { MessageListItem, UserListItem } from "./data"
|
||||
|
||||
import "./Layout.css";
|
||||
|
||||
interface ChatViewProps {
|
||||
channels: Channel[];
|
||||
users: User[];
|
||||
messages: Message[];
|
||||
channels: ListItem[];
|
||||
users: UserListItem[];
|
||||
messages: MessageListItem[];
|
||||
}
|
||||
|
||||
const ChatView = ({ channels, users, messages }: ChatViewProps) => {
|
||||
@ -33,7 +33,7 @@ const ChatView = ({ channels, users, messages }: ChatViewProps) => {
|
||||
))}
|
||||
<List items={channels}></List>
|
||||
<div className="chat-section">
|
||||
<List items={messages} renderItem={<ChatItem />}></List>
|
||||
<List items={messages} renderItem={(props) => <ChatItem {...props} />}></List>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
|
@ -2,14 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
import { Avatar } from "@/app/element/avatar";
|
||||
|
||||
export type Channel = {
|
||||
text: string;
|
||||
icon: JSX.Element;
|
||||
onClick: () => void;
|
||||
children?: Channel[];
|
||||
};
|
||||
|
||||
export const channels: Channel[] = [
|
||||
export const channels: ListItem[] = [
|
||||
{
|
||||
text: "Channel 1",
|
||||
icon: <i className="fa-sharp fa-solid fa-wave"></i>,
|
||||
@ -246,13 +239,11 @@ export const channels: Channel[] = [
|
||||
},
|
||||
];
|
||||
|
||||
export type User = {
|
||||
name: string;
|
||||
export type UserListItem = ListItem & {
|
||||
status: "online" | "busy" | "away" | "offline";
|
||||
onClick: () => void;
|
||||
};
|
||||
|
||||
export const users = [
|
||||
export const users: UserListItem[] = [
|
||||
{
|
||||
text: "John Doe",
|
||||
status: "online",
|
||||
@ -279,12 +270,11 @@ export const users = [
|
||||
},
|
||||
];
|
||||
|
||||
export type Message = {
|
||||
text: string;
|
||||
export type MessageListItem = ListItem & {
|
||||
timestamp: string;
|
||||
};
|
||||
|
||||
export const messages: Message[] = [
|
||||
export const messages: MessageListItem[] = [
|
||||
{ text: "Message 1 content", timestamp: "2024-09-24 17:02:12" },
|
||||
{ text: "Message 2 content", timestamp: "2024-07-11 04:17:12" },
|
||||
{ text: "Message 3 content", timestamp: "2024-07-30 15:32:12" },
|
||||
|
Loading…
Reference in New Issue
Block a user