このコミットが含まれているのは:
@@ -208,12 +208,12 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<nav className="px-3 flex justify-between items-center w-full
|
||||
border-b top-nav-themed">
|
||||
<nav className="top-nav-root px-3 flex justify-between items-center w-full
|
||||
border-b">
|
||||
<div className="flex items-center gap-2 h-12">
|
||||
<PrefetchLink
|
||||
to="/posts"
|
||||
className="mx-4 text-xl font-bold"
|
||||
className="top-nav-link mx-4 text-xl font-bold"
|
||||
onClick={() => {
|
||||
scroll (0, 0)
|
||||
}}>
|
||||
@@ -223,7 +223,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
<div ref={navRef} className="relative hidden md:flex h-12 items-center">
|
||||
<div aria-hidden
|
||||
className={cn ('absolute inset-y-0 h-12',
|
||||
'top-nav-themed-active',
|
||||
'top-nav-highlight',
|
||||
highlightTransitionClass)}
|
||||
style={{ width: hl.width,
|
||||
transform: `translateX(${ hl.left }px)`,
|
||||
@@ -245,8 +245,8 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
ref={(el: (HTMLAnchorElement | null)) => {
|
||||
itemsRef.current[i] = el
|
||||
}}
|
||||
className={cn ('relative z-10 flex h-full items-center px-5',
|
||||
(i === openItemIdx) && 'top-nav-themed-active font-bold')}>
|
||||
className={cn ('top-nav-link relative z-10 flex h-full items-center px-5',
|
||||
(i === openItemIdx) && 'font-bold')}>
|
||||
{item.name}
|
||||
</PrefetchLink>
|
||||
</motion.div>))}
|
||||
@@ -260,8 +260,8 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
setMoreVsbl (true)
|
||||
measure (-1)
|
||||
}}
|
||||
className={cn ('relative z-10 flex h-full items-center px-5',
|
||||
(openItemIdx < 0 || moreVsbl) && 'top-nav-themed-active font-bold')}>
|
||||
className={cn ('top-nav-link relative z-10 flex h-full items-center px-5',
|
||||
(openItemIdx < 0 || moreVsbl) && 'font-bold')}>
|
||||
その他 »
|
||||
</PrefetchLink>
|
||||
</div>
|
||||
@@ -271,7 +271,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
|
||||
<button
|
||||
type="button"
|
||||
className="md:hidden ml-auto border-0 bg-transparent pr-4"
|
||||
className="top-nav-link md:hidden ml-auto border-0 bg-transparent pr-4"
|
||||
onClick={() => {
|
||||
setMenuOpen (!(menuOpen))
|
||||
}}>
|
||||
@@ -284,8 +284,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
key="submenu-shell"
|
||||
{...(animationsOff ? { }
|
||||
: { layout: true })}
|
||||
className="relative z-20 hidden md:block overflow-hidden
|
||||
top-nav-themed-submenu"
|
||||
className="relative z-20 hidden md:block overflow-hidden top-nav-submenu"
|
||||
animate={{ height: submenuHeight }}
|
||||
onMouseLeave={() => {
|
||||
if (moreVsbl)
|
||||
@@ -346,7 +345,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
to={subItem.to}
|
||||
target={subItem.to.slice (0, 2) === '//' ? '_blank' : undefined}
|
||||
onClick={() => setMoreVsbl (false)}
|
||||
className="h-full flex items-center px-3">
|
||||
className="top-nav-link h-full flex items-center px-3">
|
||||
{subItem.name}
|
||||
</PrefetchLink>
|
||||
</motion.div>)))}
|
||||
@@ -373,7 +372,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
target={item.to.slice (0, 2) === '//'
|
||||
? '_blank'
|
||||
: undefined}
|
||||
className="h-full flex items-center px-3">
|
||||
className="top-nav-link h-full flex items-center px-3">
|
||||
{item.name}
|
||||
</PrefetchLink>
|
||||
</div>)))}
|
||||
@@ -412,7 +411,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
target={item.to.slice (0, 2) === '//'
|
||||
? '_blank'
|
||||
: undefined}
|
||||
className="h-full flex items-center px-3">
|
||||
className="top-nav-link h-full flex items-center px-3">
|
||||
{item.name}
|
||||
</PrefetchLink>
|
||||
</motion.div>)))}
|
||||
@@ -426,16 +425,16 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
? (
|
||||
menuOpen && (
|
||||
<div
|
||||
className={cn ('top-nav-themed top-nav-themed-submenu flex flex-col md:hidden',
|
||||
className={cn ('top-nav-root top-nav-mobile-menu flex flex-col md:hidden',
|
||||
'items-start')}>
|
||||
<Separator/>
|
||||
{visibleMenu.map ((item, i) => (
|
||||
<Fragment key={i}>
|
||||
<PrefetchLink
|
||||
to={i === openItemIdx ? item.to : '#'}
|
||||
className={cn ('w-full min-h-[40px] flex items-center pl-8',
|
||||
className={cn ('top-nav-link w-full min-h-[40px] flex items-center pl-8',
|
||||
((i === openItemIdx)
|
||||
&& 'top-nav-themed-active font-bold'))}
|
||||
&& 'top-nav-mobile-active font-bold'))}
|
||||
onClick={(ev: MouseEvent<HTMLAnchorElement>) => {
|
||||
if (i !== openItemIdx)
|
||||
{
|
||||
@@ -447,7 +446,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
</PrefetchLink>
|
||||
|
||||
{i === openItemIdx && (
|
||||
<div className="top-nav-themed-submenu w-full">
|
||||
<div className="top-nav-submenu w-full">
|
||||
{item.subMenu
|
||||
.filter (subItem => subItem.visible ?? true)
|
||||
.map ((subItem, j) => (
|
||||
@@ -463,7 +462,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
target={subItem.to.slice (0, 2) === '//'
|
||||
? '_blank'
|
||||
: undefined}
|
||||
className="w-full min-h-[36px] flex items-center pl-12">
|
||||
className="top-nav-link w-full min-h-[36px] flex items-center pl-12">
|
||||
{subItem.name}
|
||||
</PrefetchLink>)))}
|
||||
</div>)}
|
||||
@@ -473,9 +472,9 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
ref={(el: (HTMLAnchorElement | null)) => {
|
||||
itemsRef.current[visibleMenu.length] = el
|
||||
}}
|
||||
className={cn ('w-full min-h-[40px] flex items-center pl-8',
|
||||
className={cn ('top-nav-link w-full min-h-[40px] flex items-center pl-8',
|
||||
((openItemIdx < 0)
|
||||
&& 'top-nav-themed-active font-bold'))}>
|
||||
&& 'top-nav-mobile-active font-bold'))}>
|
||||
その他 »
|
||||
</PrefetchLink>
|
||||
<TopNavUser user={user} sp/>
|
||||
@@ -486,7 +485,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
{menuOpen && (
|
||||
<motion.div
|
||||
key="spmenu"
|
||||
className={cn ('top-nav-themed top-nav-themed-submenu flex flex-col md:hidden',
|
||||
className={cn ('top-nav-root top-nav-mobile-menu flex flex-col md:hidden',
|
||||
'items-start')}
|
||||
variants={{ closed: { clipPath: 'inset(0 0 100% 0)',
|
||||
height: 0 },
|
||||
@@ -501,9 +500,9 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
<Fragment key={i}>
|
||||
<PrefetchLink
|
||||
to={i === openItemIdx ? item.to : '#'}
|
||||
className={cn ('w-full min-h-[40px] flex items-center pl-8',
|
||||
className={cn ('top-nav-link w-full min-h-[40px] flex items-center pl-8',
|
||||
((i === openItemIdx)
|
||||
&& 'top-nav-themed-active font-bold'))}
|
||||
&& 'top-nav-mobile-active font-bold'))}
|
||||
onClick={(ev: MouseEvent<HTMLAnchorElement>) => {
|
||||
if (i !== openItemIdx)
|
||||
{
|
||||
@@ -518,7 +517,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
{i === openItemIdx && (
|
||||
<motion.div
|
||||
key={`sp-sub-${ i }`}
|
||||
className="top-nav-themed-submenu w-full"
|
||||
className="top-nav-submenu w-full"
|
||||
variants={{ closed: { clipPath: 'inset(0 0 100% 0)',
|
||||
height: 0,
|
||||
opacity: 0 },
|
||||
@@ -544,7 +543,7 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
target={subItem.to.slice (0, 2) === '//'
|
||||
? '_blank'
|
||||
: undefined}
|
||||
className="w-full min-h-[36px] flex items-center pl-12">
|
||||
className="top-nav-link w-full min-h-[36px] flex items-center pl-12">
|
||||
{subItem.name}
|
||||
</PrefetchLink>)))}
|
||||
</motion.div>)}
|
||||
@@ -555,9 +554,9 @@ const TopNav: FC<Props> = ({ user }) => {
|
||||
ref={(el: (HTMLAnchorElement | null)) => {
|
||||
itemsRef.current[visibleMenu.length] = el
|
||||
}}
|
||||
className={cn ('w-full min-h-[40px] flex items-center pl-8',
|
||||
className={cn ('top-nav-link w-full min-h-[40px] flex items-center pl-8',
|
||||
((openItemIdx < 0)
|
||||
&& 'top-nav-themed-active font-bold'))}>
|
||||
&& 'top-nav-mobile-active font-bold'))}>
|
||||
その他 »
|
||||
</PrefetchLink>
|
||||
<TopNavUser user={user} sp/>
|
||||
|
||||
@@ -15,7 +15,11 @@ import type { ClientAnimationMode,
|
||||
ClientThumbnailMode } from '@/lib/settings'
|
||||
|
||||
type Props = {
|
||||
sectionClassName: string }
|
||||
sectionClassName: string
|
||||
onDirtyStateChange?: (
|
||||
dirty: boolean,
|
||||
discard: () => void,
|
||||
) => void }
|
||||
|
||||
type SegmentedOption<T extends string> = {
|
||||
value: T
|
||||
@@ -95,7 +99,10 @@ const SettingBlock: FC<SettingBlockProps> = (
|
||||
</div>)
|
||||
|
||||
|
||||
const BehaviourSettingsSection: FC<Props> = ({ sectionClassName }) => {
|
||||
const BehaviourSettingsSection: FC<Props> = (
|
||||
{ sectionClassName,
|
||||
onDirtyStateChange },
|
||||
) => {
|
||||
const [savedSettings, setSavedSettings] =
|
||||
useState<ClientBehaviorSettings> (() => getClientBehaviorSettings ())
|
||||
const [draftSettings, setDraftSettings] =
|
||||
@@ -129,6 +136,14 @@ const BehaviourSettingsSection: FC<Props> = ({ sectionClassName }) => {
|
||||
setDraftSettings (savedSettings)
|
||||
}
|
||||
|
||||
useEffect (() => {
|
||||
onDirtyStateChange?.(hasUnsavedChanges, handleDiscard)
|
||||
|
||||
return () => {
|
||||
onDirtyStateChange?.(false, () => { })
|
||||
}
|
||||
}, [handleDiscard, hasUnsavedChanges, onDirtyStateChange])
|
||||
|
||||
return (
|
||||
<section className={sectionClassName}>
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
|
||||
@@ -19,10 +19,17 @@ import type { FC } from 'react'
|
||||
|
||||
type Props = {
|
||||
sectionClassName: string
|
||||
onDirtyStateChange?: (
|
||||
dirty: boolean,
|
||||
discard: () => void,
|
||||
) => void
|
||||
}
|
||||
|
||||
|
||||
const KeyboardSettingsSection: FC<Props> = ({ sectionClassName }) => {
|
||||
const KeyboardSettingsSection: FC<Props> = (
|
||||
{ sectionClassName,
|
||||
onDirtyStateChange },
|
||||
) => {
|
||||
const {
|
||||
keyboardSettings,
|
||||
saveKeyboardSettings,
|
||||
@@ -127,6 +134,14 @@ const KeyboardSettingsSection: FC<Props> = ({ sectionClassName }) => {
|
||||
setCapturingActionId (null)
|
||||
}
|
||||
|
||||
useEffect (() => {
|
||||
onDirtyStateChange?.(hasUnsavedChanges, handleDiscard)
|
||||
|
||||
return () => {
|
||||
onDirtyStateChange?.(false, () => { })
|
||||
}
|
||||
}, [handleDiscard, hasUnsavedChanges, onDirtyStateChange])
|
||||
|
||||
return (
|
||||
<section className={sectionClassName}>
|
||||
<div className="flex flex-wrap items-start justify-between gap-3">
|
||||
|
||||
新しい課題から参照
ユーザをブロックする