Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: modify styling of Menu & add CascaderList #489

Merged
merged 3 commits into from
Nov 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion packages/base-ui/.gitignore

This file was deleted.

13 changes: 2 additions & 11 deletions packages/base-ui/src/components/custom-label/CustomLabel.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { LocaleService } from '@univerjs/core';
import { CheckMarkSingle } from '@univerjs/icons';
import { useDependency } from '@wendellhu/redi/react-bindings';

import { ComponentManager } from '../../Common';
Expand All @@ -8,8 +7,6 @@ import { IMenuSelectorItem } from '../../services/menu/menu';
export type ICustomLabelProps = {
value?: string | number | undefined;

selected?: boolean;

onChange?(v: string | number): void;
} & Pick<IMenuSelectorItem<unknown>, 'label' | 'icon' | 'title'>;

Expand All @@ -19,19 +16,13 @@ export type ICustomLabelProps = {
* @returns
*/
export function CustomLabel(props: ICustomLabelProps): JSX.Element | null {
const { title, icon, label, selected } = props;
const { title, icon, label } = props;
const localeService = useDependency(LocaleService);
const componentManager = useDependency(ComponentManager);

const nodes = [];
let index = 0;
if (selected) {
nodes.push(
<span key={index++}>
<CheckMarkSingle style={{ color: 'rgb(var(--success-color))' }} />
</span>
);
}

if (icon) {
const Icon = componentManager.get(icon);
Icon && nodes.push(<Icon key={index++} extend={{ colorChannel1: 'rgb(var(--primary-color))' }} />);
Expand Down
17 changes: 8 additions & 9 deletions packages/base-ui/src/components/menu/Menu.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import {
MenuItemGroup as DesignMenuItemGroup,
SubMenu as DesignSubMenu,
} from '@univerjs/design';
import { MoreSingle } from '@univerjs/icons';
import { CheckMarkSingle, MoreSingle } from '@univerjs/icons';
import { useDependency } from '@wendellhu/redi/react-bindings';
import clsx from 'clsx';
import { useState } from 'react';
Expand Down Expand Up @@ -121,14 +121,13 @@ function MenuOptionsWrapper(props: IBaseMenuProps) {

return (
<DesignMenuItem key={key} eventKey={key} className={_className} onClick={handleClick}>
<span className={styles.menuItemContent}>
<CustomLabel
selected={typeof value !== 'undefined' && value === option.value}
value={option.value}
label={option.label}
icon={option.icon}
onChange={onChange}
/>
<span className={clsx(styles.menuItemContent, styles.menuItemSelectable)}>
{typeof value !== 'undefined' && String(value) === String(option.value) && (
<span className={styles.menuItemSelectableIcon}>
<CheckMarkSingle style={{ color: 'rgb(var(--success-color))' }} />
</span>
)}
<CustomLabel value={option.value} label={option.label} icon={option.icon} onChange={onChange} />
</span>
</DesignMenuItem>
);
Expand Down
15 changes: 15 additions & 0 deletions packages/base-ui/src/components/menu/index.module.less
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,21 @@
align-items: center;
}

&-selectable {
position: relative;
padding-left: var(--padding-xl);

&-icon {
position: absolute;
left: 0;

display: inline-flex;
align-items: center;

font-size: var(--font-size-lg);
}
}

&-more-icon {
font-size: var(--font-size-sm);
color: rgb(var(--text-color));
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import type { Meta } from '@storybook/react';
import { useState } from 'react';

import { CascaderList } from './CascaderList';

const meta: Meta<typeof CascaderList> = {
title: 'Components / CascaderList',
component: CascaderList,
parameters: {
layout: 'centered',
},
tags: ['autodocs'],
};

export default meta;

export const Playground = {
render() {
const [value, setValue] = useState<string[]>([]);

const options = [
{
value: '常规',
label: '常规',
children: [
{
value: 'hangzhou',
label: 'Hangzhou',
},
],
},
{
value: '货币',
label: '货币',
children: [
{
value: 'nanjing',
label: 'Nanjing',
},
],
},
{
value: '自定义',
label: '自定义',
children: [
{
value: 'beijing',
label: 'beijing',
children: [
{
value: '-1,234.00',
label: '-1,234.00',
},
{
value: '(1,234.00)',
label: '(1,234.00)',
},
],
},
],
},
{
value: '日期',
label: '日期',
children: [],
},
{
value: '会计专用',
label: '会计专用',
},
];

function handleChange(value: string[]) {
setValue(value);
}

return <CascaderList value={value} options={options} onChange={handleChange} />;
},
};
100 changes: 100 additions & 0 deletions packages/design/src/components/cascader-list/CascaderList.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { CheckMarkSingle } from '@univerjs/icons';
import clsx from 'clsx';
import { useMemo } from 'react';

import styles from './index.module.less';

interface IOption {
label: string;
value: string;
color?: string;
children?: IOption[];
}

export interface ICascaderListProps {
/**
* The value of select
*/
value: string[];

/**
* The options of select
* @default []
*/
options?: IOption[];

/**
* The callback function that is triggered when the value is changed
*/
onChange: (value: string[]) => void;
}

export function CascaderList(props: ICascaderListProps) {
const { value, options = [], onChange } = props;

const activeOptions = useMemo(() => {
const activeOptions = [options];
value.forEach((item, index) => {
const option = activeOptions[index].find((option) => option.value === item);

if (option?.children) {
activeOptions.push(option.children);
}
});

return activeOptions;
}, [value]);

function handleChange(index: number, v: string) {
if (v === props.value[index]) {
return;
}

if (props.value[index + 1]) {
const newValue = props.value.slice(0, index + 1);
newValue[index] = v;

onChange(newValue);
return;
}

const newValue = [...props.value];
newValue[index] = v;

onChange(newValue);
}

return (
<section className={styles.cascaderList}>
{activeOptions.map((options, index) =>
options.length ? (
<ul key={index} className={styles.cascaderListBoard}>
{options.map((option) => (
<li
key={option.value}
className={clsx(styles.cascaderListItem, {
[styles.cascaderListItemActive]: option.value === value[index],
})}
>
<a
className={styles.cascaderListOption}
onClick={() => handleChange(index, option.value)}
>
<span className={styles.cascaderListCheckMark}>
{option.value === value[index] && <CheckMarkSingle />}
</span>
<span>{option.label}</span>
</a>
</li>
))}
</ul>
) : (
<section key={index} className={styles.cascaderListEmpty}>
</section>
)
)}
{value.length <= 0 && <section className={styles.cascaderListEmpty}>无</section>}
</section>
);
}
91 changes: 91 additions & 0 deletions packages/design/src/components/cascader-list/index.module.less
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
.cascader-list {
overflow-y: auto;
display: flex;

height: 100%;
max-height: 348px;
padding: var(--padding-sm);

color: rgb(var(--text-color));

border: 1px solid rgb(var(--border-color));
border-radius: var(--border-radius-base);

&-board {
overflow: auto;

height: 100%;
margin: 0;
padding: 0;

list-style: none;

&:not(:last-child) {
margin-right: var(--margin-xs);
padding-right: var(--padding-sm);
border-right: 1px solid rgb(var(--border-color));
}

&:first-child {
.cascader-list {
&-item {
&-active {
color: rgb(var(--primary-color));
}
}

&-option {
padding: var(--padding-xs) 0;
}

&-check-mark {
display: none;
}
}
}

&:not(:first-child) {
.cascader-list {
&-item {
&-active {
background-color: rgb(var(--bg-color-hover));
}
}

&-option {
padding: var(--padding-xs) 28px;
}
}
}
}

&-item {
border-radius: var(--border-radius-base);
}

&-option {
cursor: pointer;
position: relative;
display: block;
font-size: var(--font-size-xs);
}

&-check-mark {
position: absolute;
top: 50%;
left: var(--margin-xs);
transform: translateY(-50%);

display: flex;
align-items: center;

font-size: var(--font-size-lg);
color: rgb(var(--primary-color));
}

&-empty {
padding-right: 120px;
font-size: var(--font-size-xs);
color: rgb(var(--text-color-secondary));
}
}
1 change: 1 addition & 0 deletions packages/design/src/components/cascader-list/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { CascaderList, type ICascaderListProps } from './CascaderList';
Loading
Loading