refactor(ui): update styling for improved visual consistency across components
- Changed background classes to 'bg-material-ultra-thick' in various UI elements including media, buttons, and modals for a cohesive design. - Standardized text color usage to 'text-text-secondary' and 'text-material-opaque' in entry items and notifications for better readability. - Enhanced hover effects and transitions for buttons and interactive elements to align with design standards. Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
parent
9da9dec7ec
commit
a525406600
|
|
@ -45,7 +45,7 @@ export const AnimatedCommandButton: FC<AnimatedCommandButtonProps & HTMLMotionPr
|
|||
"rounded-md p-1.5 duration-200",
|
||||
variant === "solid" || variant === "ghost"
|
||||
? "border-accent/5 bg-accent/80 text-accent border backdrop-blur"
|
||||
: "text-accent bg-slate-50/50 dark:bg-slate-900/50",
|
||||
: "text-accent hover:bg-material-ultra-thick",
|
||||
variant === "ghost" && "bg-theme-item-active hover:bg-theme-item-hover",
|
||||
className,
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -72,12 +72,12 @@
|
|||
@apply bg-green-100 dark:bg-green-900;
|
||||
|
||||
&::before {
|
||||
@apply bg-green-500;
|
||||
@apply bg-green;
|
||||
}
|
||||
|
||||
&::after {
|
||||
content: " +";
|
||||
@apply absolute left-0 text-green-500;
|
||||
@apply text-green absolute left-0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -90,7 +90,7 @@
|
|||
|
||||
&::after {
|
||||
content: " -";
|
||||
@apply absolute left-0 text-red-500;
|
||||
@apply text-red absolute left-0;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ interface CollapseProps {
|
|||
defaultOpen?: boolean
|
||||
collapseId?: string
|
||||
onOpenChange?: (isOpened: boolean) => void
|
||||
contentClassName?: string
|
||||
}
|
||||
|
||||
export const CollapseGroup: Component<{
|
||||
|
|
@ -91,7 +92,9 @@ export const CollapseControlled: Component<
|
|||
</div>
|
||||
)}
|
||||
</div>
|
||||
<CollapseContent isOpened={props.isOpened}>{props.children}</CollapseContent>
|
||||
<CollapseContent isOpened={props.isOpened} className={props.contentClassName}>
|
||||
{props.children}
|
||||
</CollapseContent>
|
||||
</div>
|
||||
)
|
||||
export const CollapseContent: Component<{
|
||||
|
|
|
|||
|
|
@ -52,12 +52,13 @@ export const FABBase = typescriptHappyForwardRef(
|
|||
"mt-2 flex items-center justify-center",
|
||||
"size-9 text-lg md:text-base",
|
||||
"outline-accent hover:opacity-100 focus:opacity-100 focus:outline-none",
|
||||
"rounded-xl border border-zinc-400/20 backdrop-blur-lg dark:border-zinc-500/30 dark:text-zinc-200",
|
||||
"bg-zinc-50/80 shadow-lg dark:bg-neutral-900/80",
|
||||
"hover:border-border bg-background relative rounded-xl border border-transparent",
|
||||
"group duration-200",
|
||||
className,
|
||||
)}
|
||||
{...rest}
|
||||
>
|
||||
<div className="shadow-perfect border-border/50 pointer-events-none absolute inset-0 rounded-xl border shadow-xl duration-200 group-hover:opacity-0" />
|
||||
{children}
|
||||
</m.button>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -143,7 +143,9 @@ const TocHoverCard: React.FC<TocHoverCardProps> = ({
|
|||
exit={{ opacity: 0, x: 110, transition: { duration: 0.1 } }}
|
||||
transition={{ duration: 0.5, type: "spring" }}
|
||||
className={cn(
|
||||
"relative z-10 -mt-1 rounded-xl border bg-white px-3 py-1 text-xs drop-shadow-xl dark:bg-neutral-950",
|
||||
"relative z-10 -mt-1 rounded-xl border",
|
||||
"px-3 py-1 text-xs",
|
||||
"bg-material-ultra-thick backdrop-blur-background shadow-context-menu",
|
||||
"scrollbar-none max-h-[calc(100svh-4rem)] overflow-auto",
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ export const BlockError = (props: { error: any; message: string }) => {
|
|||
captureException(props.error)
|
||||
}, [])
|
||||
return (
|
||||
<div className="center flex min-h-12 flex-col rounded bg-red-400 py-4 text-sm text-white dark:bg-red-800">
|
||||
<div className="center bg-red flex min-h-12 flex-col rounded py-4 text-sm text-white">
|
||||
{props.message}
|
||||
|
||||
<pre className="m-0 bg-transparent">{props.error?.message}</pre>
|
||||
|
|
|
|||
|
|
@ -334,7 +334,7 @@ const FallbackMedia: FC<MediaProps> = ({ type, mediaContainerClassName, classNam
|
|||
<div
|
||||
className={cn(
|
||||
"size-full",
|
||||
"center rounded bg-zinc-100 dark:bg-neutral-900",
|
||||
"center bg-material-ultra-thick rounded",
|
||||
"not-prose @container !flex max-h-full flex-col space-y-1 p-4",
|
||||
mediaContainerClassName,
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ const ControlBar = memo(() => {
|
|||
dragMomentum={false}
|
||||
dragConstraints={{ current: document.documentElement }}
|
||||
className={cn(
|
||||
"absolute inset-x-2 bottom-2 h-8 rounded-2xl border bg-zinc-100/90 backdrop-blur-xl dark:border-transparent dark:bg-neutral-700/90",
|
||||
"bg-material-thick backdrop-blur-background border-border absolute inset-x-2 bottom-2 h-8 rounded-2xl border",
|
||||
"flex items-center gap-3 px-3",
|
||||
"mx-auto max-w-[80vw]",
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -15,11 +15,13 @@ export const VolumeSlider: FC<{
|
|||
onVolumeChange?.(values[0]!)
|
||||
}}
|
||||
>
|
||||
<Slider.Track className="relative w-1 grow rounded bg-zinc-500">
|
||||
<Slider.Range className="absolute w-full rounded bg-black dark:bg-white" />
|
||||
<Slider.Track className="relative w-1 grow rounded bg-white dark:bg-neutral-800">
|
||||
<Slider.Range className="absolute w-full rounded bg-zinc-500/40 dark:bg-neutral-600" />
|
||||
</Slider.Track>
|
||||
|
||||
{/* indicator */}
|
||||
<Slider.Thumb
|
||||
className="block size-3 rounded-full bg-black dark:bg-white"
|
||||
className="block size-3 rounded-full bg-zinc-500 dark:bg-zinc-400"
|
||||
aria-label="Volume"
|
||||
/>
|
||||
</Slider.Root>
|
||||
|
|
|
|||
|
|
@ -17,14 +17,14 @@ export const PeekModalBaseButton = ({
|
|||
<Tooltip>
|
||||
<TooltipTrigger
|
||||
type="button"
|
||||
aria-label={label}
|
||||
className={cn(
|
||||
"no-drag-region center bg-background flex size-8 rounded-full p-1 shadow-sm ring-1 ring-zinc-200 dark:ring-neutral-800",
|
||||
"no-drag-region center bg-background ring-border flex size-8 rounded-full p-1 shadow-sm ring-1 empty:hidden",
|
||||
className,
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
{icon}
|
||||
<span className="sr-only">{label}</span>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{label}</TooltipContent>
|
||||
</Tooltip>
|
||||
|
|
|
|||
|
|
@ -0,0 +1,5 @@
|
|||
import { createContext, useContext } from "react"
|
||||
|
||||
export const InPeekModal = createContext(false)
|
||||
InPeekModal.displayName = "InPeekModal"
|
||||
export const useInPeekModal = () => useContext(InPeekModal)
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
import { getStableRouterNavigate } from "@follow/components/atoms/route.js"
|
||||
import { RootPortalProvider } from "@follow/components/ui/portal/provider.js"
|
||||
import type { PropsWithChildren, ReactNode } from "react"
|
||||
import { createContext, useContext, useState } from "react"
|
||||
import { useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { m } from "~/components/common/Motion"
|
||||
|
|
@ -9,9 +9,8 @@ import { m } from "~/components/common/Motion"
|
|||
import { PeekModalBaseButton } from "../components/base"
|
||||
import { FixedModalCloseButton } from "../components/close"
|
||||
import { useCurrentModal, useModalStack } from "../stacked/hooks"
|
||||
import { InPeekModal } from "./InPeekModal"
|
||||
|
||||
const InPeekModal = createContext(false)
|
||||
export const useInPeekModal = () => useContext(InPeekModal)
|
||||
export const PeekModal = (
|
||||
props: PropsWithChildren<{
|
||||
to?: string
|
||||
|
|
@ -32,7 +31,7 @@ export const PeekModal = (
|
|||
return (
|
||||
<RootPortalProvider value={rootRef as HTMLElement}>
|
||||
<div
|
||||
className="scrollbar-none relative mx-auto mt-[10vh] max-w-full overflow-auto px-2 lg:max-w-[65rem] lg:p-0"
|
||||
className="scrollbar-none relative mx-auto mt-[10vh] max-w-full overflow-hidden px-2 lg:max-w-[65rem] lg:p-0"
|
||||
ref={setRootRef}
|
||||
>
|
||||
<m.div
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ export const ModalOverlay = forwardRef(
|
|||
id="modal-overlay"
|
||||
className={cn(
|
||||
// NOTE: pointer-events-none is required, if remove this, when modal is closing, you can not click element behind the modal
|
||||
"!pointer-events-none fixed inset-0 rounded-[var(--fo-window-radius)] bg-zinc-50/80 dark:bg-neutral-900/80",
|
||||
"bg-material-ultra-thick !pointer-events-none fixed inset-0 rounded-[var(--fo-window-radius)]",
|
||||
blur && "backdrop-blur-sm",
|
||||
className,
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -6,9 +6,9 @@ export const Paper: Component<{
|
|||
}> = ({ children, className, as: As = "main" }) => (
|
||||
<As
|
||||
className={cn(
|
||||
"relative bg-white md:col-start-1 lg:col-auto dark:bg-zinc-900",
|
||||
"bg-background relative md:col-start-1 lg:col-auto",
|
||||
"-m-4 p-[2rem_1rem] md:m-0 lg:p-[30px_45px]",
|
||||
"rounded-[0_6px_6px_0] border-zinc-200/70 lg:border dark:border-neutral-800",
|
||||
"border-border rounded-[0_6px_6px_0] lg:border",
|
||||
"shadow-perfect perfect-sm",
|
||||
"min-w-0",
|
||||
"print:!border-none print:!bg-transparent print:!shadow-none",
|
||||
|
|
|
|||
|
|
@ -31,8 +31,10 @@ const NormalDailyReportTitle: FC<DailyItemProps> = ({ day }) => {
|
|||
return (
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setOpenedSummary(day)}
|
||||
className="border-b pb-6 last:border-b-0"
|
||||
onClick={() => {
|
||||
setOpenedSummary(day)
|
||||
}}
|
||||
className="border-b last:border-b-0"
|
||||
>
|
||||
<DailyReportTitle title={title} startDate={startDate} endDate={endDate} />
|
||||
</button>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,5 @@
|
|||
import { Spring } from "@follow/components/constants/spring.js"
|
||||
import { EmptyIcon } from "@follow/components/icons/empty.jsx"
|
||||
import { AutoResizeHeight } from "@follow/components/ui/auto-resize-height/index.jsx"
|
||||
import { Card, CardContent } from "@follow/components/ui/card/index.jsx"
|
||||
import { LoadingCircle } from "@follow/components/ui/loading/index.jsx"
|
||||
import { RootPortal } from "@follow/components/ui/portal/index.js"
|
||||
import { ScrollArea } from "@follow/components/ui/scroll-area/index.js"
|
||||
|
|
@ -12,6 +11,7 @@ import {
|
|||
} from "@follow/components/ui/tooltip/index.jsx"
|
||||
import { nextFrame, stopPropagation } from "@follow/utils/dom"
|
||||
import { cn, isBizId } from "@follow/utils/utils"
|
||||
import { noop } from "foxact/noop"
|
||||
import type { Components } from "hast-util-to-jsx-runtime"
|
||||
import type { Variant } from "motion/react"
|
||||
import { m, useAnimationControls } from "motion/react"
|
||||
|
|
@ -21,7 +21,7 @@ import { Trans, useTranslation } from "react-i18next"
|
|||
|
||||
import { MenuItemText } from "~/atoms/context-menu"
|
||||
import { useGeneralSettingSelector } from "~/atoms/settings/general"
|
||||
import { Collapse } from "~/components/ui/collapse"
|
||||
import { CollapseControlled } from "~/components/ui/collapse"
|
||||
import { RelativeTime } from "~/components/ui/datetime"
|
||||
import {
|
||||
DropdownMenu,
|
||||
|
|
@ -57,18 +57,34 @@ import { remarkSnowflakeId } from "./plugins/parse-snowflake"
|
|||
import type { DailyItemProps, DailyView } from "./types"
|
||||
import { useParseDailyDate } from "./useParseDailyDate"
|
||||
|
||||
export const DailyItem = ({ view, day, className }: DailyItemProps) => {
|
||||
export const DailyItem = ({
|
||||
view,
|
||||
day,
|
||||
className,
|
||||
onClick,
|
||||
isOpened,
|
||||
}: DailyItemProps & {
|
||||
isOpened: boolean
|
||||
onClick: () => void
|
||||
}) => {
|
||||
const { title, startDate, endDate } = useParseDailyDate(day)
|
||||
|
||||
return (
|
||||
<Collapse
|
||||
<CollapseControlled
|
||||
isOpened={isOpened}
|
||||
onOpenChange={noop}
|
||||
collapseId={`${day}`}
|
||||
hideArrow
|
||||
title={<DailyReportTitle title={title} startDate={startDate} endDate={endDate} />}
|
||||
className={cn(className, "mx-auto w-full max-w-lg border-b pb-6 last:border-b-0")}
|
||||
contentClassName="flex-1 flex flex-col"
|
||||
title={
|
||||
<button type="button" className="container" onClick={onClick}>
|
||||
<DailyReportTitle title={title} startDate={startDate} endDate={endDate} />
|
||||
</button>
|
||||
}
|
||||
className={cn(className, "mx-auto w-full max-w-lg border-b last:border-b-0")}
|
||||
>
|
||||
<DailyReportContent endDate={endDate} view={view} startDate={startDate} />
|
||||
</Collapse>
|
||||
</CollapseControlled>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -93,8 +109,9 @@ export const DailyReportTitle = ({
|
|||
|
||||
return (
|
||||
<m.div
|
||||
className="flex items-center justify-center gap-2 text-base"
|
||||
className="flex items-center justify-center gap-2 pb-6 text-base"
|
||||
layoutId={`daily-report-title-${title}`}
|
||||
transition={Spring.presets.smooth}
|
||||
>
|
||||
<i className="i-mgc-ai-cute-re" />
|
||||
<div className="font-medium">{t("ai_daily.title", { title })}</div>
|
||||
|
|
@ -167,29 +184,30 @@ interface DailyReportContentProps {
|
|||
endDate: number
|
||||
}
|
||||
|
||||
export const DailyReportContent: Component<DailyReportContentProps> = ({
|
||||
const DailyReportContent: Component<DailyReportContentProps> = ({
|
||||
endDate,
|
||||
startDate,
|
||||
|
||||
view,
|
||||
className,
|
||||
}) => {
|
||||
const content = useQueryData({ endDate, startDate, view })
|
||||
|
||||
const RelatedEntryLink = useState(() => createRelatedEntryLink("modal"))[0]
|
||||
|
||||
return (
|
||||
<Card className="border-none bg-transparent">
|
||||
<CardContent className={cn("space-y-0 p-0", className)}>
|
||||
<ScrollArea.ScrollArea mask={false} flex viewportClassName="h-[calc(100vh-176px)]">
|
||||
<AutoResizeHeight spring>
|
||||
{content.isLoading ? (
|
||||
<LoadingCircle
|
||||
size="large"
|
||||
className="center flex h-[calc(100vh-176px)] text-center"
|
||||
/>
|
||||
) : (
|
||||
!!content.data && (
|
||||
<div className="relative h-0 flex-1 grow">
|
||||
<div className="absolute inset-0 flex">
|
||||
<ScrollArea.ScrollArea mask flex viewportClassName="grow" rootClassName="grow">
|
||||
{content.isLoading ? (
|
||||
<LoadingCircle size="large" className="center flex h-[calc(100vh-176px)] text-center" />
|
||||
) : (
|
||||
!!content.data && (
|
||||
<m.div
|
||||
initial={{ opacity: 0 }}
|
||||
animate={{ opacity: 1 }}
|
||||
transition={Spring.smooth(0.35)}
|
||||
className="overflow-hidden"
|
||||
>
|
||||
<Markdown
|
||||
applyMiddleware={(pipeline) => {
|
||||
pipeline.use(remarkSnowflakeId)
|
||||
|
|
@ -205,12 +223,12 @@ export const DailyReportContent: Component<DailyReportContentProps> = ({
|
|||
>
|
||||
{content.data}
|
||||
</Markdown>
|
||||
)
|
||||
)}
|
||||
</AutoResizeHeight>
|
||||
</m.div>
|
||||
)
|
||||
)}
|
||||
</ScrollArea.ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -457,7 +475,7 @@ const EntryToastPreview = ({ entryId }: { entryId: string }) => {
|
|||
}
|
||||
|
||||
const EntryModalPreview = ({ entryId }: { entryId: string }) => (
|
||||
<Paper className="!p-0">
|
||||
<Paper className="p-0 !pt-16 empty:hidden">
|
||||
<EntryContent
|
||||
className="h-auto [&_#entry-action-header-bar]:!bg-transparent"
|
||||
entryId={entryId}
|
||||
|
|
@ -503,7 +521,7 @@ const EntryMoreActions: FC<{ entryId: string }> = ({ entryId }) => {
|
|||
<i className="i-mgc-more-1-cute-re" />
|
||||
</DropdownMenuTrigger>
|
||||
<RootPortal>
|
||||
<DropdownMenuContent>
|
||||
<DropdownMenuContent alignOffset={20} sideOffset={30}>
|
||||
{availableActions.map((config) =>
|
||||
config instanceof MenuItemText ? (
|
||||
<CommandDropdownMenuItem
|
||||
|
|
|
|||
|
|
@ -57,8 +57,9 @@ const SummaryDetailContent = () => {
|
|||
const ctxValue = React.useContext(EntryContentPlaceholderContext)
|
||||
const openedSummary = useAtomValue(ctxValue.openedSummary)
|
||||
const setOpenedSummary = useSetAtom(ctxValue.openedSummary)
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<>
|
||||
<div className="fade-in-0 absolute right-4 top-4 duration-200">
|
||||
<PeekModalBaseButton
|
||||
icon={<i className="i-mgc-close-cute-re" />}
|
||||
|
|
@ -67,27 +68,35 @@ const SummaryDetailContent = () => {
|
|||
/>
|
||||
</div>
|
||||
|
||||
<CollapseGroup
|
||||
defaultOpenId={`${openedSummary}`}
|
||||
onOpenChange={(state) => {
|
||||
const isAllClosed = Object.values(state).every((v) => !v)
|
||||
if (isAllClosed) {
|
||||
setOpenedSummary(null)
|
||||
}
|
||||
}}
|
||||
>
|
||||
<CollapseGroup defaultOpenId={`${openedSummary}`}>
|
||||
<DailyItem
|
||||
isOpened={openedSummary === DayOf.Today}
|
||||
onClick={() => {
|
||||
if (openedSummary === DayOf.Today) {
|
||||
setOpenedSummary(null)
|
||||
} else {
|
||||
setOpenedSummary(DayOf.Today)
|
||||
}
|
||||
}}
|
||||
day={DayOf.Today}
|
||||
view={view as DailyView}
|
||||
className={cn(openedSummary === DayOf.Today && "grow", "pt-6")}
|
||||
/>
|
||||
|
||||
<DailyItem
|
||||
isOpened={openedSummary === DayOf.Yesterday}
|
||||
onClick={() => {
|
||||
if (openedSummary === DayOf.Yesterday) {
|
||||
setOpenedSummary(null)
|
||||
} else {
|
||||
setOpenedSummary(DayOf.Yesterday)
|
||||
}
|
||||
}}
|
||||
day={DayOf.Yesterday}
|
||||
view={view as DailyView}
|
||||
className={cn(openedSummary === DayOf.Yesterday && "grow", "pt-6")}
|
||||
/>
|
||||
</CollapseGroup>
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,20 +14,18 @@ export function ArticleItem({ entryId, entryPreview, translation }: UniversalIte
|
|||
|
||||
export function ArticleItemStateLess({ entry, feed }: EntryItemStatelessProps) {
|
||||
return (
|
||||
<div className="relative select-none rounded-md text-zinc-700 transition-colors dark:text-neutral-400">
|
||||
<div className="bg-theme-background text-text relative select-none rounded-md transition-colors">
|
||||
<div className="relative">
|
||||
<div className="group relative flex py-4 pl-3 pr-2">
|
||||
<FeedIcon className="mr-2 size-5 rounded-sm" feed={feed} fallback />
|
||||
<div className="-mt-0.5 line-clamp-4 flex-1 text-sm leading-tight">
|
||||
<div className="flex gap-1 text-[10px] font-bold text-zinc-400 dark:text-neutral-500">
|
||||
<div className="text-text-secondary flex gap-1 text-[10px] font-bold">
|
||||
<FeedTitle feed={feed} />
|
||||
<span>·</span>
|
||||
<span>{!!entry.publishedAt && <RelativeTime date={entry.publishedAt} />}</span>
|
||||
</div>
|
||||
<div className="relative my-1 break-words font-medium">{entry.title}</div>
|
||||
<div className="mt-1.5 text-[13px] text-zinc-400 dark:text-neutral-500">
|
||||
{entry.description}
|
||||
</div>
|
||||
<div className="text-text-secondary mt-1.5 text-[13px]">{entry.description}</div>
|
||||
</div>
|
||||
{entry.media?.[0] ? (
|
||||
<Media
|
||||
|
|
@ -56,12 +54,12 @@ export function ArticleItemStateLess({ entry, feed }: EntryItemStatelessProps) {
|
|||
}
|
||||
|
||||
export const ArticleItemSkeleton = (
|
||||
<div className="relative h-[120px] rounded-md text-zinc-700 transition-colors dark:text-neutral-400">
|
||||
<div className="relative h-[120px] rounded-md">
|
||||
<div className="relative">
|
||||
<div className="group relative flex py-4 pl-3 pr-2">
|
||||
<Skeleton className="mr-2 size-5 rounded-sm" />
|
||||
<div className="-mt-0.5 flex-1 text-sm leading-tight">
|
||||
<div className="flex gap-1 text-[10px] font-bold text-zinc-400 dark:text-neutral-500">
|
||||
<div className="text-material-opaque flex gap-1 text-[10px] font-bold">
|
||||
<Skeleton className="h-3 w-24" />
|
||||
<span>·</span>
|
||||
<Skeleton className="h-3 w-12 shrink-0" />
|
||||
|
|
@ -70,7 +68,7 @@ export const ArticleItemSkeleton = (
|
|||
<Skeleton className="h-3.5 w-full" />
|
||||
<Skeleton className="mt-1 h-3.5 w-3/4" />
|
||||
</div>
|
||||
<div className="mt-1.5 text-[13px] text-zinc-400 dark:text-neutral-500">
|
||||
<div className="text-material-opaque mt-1.5 text-[13px]">
|
||||
<Skeleton className="h-3 w-full" />
|
||||
<Skeleton className="mt-1 h-3 w-4/5" />
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -13,11 +13,11 @@ export function AudioItem({ entryId, entryPreview, translation }: UniversalItemP
|
|||
|
||||
export function AudioItemStateLess({ entry, feed }: EntryItemStatelessProps) {
|
||||
return (
|
||||
<div className="bg-theme-background relative mx-auto w-full max-w-lg select-none rounded-md text-zinc-700 transition-colors dark:text-neutral-400">
|
||||
<div className="relative mx-auto w-full max-w-lg select-none rounded-md">
|
||||
<div className="relative">
|
||||
<div className="group relative flex py-4 pl-3 pr-2">
|
||||
<div className="-mt-0.5 line-clamp-4 flex-1 text-sm leading-tight">
|
||||
<div className="flex gap-1 text-[10px] font-bold text-zinc-400 dark:text-neutral-500">
|
||||
<div className="text-material-opaque flex gap-1 text-[10px] font-bold">
|
||||
<FeedTitle feed={feed} />
|
||||
<span>·</span>
|
||||
<span>{!!entry.publishedAt && <RelativeTime date={entry.publishedAt} />}</span>
|
||||
|
|
@ -55,11 +55,11 @@ export function AudioItemStateLess({ entry, feed }: EntryItemStatelessProps) {
|
|||
}
|
||||
|
||||
export const AudioItemSkeleton = (
|
||||
<div className="bg-theme-background relative mx-auto w-full select-none rounded-md text-zinc-700 transition-colors dark:text-neutral-400">
|
||||
<div className="relative mx-auto w-full select-none rounded-md">
|
||||
<div className="relative">
|
||||
<div className="group relative flex py-4 pl-3 pr-2">
|
||||
<div className="-mt-0.5 line-clamp-4 flex-1 text-sm leading-tight">
|
||||
<div className="flex gap-1 text-[10px] font-bold text-zinc-400 dark:text-neutral-500">
|
||||
<div className="text-material-opaque flex gap-1 text-[10px] font-bold">
|
||||
<Skeleton className="h-3 w-20" />
|
||||
<span>·</span>
|
||||
<Skeleton className="h-3 w-10" />
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ export function NotificationItemStateLess({ entry, feed }: EntryItemStatelessPro
|
|||
<FeedIcon feed={feed} fallback />
|
||||
|
||||
<div className="-mt-0.5 line-clamp-4 flex-1 text-sm leading-tight">
|
||||
<div className="flex gap-1 text-[10px] font-bold text-zinc-400 dark:text-neutral-500">
|
||||
<div className="text-text-secondary flex gap-1 text-[10px] font-bold">
|
||||
<FeedTitle feed={feed} />
|
||||
<span>·</span>
|
||||
<span>{!!entry.publishedAt && <RelativeTime date={entry.publishedAt} />}</span>
|
||||
|
|
@ -35,7 +35,7 @@ export const NotificationItemSkeleton = (
|
|||
<div className="group relative flex py-4 pl-3 pr-2">
|
||||
<Skeleton className="mr-2 size-5 shrink-0 overflow-hidden rounded-sm" />
|
||||
<div className="-mt-0.5 line-clamp-4 flex-1 text-sm leading-tight">
|
||||
<div className="flex gap-1 text-[10px] font-bold text-zinc-400 dark:text-neutral-500">
|
||||
<div className="text-material-opaque flex gap-1 text-[10px] font-bold">
|
||||
<Skeleton className="h-3 w-32 truncate" />
|
||||
<span>·</span>
|
||||
<Skeleton className="h-3 w-12 shrink-0" />
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import type { EntryItemStatelessProps } from "../types"
|
|||
|
||||
export function PictureItemStateLess({ entry, feed }: EntryItemStatelessProps) {
|
||||
return (
|
||||
<div className="bg-theme-background relative mx-auto max-w-md select-none rounded-md text-zinc-700 transition-colors dark:text-neutral-400">
|
||||
<div className="bg-theme-background text-text relative mx-auto max-w-md select-none rounded-md transition-colors">
|
||||
<div className="relative">
|
||||
<div className="p-1.5">
|
||||
<div className="relative flex gap-2 overflow-x-auto">
|
||||
|
|
@ -49,7 +49,7 @@ export function PictureItemStateLess({ entry, feed }: EntryItemStatelessProps) {
|
|||
<div className="mt-1 flex items-center gap-1 truncate text-[13px]">
|
||||
<FeedIcon feed={feed} fallback className="size-4" />
|
||||
<FeedTitle feed={feed} />
|
||||
<span className="text-zinc-500">·</span>
|
||||
<span className="text-material-opaque">·</span>
|
||||
{!!entry.publishedAt && <RelativeTime date={entry.publishedAt} />}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ MasonryItemFixedDimensionWrapper.whyDidYouRender = {
|
|||
}
|
||||
|
||||
export const PictureItemSkeleton = (
|
||||
<div className="bg-theme-background relative max-w-md rounded-md text-zinc-700 transition-colors dark:text-neutral-400">
|
||||
<div className="relative max-w-md rounded-md">
|
||||
<div className="relative">
|
||||
<div className="p-1.5">
|
||||
<div className="relative flex gap-2 overflow-x-auto">
|
||||
|
|
@ -193,7 +193,7 @@ export const PictureItemSkeleton = (
|
|||
<div className="mt-1 flex items-center gap-1 truncate text-[13px]">
|
||||
<Skeleton className="mr-0.5 size-4" />
|
||||
<Skeleton className="h-3 w-1/2" />
|
||||
<span className="text-zinc-500">·</span>
|
||||
<span className="text-material-opaque">·</span>
|
||||
<Skeleton className="h-3 w-12" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -199,7 +199,7 @@ export function SocialMediaItemStateLess({ entry, feed }: EntryItemStatelessProp
|
|||
}
|
||||
|
||||
export const SocialMediaItemSkeleton = (
|
||||
<div className="bg-theme-background relative m-auto w-[645px] rounded-md text-zinc-700 transition-colors dark:text-neutral-400">
|
||||
<div className="relative m-auto w-[645px] rounded-md">
|
||||
<div className="relative">
|
||||
<div className="group relative flex px-8 py-6">
|
||||
<Skeleton className="mr-2 size-9" />
|
||||
|
|
@ -207,7 +207,7 @@ export const SocialMediaItemSkeleton = (
|
|||
<div className="-mt-0.5 line-clamp-5 flex-1 text-sm">
|
||||
<div className="flex w-[calc(100%-10rem)] space-x-1">
|
||||
<Skeleton className="h-4 w-16" />
|
||||
<span className="text-zinc-500">·</span>
|
||||
<span className="text-material-opaque">·</span>
|
||||
<Skeleton className="h-4 w-12" />
|
||||
</div>
|
||||
<div className="relative mt-0.5 text-sm">
|
||||
|
|
|
|||
|
|
@ -190,7 +190,7 @@ const PreviewVideoModalContent: ModalContentComponent<{
|
|||
|
||||
export function VideoItemStateLess({ entry, feed }: EntryItemStatelessProps) {
|
||||
return (
|
||||
<div className="bg-theme-background relative mx-auto w-full max-w-lg rounded-md text-zinc-700 transition-colors dark:text-neutral-400">
|
||||
<div className="bg-theme-background text-text relative mx-auto w-full max-w-lg rounded-md transition-colors">
|
||||
<div className="relative">
|
||||
<div className="p-1.5">
|
||||
<div className="w-full">
|
||||
|
|
@ -221,10 +221,10 @@ export function VideoItemStateLess({ entry, feed }: EntryItemStatelessProps) {
|
|||
<div className="relative mb-1 mt-1.5 truncate font-medium leading-none">
|
||||
{entry.title}
|
||||
</div>
|
||||
<div className="mt-1 flex items-center gap-1 truncate text-[13px]">
|
||||
<div className="text-text-secondary mt-1 flex items-center gap-1 truncate text-[13px]">
|
||||
<FeedIcon feed={feed} fallback className="size-4" />
|
||||
<FeedTitle feed={feed} />
|
||||
<span className="text-zinc-500">·</span>
|
||||
<span className="text-material-opaque">·</span>
|
||||
{!!entry.publishedAt && <RelativeTime date={entry.publishedAt} />}
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -235,7 +235,7 @@ export function VideoItemStateLess({ entry, feed }: EntryItemStatelessProps) {
|
|||
}
|
||||
|
||||
export const VideoItemSkeleton = (
|
||||
<div className="bg-theme-background relative mx-auto w-full max-w-lg rounded-md text-zinc-700 transition-colors dark:text-neutral-400">
|
||||
<div className="relative mx-auto w-full max-w-lg rounded-md">
|
||||
<div className="relative">
|
||||
<div className="p-1.5">
|
||||
<div className="w-full">
|
||||
|
|
@ -250,7 +250,7 @@ export const VideoItemSkeleton = (
|
|||
<div className="mt-1 flex items-center gap-1 truncate text-[13px]">
|
||||
<Skeleton className="mr-0.5 size-4" />
|
||||
<Skeleton className="h-3 w-1/2" />
|
||||
<span className="text-zinc-500">·</span>
|
||||
<span className="text-material-opaque">·</span>
|
||||
<Skeleton className="h-3 w-12" />
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -139,7 +139,6 @@ export const EntryItemWrapper: FC<
|
|||
<div
|
||||
className={cn(
|
||||
"hover:bg-theme-item-hover relative duration-200",
|
||||
asRead ? "text-zinc-700 dark:text-neutral-400" : "text-zinc-900 dark:text-neutral-300",
|
||||
views[view as FeedViewType]?.wideMode ? "rounded-md" : "px-2",
|
||||
(isActive || isContextMenuOpen) && "!bg-theme-item-active",
|
||||
itemClassName,
|
||||
|
|
|
|||
|
|
@ -280,7 +280,7 @@ function AudioCover({
|
|||
>
|
||||
<button
|
||||
type="button"
|
||||
className="center bg-theme-background hover:bg-accent size-10 rounded-full opacity-95 hover:text-white hover:opacity-100"
|
||||
className="center bg-material-opaque hover:bg-accent size-10 rounded-full opacity-95 hover:text-white hover:opacity-100"
|
||||
>
|
||||
<i
|
||||
className={cn("size-6", {
|
||||
|
|
@ -296,17 +296,17 @@ function AudioCover({
|
|||
<div className="absolute bottom-0 w-full overflow-hidden rounded-b-sm text-center">
|
||||
<div
|
||||
className={cn(
|
||||
"absolute left-0 top-0 size-full bg-white/50 opacity-0 duration-200 group-hover:opacity-100 dark:bg-neutral-900/70",
|
||||
"bg-material-ultra-thick absolute left-0 top-0 size-full opacity-0 duration-200 group-hover:opacity-100",
|
||||
isMobile && "opacity-100",
|
||||
)}
|
||||
/>
|
||||
<div
|
||||
className={cn(
|
||||
"text-[13px] opacity-0 backdrop-blur-none duration-200 group-hover:opacity-100 group-hover:backdrop-blur-sm",
|
||||
isMobile && "opacity-100 backdrop-blur-sm",
|
||||
"group-hover:backdrop-blur-background text-[13px] opacity-0 backdrop-blur-none duration-200 group-hover:opacity-100",
|
||||
isMobile && "backdrop-blur-background opacity-100",
|
||||
)}
|
||||
>
|
||||
{formatEstimatedMins(estimatedMins)}
|
||||
{formatEstimatedMins(10)}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -111,9 +111,9 @@ export function AISummary({ entryId }: { entryId: string }) {
|
|||
>
|
||||
{summary.isLoading ? (
|
||||
<div className="space-y-2">
|
||||
<div className="h-3 w-full rounded-lg bg-neutral-200/30 dark:bg-neutral-700/30" />
|
||||
<div className="h-3 w-[92%] rounded-lg bg-neutral-200/30 dark:bg-neutral-700/30" />
|
||||
<div className="h-3 w-[85%] rounded-lg bg-neutral-200/30 dark:bg-neutral-700/30" />
|
||||
<div className="bg-material-ultra-thick h-3 w-full rounded-lg" />
|
||||
<div className="bg-material-ultra-thick h-3 w-[92%] rounded-lg" />
|
||||
<div className="bg-material-ultra-thick h-3 w-[85%] rounded-lg" />
|
||||
</div>
|
||||
) : (
|
||||
<div className="animate-in fade-in-0 duration-500">{summary.data}</div>
|
||||
|
|
|
|||
|
|
@ -11,11 +11,11 @@ export const EntryPlaceholderLogo = () => {
|
|||
data-hide-in-print
|
||||
onContextMenu={stopPropagation}
|
||||
className={
|
||||
"flex w-full min-w-0 flex-col items-center justify-center gap-1 px-12 pb-6 text-center text-lg font-medium text-zinc-400 duration-500"
|
||||
"text-text-secondary flex w-full min-w-0 flex-col items-center justify-center gap-1 px-12 pb-6 text-center text-lg font-medium duration-500"
|
||||
}
|
||||
>
|
||||
<Logo className="size-16 opacity-40 grayscale" />
|
||||
<div className="line-clamp-3 w-[60ch] max-w-full">{title}</div>
|
||||
<Logo className="[&_rect]:fill-text-secondary size-16 opacity-40" />
|
||||
<div className="line-clamp-3 w-[60ch] max-w-full opacity-70">{title}</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ export const EntryTitle = ({ entryId, compact }: EntryLinkProps) => {
|
|||
|
||||
{/* Meta Information with improved layout */}
|
||||
<div className="flex flex-wrap items-center gap-x-6 gap-y-2 text-sm">
|
||||
<div className="text-text-secondary [&>div:hover]:multi-['text-gray-700;dark:text-gray-300'] flex flex-wrap items-center gap-4 [&>div]:transition-colors">
|
||||
<div className="text-text-secondary [&>div:hover]:multi-[text-text] flex flex-wrap items-center gap-4 [&>div]:transition-colors">
|
||||
<div
|
||||
className="flex items-center text-xs font-medium"
|
||||
onClick={() =>
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import {
|
|||
} from "~/atoms/readability"
|
||||
import { useUISettingKey } from "~/atoms/settings/ui"
|
||||
import { ShadowDOM } from "~/components/common/ShadowDOM"
|
||||
import { useInPeekModal } from "~/components/ui/modal/inspire/PeekModal"
|
||||
import { useInPeekModal } from "~/components/ui/modal/inspire/InPeekModal"
|
||||
import { useRouteParamsSelector } from "~/hooks/biz/useRouteParams"
|
||||
import { useAuthQuery } from "~/hooks/common"
|
||||
import { useFeedSafeUrl } from "~/hooks/common/useFeedSafeUrl"
|
||||
|
|
@ -154,16 +154,7 @@ export const EntryContent: Component<EntryContentProps> = ({
|
|||
|
||||
<div className="@container relative flex size-full flex-col overflow-hidden print:size-auto print:overflow-visible">
|
||||
<EntryTimelineSidebar entryId={entry.entries.id} />
|
||||
<ScrollArea.ScrollArea
|
||||
mask={false}
|
||||
rootClassName={cn(
|
||||
"h-0 min-w-0 grow overflow-y-auto print:h-auto print:overflow-visible",
|
||||
className,
|
||||
)}
|
||||
scrollbarClassName="mr-[1.5px] print:hidden"
|
||||
viewportClassName="p-5"
|
||||
ref={scrollerRef}
|
||||
>
|
||||
<EntryScrollArea className={className} scrollerRef={scrollerRef}>
|
||||
<div
|
||||
style={stableRenderStyle}
|
||||
className="animate-in fade-in slide-in-from-bottom-24 f-motion-reduce:fade-in-0 f-motion-reduce:slide-in-from-bottom-0 select-text duration-200 ease-in-out"
|
||||
|
|
@ -238,9 +229,33 @@ export const EntryContent: Component<EntryContentProps> = ({
|
|||
<SupportCreator entryId={entryId} />
|
||||
</article>
|
||||
</div>
|
||||
</ScrollArea.ScrollArea>
|
||||
</EntryScrollArea>
|
||||
<SourceContentPanel src={safeUrl ?? "#"} />
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const EntryScrollArea: Component<{
|
||||
scrollerRef: React.RefObject<HTMLDivElement>
|
||||
}> = ({ children, className, scrollerRef }) => {
|
||||
const isInPeekModal = useInPeekModal()
|
||||
|
||||
if (isInPeekModal) {
|
||||
return <div className="p-5">{children}</div>
|
||||
}
|
||||
return (
|
||||
<ScrollArea.ScrollArea
|
||||
mask={false}
|
||||
rootClassName={cn(
|
||||
"h-0 min-w-0 grow overflow-y-auto print:h-auto print:overflow-visible",
|
||||
className,
|
||||
)}
|
||||
scrollbarClassName="mr-[1.5px] print:hidden"
|
||||
viewportClassName="p-5"
|
||||
ref={scrollerRef}
|
||||
>
|
||||
{children}
|
||||
</ScrollArea.ScrollArea>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -45,14 +45,6 @@ export interface EntryContentClassNames {
|
|||
header?: string
|
||||
}
|
||||
|
||||
export const SummaryLoadingSkeleton = (
|
||||
<div className="space-y-2">
|
||||
<span className="block h-3 w-full animate-pulse rounded-xl bg-zinc-200 dark:bg-neutral-800" />
|
||||
<span className="block h-3 w-full animate-pulse rounded-xl bg-zinc-200 dark:bg-neutral-800" />
|
||||
<span className="block h-3 w-full animate-pulse rounded-xl bg-zinc-200 dark:bg-neutral-800" />
|
||||
</div>
|
||||
)
|
||||
|
||||
export const TitleMetaHandler: Component<{
|
||||
entryId: string
|
||||
}> = ({ entryId }) => {
|
||||
|
|
|
|||
|
|
@ -58,7 +58,7 @@ const ShortcutModalContent = () => {
|
|||
<div className="text-text-secondary mb-2 pl-4 text-xs font-medium capitalize">
|
||||
{t(shortcutsType[type])}
|
||||
</div>
|
||||
<div className="rounded-md border text-[13px] text-zinc-600 dark:text-zinc-300">
|
||||
<div className="text-text rounded-md border text-[13px]">
|
||||
{Object.keys(shortcuts[type]).map((action, index) => (
|
||||
<div
|
||||
key={`${type}-${action}`}
|
||||
|
|
|
|||
|
|
@ -414,8 +414,8 @@ const PlaybackRateSelector = () => {
|
|||
type="button"
|
||||
className={cn(
|
||||
"center hover:bg-theme-item-hover rounded-md p-1 font-mono",
|
||||
playbackRate === rate && "bg-theme-item-hover text-black dark:text-white",
|
||||
playbackRate !== rate && "text-zinc-500",
|
||||
playbackRate === rate && "bg-theme-item-hover text-text",
|
||||
playbackRate !== rate && "text-text-secondary",
|
||||
)}
|
||||
onClick={() => AudioPlayer.setPlaybackRate(rate)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export const TimeStamp = (props: { time: string }) => {
|
|||
|
||||
return (
|
||||
<span
|
||||
className="text-accent dark:text-theme-accent-500 cursor-pointer tabular-nums"
|
||||
className="text-accent cursor-pointer tabular-nums"
|
||||
onClick={() => {
|
||||
AudioPlayer.mount({
|
||||
type: "audio",
|
||||
|
|
|
|||
|
|
@ -306,7 +306,7 @@ function FeedCategoryImpl({ data: ids, view, categoryOpenStateData }: FeedCatego
|
|||
}}
|
||||
className="center hover:bg-material-ultra-thick -ml-1 flex size-5 shrink-0 rounded-lg"
|
||||
>
|
||||
<i className="i-mgc-close-cute-re text-red-500 dark:text-red-400" />
|
||||
<i className="i-mgc-close-cute-re text-red" />
|
||||
</MotionButtonBase>
|
||||
) : (
|
||||
<div className="center mr-2 size-[16px]">
|
||||
|
|
@ -435,7 +435,7 @@ const RenameCategoryForm: FC<{
|
|||
/>
|
||||
<MotionButtonBase
|
||||
type="submit"
|
||||
className="center hover:bg-material-ultra-thick -mr-1 flex size-5 shrink-0 rounded-lg text-green-600 dark:text-green-400"
|
||||
className="center hover:bg-material-ultra-thick text-green -mr-1 flex size-5 shrink-0 rounded-lg"
|
||||
>
|
||||
<i className="i-mgc-check-filled size-3" />
|
||||
</MotionButtonBase>
|
||||
|
|
|
|||
|
|
@ -178,12 +178,7 @@ const FeedItemImpl = ({ view, feedId, className }: FeedItemProps) => {
|
|||
}}
|
||||
{...contextMenuProps}
|
||||
>
|
||||
<div
|
||||
className={cn(
|
||||
"flex min-w-0 items-center",
|
||||
isFeed && feed.errorAt && "text-red-900 dark:text-red-500",
|
||||
)}
|
||||
>
|
||||
<div className={cn("flex min-w-0 items-center", isFeed && feed.errorAt && "text-red")}>
|
||||
<FeedIcon fallback feed={feed} size={16} />
|
||||
<FeedTitle feed={feed} />
|
||||
{isFeed && feed.errorAt && (
|
||||
|
|
|
|||
|
|
@ -145,7 +145,7 @@ const SortButton = () => {
|
|||
animate={{ opacity: 1, scale: 1, y: 0 }}
|
||||
exit={{ opacity: 0, scale: 0.98, y: 10 }}
|
||||
transition={{ type: "spring", duration: 0.3 }}
|
||||
className="border-border bg-theme-background relative z-10 rounded-md border p-3 shadow-md dark:shadow-zinc-500/20"
|
||||
className="border-border bg-theme-background shadow-context-menu relative z-10 rounded-md border p-3"
|
||||
>
|
||||
<HoverCard.Arrow className="fill-border -translate-x-4" />
|
||||
<section className="w-[170px] text-center">
|
||||
|
|
|
|||
|
|
@ -7,12 +7,7 @@ export const UnreadNumber = ({ unread, className }: { unread: number; className?
|
|||
|
||||
if (!unread) return null
|
||||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"center text-[0.65rem] tabular-nums text-zinc-400 dark:text-neutral-500",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
<div className={cn("center text-text-tertiary text-[0.65rem] tabular-nums", className)}>
|
||||
{!showUnreadCount ? <i className="i-mgc-round-cute-fi text-[0.3rem]" /> : unread}
|
||||
</div>
|
||||
)
|
||||
|
|
|
|||
|
|
@ -1,46 +0,0 @@
|
|||
.mask-scroller {
|
||||
mask:
|
||||
linear-gradient(to right, white, transparent) 0 50% / 0.8rem 100% no-repeat,
|
||||
linear-gradient(white, white) 50% 50% / 100% 100% no-repeat,
|
||||
linear-gradient(to right, transparent, white) 100% 50% / 0.8rem 100% no-repeat;
|
||||
mask-composite: exclude;
|
||||
mask-size:
|
||||
calc((var(--scroll-progress-left) / 100) * 0.8rem) 100%,
|
||||
100% 100%,
|
||||
calc((100 - (100 * (var(--scroll-progress-right) / 100))) * 1px) 100%;
|
||||
}
|
||||
|
||||
@supports (animation-timeline: scroll()) {
|
||||
.mask-scroller {
|
||||
mask:
|
||||
linear-gradient(to right, white, transparent) 0 50% / 0.8rem 100% no-repeat,
|
||||
linear-gradient(white, white) 50% 50% / 100% 100% no-repeat,
|
||||
linear-gradient(to right, transparent, white) 100% 50% / 1.6rem 100% no-repeat;
|
||||
mask-composite: exclude;
|
||||
animation:
|
||||
mask-left both linear,
|
||||
mask-right both linear;
|
||||
animation-timeline: scroll(self);
|
||||
animation-range:
|
||||
0 50px,
|
||||
calc(100% - 50px) 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mask-left {
|
||||
100% {
|
||||
mask-size:
|
||||
0.8rem 100%,
|
||||
100% 100%,
|
||||
0.8rem 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mask-right {
|
||||
100% {
|
||||
mask-size:
|
||||
0.8rem 100%,
|
||||
100% 100%,
|
||||
0 100%;
|
||||
}
|
||||
}
|
||||
|
|
@ -135,7 +135,7 @@ export function FeedColumn({ children, className }: PropsWithChildren<{ classNam
|
|||
className={cn(
|
||||
"relative flex h-full flex-col pt-2.5",
|
||||
|
||||
!feedColumnShow && ELECTRON_BUILD && "bg-zinc-200 dark:bg-neutral-800",
|
||||
!feedColumnShow && ELECTRON_BUILD && "bg-material-opaque",
|
||||
className,
|
||||
)}
|
||||
onClick={useCallback(() => navigateBackHome(), [navigateBackHome])}
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ export const UserAvatar = forwardRef<
|
|||
}}
|
||||
{...props}
|
||||
className={cn(
|
||||
"flex h-20 items-center justify-center gap-2 px-5 py-2 font-medium text-zinc-600 dark:text-zinc-300",
|
||||
"text-text-secondary flex h-20 items-center justify-center gap-2 px-5 py-2 font-medium",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ export const UserAvatar = ({ className }: { className?: string }) => {
|
|||
return (
|
||||
<div
|
||||
className={cn(
|
||||
"flex h-20 items-center justify-center gap-8 px-10 py-4 text-2xl font-medium text-zinc-600 dark:text-zinc-300",
|
||||
"text-text-secondary flex h-20 items-center justify-center gap-8 px-10 py-4 text-2xl font-medium",
|
||||
className,
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -1,12 +1,8 @@
|
|||
import { Spring } from "@follow/components/constants/spring"
|
||||
import { cn } from "@follow/utils/utils"
|
||||
import type { Spring } from "motion/react"
|
||||
import { m } from "motion/react"
|
||||
import { useEffect, useRef, useState } from "react"
|
||||
|
||||
const softSpringPreset: Spring = {
|
||||
type: "spring",
|
||||
}
|
||||
|
||||
interface AnimateChangeInHeightProps {
|
||||
children: React.ReactNode
|
||||
className?: string
|
||||
|
|
@ -56,7 +52,7 @@ export const AutoResizeHeight: React.FC<AnimateChangeInHeightProps> = ({
|
|||
transition={
|
||||
spring
|
||||
? {
|
||||
...softSpringPreset,
|
||||
...Spring.presets.softSpring,
|
||||
duration,
|
||||
}
|
||||
: {
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ export const AvatarFallback = React.forwardRef<
|
|||
<AvatarPrimitive.Fallback
|
||||
ref={ref}
|
||||
className={cn(
|
||||
"bg-material-medium flex size-full items-center justify-center rounded-full text-xs",
|
||||
"bg-material-opaque flex size-full items-center justify-center rounded-full text-xs",
|
||||
className,
|
||||
)}
|
||||
{...props}
|
||||
|
|
|
|||
|
|
@ -79,7 +79,7 @@ export const ActionButton = React.forwardRef<
|
|||
className={cn(
|
||||
"no-drag-region pointer-events-auto inline-flex items-center justify-center",
|
||||
active && typeof icon !== "function" && "bg-zinc-500/15 hover:bg-zinc-500/20",
|
||||
"hover:bg-material-ultra-thick data-[state=open]:bg-material-ultra-thick rounded-md duration-200",
|
||||
"hover:bg-theme-item-hover data-[state=open]:bg-theme-item-active rounded-md duration-200",
|
||||
"disabled:cursor-not-allowed disabled:opacity-50",
|
||||
clickableDisabled && "cursor-not-allowed opacity-50",
|
||||
actionButtonStyleVariant.size[size],
|
||||
|
|
|
|||
|
|
@ -57,7 +57,8 @@ export const MagneticHoverEffect = <T extends React.ElementType = "div">({
|
|||
className={cn(
|
||||
"relative",
|
||||
"inline-block transition-all duration-200 ease-out",
|
||||
"before:backdrop-blur-background before:absolute before:-inset-x-2 before:inset-y-0 before:z-[-1] before:scale-0 before:rounded-xl before:bg-black/[0.03] before:opacity-0 before:transition-all before:duration-200 before:[transform-origin:var(--origin-x)_var(--origin-y)] hover:before:scale-100 hover:before:opacity-100 dark:before:bg-white/[0.10]",
|
||||
"hover:bg-material-ultra-thick",
|
||||
"before:backdrop-blur-background before:absolute before:-inset-x-2 before:inset-y-0 before:z-[-1] before:scale-0 before:rounded-xl before:opacity-0 before:transition-all before:duration-200 before:[transform-origin:var(--origin-x)_var(--origin-y)] hover:before:scale-100 hover:before:opacity-100",
|
||||
rest.className,
|
||||
)}
|
||||
>
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ export function FeedIcon({
|
|||
}
|
||||
className={cn(
|
||||
"flex shrink-0 items-center justify-center rounded-sm",
|
||||
"bg-[var(--fo-light-background)] text-white dark:bg-[var(--fo-dark-background)]",
|
||||
"bg-material-ultra-thick text-white",
|
||||
"mr-2",
|
||||
className,
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,5 @@
|
|||
import { cn } from "@follow/utils/utils"
|
||||
|
||||
export function Skeleton({ className, ...props }: React.HTMLAttributes<HTMLDivElement>) {
|
||||
return (
|
||||
<div
|
||||
className={cn("animate-pulse rounded-md bg-gray-200 dark:bg-neutral-800", className)}
|
||||
{...props}
|
||||
/>
|
||||
)
|
||||
return <div className={cn("bg-material-opaque animate-pulse rounded-md", className)} {...props} />
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue