feat(core): replace all radio-button-group usage (#7352)

This commit is contained in:
Cats Juice
2024-06-27 17:54:30 +08:00
committed by GitHub
parent 3b3b7ec054
commit fa2305b0e2
11 changed files with 163 additions and 213 deletions

View File

@@ -45,6 +45,7 @@
"@radix-ui/react-toolbar": "^1.0.4", "@radix-ui/react-toolbar": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.7", "@radix-ui/react-tooltip": "^1.0.7",
"@toeverything/theme": "^0.7.35", "@toeverything/theme": "^0.7.35",
"@radix-ui/react-visually-hidden": "^1.1.0",
"@vanilla-extract/dynamic": "^2.1.0", "@vanilla-extract/dynamic": "^2.1.0",
"bytes": "^3.1.2", "bytes": "^3.1.2",
"check-password-strength": "^2.0.10", "check-password-strength": "^2.0.10",

View File

@@ -6,6 +6,7 @@ import type {
DialogProps, DialogProps,
} from '@radix-ui/react-dialog'; } from '@radix-ui/react-dialog';
import * as Dialog from '@radix-ui/react-dialog'; import * as Dialog from '@radix-ui/react-dialog';
import * as VisuallyHidden from '@radix-ui/react-visually-hidden';
import { assignInlineVars } from '@vanilla-extract/dynamic'; import { assignInlineVars } from '@vanilla-extract/dynamic';
import clsx from 'clsx'; import clsx from 'clsx';
import type { CSSProperties } from 'react'; import type { CSSProperties } from 'react';
@@ -101,7 +102,13 @@ export const Modal = forwardRef<HTMLDivElement, ModalProps>(
<Dialog.Title className={styles.modalHeader}> <Dialog.Title className={styles.modalHeader}>
{title} {title}
</Dialog.Title> </Dialog.Title>
) : null} ) : (
// Refer: https://www.radix-ui.com/primitives/docs/components/dialog#title
// If you want to hide the title, wrap it inside our Visually Hidden utility like this <VisuallyHidden asChild>.
<VisuallyHidden.Root asChild>
<Dialog.Title></Dialog.Title>
</VisuallyHidden.Root>
)}
{description ? ( {description ? (
<Dialog.Description className={styles.modalDescription}> <Dialog.Description className={styles.modalDescription}>
{description} {description}

View File

@@ -1,4 +1,5 @@
import { RadioButton, RadioButtonGroup, Switch } from '@affine/component'; import type { RadioItem } from '@affine/component';
import { RadioGroup, Switch } from '@affine/component';
import { import {
SettingHeader, SettingHeader,
SettingRow, SettingRow,
@@ -8,7 +9,7 @@ import { useI18n } from '@affine/i18n';
import type { AppSetting } from '@toeverything/infra'; import type { AppSetting } from '@toeverything/infra';
import { fontStyleOptions, windowFrameStyleOptions } from '@toeverything/infra'; import { fontStyleOptions, windowFrameStyleOptions } from '@toeverything/infra';
import { useTheme } from 'next-themes'; import { useTheme } from 'next-themes';
import { useCallback } from 'react'; import { useCallback, useMemo } from 'react';
import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper'; import { useAppSettingHelper } from '../../../../../hooks/affine/use-app-setting-helper';
import { LanguageMenu } from '../../../language-menu'; import { LanguageMenu } from '../../../language-menu';
@@ -19,75 +20,79 @@ export const ThemeSettings = () => {
const t = useI18n(); const t = useI18n();
const { setTheme, theme } = useTheme(); const { setTheme, theme } = useTheme();
const radioItems = useMemo<RadioItem[]>(
() => [
{
value: 'system',
label: t['com.affine.themeSettings.system'](),
testId: 'system-theme-trigger',
},
{
value: 'light',
label: t['com.affine.themeSettings.light'](),
testId: 'light-theme-trigger',
},
{
value: 'dark',
label: t['com.affine.themeSettings.dark'](),
testId: 'dark-theme-trigger',
},
],
[t]
);
return ( return (
<RadioButtonGroup <RadioGroup
items={radioItems}
value={theme}
width={250} width={250}
className={settingWrapper} className={settingWrapper}
value={theme} onChange={useCallback(
onValueChange={useCallback(
(value: string) => { (value: string) => {
setTheme(value); setTheme(value);
}, },
[setTheme] [setTheme]
)} )}
> />
<RadioButton value="system" data-testid="system-theme-trigger">
{t['com.affine.themeSettings.system']()}
</RadioButton>
<RadioButton value="light" data-testid="light-theme-trigger">
{t['com.affine.themeSettings.light']()}
</RadioButton>
<RadioButton value="dark" data-testid="dark-theme-trigger">
{t['com.affine.themeSettings.dark']()}
</RadioButton>
</RadioButtonGroup>
); );
}; };
const FontFamilySettings = () => { const FontFamilySettings = () => {
const t = useI18n(); const t = useI18n();
const { appSettings, updateSettings } = useAppSettingHelper(); const { appSettings, updateSettings } = useAppSettingHelper();
const radioItems = useMemo(() => {
return fontStyleOptions.map(({ key, value }) => {
const label =
key === 'Mono'
? t[`com.affine.appearanceSettings.fontStyle.mono`]()
: key === 'Sans'
? t['com.affine.appearanceSettings.fontStyle.sans']()
: key === 'Serif'
? t['com.affine.appearanceSettings.fontStyle.serif']()
: '';
return {
value: key,
label,
testId: 'system-font-style-trigger',
style: { fontFamily: value },
} satisfies RadioItem;
});
}, [t]);
return ( return (
<RadioButtonGroup <RadioGroup
items={radioItems}
value={appSettings.fontStyle}
width={250} width={250}
className={settingWrapper} className={settingWrapper}
value={appSettings.fontStyle} onChange={useCallback(
onValueChange={useCallback( (value: AppSetting['fontStyle']) => {
(key: AppSetting['fontStyle']) => { updateSettings('fontStyle', value);
updateSettings('fontStyle', key);
}, },
[updateSettings] [updateSettings]
)} )}
> />
{fontStyleOptions.map(({ key, value }) => {
let font = '';
switch (key) {
case 'Sans':
font = t['com.affine.appearanceSettings.fontStyle.sans']();
break;
case 'Serif':
font = t['com.affine.appearanceSettings.fontStyle.serif']();
break;
case 'Mono':
font = t[`com.affine.appearanceSettings.fontStyle.mono`]();
break;
default:
break;
}
return (
<RadioButton
key={key}
value={key}
data-testid="system-font-style-trigger"
style={{
fontFamily: value,
}}
>
{font}
</RadioButton>
);
})}
</RadioButtonGroup>
); );
}; };
@@ -152,22 +157,19 @@ export const AppearanceSettings = () => {
name={t['com.affine.appearanceSettings.windowFrame.title']()} name={t['com.affine.appearanceSettings.windowFrame.title']()}
desc={t['com.affine.appearanceSettings.windowFrame.description']()} desc={t['com.affine.appearanceSettings.windowFrame.description']()}
> >
<RadioButtonGroup <RadioGroup
items={windowFrameStyleOptions.map(option => ({
value: option,
label:
t[`com.affine.appearanceSettings.windowFrame.${option}`](),
}))}
value={appSettings.windowFrameStyle}
className={settingWrapper} className={settingWrapper}
width={250} width={250}
defaultValue={appSettings.windowFrameStyle} onChange={(value: AppSetting['windowFrameStyle']) => {
onValueChange={(value: AppSetting['windowFrameStyle']) => {
updateSettings('windowFrameStyle', value); updateSettings('windowFrameStyle', value);
}} }}
> />
{windowFrameStyleOptions.map(option => {
return (
<RadioButton value={option} key={option}>
{t[`com.affine.appearanceSettings.windowFrame.${option}`]()}
</RadioButton>
);
})}
</RadioButtonGroup>
</SettingRow> </SettingRow>
) : null} ) : null}
</SettingWrapper> </SettingWrapper>

View File

@@ -1,11 +1,4 @@
import { import { Input, notify, RadioGroup, Skeleton, Switch } from '@affine/component';
Input,
notify,
RadioButton,
RadioButtonGroup,
Skeleton,
Switch,
} from '@affine/component';
import { PublicLinkDisableModal } from '@affine/component/disable-public-link'; import { PublicLinkDisableModal } from '@affine/component/disable-public-link';
import { Button } from '@affine/component/ui/button'; import { Button } from '@affine/component/ui/button';
import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu'; import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu';
@@ -97,6 +90,17 @@ export const AffineSharePage = (props: ShareMenuProps) => {
const t = useI18n(); const t = useI18n();
const modeOptions = useMemo(
() => [
{ value: 'page', label: t['com.affine.pageMode.page']() },
{
value: 'edgeless',
label: t['com.affine.pageMode.edgeless'](),
},
],
[t]
);
const onClickCreateLink = useAsyncCallback(async () => { const onClickCreateLink = useAsyncCallback(async () => {
try { try {
await shareService.share.enableShare( await shareService.share.enableShare(
@@ -265,18 +269,12 @@ export const AffineSharePage = (props: ShareMenuProps) => {
{t['com.affine.share-menu.ShareMode']()} {t['com.affine.share-menu.ShareMode']()}
</div> </div>
<div> <div>
<RadioButtonGroup <RadioGroup
className={styles.radioButtonGroup} className={styles.radioButtonGroup}
value={mode} value={mode}
onValueChange={onShareModeChange} onChange={onShareModeChange}
> items={modeOptions}
<RadioButton className={styles.radioButton} value={'page'}> />
{t['com.affine.pageMode.page']()}
</RadioButton>
<RadioButton className={styles.radioButton} value={'edgeless'}>
{t['com.affine.pageMode.edgeless']()}
</RadioButton>
</RadioButtonGroup>
</div> </div>
</div> </div>
{isSharedPage ? ( {isSharedPage ? (

View File

@@ -173,9 +173,6 @@ export const rulesTitleHighlight = style({
fontStyle: 'italic', fontStyle: 'italic',
fontWeight: 800, fontWeight: 800,
}); });
export const tabButton = style({
height: 28,
});
export const icon = style({ export const icon = style({
color: cssVar('iconColor'), color: cssVar('iconColor'),
}); });

View File

@@ -1,9 +1,4 @@
import { import { Button, Modal, RadioGroup } from '@affine/component';
Button,
Modal,
RadioButton,
RadioButtonGroup,
} from '@affine/component';
import { useAllPageListConfig } from '@affine/core/hooks/affine/use-all-page-list-config'; import { useAllPageListConfig } from '@affine/core/hooks/affine/use-all-page-list-config';
import type { Collection } from '@affine/env/filter'; import type { Collection } from '@affine/env/filter';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
@@ -137,29 +132,24 @@ export const EditCollection = ({
); );
const switchMode = useMemo( const switchMode = useMemo(
() => ( () => (
<RadioButtonGroup <RadioGroup
width={158} key="mode-switcher"
style={{ height: 32 }} style={{ minWidth: 158 }}
value={mode} value={mode}
onValueChange={(mode: 'page' | 'rule') => { onChange={setMode}
setMode(mode); items={[
}} {
> value: 'page',
<RadioButton label: t['com.affine.editCollection.pages'](),
spanStyle={styles.tabButton} testId: 'edit-collection-pages-button',
value="page" },
data-testid="edit-collection-pages-button" {
> value: 'rule',
{t['com.affine.editCollection.pages']()} label: t['com.affine.editCollection.rules'](),
</RadioButton> testId: 'edit-collection-rules-button',
<RadioButton },
spanStyle={styles.tabButton} ]}
value="rule" />
data-testid="edit-collection-rules-button"
>
{t['com.affine.editCollection.rules']()}
</RadioButton>
</RadioButtonGroup>
), ),
[mode, t] [mode, t]
); );

View File

@@ -4,5 +4,4 @@ export const filterTab = style({
fontSize: cssVar('fontXs'), fontSize: cssVar('fontXs'),
fontWeight: 600, fontWeight: 600,
textTransform: 'capitalize', textTransform: 'capitalize',
minWidth: '91px',
}); });

View File

@@ -1,4 +1,4 @@
import { RadioButton, RadioButtonGroup } from '@affine/component'; import { RadioGroup, type RadioItem } from '@affine/component';
import type { AllPageFilterOption } from '@affine/core/atoms'; import type { AllPageFilterOption } from '@affine/core/atoms';
import { allPageFilterSelectAtom } from '@affine/core/atoms'; import { allPageFilterSelectAtom } from '@affine/core/atoms';
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper'; import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
@@ -6,7 +6,7 @@ import { WorkspaceSubPath } from '@affine/core/shared';
import { useI18n } from '@affine/i18n'; import { useI18n } from '@affine/i18n';
import { useService, WorkspaceService } from '@toeverything/infra'; import { useService, WorkspaceService } from '@toeverything/infra';
import { useAtom } from 'jotai'; import { useAtom } from 'jotai';
import { useCallback, useEffect, useState } from 'react'; import { useCallback, useEffect, useMemo, useState } from 'react';
import * as styles from './index.css'; import * as styles from './index.css';
@@ -45,28 +45,33 @@ export const WorkspaceModeFilterTab = ({
}, [activeFilter, filterMode, setFilterMode, value]); }, [activeFilter, filterMode, setFilterMode, value]);
return ( return (
<RadioButtonGroup value={value} onValueChange={handleValueChange}> <RadioGroup
<RadioButton style={{ maxWidth: '100%', width: 273 }}
spanStyle={styles.filterTab} value={value}
value="docs" onChange={handleValueChange}
data-testid="workspace-docs-button" items={useMemo<RadioItem[]>(
> () => [
{t['com.affine.docs.header']()} {
</RadioButton> value: 'docs',
<RadioButton label: t['com.affine.docs.header'](),
spanStyle={styles.filterTab} testId: 'workspace-docs-button',
value="collections" className: styles.filterTab,
data-testid="workspace-collections-button" },
> {
{t['com.affine.collections.header']()} value: 'collections',
</RadioButton> label: t['com.affine.collections.header'](),
<RadioButton testId: 'workspace-collections-button',
spanStyle={styles.filterTab} className: styles.filterTab,
value="tags" },
data-testid="workspace-tags-button" {
> value: 'tags',
{t['Tags']()} label: t['Tags'](),
</RadioButton> testId: 'workspace-tags-button',
</RadioButtonGroup> className: styles.filterTab,
},
],
[t]
)}
/>
); );
}; };

View File

@@ -1,47 +0,0 @@
import { cssVar } from '@toeverything/theme';
import { createVar, style } from '@vanilla-extract/css';
export const activeIdx = createVar();
export const switchRootWrapper = style({
height: '52px',
display: 'flex',
alignItems: 'center',
flexShrink: 0,
});
export const switchRoot = style({
vars: {
[activeIdx]: '0',
},
display: 'flex',
alignItems: 'center',
gap: '8px',
height: '32px',
borderRadius: '12px',
padding: '4px',
position: 'relative',
background: cssVar('backgroundSecondaryColor'),
'::after': {
content: '""',
display: 'block',
width: '24px',
height: '24px',
background: cssVar('backgroundPrimaryColor'),
boxShadow: cssVar('shadow1'),
borderRadius: '8px',
position: 'absolute',
transform: `translateX(calc(${activeIdx} * 32px))`,
transition: 'all .15s',
},
});
export const button = style({
width: '24px',
height: '24px',
borderRadius: '8px',
color: cssVar('iconColor'),
position: 'relative',
zIndex: 1,
selectors: {
'&[data-active=true]': {
pointerEvents: 'none',
},
},
});

View File

@@ -1,8 +1,9 @@
import { IconButton } from '@affine/component'; import type { RadioItem } from '@affine/component';
import { assignInlineVars } from '@vanilla-extract/dynamic'; import { RadioGroup } from '@affine/component';
import { cssVar } from '@toeverything/theme';
import { useMemo } from 'react';
import type { SidebarTab, SidebarTabName } from '../multi-tabs/sidebar-tab'; import type { SidebarTab, SidebarTabName } from '../multi-tabs/sidebar-tab';
import * as styles from './header-switcher.css';
export interface MultiTabSidebarHeaderSwitcherProps { export interface MultiTabSidebarHeaderSwitcherProps {
tabs: SidebarTab[]; tabs: SidebarTab[];
@@ -17,30 +18,26 @@ export const MultiTabSidebarHeaderSwitcher = ({
activeTabName, activeTabName,
setActiveTabName, setActiveTabName,
}: MultiTabSidebarHeaderSwitcherProps) => { }: MultiTabSidebarHeaderSwitcherProps) => {
const activeExtension = tabs.find(ext => ext.name === activeTabName); const tabItems = useMemo(() => {
return tabs.map(extension => {
const vars = assignInlineVars({ return {
[styles.activeIdx]: String( value: extension.name,
tabs.findIndex(ext => ext.name === activeExtension?.name) ?? 0 label: extension.icon,
), style: { padding: 0, fontSize: 20, width: 24 },
}); } satisfies RadioItem;
});
}, [tabs]);
return ( return (
<div className={styles.switchRootWrapper}> <RadioGroup
<div className={styles.switchRoot} style={vars}> borderRadius={8}
{tabs.map(extension => { itemHeight={24}
return ( padding={4}
<IconButton gap={8}
onClick={() => setActiveTabName(extension.name)} items={tabItems}
key={extension.name} value={activeTabName}
data-active={activeExtension === extension} onChange={setActiveTabName}
className={styles.button} activeItemStyle={{ color: cssVar('primaryColor') }}
> />
{extension.icon}
</IconButton>
);
})}
</div>
</div>
); );
}; };

View File

@@ -307,6 +307,7 @@ __metadata:
"@radix-ui/react-toast": "npm:^1.1.5" "@radix-ui/react-toast": "npm:^1.1.5"
"@radix-ui/react-toolbar": "npm:^1.0.4" "@radix-ui/react-toolbar": "npm:^1.0.4"
"@radix-ui/react-tooltip": "npm:^1.0.7" "@radix-ui/react-tooltip": "npm:^1.0.7"
"@radix-ui/react-visually-hidden": "npm:^1.1.0"
"@storybook/addon-actions": "npm:^7.6.17" "@storybook/addon-actions": "npm:^7.6.17"
"@storybook/addon-essentials": "npm:^7.6.17" "@storybook/addon-essentials": "npm:^7.6.17"
"@storybook/addon-interactions": "npm:^7.6.17" "@storybook/addon-interactions": "npm:^7.6.17"
@@ -12840,7 +12841,7 @@ __metadata:
languageName: node languageName: node
linkType: hard linkType: hard
"@radix-ui/react-visually-hidden@npm:1.1.0": "@radix-ui/react-visually-hidden@npm:1.1.0, @radix-ui/react-visually-hidden@npm:^1.1.0":
version: 1.1.0 version: 1.1.0
resolution: "@radix-ui/react-visually-hidden@npm:1.1.0" resolution: "@radix-ui/react-visually-hidden@npm:1.1.0"
dependencies: dependencies: