Sergei Garin 2024-07-03 21:20:17 +03:00 committed by GitHub
parent ee39fd7f53
commit 3de873f97c
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 84 additions and 91 deletions

View File

@ -74,9 +74,11 @@ export const BUTTON_STYLES = twv.tv({
'group',
// we need to set the height to max-content to prevent the button from growing in flex containers
'h-[max-content]',
// basic outline
'outline-offset-[1px] outline-transparent',
// buttons always have borders
// so keep them in mind when setting paddings
'border border-transparent',
'border-0.5 border-transparent',
// button reset styles
'whitespace-nowrap cursor-pointer select-none appearance-none',
// Align the content by the center
@ -87,7 +89,7 @@ export const BUTTON_STYLES = twv.tv({
variants: {
isDisabled: { true: 'disabled:opacity-50 disabled:cursor-not-allowed' },
isFocused: {
true: 'focus:outline-none focus-visible:outline focus-visible:outline-primary focus-visible:outline-offset-2',
true: 'focus:outline-none focus-visible:outline-2 focus-visible:outline-black focus-visible:outline-offset-[-2px]',
},
isActive: {
none: '',
@ -107,10 +109,10 @@ export const BUTTON_STYLES = twv.tv({
variant: 'body',
color: 'custom',
weight: 'semibold',
className: 'flex px-[11px] py-[5px]',
className: 'flex px-[11px] py-[5.5px]',
}),
content: 'gap-2',
icon: 'mb-[-0.3cap]',
icon: 'mb-[-0.1cap] h-4.5 w-4.5',
extraClickZone: 'after:inset-[-6px]',
},
medium: {
@ -118,9 +120,9 @@ export const BUTTON_STYLES = twv.tv({
variant: 'body',
color: 'custom',
weight: 'semibold',
className: 'flex px-[9px] py-[3px]',
className: 'flex px-[9px] py-[3.5px]',
}),
icon: 'mb-[-0.3cap]',
icon: 'mb-[-0.1cap] h-4 w-4',
content: 'gap-2',
extraClickZone: 'after:inset-[-8px]',
},
@ -129,9 +131,9 @@ export const BUTTON_STYLES = twv.tv({
variant: 'body',
color: 'custom',
weight: 'medium',
className: 'flex px-[7px] py-[1px]',
className: 'flex px-[7px] py-[1.5px]',
}),
icon: 'mb-[-0.3cap]',
icon: 'mb-[-0.1cap] h-3.5 w-3.5',
content: 'gap-1',
extraClickZone: 'after:inset-[-10px]',
},
@ -140,9 +142,10 @@ export const BUTTON_STYLES = twv.tv({
variant: 'body',
color: 'custom',
weight: 'medium',
className: 'flex px-[5px] py-[1px]',
disableLineHeightCompensation: true,
className: 'flex px-[5px] pt-[0.5px] pb-[2.5px]',
}),
icon: 'mb-[-0.3cap]',
icon: 'mb-[-0.2cap] h-3 w-3',
content: 'gap-1',
extraClickZone: 'after:inset-[-12px]',
},
@ -150,17 +153,24 @@ export const BUTTON_STYLES = twv.tv({
base: text.TEXT_STYLE({
variant: 'body',
color: 'custom',
className: 'flex px-[3px] py-[0px]',
className: 'flex px-[3px] pt-[0.5px] pb-[2.5px] leading-[16px]',
// we need to disable line height compensation for this size
// because otherwise the text will be too high in the button
disableLineHeightCompensation: true,
}),
content: 'gap-0.5',
icon: 'mb-[-0.1cap]',
extraClickZone: 'after:inset-[-12px]',
},
},
iconOnly: {
true: { base: text.TEXT_STYLE({ disableLineHeightCompensation: true }), icon: 'mb-[unset]' },
true: {
base: text.TEXT_STYLE({
disableLineHeightCompensation: true,
className: 'border-0 outline-offset-[5px]',
}),
icon: 'mb-[unset]',
},
},
rounded: {
full: 'rounded-full',
@ -173,10 +183,11 @@ export const BUTTON_STYLES = twv.tv({
xxxlarge: 'rounded-3xl',
},
variant: {
custom: 'focus-visible:outline-offset-2',
custom: '',
link: {
base: 'inline-block px-0 py-0 rounded-sm text-primary/50 underline hover:text-primary border-none',
icon: 'h-[1.25cap] mt-[0.25cap]',
base: 'inline-block px-0 py-0 rounded-sm text-primary/50 underline hover:text-primary border-0',
content: 'gap-1.5',
icon: 'h-[1.25cap] w-[1.25cap] mt-[0.25cap]',
},
primary: 'bg-primary text-white hover:bg-primary/70',
tertiary: 'bg-accent text-white hover:bg-accent-dark',
@ -184,17 +195,16 @@ export const BUTTON_STYLES = twv.tv({
delete:
'bg-danger/80 hover:bg-danger text-white focus-visible:outline-danger focus-visible:bg-danger',
icon: {
base: 'border-0 opacity-80 hover:opacity-100 focus-visible:opacity-100 text-primary',
base: 'opacity-80 hover:opacity-100 focus-visible:opacity-100',
wrapper: 'w-full h-full',
content: 'w-full h-full',
extraClickZone: 'w-full h-full',
},
ghost:
'text-primary hover:text-primary/80 hover:bg-white focus-visible:text-primary/80 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 hover:border-primary focus-visible:outline-offset-2 hover:bg-primary/10',
bar: 'rounded-full border-0.5 border-primary/20 transition-colors hover:bg-primary/10',
submit: 'bg-invite text-white opacity-80 hover:opacity-100',
outline: 'border-primary/40 text-primary hover:border-primary hover:bg-primary/5',
bar: 'border-primary/20 hover:bg-primary/5',
},
iconPosition: {
start: { content: '' },
@ -230,7 +240,7 @@ export const BUTTON_STYLES = twv.tv({
loader: 'absolute inset-0 flex items-center justify-center',
content: 'flex items-center gap-[0.5em]',
text: 'inline-flex items-center justify-center gap-1',
icon: 'h-[2cap] flex-none aspect-square',
icon: 'h-[1.906cap] w-[1.906cap] flex-none aspect-square flex items-center justify-center',
},
defaultVariants: {
isActive: 'none',
@ -243,42 +253,16 @@ export const BUTTON_STYLES = twv.tv({
showIconOnHover: false,
},
compoundVariants: [
{ isFocused: true, iconOnly: true, class: 'focus-visible:outline-offset-3' },
{
variant: 'link',
isFocused: true,
class: 'focus-visible:outline-offset-1',
},
{
size: 'xxsmall',
iconOnly: true,
class: { base: 'p-0 rounded-full', icon: 'h-[1.25cap] -mt-[0.1cap]' },
},
{
size: 'xsmall',
iconOnly: true,
class: { base: 'p-0 rounded-full', icon: 'h-[1.45cap] -mt-[0.1cap]' },
},
{
size: 'small',
iconOnly: true,
class: { base: 'p-0 rounded-full', icon: 'h-[1.65cap] -mt-[0.1cap]' },
},
{
size: 'medium',
iconOnly: true,
class: { base: 'p-0 rounded-full', icon: 'h-[2cap] -mt-[0.1cap]' },
},
{
size: 'large',
iconOnly: true,
class: { base: 'p-0 rounded-full', icon: 'h-[3.65cap]' },
},
{
size: 'hero',
class: { base: 'p-0 rounded-full', icon: 'h-[5.5cap]' },
iconOnly: true,
},
{ isFocused: true, iconOnly: true, class: 'focus-visible:outline-offset-[3px]' },
{ size: 'custom', iconOnly: true, class: { icon: 'w-full h-full' } },
{ size: 'xxsmall', iconOnly: true, class: { base: 'p-0 rounded-full', icon: 'w-2.5 h-2.5' } },
{ size: 'xsmall', iconOnly: true, class: { base: 'p-0 rounded-full', icon: 'w-3 h-3' } },
{ size: 'small', iconOnly: true, class: { base: 'p-0 rounded-full', icon: 'w-3.5 h-3.5' } },
{ size: 'medium', iconOnly: true, class: { base: 'p-0 rounded-full', icon: 'w-4 h-4' } },
{ size: 'large', iconOnly: true, class: { base: 'p-0 rounded-full', icon: 'w-4.5 h-4.5' } },
{ size: 'hero', iconOnly: true, class: { base: 'p-0 rounded-full', icon: 'w-12 h-12' } },
{ variant: 'link', isFocused: true, class: 'focus-visible:outline-offset-1' },
{ variant: 'link', size: 'xxsmall', class: 'font-medium' },
{ variant: 'link', size: 'xsmall', class: 'font-medium' },
{ variant: 'link', size: 'small', class: 'font-medium' },

View File

@ -36,7 +36,7 @@ export function CloseButton(props: CloseButtonProps) {
variant="icon"
className={values =>
tailwindMerge.twMerge(
'h-3 w-3 bg-primary/30 hover:bg-red-500/80 focus-visible:bg-red-500/80 focus-visible:outline-offset-1',
'bg-primary/30 hover:bg-red-500/80 focus-visible:bg-red-500/80 focus-visible:outline-offset-1',
// eslint-disable-next-line @typescript-eslint/no-unsafe-argument
// @ts-expect-error ts fails to infer the type of the className prop
typeof className === 'function' ? className(values) : className
@ -44,7 +44,7 @@ export function CloseButton(props: CloseButtonProps) {
}
tooltip={tooltip}
showIconOnHover
size="custom"
size="xsmall"
rounded="full"
extraClickZone="medium"
icon={icon}

View File

@ -27,11 +27,12 @@ export const TEXT_STYLE = twv.tv({
variants: {
color: {
custom: '',
primary: 'text-primary/90',
primary: 'text-primary',
danger: 'text-danger',
success: 'text-share',
disabled: 'text-primary/30',
invert: 'text-white/80',
invert: 'text-white',
inherit: 'text-inherit',
},
font: {
default: '',

View File

@ -44,7 +44,7 @@ export default function SvgMask(props: SvgMaskProps) {
...(invert ? { WebkitMaskComposite: 'exclude, exclude' } : {}),
/* eslint-enable @typescript-eslint/naming-convention */
}}
className={tailwindMerge.twMerge('inline-block size-max', className)}
className={tailwindMerge.twMerge('inline-block h-max w-max', className)}
>
{/* This is required for this component to have the right size. */}
<img alt={alt} src={src} className="pointer-events-none opacity-0" draggable={false} />

View File

@ -53,7 +53,7 @@ export default function Label(props: InternalLabelProps) {
typeof childrenRaw !== 'string' ? (
childrenRaw
) : (
<ariaComponents.Text truncate="1" className="max-w-24 text-inherit">
<ariaComponents.Text truncate="1" className="max-w-24" color="invert" variant="body">
{childrenRaw}
</ariaComponents.Text>
)
@ -75,7 +75,7 @@ export default function Label(props: InternalLabelProps) {
title={title}
disabled={isDisabled}
className={tailwindMerge.twMerge(
'focus-child relative flex h-text items-center whitespace-nowrap rounded-inherit px-label-x transition-all selectable after:pointer-events-none after:absolute after:inset after:rounded-full',
'focus-child relative flex items-center whitespace-nowrap rounded-inherit px-[7px] opacity-75 transition-all after:pointer-events-none after:absolute after:inset after:rounded-full hover:opacity-100 focus:opacity-100',
active && 'active',
negated && 'after:border-2 after:border-delete',
className,

View File

@ -10,6 +10,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 Label from '#/components/dashboard/Label'
import FocusArea from '#/components/styled/FocusArea'
import FocusRing from '#/components/styled/FocusRing'
@ -82,7 +83,7 @@ function Tags(props: InternalTagsProps) {
return (
<div
data-testid="asset-search-tag-names"
className="pointer-events-auto flex flex-wrap gap-2 whitespace-nowrap px-search-suggestions"
className="pointer-events-auto flex flex-wrap gap-2 whitespace-nowrap px-1.5"
>
{(isCloud ? AssetQuery.tagNames : AssetQuery.localTagNames).flatMap(entry => {
const [key, tag] = entry
@ -90,15 +91,17 @@ function Tags(props: InternalTagsProps) {
? []
: [
<FocusRing key={key}>
<aria.Button
className="h-text rounded-full bg-frame px-button-x transition-all hover:bg-selected-frame"
<ariaComponents.Button
variant="bar"
size="xsmall"
className="min-w-12"
onPress={() => {
querySource.current = QuerySource.internal
setQuery(query.add({ [key]: [[]] }))
}}
>
{tag + ':'}
</aria.Button>
</ariaComponents.Button>
</FocusRing>,
]
})}
@ -280,7 +283,7 @@ export default function AssetSearchBar(props: AssetSearchBarProps) {
data-testid="asset-search-bar"
{...aria.mergeProps<aria.LabelProps>()(innerProps, {
className:
'z-1 group relative flex h-row grow max-w-[60em] items-center gap-asset-search-bar rounded-full px-3 text-primary',
'z-1 group relative flex grow max-w-[60em] items-center gap-asset-search-bar rounded-full px-1.5 py-1 text-primary',
ref: rootRef,
onFocus: () => {
setAreSuggestionsVisible(true)
@ -297,14 +300,17 @@ export default function AssetSearchBar(props: AssetSearchBarProps) {
>
<div className="relative size-4 placeholder" />
<div
className={tailwindMerge.twMerge(
'pointer-events-none absolute left top z-1 flex w-full flex-col overflow-hidden rounded-default border-0.5 border-primary/20 -outline-offset-1 outline-primary transition-colors before:absolute before:inset before:backdrop-blur-default group-focus-within:outline group-focus-within:outline-2 hover:before:bg-frame',
areSuggestionsVisible && 'before:bg-frame'
)}
className={ariaComponents.DIALOG_BACKGROUND({
className: tailwindMerge.twMerge(
'absolute left-0 top-0 z-1 flex w-full flex-col overflow-hidden rounded-default border-0.5 border-primary/20 -outline-offset-1 outline-primary transition-colors',
areSuggestionsVisible ? '' : 'bg-transparent'
),
})}
>
<div className="padding relative h-[30px]" />
<div className="h-[32px]" />
{areSuggestionsVisible && (
<div className="relative flex flex-col gap-search-suggestions">
<div className="relative mt-3 flex flex-col gap-3">
{/* Tags (`name:`, `modified:`, etc.) */}
<Tags
isCloud={isCloud}
@ -316,7 +322,7 @@ export default function AssetSearchBar(props: AssetSearchBarProps) {
{isCloud && labels.length !== 0 && (
<div
data-testid="asset-search-labels"
className="pointer-events-auto flex gap-2 p-search-suggestions"
className="pointer-events-auto flex gap-2 px-1.5"
>
{[...labels]
.sort((a, b) => string.compareCaseInsensitive(a.value, b.value))
@ -354,7 +360,7 @@ export default function AssetSearchBar(props: AssetSearchBarProps) {
</div>
)}
{/* Suggestions */}
<div className="flex max-h-search-suggestions-list flex-col overflow-y-auto">
<div className="flex max-h-search-suggestions-list flex-col overflow-y-auto overflow-x-hidden pb-0.5 pl-0.5">
{suggestions.map((suggestion, index) => (
// This should not be a `<button>`, since `render()` may output a
// tree containing a button.
@ -367,8 +373,8 @@ export default function AssetSearchBar(props: AssetSearchBarProps) {
}
}}
className={tailwindMerge.twMerge(
'pointer-events-auto mx-search-suggestion cursor-pointer rounded-default px-search-suggestions py-search-suggestion-y text-left transition-colors last:mb-search-suggestion hover:bg-selected-frame',
selectedIndices.has(index) && 'bg-frame',
'flex cursor-pointer rounded-l-default rounded-r-sm px-[7px] py-0.5 text-left transition-[background-color] hover:bg-primary/5',
selectedIndices.has(index) && 'bg-primary/10',
index === selectedIndex && 'bg-selected-frame'
)}
onPress={event => {
@ -391,14 +397,19 @@ export default function AssetSearchBar(props: AssetSearchBarProps) {
}
}}
>
{suggestion.render()}
<ariaComponents.Text variant="body" truncate="1" className="w-full">
{suggestion.render()}
</ariaComponents.Text>
</aria.Button>
))}
</div>
</div>
)}
</div>
<SvgMask src={FindIcon} className="absolute left-3 z-1 text-primary/30" />
<SvgMask
src={FindIcon}
className="absolute left-2 top-[50%] z-1 mt-[1px] -translate-y-1/2 text-primary/40"
/>
<FocusRing placement="before">
<aria.SearchField
aria-label={getText('assetSearchFieldLabel')}
@ -419,7 +430,7 @@ export default function AssetSearchBar(props: AssetSearchBarProps) {
: getText('remoteBackendSearchPlaceholder')
: getText('localBackendSearchPlaceholder')
}
className="focus-child peer text relative z-1 w-full bg-transparent placeholder-primary/20"
className="focus-child peer text relative z-1 w-full bg-transparent placeholder-primary/40"
onChange={event => {
if (querySource.current !== QuerySource.internal) {
querySource.current = QuerySource.typing

View File

@ -9,7 +9,6 @@ import * as backendHooks from '#/hooks/backendHooks'
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 Label from '#/components/dashboard/Label'
import Button from '#/components/styled/Button'
@ -132,20 +131,18 @@ export default function Labels(props: LabelsProps) {
)
})}
<ariaComponents.Button
size="custom"
variant="bar"
className="px-2"
isActive={false}
size="xsmall"
variant="outline"
className="pl-1 pr-2"
/* eslint-disable-next-line no-restricted-syntax */
icon={<img src={PlusIcon} alt="" className="ml-auto mt-[1px] size-[8px]" />}
onPress={event => {
if (event.target instanceof HTMLElement) {
setModal(<NewLabelModal backend={backend} eventTarget={event.target} />)
}
}}
>
{/* This is a non-standard-sized icon. */}
{/* eslint-disable-next-line no-restricted-syntax */}
<img src={PlusIcon} className="mr-[6px] size-[6px]" />
<aria.Text className="text-header">{getText('newLabelButtonLabel')}</aria.Text>
{getText('newLabelButtonLabel')}
</ariaComponents.Button>
</div>
</div>