update to mantine 7

This commit is contained in:
Athou
2023-12-29 23:09:30 +01:00
parent 2742b7fff6
commit 7e02380858
52 changed files with 653 additions and 751 deletions

View File

@@ -0,0 +1,11 @@
import { TypographyStylesProvider } from "@mantine/core"
import { type ReactNode } from "react"
/**
* This component is used to provide basic styles to html typography elements.
*
* see https://mantine.dev/core/typography-styles-provider/
*/
export const BasicHtmlStyles = (props: { children: ReactNode }) => {
return <TypographyStylesProvider pl={0}>{props.children}</TypographyStylesProvider>
}

View File

@@ -1,6 +1,7 @@
import { Box, type MantineTheme, Mark, TypographyStylesProvider, useMantineTheme } from "@mantine/core"
import { Box, type MantineTheme, Mark, useMantineTheme } from "@mantine/core"
import { Constants } from "app/constants"
import { calculatePlaceholderSize } from "app/utils"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
import escapeStringRegexp from "escape-string-regexp"
import { type ChildrenNode, Interweave, Matcher, type MatchResponse, type Node, type TransformCallback } from "interweave"
@@ -21,7 +22,11 @@ const useStyles = tss
// break long links or long words
overflowWrap: "anywhere",
"& a": {
color: theme.fn.variant({ color: theme.primaryColor, variant: "subtle" }).color,
color: theme.variantColorResolver({
theme,
color: theme.primaryColor,
variant: "subtle",
}).color,
},
"& iframe": {
maxWidth: "100%",
@@ -96,11 +101,11 @@ const Content = React.memo((props: ContentProps) => {
const matchers = props.highlight ? [new HighlightMatcher(props.highlight)] : []
return (
<TypographyStylesProvider>
<BasicHtmlStyles>
<Box className={classes.content}>
<Interweave content={props.content} transform={transform} matchers={matchers} />
</Box>
</TypographyStylesProvider>
</BasicHtmlStyles>
)
})
Content.displayName = "Content"

View File

@@ -1,13 +1,13 @@
import { TypographyStylesProvider } from "@mantine/core"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
export function Enclosure(props: { enclosureType: string; enclosureUrl: string }) {
const hasVideo = props.enclosureType && props.enclosureType.indexOf("video") === 0
const hasAudio = props.enclosureType && props.enclosureType.indexOf("audio") === 0
const hasImage = props.enclosureType && props.enclosureType.indexOf("image") === 0
const hasVideo = props.enclosureType?.startsWith("video")
const hasAudio = props.enclosureType?.startsWith("audio")
const hasImage = props.enclosureType?.startsWith("image")
return (
<TypographyStylesProvider>
<BasicHtmlStyles>
{hasVideo && (
<video controls>
<source src={props.enclosureUrl} type={props.enclosureType} />
@@ -19,6 +19,6 @@ export function Enclosure(props: { enclosureType: string; enclosureUrl: string }
</audio>
)}
{hasImage && <ImageWithPlaceholderWhileLoading src={props.enclosureUrl} alt="enclosure" />}
</TypographyStylesProvider>
</BasicHtmlStyles>
)
}

View File

@@ -1,7 +1,7 @@
import { Box, Divider, type MantineTheme, Paper, useMantineTheme } from "@mantine/core"
import { type MantineNumberSize } from "@mantine/styles"
import { Box, Divider, type MantineRadius, type MantineSpacing, type MantineTheme, Paper, useMantineTheme } from "@mantine/core"
import { Constants } from "app/constants"
import { type Entry, type ViewMode } from "app/types"
import { useColorScheme } from "hooks/useColorScheme"
import { useViewMode } from "hooks/useViewMode"
import React from "react"
import { useSwipeable } from "react-swipeable"
@@ -27,6 +27,7 @@ interface FeedEntryProps {
const useStyles = tss
.withParams<{
theme: MantineTheme
colorScheme: "light" | "dark"
read: boolean
expanded: boolean
viewMode: ViewMode
@@ -34,9 +35,9 @@ const useStyles = tss
showSelectionIndicator: boolean
maxWidth?: number
}>()
.create(({ theme, read, expanded, viewMode, rtl, showSelectionIndicator, maxWidth }) => {
.create(({ theme, colorScheme, read, expanded, viewMode, rtl, showSelectionIndicator, maxWidth }) => {
let backgroundColor
if (theme.colorScheme === "dark") {
if (colorScheme === "dark") {
backgroundColor = read ? "inherit" : theme.colors.dark[5]
} else {
backgroundColor = read && !expanded ? theme.colors.gray[0] : "inherit"
@@ -58,12 +59,12 @@ const useStyles = tss
let backgroundHoverColor = backgroundColor
if (!expanded && !read) {
backgroundHoverColor = theme.colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[1]
backgroundHoverColor = colorScheme === "dark" ? theme.colors.dark[6] : theme.colors.gray[1]
}
let paperBorderLeftColor
if (showSelectionIndicator) {
const borderLeftColor = theme.colorScheme === "dark" ? theme.colors.orange[4] : theme.colors.orange[6]
const borderLeftColor = colorScheme === "dark" ? theme.colors[theme.primaryColor][4] : theme.colors[theme.primaryColor][6]
paperBorderLeftColor = `${borderLeftColor} !important`
}
@@ -73,7 +74,7 @@ const useStyles = tss
borderLeftColor: paperBorderLeftColor,
marginTop: marginY,
marginBottom: marginY,
[theme.fn.smallerThan(Constants.layout.mobileBreakpoint)]: {
[`@media (max-width: ${Constants.layout.mobileBreakpoint}px)`]: {
marginTop: mobileMarginY,
marginBottom: mobileMarginY,
},
@@ -96,9 +97,11 @@ const useStyles = tss
export function FeedEntry(props: FeedEntryProps) {
const theme = useMantineTheme()
const colorScheme = useColorScheme()
const { viewMode } = useViewMode()
const { classes, cx } = useStyles({
theme,
colorScheme,
read: props.entry.read,
expanded: props.expanded,
viewMode,
@@ -111,17 +114,17 @@ export function FeedEntry(props: FeedEntryProps) {
onSwipedRight: props.onSwipedRight,
})
let paddingX: MantineNumberSize = "xs"
let paddingX: MantineSpacing = "xs"
if (viewMode === "title" || viewMode === "cozy") paddingX = 6
let paddingY: MantineNumberSize = "xs"
let paddingY: MantineSpacing = "xs"
if (viewMode === "title") {
paddingY = 4
} else if (viewMode === "cozy") {
paddingY = 8
}
let borderRadius: MantineNumberSize = "sm"
let borderRadius: MantineRadius = "sm"
if (viewMode === "title") {
borderRadius = 0
} else if (viewMode === "cozy") {

View File

@@ -1,7 +1,8 @@
import { Box, type MantineTheme, Text, useMantineTheme } from "@mantine/core"
import { Box, Text } from "@mantine/core"
import { type Entry } from "app/types"
import { RelativeDate } from "components/RelativeDate"
import { OnDesktop } from "components/responsive/OnDesktop"
import { useColorScheme } from "hooks/useColorScheme"
import { tss } from "tss"
import { FeedEntryTitle } from "./FeedEntryTitle"
import { FeedFavicon } from "./FeedFavicon"
@@ -12,10 +13,10 @@ export interface FeedEntryHeaderProps {
const useStyles = tss
.withParams<{
theme: MantineTheme
colorScheme: "light" | "dark"
read: boolean
}>()
.create(({ read, theme }) => ({
.create(({ colorScheme, read }) => ({
wrapper: {
display: "flex",
alignItems: "center",
@@ -23,7 +24,7 @@ const useStyles = tss
},
title: {
flexGrow: 1,
fontWeight: theme.colorScheme === "light" && !read ? "bold" : "inherit",
fontWeight: colorScheme === "light" && !read ? "bold" : "inherit",
whiteSpace: "nowrap",
overflow: "hidden",
textOverflow: "ellipsis",
@@ -41,9 +42,9 @@ const useStyles = tss
}))
export function FeedEntryCompactHeader(props: FeedEntryHeaderProps) {
const theme = useMantineTheme()
const colorScheme = useColorScheme()
const { classes } = useStyles({
theme,
colorScheme,
read: props.entry.read,
})
return (
@@ -52,7 +53,7 @@ export function FeedEntryCompactHeader(props: FeedEntryHeaderProps) {
<FeedFavicon url={props.entry.iconUrl} />
</Box>
<OnDesktop>
<Text color="dimmed" className={classes.feedName}>
<Text c="dimmed" className={classes.feedName}>
{props.entry.feedName}
</Text>
</OnDesktop>
@@ -60,7 +61,7 @@ export function FeedEntryCompactHeader(props: FeedEntryHeaderProps) {
<FeedEntryTitle entry={props.entry} />
</Box>
<OnDesktop>
<Text color="dimmed" className={classes.date}>
<Text c="dimmed" className={classes.date}>
<RelativeDate date={props.entry.date} />
</Text>
</OnDesktop>

View File

@@ -7,6 +7,7 @@ import { useAppDispatch, useAppSelector } from "app/store"
import { type Entry } from "app/types"
import { truncate } from "app/utils"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useColorScheme } from "hooks/useColorScheme"
import { Item, Menu, Separator } from "react-contexify"
import { TbArrowBarToDown, TbExternalLink, TbEyeCheck, TbEyeOff, TbRss, TbStar, TbStarOff } from "react-icons/tb"
import { tss } from "tss"
@@ -19,30 +20,31 @@ const iconSize = 16
const useStyles = tss
.withParams<{
theme: MantineTheme
colorScheme: "light" | "dark"
}>()
.create(({ theme }) => ({
.create(({ theme, colorScheme }) => ({
menu: {
// apply mantine theme from MenuItem.styles.ts
fontSize: theme.fontSizes.sm,
"--contexify-item-color": `${theme.colorScheme === "dark" ? theme.colors.dark[0] : theme.black} !important`,
"--contexify-activeItem-color": `${theme.colorScheme === "dark" ? theme.colors.dark[0] : theme.black} !important`,
"--contexify-activeItem-bgColor": `${
theme.colorScheme === "dark" ? theme.fn.rgba(theme.colors.dark[3], 0.35) : theme.colors.gray[1]
} !important`,
"--contexify-item-color": `${colorScheme === "dark" ? theme.colors.dark[0] : theme.black} !important`,
"--contexify-activeItem-color": `${colorScheme === "dark" ? theme.colors.dark[0] : theme.black} !important`,
"--contexify-activeItem-bgColor": `${colorScheme === "dark" ? theme.colors.dark[4] : theme.colors.gray[1]} !important`,
},
}))
export function FeedEntryContextMenu(props: FeedEntryContextMenuProps) {
const theme = useMantineTheme()
const colorScheme = useColorScheme()
const { classes } = useStyles({
theme,
colorScheme,
})
const sourceType = useAppSelector(state => state.entries.source.type)
const dispatch = useAppDispatch()
const { openLinkInBackgroundTab } = useBrowserExtension()
return (
<Menu id={Constants.dom.entryContextMenuId(props.entry)} theme={theme.colorScheme} animation={false} className={classes.menu}>
<Menu id={Constants.dom.entryContextMenuId(props.entry)} theme={colorScheme} animation={false} className={classes.menu}>
<Item
onClick={() => {
window.open(props.entry.url, "_blank", "noreferrer")

View File

@@ -1,5 +1,5 @@
import { t, Trans } from "@lingui/macro"
import { Group, Indicator, MultiSelect, Popover } from "@mantine/core"
import { Group, Indicator, Popover, TagsInput } from "@mantine/core"
import { markEntriesUpToEntry, markEntry, starEntry, tagEntry } from "app/entries/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { type Entry } from "app/types"
@@ -38,8 +38,8 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
)
return (
<Group position="apart">
<Group spacing={spacing}>
<Group justify="space-between">
<Group gap={spacing}>
{props.entry.markable && (
<ActionButton
icon={props.entry.read ? <TbEyeOff size={18} /> : <TbEyeCheck size={18} />}
@@ -72,22 +72,21 @@ export function FeedEntryFooter(props: FeedEntryFooterProps) {
)}
{tags && (
<Popover withArrow withinPortal shadow="md" closeOnClickOutside={!mobile}>
<Popover withArrow shadow="md" closeOnClickOutside={!mobile}>
<Popover.Target>
<Indicator label={props.entry.tags.length} disabled={props.entry.tags.length === 0} inline size={16}>
<ActionButton icon={<TbTag size={18} />} label={<Trans>Tags</Trans>} />
</Indicator>
</Popover.Target>
<Popover.Dropdown>
<MultiSelect
<TagsInput
placeholder={t`Tags`}
data={tags}
placeholder="Tags"
searchable
creatable
autoFocus
getCreateLabel={query => t`Create tag: ${query}`}
value={props.entry.tags}
onChange={onTagsChange}
comboboxProps={{
withinPortal: false,
}}
/>
</Popover.Dropdown>
</Popover>

View File

@@ -1,6 +1,7 @@
import { Box, type MantineTheme, Space, Text, useMantineTheme } from "@mantine/core"
import { Box, Space, Text } from "@mantine/core"
import { type Entry } from "app/types"
import { RelativeDate } from "components/RelativeDate"
import { useColorScheme } from "hooks/useColorScheme"
import { tss } from "tss"
import { FeedEntryTitle } from "./FeedEntryTitle"
import { FeedFavicon } from "./FeedFavicon"
@@ -12,12 +13,12 @@ export interface FeedEntryHeaderProps {
const useStyles = tss
.withParams<{
theme: MantineTheme
colorScheme: "light" | "dark"
read: boolean
}>()
.create(({ theme, read }) => ({
.create(({ colorScheme, read }) => ({
headerText: {
fontWeight: theme.colorScheme === "light" && !read ? "bold" : "inherit",
fontWeight: colorScheme === "light" && !read ? "bold" : "inherit",
},
headerSubtext: {
display: "flex",
@@ -27,9 +28,9 @@ const useStyles = tss
}))
export function FeedEntryHeader(props: FeedEntryHeaderProps) {
const theme = useMantineTheme()
const colorScheme = useColorScheme()
const { classes } = useStyles({
theme,
colorScheme,
read: props.entry.read,
})
return (
@@ -40,7 +41,7 @@ export function FeedEntryHeader(props: FeedEntryHeaderProps) {
<Box className={classes.headerSubtext}>
<FeedFavicon url={props.entry.iconUrl} />
<Space w={6} />
<Text color="dimmed">
<Text c="dimmed">
{props.entry.feedName}
<span> · </span>
<RelativeDate date={props.entry.date} />
@@ -48,7 +49,7 @@ export function FeedEntryHeader(props: FeedEntryHeaderProps) {
</Box>
{props.expanded && (
<Box className={classes.headerSubtext}>
<Text color="dimmed">
<Text c="dimmed">
{props.entry.author && <span>by {props.entry.author}</span>}
{props.entry.author && props.entry.categories && <span>&nbsp;·&nbsp;</span>}
{props.entry.categories && <span>{props.entry.categories}</span>}

View File

@@ -1,6 +1,7 @@
import { Box, TypographyStylesProvider } from "@mantine/core"
import { Box } from "@mantine/core"
import { Constants } from "app/constants"
import { calculatePlaceholderSize } from "app/utils"
import { BasicHtmlStyles } from "components/content/BasicHtmlStyles"
import { ImageWithPlaceholderWhileLoading } from "components/ImageWithPlaceholderWhileLoading"
import { Content } from "./Content"
@@ -20,7 +21,7 @@ export function Media(props: MediaProps) {
maxWidth: Constants.layout.entryMaxWidth,
})
return (
<TypographyStylesProvider>
<BasicHtmlStyles>
<ImageWithPlaceholderWhileLoading
src={props.thumbnailUrl}
alt="media thumbnail"
@@ -34,6 +35,6 @@ export function Media(props: MediaProps) {
<Content content={props.description} />
</Box>
)}
</TypographyStylesProvider>
</BasicHtmlStyles>
)
}

View File

@@ -2,6 +2,7 @@ import { ActionIcon, Box, type MantineTheme, SimpleGrid, useMantineTheme } from
import { Constants } from "app/constants"
import { useAppSelector } from "app/store"
import { type SharingSettings } from "app/types"
import { useColorScheme } from "hooks/useColorScheme"
import { type IconType } from "react-icons"
import { tss } from "tss"
@@ -10,20 +11,23 @@ type Color = `#${string}`
const useStyles = tss
.withParams<{
theme: MantineTheme
colorScheme: "light" | "dark"
color: Color
}>()
.create(({ theme, color }) => ({
.create(({ theme, colorScheme, color }) => ({
socialIcon: {
color,
backgroundColor: theme.colorScheme === "dark" ? theme.colors.gray[2] : "white",
backgroundColor: colorScheme === "dark" ? theme.colors.gray[2] : "white",
borderRadius: "50%",
},
}))
function ShareButton({ url, icon, color }: { url: string; icon: IconType; color: Color }) {
const theme = useMantineTheme()
const colorScheme = useColorScheme()
const { classes } = useStyles({
theme,
colorScheme,
color,
})
@@ -33,7 +37,7 @@ function ShareButton({ url, icon, color }: { url: string; icon: IconType; color:
}
return (
<ActionIcon>
<ActionIcon variant="transparent">
<a href={url} target="_blank" rel="noreferrer" onClick={onClick}>
<Box p={6} className={classes.socialIcon}>
{icon({ size: 18 })}
@@ -51,7 +55,7 @@ export function ShareButtons(props: { url: string; description: string }) {
return (
<SimpleGrid cols={4}>
{(Object.keys(Constants.sharing) as Array<keyof SharingSettings>)
.filter(site => sharingSettings && sharingSettings[site])
.filter(site => sharingSettings?.[site])
.map(site => (
<ShareButton
key={site}

View File

@@ -35,11 +35,11 @@ export function AddCategory() {
<Stack>
<TextInput label={<Trans>Category</Trans>} placeholder={t`Category`} {...form.getInputProps("name")} required />
<CategorySelect label={<Trans>Parent</Trans>} {...form.getInputProps("parentId")} clearable />
<Group position="center">
<Group justify="center">
<Button variant="default" onClick={async () => await dispatch(redirectToSelectedSource())}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" leftIcon={<TbFolderPlus size={16} />} loading={addCategory.loading}>
<Button type="submit" leftSection={<TbFolderPlus size={16} />} loading={addCategory.loading}>
<Trans>Add</Trans>
</Button>
</Group>

View File

@@ -1,5 +1,6 @@
import { t } from "@lingui/macro"
import { Select, type SelectItem, type SelectProps } from "@mantine/core"
import { Select, type SelectProps } from "@mantine/core"
import { type ComboboxItem } from "@mantine/core/lib/components/Combobox/Combobox.types"
import { Constants } from "app/constants"
import { useAppSelector } from "app/store"
import { flattenCategoryTree } from "app/utils"
@@ -12,9 +13,9 @@ type CategorySelectProps = Partial<SelectProps> & {
export function CategorySelect(props: CategorySelectProps) {
const rootCategory = useAppSelector(state => state.tree.rootCategory)
const categories = rootCategory && flattenCategoryTree(rootCategory)
const selectData: SelectItem[] | undefined = categories
const selectData: ComboboxItem[] | undefined = categories
?.filter(c => c.id !== Constants.categories.all.id)
.filter(c => !props.withoutCategoryIds || !props.withoutCategoryIds.includes(c.id))
.filter(c => !props.withoutCategoryIds?.includes(c.id))
.sort((c1, c2) => c1.name.localeCompare(c2.name))
.map(c => ({
label: c.parentName ? t`${c.name} (in ${c.parentName})` : c.name,

View File

@@ -37,7 +37,7 @@ export function ImportOpml() {
<Stack>
<FileInput
label={<Trans>OPML file</Trans>}
icon={<TbFileImport />}
leftSection={<TbFileImport />}
// https://github.com/mantinedev/mantine/issues/5401
{...{ placeholder: t`OPML file` }}
description={
@@ -50,11 +50,11 @@ export function ImportOpml() {
required
accept="application/xml"
/>
<Group position="center">
<Group justify="center">
<Button variant="default" onClick={async () => await dispatch(redirectToSelectedSource())}>
<Trans>Cancel</Trans>
</Button>
<Button type="submit" leftIcon={<TbFileImport size={16} />} loading={importOpml.loading}>
<Button type="submit" leftSection={<TbFileImport size={16} />} loading={importOpml.loading}>
<Trans>Import</Trans>
</Button>
</Group>

View File

@@ -108,7 +108,7 @@ export function Subscribe() {
</Stepper.Step>
</Stepper>
<Group position="center" mt="xl">
<Group justify="center" mt="xl">
<Button variant="default" onClick={previousStep}>
<Trans>Back</Trans>
</Button>
@@ -118,7 +118,7 @@ export function Subscribe() {
</Button>
)}
{activeStep === 1 && (
<Button type="submit" leftIcon={<TbRss size={16} />} loading={fetchFeed.loading || subscribe.loading}>
<Button type="submit" leftSection={<TbRss size={16} />} loading={fetchFeed.loading || subscribe.loading}>
<Trans>Subscribe</Trans>
</Button>
)}