mirror of
https://github.com/enso-org/enso.git
synced 2024-11-29 15:21:57 +03:00
Improve sidebar view
This commit is contained in:
parent
53f04b9fa1
commit
18d88baaaa
@ -16,8 +16,8 @@ import SvgMask from '#/components/SvgMask'
|
||||
|
||||
/** Props for a {@link Button}. */
|
||||
export type ButtonProps =
|
||||
| (BaseButtonProps & Omit<aria.ButtonProps, 'onPress'> & PropsWithoutHref)
|
||||
| (BaseButtonProps & Omit<aria.LinkProps, 'onPress'> & PropsWithHref)
|
||||
| (BaseButtonProps & Omit<aria.ButtonProps, 'children' | 'onPress'> & PropsWithoutHref)
|
||||
| (BaseButtonProps & Omit<aria.LinkProps, 'children' | 'onPress'> & PropsWithHref)
|
||||
|
||||
/**
|
||||
* Props for a button with an href.
|
||||
@ -53,6 +53,8 @@ export interface BaseButtonProps extends Omit<twv.VariantProps<typeof BUTTON_STY
|
||||
*/
|
||||
readonly onPress?: (event: aria.PressEvent) => Promise<void> | void
|
||||
|
||||
readonly children?: React.ReactNode
|
||||
|
||||
readonly testId?: string
|
||||
}
|
||||
|
||||
@ -69,7 +71,7 @@ export const BUTTON_STYLES = twv.tv({
|
||||
custom: '',
|
||||
hero: 'px-8 py-4 text-lg',
|
||||
large: 'px-6 py-3 text-base',
|
||||
medium: 'px-4 py-2 text-sm',
|
||||
medium: { base: 'px-[7px] py-[3px] text-xs', text: 'pt-[1px] pb-[3px] leading-5' },
|
||||
small: 'px-3 py-1 text-xs',
|
||||
xsmall: 'px-2 py-1 text-xs',
|
||||
xxsmall: 'px-1.5 py-0.5 text-xs',
|
||||
@ -88,15 +90,16 @@ export const BUTTON_STYLES = twv.tv({
|
||||
link: 'inline-flex px-0 py-0 rounded-sm text-primary/50 underline hover:text-primary focus-visible:outline-offset-0',
|
||||
primary: 'bg-primary text-white hover:bg-primary/70 focus-visible:outline-offset-2',
|
||||
tertiary: 'bg-share text-white hover:bg-share/90 focus-visible:outline-offset-2',
|
||||
cancel: 'bg-selected-frame opacity-80 hover:opacity-100 focus-visible:outline-offset-2',
|
||||
delete: 'bg-delete text-white focus-visible:outline-offset-2',
|
||||
cancel: 'bg-white/50 hover:bg-white focus-visible:outline-offset-2',
|
||||
delete: 'bg-danger text-white focus-visible:outline-offset-2',
|
||||
icon: {
|
||||
base: 'opacity-70 hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-offset-0',
|
||||
base: 'opacity-80 hover:opacity-100 focus-visible:opacity-100 focus-visible:outline-offset-0',
|
||||
wrapper: 'w-full h-full',
|
||||
content: 'w-full h-full',
|
||||
icon: 'w-fit h-fit',
|
||||
},
|
||||
|
||||
ghost:
|
||||
'opacity-80 hover:opacity-100 hover:bg-white focus-visible:opacity-100 focus-visible:bg-white',
|
||||
submit: 'bg-invite text-white opacity-80 hover:opacity-100 focus-visible:outline-offset-2',
|
||||
outline:
|
||||
'border-primary/40 text-primary font-bold hover:border-primary/90 focus-visible:outline-offset-2',
|
||||
@ -114,7 +117,8 @@ export const BUTTON_STYLES = twv.tv({
|
||||
wrapper: 'relative block',
|
||||
loader:
|
||||
'animate-appear-delayed absolute inset-0 flex items-center justify-center duration-1000',
|
||||
content: 'flex items-center gap-[0.5em] delay-1000 duration-0',
|
||||
content: 'flex items-center gap-[0.75em] delay-1000 duration-0',
|
||||
text: '',
|
||||
icon: 'h-[1.5em] flex-none',
|
||||
},
|
||||
defaultVariants: {
|
||||
@ -194,6 +198,7 @@ export const Button = React.forwardRef(function Button(
|
||||
loader,
|
||||
extraClickZone,
|
||||
icon: iconClasses,
|
||||
text,
|
||||
} = BUTTON_STYLES({
|
||||
isDisabled,
|
||||
loading: isLoading,
|
||||
@ -219,7 +224,7 @@ export const Button = React.forwardRef(function Button(
|
||||
return (
|
||||
<>
|
||||
{icon != null && <SvgMask src={icon} className={iconClasses()} />}
|
||||
<>{children}</>
|
||||
<span className={text()}>{children}</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
@ -14,16 +14,23 @@ const STYLES = twv.tv({
|
||||
base: 'flex w-full flex-auto',
|
||||
variants: {
|
||||
wrap: { true: 'flex-wrap' },
|
||||
direction: { column: 'flex-col justify-center', row: 'flex-row items-center' },
|
||||
direction: { column: 'flex-col', row: 'flex-row' },
|
||||
gap: {
|
||||
custom: '',
|
||||
large: 'gap-3.5',
|
||||
medium: 'gap-2',
|
||||
small: 'gap-1.5',
|
||||
xsmall: 'gap-1',
|
||||
xxsmall: 'gap-0.5',
|
||||
none: 'gap-0',
|
||||
},
|
||||
align: { start: 'justify-start', center: 'justify-center', end: 'justify-end' },
|
||||
},
|
||||
compoundVariants: [
|
||||
{ direction: 'column', align: 'start', class: 'items-start' },
|
||||
{ direction: 'column', align: 'center', class: 'items-center' },
|
||||
{ direction: 'column', align: 'end', class: 'items-end' },
|
||||
],
|
||||
})
|
||||
|
||||
/**
|
||||
|
@ -38,11 +38,11 @@ export function PaywallDialog(props: PaywallDialogProps) {
|
||||
<div className="flex flex-col">
|
||||
<components.PaywallLock feature={feature} className="mb-2" />
|
||||
|
||||
<p className="text-base text-primary/60">{getText(descriptionTextId)}</p>
|
||||
<p className="text-base text-primary/70">{getText(descriptionTextId)}</p>
|
||||
|
||||
<components.PaywallBulletPoints bulletPointsTextId={bulletPointsTextId} className="my-4" />
|
||||
|
||||
<upgradeButton.UpgradeButton feature={feature} rounding="xlarge" className="mt-2" />
|
||||
<upgradeButton.UpgradeButton feature={feature} rounded="xlarge" className="mt-2" />
|
||||
</div>
|
||||
</ariaComponents.Dialog>
|
||||
)
|
||||
|
@ -40,7 +40,7 @@ export function PaywallBulletPoints(props: PaywallBulletPointsProps) {
|
||||
return (
|
||||
<ul
|
||||
className={tw.twMerge(
|
||||
'm-0 flex w-full list-inside list-none flex-col gap-1 text-base',
|
||||
'm-0 flex w-full list-inside list-none flex-col gap-1 text-base text-primary/70',
|
||||
className
|
||||
)}
|
||||
>
|
||||
|
@ -1,10 +1,8 @@
|
||||
/** @file A styled button representing a tab on a sidebar. */
|
||||
import * as React from 'react'
|
||||
|
||||
import * as aria from '#/components/aria'
|
||||
import StatelessSpinner, * as statelessSpinner from '#/components/StatelessSpinner'
|
||||
import SvgMask from '#/components/SvgMask'
|
||||
import UnstyledButton from '#/components/UnstyledButton'
|
||||
import type * as aria from '#/components/aria'
|
||||
import * as ariaComponent from '#/components/AriaComponents'
|
||||
|
||||
// ========================
|
||||
// === SidebarTabButton ===
|
||||
@ -20,38 +18,23 @@ export interface SidebarTabButtonProps {
|
||||
readonly icon: string
|
||||
readonly label: string
|
||||
readonly onPress: (event: aria.PressEvent) => void
|
||||
readonly isPending?: boolean
|
||||
}
|
||||
|
||||
/** A styled button representing a tab on a sidebar. */
|
||||
export default function SidebarTabButton(props: SidebarTabButtonProps) {
|
||||
const {
|
||||
isDisabled = false,
|
||||
autoFocus = false,
|
||||
active = false,
|
||||
icon,
|
||||
label,
|
||||
onPress,
|
||||
isPending = false,
|
||||
} = props
|
||||
const { isDisabled = false, active = false, icon, label, onPress } = props
|
||||
|
||||
return (
|
||||
<UnstyledButton
|
||||
autoFocus={autoFocus}
|
||||
<ariaComponent.Button
|
||||
variant="ghost"
|
||||
size="medium"
|
||||
onPress={onPress}
|
||||
isDisabled={isDisabled}
|
||||
className={`relative rounded-full ${active ? 'focus-default' : ''}`}
|
||||
icon={icon}
|
||||
rounded="full"
|
||||
className={active ? 'bg-white opacity-100' : ''}
|
||||
>
|
||||
<div
|
||||
className={`button icon-with-text h-row px-button-x transition-colors selectable hover:bg-selected-frame ${active ? 'disabled bg-selected-frame active' : ''}`}
|
||||
>
|
||||
{active && isPending ? (
|
||||
<StatelessSpinner state={statelessSpinner.SpinnerState.loadingMedium} size={16} />
|
||||
) : (
|
||||
<SvgMask src={icon} />
|
||||
)}
|
||||
<aria.Text className="text">{label}</aria.Text>
|
||||
</div>
|
||||
</UnstyledButton>
|
||||
{label}
|
||||
</ariaComponent.Button>
|
||||
)
|
||||
}
|
||||
|
@ -7,6 +7,7 @@ import * as modalProvider from '#/providers/ModalProvider'
|
||||
import * as textProvider from '#/providers/TextProvider'
|
||||
|
||||
import * as aria from '#/components/aria'
|
||||
import * as ariaComponents from '#/components/AriaComponents'
|
||||
import SettingsSection from '#/components/styled/settings/SettingsSection'
|
||||
import UnstyledButton from '#/components/UnstyledButton'
|
||||
|
||||
@ -31,8 +32,10 @@ export default function DeleteUserAccountSettingsSection() {
|
||||
className="flex flex-col items-start gap-settings-section-header rounded-2.5xl border-2 border-danger px-[1rem] pb-[0.9375rem] pt-[0.5625rem]"
|
||||
>
|
||||
<div className="flex gap-buttons">
|
||||
<UnstyledButton
|
||||
className="button bg-danger px-delete-user-account-button-x text-inversed opacity-full hover:opacity-full"
|
||||
<ariaComponents.Button
|
||||
variant="delete"
|
||||
size="medium"
|
||||
rounded="full"
|
||||
onPress={() => {
|
||||
setModal(
|
||||
<ConfirmDeleteUserModal
|
||||
@ -44,11 +47,9 @@ export default function DeleteUserAccountSettingsSection() {
|
||||
)
|
||||
}}
|
||||
>
|
||||
<aria.Text className="text inline-block">
|
||||
{getText('deleteUserAccountButtonLabel')}
|
||||
</aria.Text>
|
||||
</UnstyledButton>
|
||||
<aria.Text className="text my-auto">{getText('deleteUserAccountWarning')}</aria.Text>
|
||||
{getText('deleteUserAccountButtonLabel')}
|
||||
</ariaComponents.Button>
|
||||
<aria.Text className="text-md my-auto">{getText('deleteUserAccountWarning')}</aria.Text>
|
||||
</div>
|
||||
</SettingsSection>
|
||||
)
|
||||
|
@ -19,7 +19,7 @@ export default function MembersSettingsTabBar() {
|
||||
return (
|
||||
<HorizontalMenuBar>
|
||||
<ariaComponents.DialogTrigger>
|
||||
<ariaComponents.Button variant="cancel" rounded="full" size="small">
|
||||
<ariaComponents.Button variant="cancel" rounded="full" size="medium">
|
||||
{getText('inviteMembers')}
|
||||
</ariaComponents.Button>
|
||||
|
||||
|
@ -221,7 +221,7 @@ export function UserGroupsSettingsTabContent() {
|
||||
feature="userGroupsFull"
|
||||
variant="cancel"
|
||||
size="medium"
|
||||
rounding="full"
|
||||
rounded="full"
|
||||
iconPosition="end"
|
||||
tooltip={getText('userGroupsPaywallMessage')}
|
||||
>
|
||||
@ -231,7 +231,7 @@ export function UserGroupsSettingsTabContent() {
|
||||
<div className="flex items-center gap-2">
|
||||
<ariaComponents.Button
|
||||
variant="cancel"
|
||||
rounding="full"
|
||||
rounded="full"
|
||||
size="medium"
|
||||
onPress={event => {
|
||||
const placeholderId = backendModule.newPlaceholderUserGroupId()
|
||||
|
@ -12,6 +12,7 @@ import * as textProvider from '#/providers/TextProvider'
|
||||
import SettingsTab from '#/layouts/Settings/SettingsTab'
|
||||
|
||||
import * as aria from '#/components/aria'
|
||||
import * as ariaComponents from '#/components/AriaComponents'
|
||||
import FocusArea from '#/components/styled/FocusArea'
|
||||
import SidebarTabButton from '#/components/styled/SidebarTabButton'
|
||||
|
||||
@ -133,19 +134,22 @@ export default function SettingsSidebar(props: SettingsSidebarProps) {
|
||||
>
|
||||
{section.name}
|
||||
</aria.Header>
|
||||
{section.tabs.map(tab => (
|
||||
<SidebarTabButton
|
||||
key={tab.settingsTab}
|
||||
isDisabled={(tab.organizationOnly ?? false) && !isUserInOrganization}
|
||||
id={tab.settingsTab}
|
||||
icon={tab.icon}
|
||||
label={tab.name}
|
||||
active={tab.settingsTab === settingsTab}
|
||||
onPress={() => {
|
||||
setSettingsTab(tab.settingsTab)
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
|
||||
<ariaComponents.ButtonGroup gap="xxsmall" direction="column" align="start">
|
||||
{section.tabs.map(tab => (
|
||||
<SidebarTabButton
|
||||
key={tab.settingsTab}
|
||||
isDisabled={(tab.organizationOnly ?? false) && !isUserInOrganization}
|
||||
id={tab.settingsTab}
|
||||
icon={tab.icon}
|
||||
label={tab.name}
|
||||
active={tab.settingsTab === settingsTab}
|
||||
onPress={() => {
|
||||
setSettingsTab(tab.settingsTab)
|
||||
}}
|
||||
/>
|
||||
))}
|
||||
</ariaComponents.ButtonGroup>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
|
Loading…
Reference in New Issue
Block a user