-
Notifications
You must be signed in to change notification settings - Fork 141
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
chore: Heading/LanguageSwitcher, Icon, InformationPanelの内部ロジックを整理する #5433
base: master
Are you sure you want to change the base?
Changes from 10 commits
cf42165
2f331c6
c6679c6
4de323e
d7f40bc
6e1f16b
28b3929
9daba33
56de182
69701f1
bb29b8c
564778a
50da1fb
242bf32
e240c52
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,14 @@ | ||
'use client' | ||
|
||
import React, { HTMLAttributes, ReactNode, useCallback, useMemo } from 'react' | ||
import React, { | ||
type FC, | ||
type HTMLAttributes, | ||
type KeyboardEvent, | ||
type MouseEvent, | ||
type ReactNode, | ||
memo, | ||
useMemo, | ||
} from 'react' | ||
import { VariantProps, tv } from 'tailwind-variants' | ||
|
||
import { type DecoratorsType } from '../../../hooks/useDecorators' | ||
|
@@ -20,7 +28,7 @@ export type Props = { | |
decorators?: DecoratorsType<'triggerLabel' | 'checkIconAlt'> | ||
/** 言語切替UIで言語を選択した時に発火するコールバック関数 */ | ||
onLanguageSelect?: (code: string) => void | ||
} & VariantProps<typeof appLauncher> | ||
} & VariantProps<typeof classNameGenerator> | ||
|
||
type ElementProps = Omit<HTMLAttributes<HTMLElement>, keyof Props> | ||
|
||
|
@@ -29,7 +37,27 @@ const CHECK_ICON_ALT = '選択中' | |
const ARROW_KEY_REGEX = /^Arrow(Up|Down|Left|Right)$/ | ||
const ARROW_UPS_REGEX = /^Arrow(Up|Left)$/ | ||
|
||
const appLauncher = tv({ | ||
const ON_KEY_DOWN_CONTENT = (e: KeyboardEvent<HTMLDivElement>) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. imo: 関数も大文字にするの見慣れない感じがしました。特に定数感を強めなくても There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. あー、どうなんだろう。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. (個人的には)あまり見慣れないなぁと思いました。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 難しいな。 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. キャメルケースに戻すのは自分に良さそうに思いました! There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. onになりました。 https://kufuinc.slack.com/archives/CGC58MW01/p1741237708852539 まとめ
|
||
if (!ARROW_KEY_REGEX.test(e.key)) { | ||
return | ||
} | ||
|
||
e.preventDefault() | ||
|
||
const buttons = tabbable(e.currentTarget) | ||
const i = buttons.indexOf(e.target as HTMLElement) | ||
let buttonAt = 0 | ||
|
||
if (ARROW_UPS_REGEX.test(e.key)) { | ||
buttonAt = i - 1 | ||
} else if (i + 1 === buttons.length) { | ||
buttonAt = i + 1 | ||
} | ||
|
||
buttons.at(buttonAt)?.focus() | ||
} | ||
|
||
const classNameGenerator = tv({ | ||
slots: { | ||
switchButton: [ | ||
'shr-border-none shr-font-normal shr-text-white shr-transition-transform shr-duration-100 shr-bg-transparent shr-px-0.25', | ||
|
@@ -64,7 +92,7 @@ const appLauncher = tv({ | |
}, | ||
}) | ||
|
||
export const LanguageSwitcher: React.FC<Props & ElementProps> = ({ | ||
export const LanguageSwitcher: FC<Props & ElementProps> = ({ | ||
narrow, | ||
enableNew, | ||
invert = enableNew, | ||
|
@@ -75,7 +103,13 @@ export const LanguageSwitcher: React.FC<Props & ElementProps> = ({ | |
onLanguageSelect, | ||
...rest | ||
}) => { | ||
const locales = useMemo(() => Object.entries(localeMap), [localeMap]) | ||
const { locales, defaultCurrentLang } = useMemo( | ||
() => ({ | ||
locales: Object.entries(localeMap), | ||
defaultCurrentLang: Object.keys(localeMap)[0], | ||
}), | ||
[localeMap], | ||
) | ||
const decoratedTexts = useMemo(() => { | ||
if (!decorators) { | ||
return { | ||
|
@@ -89,12 +123,9 @@ export const LanguageSwitcher: React.FC<Props & ElementProps> = ({ | |
checkIconAlt: decorators.checkIconAlt?.(CHECK_ICON_ALT) || CHECK_ICON_ALT, | ||
} | ||
}, [decorators]) | ||
const currentLang = useMemo( | ||
() => locale || defaultLocale || Object.keys(localeMap)[0], | ||
[locale, defaultLocale, localeMap], | ||
) | ||
const styles = useMemo(() => { | ||
const { languageButton, languageItemsList, languageItem, switchButton } = appLauncher() | ||
const currentLang = locale || defaultLocale || defaultCurrentLang | ||
const classNames = useMemo(() => { | ||
const { languageButton, languageItemsList, languageItem, switchButton } = classNameGenerator() | ||
|
||
return { | ||
languageButton: languageButton(), | ||
|
@@ -104,54 +135,34 @@ export const LanguageSwitcher: React.FC<Props & ElementProps> = ({ | |
} | ||
}, [enableNew, invert]) | ||
|
||
const handleLanguageSelect = useMemo( | ||
const onClickLanguageSelect = useMemo( | ||
uknmr marked this conversation as resolved.
Show resolved
Hide resolved
|
||
() => | ||
onLanguageSelect | ||
? (e: React.MouseEvent<HTMLButtonElement>) => { | ||
? (e: MouseEvent<HTMLButtonElement>) => { | ||
onLanguageSelect(e.currentTarget.value) | ||
} | ||
: undefined, | ||
[onLanguageSelect], | ||
) | ||
|
||
const handleKeyDown = useCallback((e: React.KeyboardEvent<HTMLDivElement>) => { | ||
if (!ARROW_KEY_REGEX.test(e.key)) { | ||
return | ||
} | ||
|
||
e.preventDefault() | ||
|
||
const buttons = tabbable(e.currentTarget) | ||
const i = buttons.indexOf(e.target as HTMLElement) | ||
let buttonAt = 0 | ||
|
||
if (ARROW_UPS_REGEX.test(e.key)) { | ||
buttonAt = i - 1 | ||
} else if (i + 1 === buttons.length) { | ||
buttonAt = i + 1 | ||
} | ||
|
||
buttons.at(buttonAt)?.focus() | ||
}, []) | ||
|
||
return ( | ||
<Dropdown {...rest}> | ||
<MemoizedDropdownTrigger | ||
narrow={narrow} | ||
invert={invert} | ||
className={styles.switchButton} | ||
className={classNames.switchButton} | ||
label={decoratedTexts.triggerLabel} | ||
/> | ||
<DropdownContent onKeyDown={handleKeyDown} role="presentation"> | ||
<ul className={styles.languageItemsList}> | ||
<DropdownContent role="presentation" onKeyDown={ON_KEY_DOWN_CONTENT}> | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ここも同様の理由でキャメルケースに戻すと良いですかね? |
||
<ul className={classNames.languageItemsList}> | ||
{locales.map(([code, label]) => ( | ||
<LanguageListItemButton | ||
key={code} | ||
code={code} | ||
className={styles.languageItem} | ||
buttonStyle={styles.languageButton} | ||
className={classNames.languageItem} | ||
buttonStyle={classNames.languageButton} | ||
current={currentLang === code} | ||
handleLanguageSelect={handleLanguageSelect} | ||
onClick={onClickLanguageSelect} | ||
iconAlt={decoratedTexts.checkIconAlt} | ||
> | ||
{label} | ||
|
@@ -163,19 +174,19 @@ export const LanguageSwitcher: React.FC<Props & ElementProps> = ({ | |
) | ||
} | ||
|
||
const LanguageListItemButton = React.memo<{ | ||
const LanguageListItemButton = memo<{ | ||
code: string | ||
children: string | ||
className: string | ||
buttonStyle: string | ||
current: boolean | ||
iconAlt: ReactNode | ||
handleLanguageSelect?: (e: React.MouseEvent<HTMLButtonElement>) => void | ||
}>(({ code, children, buttonStyle, className, current, iconAlt, handleLanguageSelect }) => ( | ||
onClick?: (e: MouseEvent<HTMLButtonElement>) => void | ||
}>(({ code, children, buttonStyle, className, current, iconAlt, onClick }) => ( | ||
<li key={code} className={className} aria-current={current} lang={code}> | ||
<Button | ||
value={code} | ||
onClick={handleLanguageSelect} | ||
onClick={onClick} | ||
wide | ||
prefix={current ? <FaCheckIcon color="MAIN" alt={iconAlt} /> : null} | ||
className={buttonStyle} | ||
|
@@ -185,7 +196,7 @@ const LanguageListItemButton = React.memo<{ | |
</li> | ||
)) | ||
|
||
const MemoizedDropdownTrigger = React.memo< | ||
const MemoizedDropdownTrigger = memo< | ||
Pick<Props, 'narrow' | 'invert'> & { className: string; label: ReactNode } | ||
>(({ narrow, invert, className, label }) => ( | ||
<DropdownTrigger> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
stateなどと依存関係がないため、定数化しました