-
Notifications
You must be signed in to change notification settings - Fork 141
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Co-authored-by: KANAMORI Yu <[email protected]>
- Loading branch information
Showing
4 changed files
with
115 additions
and
94 deletions.
There are no files selected for viewing
36 changes: 18 additions & 18 deletions
36
packages/smarthr-ui/src/components/Experimental/SideMenu/SideMenu.stories.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,32 @@ | ||
import { StoryFn } from '@storybook/react' | ||
import React from 'react' | ||
|
||
import { SideMenu } from './SideMenu' | ||
import { FaGearIcon } from '../../Icon' | ||
|
||
import { SideMenu, SideMenuGroup, SideMenuItem } from '.' | ||
|
||
export default { | ||
title: 'Experimental(実験的)/SideMenu', | ||
component: SideMenu, | ||
parameters: { | ||
layout: 'fullscreen', | ||
withTheming: true, | ||
}, | ||
} | ||
|
||
export const Default: StoryFn = () => ( | ||
<div className="shr-bg-background shr-p-2"> | ||
<div className="shr-bg-background shr-p-2 shr-max-w-[15rem]"> | ||
<SideMenu> | ||
<SideMenu.Group name="基本設定"> | ||
<SideMenu.Item href="#">評価シート</SideMenu.Item> | ||
<SideMenu.Item href="#" current> | ||
評価ロール | ||
</SideMenu.Item> | ||
<SideMenu.Item href="#">評価フロー</SideMenu.Item> | ||
</SideMenu.Group> | ||
<SideMenu.Group name="その他の設定"> | ||
<SideMenu.Item href="#">評価項目の入力必須設定</SideMenu.Item> | ||
<SideMenu.Item href="#">評価ロールの閲覧・編集権限設定</SideMenu.Item> | ||
<SideMenu.Item href="#">評価対象者の表示項目設定</SideMenu.Item> | ||
</SideMenu.Group> | ||
<SideMenuGroup title="個人設定"> | ||
<SideMenuItem href="#">アカウント</SideMenuItem> | ||
<SideMenuItem href="#" current> | ||
認証設定 | ||
</SideMenuItem> | ||
</SideMenuGroup> | ||
<SideMenuGroup title="共通設定"> | ||
<SideMenuItem href="#" prefix={<FaGearIcon />}> | ||
評価項目の表示設定 | ||
</SideMenuItem> | ||
<SideMenuItem href="#" prefix={<FaGearIcon />}> | ||
評価対象者の入力必須項目設定 | ||
</SideMenuItem> | ||
</SideMenuGroup> | ||
</SideMenu> | ||
</div> | ||
) |
36 changes: 12 additions & 24 deletions
36
packages/smarthr-ui/src/components/Experimental/SideMenu/SideMenu.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,32 +1,20 @@ | ||
import React, { ComponentProps, FC, useMemo } from 'react' | ||
import React, { ComponentPropsWithoutRef, FC, useMemo } from 'react' | ||
import { tv } from 'tailwind-variants' | ||
|
||
import { Stack } from '../../Layout' | ||
|
||
import { SideMenuGroup } from './SideMenuGroup' | ||
import { SideMenuItem } from './SideMenuItem' | ||
|
||
type SubComponents = { | ||
/** @deprecated SideMenu は削除予定です */ | ||
Group: typeof SideMenuGroup | ||
/** @deprecated SideMenu は削除予定です */ | ||
Item: typeof SideMenuItem | ||
} | ||
import { Base } from '../../Base' | ||
|
||
const sideMenu = tv({ | ||
base: 'smarthr-ui-SideMenu shr-list-none', | ||
base: 'smarthr-ui-SideMenu shr-list-none shr-py-0.5', | ||
}) | ||
|
||
/** @deprecated SideMenu コンポーネントは 2024/01 に削除予定です。別コンポーネントで代替するか、UI を見直してください。 */ | ||
export const SideMenu: FC<ComponentProps<typeof Stack>> & SubComponents = ({ | ||
className, | ||
...rest | ||
}) => { | ||
type Props = Pick<ComponentPropsWithoutRef<typeof Base>, 'radius' | 'layer' | 'className'> & { | ||
/** | ||
* @default ul | ||
*/ | ||
elementAs?: 'ul' | 'ol' | ||
} | ||
|
||
export const SideMenu: FC<Props> = ({ elementAs = 'ul', className, ...rest }) => { | ||
const styles = useMemo(() => sideMenu({ className }), [className]) | ||
return ( | ||
// eslint-disable-next-line smarthr/best-practice-for-layouts | ||
<Stack {...rest} as="ul" inline gap={0.75} className={styles} /> | ||
) | ||
return <Base {...rest} as={elementAs} className={styles} /> | ||
} | ||
SideMenu.Group = SideMenuGroup | ||
SideMenu.Item = SideMenuItem |
64 changes: 38 additions & 26 deletions
64
packages/smarthr-ui/src/components/Experimental/SideMenu/SideMenuGroup.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,48 +1,60 @@ | ||
import React, { ComponentProps, PropsWithChildren, ReactNode, useMemo } from 'react' | ||
import React, { | ||
ComponentPropsWithoutRef, | ||
ElementType, | ||
PropsWithChildren, | ||
ReactNode, | ||
useMemo, | ||
} from 'react' | ||
import { tv } from 'tailwind-variants' | ||
|
||
import { Stack } from '../../Layout' | ||
import { Text } from '../../Text' | ||
|
||
type Props = PropsWithChildren<{ | ||
/** 分類ラベル */ | ||
name: ReactNode | ||
/** 分類ラベルの HTML タグ */ | ||
nameTag?: ComponentProps<typeof Text>['as'] | ||
}> | ||
type ElementProps = ComponentProps<typeof Stack> | ||
type Props<TitleElement extends ElementType = 'p'> = PropsWithChildren<{ | ||
title: ReactNode | ||
titleElementAs?: TitleElement | ||
|
||
/** | ||
* @default ul | ||
*/ | ||
listElementAs?: 'ul' | 'ol' | ||
}> & | ||
ComponentPropsWithoutRef<TitleElement> | ||
|
||
const sideMenuGroup = tv({ | ||
slots: { | ||
wrapper: ['smarthr-ui-SideMenu-group', '[&_+_&]:shr-border-t-shorthand [&_+_&]:shr-pt-1.25'], | ||
wrapper: ['smarthr-ui-SideMenu-group', '[&:not(:first-of-type)]:shr-mt-1'], | ||
list: 'shr-list-none', | ||
groupTitle: 'shr-block shr-px-1 shr-py-0.5', | ||
}, | ||
}) | ||
|
||
export const SideMenuGroup: React.FC<Props & ElementProps> = ({ | ||
name, | ||
nameTag = 'h3', | ||
export const SideMenuGroup = <TitleElement extends ElementType = 'p'>({ | ||
title, | ||
titleElementAs, | ||
listElementAs, | ||
children, | ||
className, | ||
...rest | ||
}) => { | ||
const { wrapperStyle, listStyle } = useMemo(() => { | ||
const { wrapper, list } = sideMenuGroup() | ||
}: Props<TitleElement>) => { | ||
const { wrapperStyle, listStyle, groupTitleStyle } = useMemo(() => { | ||
const { wrapper, list, groupTitle } = sideMenuGroup() | ||
return { | ||
wrapperStyle: wrapper({ className }), | ||
listStyle: list(), | ||
groupTitleStyle: groupTitle(), | ||
} | ||
}, [className]) | ||
|
||
const TitleComponent = titleElementAs ?? 'p' | ||
const ListComponent = listElementAs ?? 'ul' | ||
|
||
return ( | ||
<Stack {...rest} as="li" gap={0.5} className={wrapperStyle}> | ||
<Text color="TEXT_GREY" leading="TIGHT" size="S" weight="normal" as={nameTag}> | ||
{name} | ||
</Text> | ||
{/* eslint-disable-next-line smarthr/best-practice-for-layouts */} | ||
<Stack as="ul" gap={0} className={listStyle}> | ||
{children} | ||
</Stack> | ||
</Stack> | ||
<li className={wrapperStyle}> | ||
<TitleComponent> | ||
<Text color="TEXT_BLACK" leading="TIGHT" size="S" weight="bold" className={groupTitleStyle}> | ||
{title} | ||
</Text> | ||
</TitleComponent> | ||
<ListComponent className={listStyle}>{children}</ListComponent> | ||
</li> | ||
) | ||
} |
73 changes: 47 additions & 26 deletions
73
packages/smarthr-ui/src/components/Experimental/SideMenu/SideMenuItem.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,56 +1,77 @@ | ||
import React, { ComponentPropsWithoutRef, PropsWithChildren, useMemo } from 'react' | ||
import React, { | ||
ComponentPropsWithoutRef, | ||
ElementType, | ||
PropsWithChildren, | ||
ReactNode, | ||
useMemo, | ||
} from 'react' | ||
import { tv } from 'tailwind-variants' | ||
|
||
type Props = PropsWithChildren<{ | ||
/** 現在地かどうか */ | ||
import { Text } from '../../Text' | ||
|
||
type BaseProps<AsElement extends ElementType> = PropsWithChildren<{ | ||
elementAs?: AsElement | ||
current?: boolean | ||
prefix?: ReactNode | ||
}> | ||
type ElementProps = Omit<ComponentPropsWithoutRef<'li'>, keyof Props> | ||
type InnerLinkProps = Omit<ComponentPropsWithoutRef<'a'>, keyof Props & keyof ElementProps> | ||
|
||
type Props<AsElement extends ElementType = 'a'> = BaseProps<AsElement> & | ||
Omit<ComponentPropsWithoutRef<AsElement>, keyof BaseProps<AsElement>> | ||
|
||
const sideMenuItem = tv({ | ||
slots: { | ||
wrapper: ['smarthr-ui-SideMenu-item', 'shr-relative shr-ps-0.75'], | ||
innerLink: [ | ||
// 親要素ではなくリンクにスタイリングするため block でいっぱいに広げている | ||
'shr-block', | ||
'shr-rounded-m shr-px-1 shr-py-0.75 shr-no-underline', | ||
wrapper: [ | ||
'smarthr-ui-SideMenu-item', | ||
'[&>a]:shr-no-underline [&>a]:shr-block', | ||
'[&>*:focus-visible]:shr-focus-indicator', | ||
], | ||
content: [ | ||
'shr-flex shr-gap-0.5 shr-p-0.75 shr-items-baseline', | ||
'aria-current-page:shr-bg-grey-9 aria-current-page:shr-font-bold', | ||
'hover:shr-bg-grey-9-darken', | ||
// フォーカスリングを前に出したいので、スタッキングコンテキストを発生させている | ||
'focus-visible:shr-focus-indicator focus-visible:shr-relative focus-visible:shr-z-1', | ||
'hover:shr-bg-head-darken', | ||
], | ||
// 視覚調整のためのtranslate 参考: https://github.com/kufu/smarthr-ui/blob/01d127a4888f5698b2bf17be855ce1e985b575ea/packages/smarthr-ui/src/components/Icon/generateIcon.tsx#L73C81-L73C106 | ||
iconWrapper: ['shr-text-grey shr-translate-y-[0.125em]'], | ||
}, | ||
variants: { | ||
current: { | ||
true: { | ||
wrapper: | ||
'before:shr-absolute before:shr-inset-y-0 before:shr-left-0 before:shr-block before:shr-w-[3px] before:shr-bg-main before:shr-content-[""]', | ||
content: | ||
'shr-ps-1.25 shr-border-[theme(colors.main)] shr-border-0 shr-border-s-4 shr-border-solid shr-bg-over-background', | ||
}, | ||
false: { | ||
content: 'shr-ps-1.5', | ||
}, | ||
}, | ||
}, | ||
}) | ||
|
||
export const SideMenuItem: React.FC<Props & ElementProps & InnerLinkProps> = ({ | ||
href, | ||
children, | ||
export const SideMenuItem = <AsElement extends ElementType = 'a'>({ | ||
elementAs, | ||
current, | ||
prefix, | ||
children, | ||
className, | ||
...rest | ||
}) => { | ||
const { wrapperStyle, innerLinkStyle } = useMemo(() => { | ||
const { wrapper, innerLink } = sideMenuItem() | ||
}: Props<AsElement>) => { | ||
const Component = elementAs ?? 'a' | ||
const { wrapperStyle, contentStyle, iconWrapperStyle } = useMemo(() => { | ||
const { wrapper, content, iconWrapper } = sideMenuItem() | ||
return { | ||
wrapperStyle: wrapper({ current, className }), | ||
innerLinkStyle: innerLink(), | ||
contentStyle: content({ current }), | ||
iconWrapperStyle: iconWrapper(), | ||
} | ||
}, [className, current]) | ||
|
||
return ( | ||
<li {...rest} className={wrapperStyle}> | ||
<a href={href} aria-current={current && 'page'} className={innerLinkStyle}> | ||
{children} | ||
</a> | ||
<li className={wrapperStyle}> | ||
<Component {...rest}> | ||
<Text size="M" leading="TIGHT" className={contentStyle}> | ||
{prefix && <span className={iconWrapperStyle}>{prefix}</span>} | ||
<Text weight={current ? 'bold' : undefined}>{children}</Text> | ||
</Text> | ||
</Component> | ||
</li> | ||
) | ||
} |