forked from Archives/Athou_commafeed
update to mantine 7
This commit is contained in:
11
commafeed-client/src/components/content/BasicHtmlStyles.tsx
Normal file
11
commafeed-client/src/components/content/BasicHtmlStyles.tsx
Normal 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>
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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> · </span>}
|
||||
{props.entry.categories && <span>{props.entry.categories}</span>}
|
||||
|
||||
@@ -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>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -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}
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user