mirror of
https://github.com/Afilmory/afilmory
synced 2026-02-01 22:48:17 +00:00
feat(gallery): add tag filtering options and UI components
- Introduced a new tag filtering mode with options for 'union' and 'intersection'. - Added Checkbox and Switch components for user interface interactions. - Updated the FilterPanel to include tag filter mode selection. - Enhanced photo filtering logic to accommodate the new tag filter mode. - Updated localization files to include new tag filter labels. Signed-off-by: Innei <tukon479@gmail.com>
This commit is contained in:
@@ -99,7 +99,11 @@ This is a pnpm workspace with multiple applications and packages:
|
||||
- Use flat keys with `.` separation (e.g., `exif.camera.model`)
|
||||
- Support pluralization with `_one` and `_other` suffixes
|
||||
- Modify English first, then other languages (ESLint auto-removes unused keys)
|
||||
- Avoid nested key conflicts in flat structure
|
||||
- **CRITICAL: Avoid nested key conflicts in flat structure**
|
||||
- ❌ WRONG: `"action.tag.mode.and": "AND"` + `"action.tag.mode.and.tooltip": "..."`
|
||||
- ✅ CORRECT: `"action.tag.mode.and": "AND"` + `"action.tag.tooltip.and": "..."`
|
||||
- Rule: A key cannot be both a string value AND a parent object
|
||||
- Each key must be completely independent in the flat structure
|
||||
|
||||
### Testing Strategy
|
||||
- Check README.md and package.json scripts for test commands
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
"@lobehub/fluent-emoji": "2.0.0",
|
||||
"@maplibre/maplibre-gl-geocoder": "^1.9.0",
|
||||
"@radix-ui/react-avatar": "1.1.10",
|
||||
"@radix-ui/react-checkbox": "1.3.3",
|
||||
"@radix-ui/react-context-menu": "2.2.15",
|
||||
"@radix-ui/react-dialog": "1.1.14",
|
||||
"@radix-ui/react-dropdown-menu": "2.1.15",
|
||||
@@ -33,6 +34,7 @@
|
||||
"@radix-ui/react-popover": "1.1.14",
|
||||
"@radix-ui/react-scroll-area": "1.2.9",
|
||||
"@radix-ui/react-slot": "1.2.3",
|
||||
"@radix-ui/react-switch": "1.2.6",
|
||||
"@radix-ui/react-tooltip": "1.2.7",
|
||||
"@react-hook/window-size": "3.1.1",
|
||||
"@remixicon/react": "4.6.0",
|
||||
|
||||
@@ -10,6 +10,7 @@ export const gallerySettingAtom = atom({
|
||||
selectedCameras: [] as string[], // Selected camera display names
|
||||
selectedLenses: [] as string[], // Selected lens display names
|
||||
selectedRatings: null as number | null, // Selected minimum rating threshold
|
||||
tagFilterMode: 'union' as 'union' | 'intersection', // Tag filtering logic mode
|
||||
tagSearchQuery: '' as string,
|
||||
cameraSearchQuery: '' as string, // Camera search query
|
||||
lensSearchQuery: '' as string, // Lens search query
|
||||
|
||||
@@ -7,6 +7,8 @@ import { gallerySettingAtom } from '~/atoms/app'
|
||||
import { Button } from '~/components/ui/button'
|
||||
import { clsxm } from '~/lib/cn'
|
||||
|
||||
import { Checkbox } from '../ui/checkbox'
|
||||
|
||||
const allTags = photoLoader.getAllTags()
|
||||
const allCameras = photoLoader.getAllCameras()
|
||||
const allLenses = photoLoader.getAllLenses()
|
||||
@@ -134,6 +136,16 @@ export const FilterPanel = () => {
|
||||
setRatingSearchQuery('')
|
||||
}, [setGallerySetting])
|
||||
|
||||
const setTagFilterMode = useCallback(
|
||||
(mode: 'union' | 'intersection') => {
|
||||
setGallerySetting((prev) => ({
|
||||
...prev,
|
||||
tagFilterMode: mode,
|
||||
}))
|
||||
},
|
||||
[setGallerySetting],
|
||||
)
|
||||
|
||||
// Search handlers with useCallback
|
||||
const onTagSearchChange = useCallback(
|
||||
(e: React.ChangeEvent<HTMLInputElement>) => {
|
||||
@@ -266,13 +278,13 @@ export const FilterPanel = () => {
|
||||
data: [] as string[],
|
||||
filteredData: [] as string[],
|
||||
selectedItems: [] as string[],
|
||||
searchQuery: '',
|
||||
searchPlaceholder: '',
|
||||
searchQuery: ratingSearchQuery,
|
||||
searchPlaceholder: t('action.rating.search'),
|
||||
emptyMessage: '',
|
||||
notFoundMessage: '',
|
||||
onToggle: () => {},
|
||||
onClear: clearRatings,
|
||||
onSearchChange: () => {},
|
||||
onSearchChange: onRatingSearchChange,
|
||||
},
|
||||
],
|
||||
[
|
||||
@@ -291,7 +303,6 @@ export const FilterPanel = () => {
|
||||
toggleTag,
|
||||
toggleCamera,
|
||||
toggleLens,
|
||||
setRating,
|
||||
clearTags,
|
||||
clearCameras,
|
||||
clearLenses,
|
||||
@@ -328,6 +339,7 @@ export const FilterPanel = () => {
|
||||
selectedCameras: [],
|
||||
selectedLenses: [],
|
||||
selectedRatings: null,
|
||||
tagFilterMode: 'union',
|
||||
tagSearchQuery: '',
|
||||
cameraSearchQuery: '',
|
||||
lensSearchQuery: '',
|
||||
@@ -385,7 +397,7 @@ export const FilterPanel = () => {
|
||||
<i className="i-mingcute-search-line absolute top-1/2 right-3 -translate-y-1/2 text-gray-400" />
|
||||
</div>
|
||||
)}
|
||||
{currentTab.count > 0 && activeTab != 'ratings' && (
|
||||
{currentTab.count > 0 && activeTab !== 'ratings' && (
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="xs"
|
||||
@@ -416,23 +428,46 @@ export const FilterPanel = () => {
|
||||
{currentTab.notFoundMessage}
|
||||
</div>
|
||||
) : (
|
||||
<div className="pb-safe-offset-4 lg:pb-safe -mx-4 -mb-4 max-h-64 overflow-y-auto px-4 lg:mx-0 lg:mb-0 lg:px-0">
|
||||
{(currentTab.filteredData).map((item) => (
|
||||
<div
|
||||
key={item}
|
||||
onClick={() => currentTab.onToggle(item)}
|
||||
className={clsxm(
|
||||
'hover:bg-accent/50 flex cursor-pointer items-center rounded-md bg-transparent px-2 py-2.5 transition-colors lg:py-2',
|
||||
currentTab.selectedItems.includes(item) && 'bg-accent/20',
|
||||
)}
|
||||
>
|
||||
<span className="mr-2 flex-1 truncate">{item}</span>
|
||||
{currentTab.selectedItems.includes(item) && (
|
||||
<i className="i-mingcute-check-line ml-auto text-green-600 dark:text-green-400" />
|
||||
)}
|
||||
<>
|
||||
{/* Tag Filter Mode Toggle - Only show for tags tab when tags are selected */}
|
||||
{activeTab === 'tags' && (
|
||||
<div className="mb-3 flex items-center gap-3 px-2 text-xs text-gray-600 dark:text-gray-400">
|
||||
<span>{t('action.tag.match.label')}</span>
|
||||
<label className="flex cursor-pointer items-center gap-1.5">
|
||||
<Checkbox
|
||||
checked={gallerySetting.tagFilterMode === 'union'}
|
||||
onCheckedChange={() => setTagFilterMode('union')}
|
||||
/>
|
||||
<span>{t('action.tag.match.any')}</span>
|
||||
</label>
|
||||
<label className="flex cursor-pointer items-center gap-1.5">
|
||||
<Checkbox
|
||||
checked={gallerySetting.tagFilterMode === 'intersection'}
|
||||
onCheckedChange={() => setTagFilterMode('intersection')}
|
||||
/>
|
||||
<span>{t('action.tag.match.all')}</span>
|
||||
</label>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<div className="pb-safe-offset-4 lg:pb-safe -mx-4 -mb-4 max-h-64 overflow-y-auto px-4 lg:mx-0 lg:mb-0 lg:px-0">
|
||||
{currentTab.filteredData.map((item) => (
|
||||
<div
|
||||
key={item}
|
||||
onClick={() => currentTab.onToggle(item)}
|
||||
className={clsxm(
|
||||
'hover:bg-accent/50 flex cursor-pointer items-center rounded-md bg-transparent px-2 py-2.5 transition-colors lg:py-2',
|
||||
currentTab.selectedItems.includes(item) && 'bg-accent/20',
|
||||
)}
|
||||
>
|
||||
<span className="mr-2 flex-1 truncate">{item}</span>
|
||||
{currentTab.selectedItems.includes(item) && (
|
||||
<i className="i-mingcute-check-line ml-auto text-green-600 dark:text-green-400" />
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
@@ -471,7 +506,7 @@ const StarRating = ({
|
||||
className={clsxm(
|
||||
'text-2xl',
|
||||
rating <= (hoveredRating ?? value ?? 0)
|
||||
? 'i-mingcute-star-fill text-yellow-400'
|
||||
? 'i-mingcute-star-fill text-yellow-400 dark:text-yellow-500'
|
||||
: 'i-mingcute-star-line text-gray-300 dark:text-gray-600',
|
||||
)}
|
||||
/>
|
||||
|
||||
140
apps/web/src/components/ui/checkbox/Checkbox.tsx
Normal file
140
apps/web/src/components/ui/checkbox/Checkbox.tsx
Normal file
@@ -0,0 +1,140 @@
|
||||
'use client'
|
||||
|
||||
import * as CheckboxPrimitive from '@radix-ui/react-checkbox'
|
||||
import type { HTMLMotionProps } from 'motion/react'
|
||||
import { m as motion } from 'motion/react'
|
||||
import * as React from 'react'
|
||||
|
||||
import { clsxm } from '~/lib/cn'
|
||||
|
||||
type CheckboxProps = React.ComponentProps<typeof CheckboxPrimitive.Root> &
|
||||
HTMLMotionProps<'button'> & {
|
||||
indeterminate?: boolean
|
||||
}
|
||||
|
||||
function Checkbox({
|
||||
className,
|
||||
onCheckedChange,
|
||||
indeterminate,
|
||||
...props
|
||||
}: CheckboxProps) {
|
||||
const [isChecked, setIsChecked] = React.useState(
|
||||
props?.checked ?? props?.defaultChecked ?? false,
|
||||
)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props?.checked !== undefined) setIsChecked(props.checked)
|
||||
}, [props?.checked])
|
||||
|
||||
// Determine the actual state including indeterminate
|
||||
const checkboxState = indeterminate
|
||||
? 'indeterminate'
|
||||
: isChecked
|
||||
? 'checked'
|
||||
: 'unchecked'
|
||||
|
||||
const handleCheckedChange = React.useCallback(
|
||||
(checked: boolean) => {
|
||||
setIsChecked(checked)
|
||||
onCheckedChange?.(checked)
|
||||
},
|
||||
[onCheckedChange],
|
||||
)
|
||||
|
||||
return (
|
||||
<CheckboxPrimitive.Root
|
||||
{...props}
|
||||
onCheckedChange={handleCheckedChange}
|
||||
asChild
|
||||
>
|
||||
<motion.button
|
||||
data-slot="checkbox"
|
||||
className={clsxm(
|
||||
'peer size-5 flex items-center justify-center shrink-0 rounded-sm bg-fill transition-colors duration-500 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:bg-accent data-[state=checked]:text-white',
|
||||
indeterminate && 'bg-accent text-white',
|
||||
className,
|
||||
)}
|
||||
whileTap={{ scale: 0.95 }}
|
||||
whileHover={{ scale: 1.05 }}
|
||||
{...props}
|
||||
>
|
||||
<CheckboxPrimitive.Indicator forceMount asChild>
|
||||
<motion.svg
|
||||
data-slot="checkbox-indicator"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
fill="none"
|
||||
viewBox="0 0 24 24"
|
||||
strokeWidth="3.5"
|
||||
stroke="currentColor"
|
||||
className="size-3.5"
|
||||
initial="unchecked"
|
||||
animate={checkboxState}
|
||||
>
|
||||
{/* Checkmark path */}
|
||||
<motion.path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M4.5 12.75l6 6 9-13.5"
|
||||
variants={{
|
||||
checked: {
|
||||
pathLength: 1,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 0.2,
|
||||
delay: 0.2,
|
||||
},
|
||||
},
|
||||
unchecked: {
|
||||
pathLength: 0,
|
||||
opacity: 0,
|
||||
transition: {
|
||||
duration: 0.2,
|
||||
},
|
||||
},
|
||||
indeterminate: {
|
||||
pathLength: 0,
|
||||
opacity: 0,
|
||||
transition: {
|
||||
duration: 0.1,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
{/* Indeterminate line */}
|
||||
<motion.path
|
||||
strokeLinecap="round"
|
||||
strokeLinejoin="round"
|
||||
d="M6 12h12"
|
||||
variants={{
|
||||
checked: {
|
||||
pathLength: 0,
|
||||
opacity: 0,
|
||||
transition: {
|
||||
duration: 0.1,
|
||||
},
|
||||
},
|
||||
unchecked: {
|
||||
pathLength: 0,
|
||||
opacity: 0,
|
||||
transition: {
|
||||
duration: 0.1,
|
||||
},
|
||||
},
|
||||
indeterminate: {
|
||||
pathLength: 1,
|
||||
opacity: 1,
|
||||
transition: {
|
||||
duration: 0.2,
|
||||
delay: 0.1,
|
||||
},
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</motion.svg>
|
||||
</CheckboxPrimitive.Indicator>
|
||||
</motion.button>
|
||||
</CheckboxPrimitive.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { Checkbox, type CheckboxProps }
|
||||
1
apps/web/src/components/ui/checkbox/index.ts
Normal file
1
apps/web/src/components/ui/checkbox/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export * from './Checkbox'
|
||||
114
apps/web/src/components/ui/switch/index.tsx
Normal file
114
apps/web/src/components/ui/switch/index.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
'use client'
|
||||
|
||||
import * as SwitchPrimitives from '@radix-ui/react-switch'
|
||||
import type { HTMLMotionProps } from 'motion/react'
|
||||
import { m as motion } from 'motion/react'
|
||||
import * as React from 'react'
|
||||
|
||||
import { clsxm as cn } from '~/lib/cn'
|
||||
|
||||
type SwitchProps = React.ComponentProps<typeof SwitchPrimitives.Root> &
|
||||
HTMLMotionProps<'button'> & {
|
||||
leftIcon?: React.ReactNode
|
||||
rightIcon?: React.ReactNode
|
||||
thumbIcon?: React.ReactNode
|
||||
}
|
||||
|
||||
function Switch({
|
||||
className,
|
||||
leftIcon,
|
||||
rightIcon,
|
||||
thumbIcon,
|
||||
onCheckedChange,
|
||||
...props
|
||||
}: SwitchProps) {
|
||||
const [isChecked, setIsChecked] = React.useState(
|
||||
props?.checked ?? props?.defaultChecked ?? false,
|
||||
)
|
||||
const [isTapped, setIsTapped] = React.useState(false)
|
||||
|
||||
React.useEffect(() => {
|
||||
if (props?.checked !== undefined) setIsChecked(props.checked)
|
||||
}, [props?.checked])
|
||||
|
||||
const handleCheckedChange = React.useCallback(
|
||||
(checked: boolean) => {
|
||||
setIsChecked(checked)
|
||||
onCheckedChange?.(checked)
|
||||
},
|
||||
[onCheckedChange],
|
||||
)
|
||||
|
||||
return (
|
||||
<SwitchPrimitives.Root
|
||||
{...props}
|
||||
onCheckedChange={handleCheckedChange}
|
||||
asChild
|
||||
>
|
||||
<motion.button
|
||||
data-slot="switch"
|
||||
className={cn(
|
||||
'focus-visible:ring-accent focus-visible:ring-offset-background data-[state=checked]:bg-accent data-[state=unchecked]:bg-fill-secondary relative flex h-6 w-10 shrink-0 cursor-pointer items-center rounded-full p-[3px] transition-colors focus:outline-none focus-visible:ring-2 focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:justify-end data-[state=unchecked]:justify-start',
|
||||
className,
|
||||
)}
|
||||
whileTap="tap"
|
||||
initial={false}
|
||||
onTapStart={() => setIsTapped(true)}
|
||||
onTapCancel={() => setIsTapped(false)}
|
||||
onTap={() => setIsTapped(false)}
|
||||
{...props}
|
||||
>
|
||||
{leftIcon && (
|
||||
<motion.div
|
||||
data-slot="switch-left-icon"
|
||||
animate={
|
||||
isChecked ? { scale: 1, opacity: 1 } : { scale: 0, opacity: 0 }
|
||||
}
|
||||
transition={{ type: 'spring', bounce: 0 }}
|
||||
className="text-text-secondary absolute top-1/2 left-1 -translate-y-1/2 [&_svg]:size-3"
|
||||
>
|
||||
{typeof leftIcon !== 'string' ? leftIcon : null}
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
{rightIcon && (
|
||||
<motion.div
|
||||
data-slot="switch-right-icon"
|
||||
animate={
|
||||
isChecked ? { scale: 0, opacity: 0 } : { scale: 1, opacity: 1 }
|
||||
}
|
||||
transition={{ type: 'spring', bounce: 0 }}
|
||||
className="text-text-secondary absolute top-1/2 right-1 -translate-y-1/2 [&_svg]:size-3"
|
||||
>
|
||||
{typeof rightIcon !== 'string' ? rightIcon : null}
|
||||
</motion.div>
|
||||
)}
|
||||
|
||||
<SwitchPrimitives.Thumb asChild>
|
||||
<motion.div
|
||||
data-slot="switch-thumb"
|
||||
whileTap="tab"
|
||||
className={cn(
|
||||
'bg-background text-text-secondary relative z-[1] flex items-center justify-center rounded-full shadow-lg ring-0 [&_svg]:size-3',
|
||||
)}
|
||||
layout
|
||||
transition={{ type: 'spring', stiffness: 300, damping: 25 }}
|
||||
style={{
|
||||
width: 18,
|
||||
height: 18,
|
||||
}}
|
||||
animate={
|
||||
isTapped
|
||||
? { width: 21, transition: { duration: 0.1 } }
|
||||
: { width: 18, transition: { duration: 0.1 } }
|
||||
}
|
||||
>
|
||||
{thumbIcon && typeof thumbIcon !== 'string' ? thumbIcon : null}
|
||||
</motion.div>
|
||||
</SwitchPrimitives.Thumb>
|
||||
</motion.button>
|
||||
</SwitchPrimitives.Root>
|
||||
)
|
||||
}
|
||||
|
||||
export { Switch, type SwitchProps }
|
||||
@@ -19,15 +19,22 @@ const filterAndSortPhotos = (
|
||||
selectedLenses: string[],
|
||||
selectedRatings: number | null,
|
||||
sortOrder: 'asc' | 'desc',
|
||||
tagFilterMode: 'union' | 'intersection' = 'union',
|
||||
) => {
|
||||
// 根据 tags、cameras、lenses 和 ratings 筛选
|
||||
let filteredPhotos = data
|
||||
|
||||
// Tags 筛选:照片必须包含至少一个选中的标签
|
||||
// Tags 筛选:根据模式进行并集或交集筛选
|
||||
if (selectedTags.length > 0) {
|
||||
filteredPhotos = filteredPhotos.filter((photo) =>
|
||||
selectedTags.some((tag) => photo.tags.includes(tag)),
|
||||
)
|
||||
filteredPhotos = filteredPhotos.filter((photo) => {
|
||||
if (tagFilterMode === 'intersection') {
|
||||
// 交集模式:照片必须包含所有选中的标签
|
||||
return selectedTags.every((tag) => photo.tags.includes(tag))
|
||||
} else {
|
||||
// 并集模式:照片必须包含至少一个选中的标签
|
||||
return selectedTags.some((tag) => photo.tags.includes(tag))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// Cameras 筛选:照片的相机必须匹配选中的相机之一
|
||||
@@ -93,6 +100,7 @@ export const getFilteredPhotos = () => {
|
||||
currentGallerySetting.selectedLenses,
|
||||
currentGallerySetting.selectedRatings,
|
||||
currentGallerySetting.sortOrder,
|
||||
currentGallerySetting.tagFilterMode,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -103,6 +111,7 @@ export const usePhotos = () => {
|
||||
selectedCameras,
|
||||
selectedLenses,
|
||||
selectedRatings,
|
||||
tagFilterMode,
|
||||
} = useAtomValue(gallerySettingAtom)
|
||||
|
||||
const masonryItems = useMemo(() => {
|
||||
@@ -112,6 +121,7 @@ export const usePhotos = () => {
|
||||
selectedLenses,
|
||||
selectedRatings,
|
||||
sortOrder,
|
||||
tagFilterMode,
|
||||
)
|
||||
}, [
|
||||
sortOrder,
|
||||
@@ -119,6 +129,7 @@ export const usePhotos = () => {
|
||||
selectedCameras,
|
||||
selectedLenses,
|
||||
selectedRatings,
|
||||
tagFilterMode,
|
||||
])
|
||||
|
||||
return masonryItems
|
||||
|
||||
@@ -95,12 +95,17 @@ const useStateRestoreFromUrl = () => {
|
||||
const ratingsFromSearchParams = searchParams.get('rating')
|
||||
? Number(searchParams.get('rating'))
|
||||
: null
|
||||
const tagModeFromSearchParams = searchParams.get('tag_mode') as
|
||||
| 'union'
|
||||
| 'intersection'
|
||||
| null
|
||||
|
||||
if (
|
||||
tagsFromSearchParams ||
|
||||
camerasFromSearchParams ||
|
||||
lensesFromSearchParams ||
|
||||
ratingsFromSearchParams !== null
|
||||
ratingsFromSearchParams !== null ||
|
||||
tagModeFromSearchParams
|
||||
) {
|
||||
setGallerySetting((prev) => ({
|
||||
...prev,
|
||||
@@ -108,14 +113,20 @@ const useStateRestoreFromUrl = () => {
|
||||
selectedCameras: camerasFromSearchParams || prev.selectedCameras,
|
||||
selectedLenses: lensesFromSearchParams || prev.selectedLenses,
|
||||
selectedRatings: ratingsFromSearchParams ?? prev.selectedRatings,
|
||||
tagFilterMode: tagModeFromSearchParams || prev.tagFilterMode,
|
||||
}))
|
||||
}
|
||||
}, [openViewer, photoId, searchParams, setGallerySetting])
|
||||
}
|
||||
|
||||
const useSyncStateToUrl = () => {
|
||||
const { selectedTags, selectedCameras, selectedLenses, selectedRatings } =
|
||||
useAtomValue(gallerySettingAtom)
|
||||
const {
|
||||
selectedTags,
|
||||
selectedCameras,
|
||||
selectedLenses,
|
||||
selectedRatings,
|
||||
tagFilterMode,
|
||||
} = useAtomValue(gallerySettingAtom)
|
||||
const [_, setSearchParams] = useSearchParams()
|
||||
const navigate = useNavigate()
|
||||
|
||||
@@ -149,19 +160,22 @@ const useSyncStateToUrl = () => {
|
||||
const cameras = selectedCameras.join(',')
|
||||
const lenses = selectedLenses.join(',')
|
||||
const rating = selectedRatings?.toString() ?? ''
|
||||
const tagMode = tagFilterMode === 'union' ? '' : tagFilterMode
|
||||
|
||||
setSearchParams((search) => {
|
||||
const currentTags = search.get('tags')
|
||||
const currentCameras = search.get('cameras')
|
||||
const currentLenses = search.get('lenses')
|
||||
const currentRating = search.get('rating')
|
||||
const currentTagMode = search.get('tag_mode')
|
||||
|
||||
// Check if anything has changed
|
||||
if (
|
||||
currentTags === tags &&
|
||||
currentCameras === cameras &&
|
||||
currentLenses === lenses &&
|
||||
currentRating === rating
|
||||
currentRating === rating &&
|
||||
currentTagMode === tagMode
|
||||
) {
|
||||
return search
|
||||
}
|
||||
@@ -196,7 +210,21 @@ const useSyncStateToUrl = () => {
|
||||
newer.delete('rating')
|
||||
}
|
||||
|
||||
// Update tag filter mode
|
||||
if (tagMode) {
|
||||
newer.set('tag_mode', tagMode)
|
||||
} else {
|
||||
newer.delete('tag_mode')
|
||||
}
|
||||
|
||||
return newer
|
||||
})
|
||||
}, [selectedTags, selectedCameras, selectedLenses, selectedRatings, setSearchParams])
|
||||
}, [
|
||||
selectedTags,
|
||||
selectedCameras,
|
||||
selectedLenses,
|
||||
selectedRatings,
|
||||
tagFilterMode,
|
||||
setSearchParams,
|
||||
])
|
||||
}
|
||||
|
||||
@@ -9,7 +9,6 @@ import { Spring } from '~/lib/spring'
|
||||
import { ContextMenuProvider } from './context-menu-provider'
|
||||
import { EventProvider } from './event-provider'
|
||||
import { I18nProvider } from './i18n-provider'
|
||||
import { SettingSync } from './setting-sync'
|
||||
import { StableRouterProvider } from './stable-router-provider'
|
||||
|
||||
export const RootProviders: FC<PropsWithChildren> = ({ children }) => (
|
||||
@@ -18,7 +17,7 @@ export const RootProviders: FC<PropsWithChildren> = ({ children }) => (
|
||||
<Provider store={jotaiStore}>
|
||||
<EventProvider />
|
||||
<StableRouterProvider />
|
||||
<SettingSync />
|
||||
|
||||
<ContextMenuProvider />
|
||||
<I18nProvider>{children}</I18nProvider>
|
||||
</Provider>
|
||||
|
||||
@@ -1,7 +0,0 @@
|
||||
const useUISettingSync = () => {}
|
||||
|
||||
export const SettingSync = () => {
|
||||
useUISettingSync()
|
||||
|
||||
return null
|
||||
}
|
||||
@@ -23,6 +23,11 @@
|
||||
"action.tag.clear": "Clear",
|
||||
"action.tag.empty": "No tags available",
|
||||
"action.tag.filter": "Tag Filter",
|
||||
"action.tag.match.all": "All tags",
|
||||
"action.tag.match.any": "Any tag",
|
||||
"action.tag.match.label": "Match:",
|
||||
"action.tag.mode.and": "AND",
|
||||
"action.tag.mode.or": "OR",
|
||||
"action.tag.not-found": "No tags match your search",
|
||||
"action.tag.search": "Search Tags",
|
||||
"action.view.github": "View GitHub Repository",
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
"action.tag.clear": "クリア",
|
||||
"action.tag.empty": "タグがありません",
|
||||
"action.tag.filter": "タグフィルター",
|
||||
"action.tag.mode.and": "AND",
|
||||
"action.tag.mode.or": "OR",
|
||||
"action.tag.not-found": "検索に一致するタグがありません",
|
||||
"action.tag.search": "タグ検索",
|
||||
"action.view.github": "GitHub リポジトリを表示",
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
"action.tag.clear": "지우기",
|
||||
"action.tag.empty": "태그가 없습니다",
|
||||
"action.tag.filter": "태그 필터",
|
||||
"action.tag.mode.and": "AND",
|
||||
"action.tag.mode.or": "OR",
|
||||
"action.tag.not-found": "검색과 일치하는 태그가 없습니다",
|
||||
"action.tag.search": "태그 검색",
|
||||
"action.view.github": "GitHub 리포지토리 보기",
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
"action.tag.clear": "清除",
|
||||
"action.tag.empty": "暂无标签",
|
||||
"action.tag.filter": "标签筛选",
|
||||
"action.tag.mode.and": "AND",
|
||||
"action.tag.mode.or": "OR",
|
||||
"action.tag.not-found": "没有标签匹配您的搜索",
|
||||
"action.tag.search": "搜索标签",
|
||||
"action.view.github": "查看 GitHub 仓库",
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
"action.tag.clear": "清除",
|
||||
"action.tag.empty": "暫無標籤",
|
||||
"action.tag.filter": "標籤篩選",
|
||||
"action.tag.mode.and": "AND",
|
||||
"action.tag.mode.or": "OR",
|
||||
"action.tag.not-found": "沒有標籤符合您的搜尋",
|
||||
"action.tag.search": "搜尋標籤",
|
||||
"action.view.github": "查看 GitHub 倉庫",
|
||||
|
||||
@@ -23,6 +23,8 @@
|
||||
"action.tag.clear": "清除",
|
||||
"action.tag.empty": "暫無標籤",
|
||||
"action.tag.filter": "標籤篩選",
|
||||
"action.tag.mode.and": "AND",
|
||||
"action.tag.mode.or": "OR",
|
||||
"action.tag.not-found": "沒有標籤符合您的搜尋",
|
||||
"action.tag.search": "搜尋標籤",
|
||||
"action.view.github": "檢視 GitHub 存放庫",
|
||||
|
||||
181
pnpm-lock.yaml
generated
181
pnpm-lock.yaml
generated
@@ -281,6 +281,9 @@ importers:
|
||||
'@radix-ui/react-avatar':
|
||||
specifier: 1.1.10
|
||||
version: 1.1.10(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-checkbox':
|
||||
specifier: 1.3.3
|
||||
version: 1.3.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-context-menu':
|
||||
specifier: 2.2.15
|
||||
version: 2.2.15(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
@@ -302,6 +305,9 @@ importers:
|
||||
'@radix-ui/react-slot':
|
||||
specifier: 1.2.3
|
||||
version: 1.2.3(@types/react@19.1.8)(react@19.1.0)
|
||||
'@radix-ui/react-switch':
|
||||
specifier: 1.2.6
|
||||
version: 1.2.6(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-tooltip':
|
||||
specifier: 1.2.7
|
||||
version: 1.2.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
@@ -2267,9 +2273,6 @@ packages:
|
||||
'@mapbox/point-geometry@1.1.0':
|
||||
resolution: {integrity: sha512-YGcBz1cg4ATXDCM/71L9xveh4dynfGmcLDqufR+nQQy3fKwsAZsWd/x4621/6uJaeB9mwOHE6hPeDgXz9uViUQ==}
|
||||
|
||||
'@mapbox/tiny-sdf@2.0.6':
|
||||
resolution: {integrity: sha512-qMqa27TLw+ZQz5Jk+RcwZGH7BQf5G/TrutJhspsca/3SHwmgKQ1iq+d3Jxz5oysPVYTGP6aXxCo5Lk9Er6YBAA==}
|
||||
|
||||
'@mapbox/tiny-sdf@2.0.7':
|
||||
resolution: {integrity: sha512-25gQLQMcpivjOSA40g3gO6qgiFPDpWRoMfd+G/GoppPIeP6JDaMMkMrEJnMZhKyyS6iKwVt5YKu02vCUyJM3Ug==}
|
||||
|
||||
@@ -2445,6 +2448,9 @@ packages:
|
||||
'@radix-ui/primitive@1.1.2':
|
||||
resolution: {integrity: sha512-XnbHrrprsNqZKQhStrSwgRUQzoCI1glLzdw79xiZPoofhGICeZRSQ3dIxAKH1gb3OHfNf4d6f+vAv3kil2eggA==}
|
||||
|
||||
'@radix-ui/primitive@1.1.3':
|
||||
resolution: {integrity: sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg==}
|
||||
|
||||
'@radix-ui/react-arrow@1.0.2':
|
||||
resolution: {integrity: sha512-fqYwhhI9IarZ0ll2cUSfKuXHlJK0qE4AfnRrPBbRwEH/4mGQn04/QFGomLi8TXWIdv9WJk//KgGm+aDxVIr1wA==}
|
||||
peerDependencies:
|
||||
@@ -2477,6 +2483,19 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-checkbox@1.3.3':
|
||||
resolution: {integrity: sha512-wBbpv+NQftHDdG86Qc0pIyXk5IR3tM8Vd0nWLKDcX8nNn4nXFOFwsKuqw2okA/1D/mpaAkmuyndrPJTYDNZtFw==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-collection@1.1.7':
|
||||
resolution: {integrity: sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw==}
|
||||
peerDependencies:
|
||||
@@ -2717,6 +2736,19 @@ packages:
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-presence@1.1.5':
|
||||
resolution: {integrity: sha512-/jfEwNDdQVBCNvjkGit4h6pMOzq8bHkopq458dPt2lMjx+eBQUohZNG9A7DtO/O5ukSbxuaNGXMjHicgwy6rQQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-primitive@1.0.2':
|
||||
resolution: {integrity: sha512-zY6G5Qq4R8diFPNwtyoLRZBxzu1Z+SXMlfYpChN7Dv8gvmx9X3qhDqiLWvKseKVJMuedFeU/Sa0Sy/Ia+t06Dw==}
|
||||
peerDependencies:
|
||||
@@ -2776,6 +2808,19 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-switch@1.2.6':
|
||||
resolution: {integrity: sha512-bByzr1+ep1zk4VubeEVViV592vu2lHE2BZY5OnzehZqOOgogN80+mNtCqPkhn2gklJqOpxWgPoYTSnhBCqpOXQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
'@types/react-dom': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
react-dom: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
'@types/react-dom':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-tooltip@1.0.5':
|
||||
resolution: {integrity: sha512-cDKVcfzyO6PpckZekODJZDe5ZxZ2fCZlzKzTmPhe4mX9qTHRfLcKgqb0OKf22xLwDequ2tVleim+ZYx3rabD5w==}
|
||||
peerDependencies:
|
||||
@@ -2869,6 +2914,15 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-previous@1.1.1':
|
||||
resolution: {integrity: sha512-2dHfToCj/pzca2Ck724OZ5L0EVrr3eHRNsG/b3xQJLA2hZpVCS99bLAX+hm1IHXDEnzU6by5z/5MIY794/a8NQ==}
|
||||
peerDependencies:
|
||||
'@types/react': '*'
|
||||
react: ^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@radix-ui/react-use-rect@1.0.0':
|
||||
resolution: {integrity: sha512-TB7pID8NRMEHxb/qQJpvSt3hQU4sqNPM1VCTjTRjEOa7cEop/QMuq8S6fb/5Tsz64kqSvB9WnwsDHtjnrM9qew==}
|
||||
peerDependencies:
|
||||
@@ -5283,9 +5337,6 @@ packages:
|
||||
resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==}
|
||||
engines: {node: '>= 0.4'}
|
||||
|
||||
earcut@3.0.1:
|
||||
resolution: {integrity: sha512-0l1/0gOjESMeQyYaK5IDiPNvFeu93Z/cO0TjZh9eZ1vyCtZnA7KMZ8rQggpsJHIbGSdrqYq9OhuveadOVHCshw==}
|
||||
|
||||
earcut@3.0.2:
|
||||
resolution: {integrity: sha512-X7hshQbLyMJ/3RPhyObLARM2sNxxmRALLKx1+NVFFnQ9gKzmCrxm9+uLIAdBcvc8FNLpctqlQ2V6AE92Ol9UDQ==}
|
||||
|
||||
@@ -5952,9 +6003,6 @@ packages:
|
||||
engines: {node: '>=18'}
|
||||
hasBin: true
|
||||
|
||||
gl-matrix@3.4.3:
|
||||
resolution: {integrity: sha512-wcCp8vu8FT22BnvKVPjXa/ICBWRq/zjFfdofZy1WSpQZpphblv12/bOQLBC1rMM7SGOFS9ltVmKOHil5+Ml7gA==}
|
||||
|
||||
gl-matrix@3.4.4:
|
||||
resolution: {integrity: sha512-latSnyDNt/8zYUB6VIJ6PCh2jBjJX6gnDsoCZ7LyW7GkqrD51EWwa9qCoGixj8YqBtETQK/xY7OmpTF8xz1DdQ==}
|
||||
|
||||
@@ -7443,9 +7491,6 @@ packages:
|
||||
resolution: {integrity: sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw==}
|
||||
engines: {node: '>=12'}
|
||||
|
||||
potpack@2.0.0:
|
||||
resolution: {integrity: sha512-Q+/tYsFU9r7xoOJ+y/ZTtdVQwTWfzjbiXBDMM/JKUux3+QPP02iUuIoeBQ+Ot6oEDlC+/PGjB/5A3K7KKb7hcw==}
|
||||
|
||||
potpack@2.1.0:
|
||||
resolution: {integrity: sha512-pcaShQc1Shq0y+E7GqJqvZj8DTthWV1KeHGdi0Z6IAin2Oi3JnLCOfwnCo84qc+HAp52wT9nK9H7FAJp5a44GQ==}
|
||||
|
||||
@@ -10174,7 +10219,7 @@ snapshots:
|
||||
|
||||
'@babel/helper-annotate-as-pure@7.27.3':
|
||||
dependencies:
|
||||
'@babel/types': 7.28.0
|
||||
'@babel/types': 7.28.2
|
||||
|
||||
'@babel/helper-compilation-targets@7.27.2':
|
||||
dependencies:
|
||||
@@ -10273,15 +10318,6 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/helper-module-transforms@7.27.3(@babel/core@7.28.3)':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/helper-module-imports': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.27.1
|
||||
'@babel/traverse': 7.28.0
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/helper-module-transforms@7.28.3(@babel/core@7.28.3)':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.3
|
||||
@@ -10302,7 +10338,7 @@ snapshots:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/helper-annotate-as-pure': 7.27.3
|
||||
'@babel/helper-wrap-function': 7.27.1
|
||||
'@babel/traverse': 7.28.0
|
||||
'@babel/traverse': 7.28.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -10340,8 +10376,8 @@ snapshots:
|
||||
'@babel/helper-wrap-function@7.27.1':
|
||||
dependencies:
|
||||
'@babel/template': 7.27.2
|
||||
'@babel/traverse': 7.28.0
|
||||
'@babel/types': 7.28.0
|
||||
'@babel/traverse': 7.28.3
|
||||
'@babel/types': 7.28.2
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -10380,7 +10416,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
'@babel/traverse': 7.28.0
|
||||
'@babel/traverse': 7.28.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -10407,7 +10443,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
'@babel/traverse': 7.28.0
|
||||
'@babel/traverse': 7.28.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -10441,7 +10477,7 @@ snapshots:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
'@babel/helper-remap-async-to-generator': 7.27.1(@babel/core@7.28.3)
|
||||
'@babel/traverse': 7.28.0
|
||||
'@babel/traverse': 7.28.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -10488,7 +10524,7 @@ snapshots:
|
||||
'@babel/helper-globals': 7.28.0
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
'@babel/helper-replace-supers': 7.27.1(@babel/core@7.28.3)
|
||||
'@babel/traverse': 7.28.0
|
||||
'@babel/traverse': 7.28.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -10502,7 +10538,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
'@babel/traverse': 7.28.0
|
||||
'@babel/traverse': 7.28.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -10559,7 +10595,7 @@ snapshots:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/helper-compilation-targets': 7.27.2
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
'@babel/traverse': 7.28.0
|
||||
'@babel/traverse': 7.28.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -10586,7 +10622,7 @@ snapshots:
|
||||
'@babel/plugin-transform-modules-amd@7.27.1(@babel/core@7.28.3)':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.3)
|
||||
'@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3)
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -10594,7 +10630,7 @@ snapshots:
|
||||
'@babel/plugin-transform-modules-commonjs@7.27.1(@babel/core@7.28.3)':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.3)
|
||||
'@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3)
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -10602,17 +10638,17 @@ snapshots:
|
||||
'@babel/plugin-transform-modules-systemjs@7.27.1(@babel/core@7.28.3)':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.3)
|
||||
'@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3)
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
'@babel/helper-validator-identifier': 7.27.1
|
||||
'@babel/traverse': 7.28.0
|
||||
'@babel/traverse': 7.28.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@babel/plugin-transform-modules-umd@7.27.1(@babel/core@7.28.3)':
|
||||
dependencies:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/helper-module-transforms': 7.27.3(@babel/core@7.28.3)
|
||||
'@babel/helper-module-transforms': 7.28.3(@babel/core@7.28.3)
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
@@ -10645,7 +10681,7 @@ snapshots:
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
'@babel/plugin-transform-destructuring': 7.28.0(@babel/core@7.28.3)
|
||||
'@babel/plugin-transform-parameters': 7.27.7(@babel/core@7.28.3)
|
||||
'@babel/traverse': 7.28.0
|
||||
'@babel/traverse': 7.28.3
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
@@ -10874,7 +10910,7 @@ snapshots:
|
||||
dependencies:
|
||||
'@babel/core': 7.28.3
|
||||
'@babel/helper-plugin-utils': 7.27.1
|
||||
'@babel/types': 7.28.0
|
||||
'@babel/types': 7.28.2
|
||||
esutils: 2.0.3
|
||||
|
||||
'@babel/runtime@7.27.6': {}
|
||||
@@ -11838,9 +11874,6 @@ snapshots:
|
||||
|
||||
'@mapbox/point-geometry@1.1.0': {}
|
||||
|
||||
'@mapbox/tiny-sdf@2.0.6':
|
||||
optional: true
|
||||
|
||||
'@mapbox/tiny-sdf@2.0.7': {}
|
||||
|
||||
'@mapbox/unitbezier@0.0.1': {}
|
||||
@@ -12059,6 +12092,8 @@ snapshots:
|
||||
|
||||
'@radix-ui/primitive@1.1.2': {}
|
||||
|
||||
'@radix-ui/primitive@1.1.3': {}
|
||||
|
||||
'@radix-ui/react-arrow@1.0.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
@@ -12088,6 +12123,22 @@ snapshots:
|
||||
'@types/react': 19.1.8
|
||||
'@types/react-dom': 19.1.6(@types/react@19.1.8)
|
||||
|
||||
'@radix-ui/react-checkbox@1.3.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.3
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0)
|
||||
'@radix-ui/react-presence': 1.1.5(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0)
|
||||
'@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.8)(react@19.1.0)
|
||||
'@radix-ui/react-use-size': 1.1.1(@types/react@19.1.8)(react@19.1.0)
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.8
|
||||
'@types/react-dom': 19.1.6(@types/react@19.1.8)
|
||||
|
||||
'@radix-ui/react-collection@1.1.7(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0)
|
||||
@@ -12370,6 +12421,16 @@ snapshots:
|
||||
'@types/react': 19.1.8
|
||||
'@types/react-dom': 19.1.6(@types/react@19.1.8)
|
||||
|
||||
'@radix-ui/react-presence@1.1.5(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0)
|
||||
'@radix-ui/react-use-layout-effect': 1.1.1(@types/react@19.1.8)(react@19.1.0)
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.8
|
||||
'@types/react-dom': 19.1.6(@types/react@19.1.8)
|
||||
|
||||
'@radix-ui/react-primitive@1.0.2(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
@@ -12433,6 +12494,21 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.8
|
||||
|
||||
'@radix-ui/react-switch@1.2.6(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@radix-ui/primitive': 1.1.3
|
||||
'@radix-ui/react-compose-refs': 1.1.2(@types/react@19.1.8)(react@19.1.0)
|
||||
'@radix-ui/react-context': 1.1.2(@types/react@19.1.8)(react@19.1.0)
|
||||
'@radix-ui/react-primitive': 2.1.3(@types/react-dom@19.1.6(@types/react@19.1.8))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
|
||||
'@radix-ui/react-use-controllable-state': 1.2.2(@types/react@19.1.8)(react@19.1.0)
|
||||
'@radix-ui/react-use-previous': 1.1.1(@types/react@19.1.8)(react@19.1.0)
|
||||
'@radix-ui/react-use-size': 1.1.1(@types/react@19.1.8)(react@19.1.0)
|
||||
react: 19.1.0
|
||||
react-dom: 19.1.0(react@19.1.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.8
|
||||
'@types/react-dom': 19.1.6(@types/react@19.1.8)
|
||||
|
||||
'@radix-ui/react-tooltip@1.0.5(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
@@ -12536,6 +12612,12 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.8
|
||||
|
||||
'@radix-ui/react-use-previous@1.1.1(@types/react@19.1.8)(react@19.1.0)':
|
||||
dependencies:
|
||||
react: 19.1.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.1.8
|
||||
|
||||
'@radix-ui/react-use-rect@1.0.0(react@19.1.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
@@ -15136,9 +15218,6 @@ snapshots:
|
||||
es-errors: 1.3.0
|
||||
gopd: 1.2.0
|
||||
|
||||
earcut@3.0.1:
|
||||
optional: true
|
||||
|
||||
earcut@3.0.2: {}
|
||||
|
||||
ejs@3.1.10:
|
||||
@@ -16067,9 +16146,6 @@ snapshots:
|
||||
- conventional-commits-filter
|
||||
- conventional-commits-parser
|
||||
|
||||
gl-matrix@3.4.3:
|
||||
optional: true
|
||||
|
||||
gl-matrix@3.4.4: {}
|
||||
|
||||
glob-parent@5.1.2:
|
||||
@@ -16895,7 +16971,7 @@ snapshots:
|
||||
'@mapbox/jsonlint-lines-primitives': 2.0.2
|
||||
'@mapbox/mapbox-gl-supported': 3.0.0
|
||||
'@mapbox/point-geometry': 0.1.0
|
||||
'@mapbox/tiny-sdf': 2.0.6
|
||||
'@mapbox/tiny-sdf': 2.0.7
|
||||
'@mapbox/unitbezier': 0.0.1
|
||||
'@mapbox/vector-tile': 1.3.1
|
||||
'@mapbox/whoots-js': 3.1.0
|
||||
@@ -16907,15 +16983,15 @@ snapshots:
|
||||
'@types/supercluster': 7.1.3
|
||||
cheap-ruler: 4.0.0
|
||||
csscolorparser: 1.0.3
|
||||
earcut: 3.0.1
|
||||
earcut: 3.0.2
|
||||
geojson-vt: 4.0.2
|
||||
gl-matrix: 3.4.3
|
||||
gl-matrix: 3.4.4
|
||||
grid-index: 1.1.0
|
||||
kdbush: 4.0.2
|
||||
martinez-polygon-clipping: 0.7.4
|
||||
murmurhash-js: 1.0.0
|
||||
pbf: 3.3.0
|
||||
potpack: 2.0.0
|
||||
potpack: 2.1.0
|
||||
quickselect: 3.0.0
|
||||
serialize-to-js: 3.1.2
|
||||
supercluster: 8.0.1
|
||||
@@ -17967,9 +18043,6 @@ snapshots:
|
||||
|
||||
postgres@3.4.7: {}
|
||||
|
||||
potpack@2.0.0:
|
||||
optional: true
|
||||
|
||||
potpack@2.1.0: {}
|
||||
|
||||
preact@10.26.8: {}
|
||||
|
||||
Reference in New Issue
Block a user