mirror of
https://github.com/Crosstalk-Solutions/project-nomad.git
synced 2026-05-28 23:26:50 +02:00
Added font awesome icons to the map markers
This commit is contained in:
parent
3b068eceb0
commit
38c4dc3fd4
|
|
@ -2,6 +2,8 @@ import { useMemo, useState } from 'react'
|
|||
import * as TablerIcons from '@tabler/icons-react'
|
||||
import type { IconProps } from '@tabler/icons-react'
|
||||
import type { ComponentType } from 'react'
|
||||
import * as FontAwesomeIcons from 'react-icons/fa'
|
||||
import type { IconType } from 'react-icons'
|
||||
|
||||
const PAGE_SIZE = 48
|
||||
|
||||
|
|
@ -11,7 +13,21 @@ type IconSelectorPopoverProps = {
|
|||
onClose: () => void
|
||||
}
|
||||
|
||||
const iconEntries = Object.entries(TablerIcons)
|
||||
const normalizeIconNameForSort = (name: string) => {
|
||||
return name
|
||||
.replace(/^Icon/, '') // Tabler: IconHome -> Home
|
||||
.replace(/^Fa/, '') // FontAwesome: FaHome -> Home
|
||||
.toLowerCase()
|
||||
}
|
||||
|
||||
type IconEntry = {
|
||||
name: string
|
||||
label: string
|
||||
library: 'tabler' | 'fa'
|
||||
Icon: ComponentType<IconProps> | IconType
|
||||
}
|
||||
|
||||
const tablerIconEntries: IconEntry[] = Object.entries(TablerIcons)
|
||||
.filter(([name, value]) => {
|
||||
return (
|
||||
name.startsWith('Icon') &&
|
||||
|
|
@ -20,7 +36,32 @@ const iconEntries = Object.entries(TablerIcons)
|
|||
(typeof value === 'function' || typeof value === 'object')
|
||||
)
|
||||
})
|
||||
.sort(([a], [b]) => a.localeCompare(b)) as Array<[string, ComponentType<IconProps>]>
|
||||
.map(([name, Icon]) => ({
|
||||
name: `tabler:${name}`,
|
||||
label: name,
|
||||
library: 'tabler' as const,
|
||||
Icon: Icon as ComponentType<IconProps>,
|
||||
}))
|
||||
|
||||
const fontAwesomeIconEntries: IconEntry[] = Object.entries(FontAwesomeIcons)
|
||||
.filter(([name, value]) => {
|
||||
return (
|
||||
name.startsWith('Fa') &&
|
||||
value !== null &&
|
||||
(typeof value === 'function' || typeof value === 'object')
|
||||
)
|
||||
})
|
||||
.map(([name, Icon]) => ({
|
||||
name: `fa:${name}`,
|
||||
label: name,
|
||||
library: 'fa' as const,
|
||||
Icon: Icon as IconType,
|
||||
}))
|
||||
|
||||
const iconEntries: IconEntry[] = [
|
||||
...tablerIconEntries,
|
||||
...fontAwesomeIconEntries,
|
||||
].sort((a, b) => a.label.localeCompare(b.label))
|
||||
|
||||
export default function IconSelectorPopover({
|
||||
selectedIcon,
|
||||
|
|
@ -33,11 +74,18 @@ export default function IconSelectorPopover({
|
|||
const filteredIcons = useMemo(() => {
|
||||
const normalizedQuery = query.trim().toLowerCase()
|
||||
|
||||
return iconEntries.filter(([name]) => name.toLowerCase().includes(normalizedQuery))
|
||||
return iconEntries.filter((entry) => {
|
||||
const normalizedLabel = normalizeIconNameForSort(entry.label)
|
||||
|
||||
return (
|
||||
entry.label.toLowerCase().includes(normalizedQuery) ||
|
||||
normalizedLabel.includes(normalizedQuery) ||
|
||||
entry.library.toLowerCase().includes(normalizedQuery)
|
||||
)
|
||||
})
|
||||
}, [query])
|
||||
|
||||
const pageCount = Math.max(1, Math.ceil(filteredIcons.length / PAGE_SIZE))
|
||||
|
||||
const pagedIcons = filteredIcons.slice(page * PAGE_SIZE, page * PAGE_SIZE + PAGE_SIZE)
|
||||
|
||||
return (
|
||||
|
|
@ -61,12 +109,12 @@ export default function IconSelectorPopover({
|
|||
|
||||
<div className="max-h-56 overflow-y-auto themed-scrollbar">
|
||||
<div className="grid grid-cols-6 gap-1">
|
||||
{pagedIcons.map(([name, Icon]) => (
|
||||
{pagedIcons.map(({ name, label, library, Icon }) => (
|
||||
<button
|
||||
key={name}
|
||||
type="button"
|
||||
title={name}
|
||||
aria-label={name}
|
||||
title={`${label} (${library})`}
|
||||
aria-label={label}
|
||||
onClick={() => {
|
||||
onSelect(name)
|
||||
onClose()
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import ViewMapMarkerPopup from './ViewMapMarkerPopup'
|
|||
import MapMarkerFormPopup from './MapMarkerFormPopup'
|
||||
import ScaleUnitToggle from './ScaleUnitSelector'
|
||||
|
||||
type ScaleUnit = 'imperial' | 'metric'
|
||||
type ScaleUnit = 'imperial' | 'metric' | 'nautical'
|
||||
|
||||
type MapCommand = {
|
||||
id: number
|
||||
|
|
@ -108,9 +108,15 @@ export default function MapComponent({
|
|||
const [hasUnsavedMarkerChanges, setHasUnsavedMarkerChanges] = useState(false)
|
||||
const [showCoordinates, setShowCoordinates] = useState(false)
|
||||
|
||||
const [scaleUnit, setScaleUnit] = useState<ScaleUnit>(
|
||||
() => (localStorage.getItem('nomad:map-scale-unit') as ScaleUnit) || 'metric'
|
||||
)
|
||||
const getInitialScaleUnit = (): ScaleUnit => {
|
||||
const stored = localStorage.getItem('nomad:map-scale-unit')
|
||||
|
||||
return stored === 'metric' || stored === 'imperial' || stored === 'nautical'
|
||||
? stored
|
||||
: 'metric'
|
||||
}
|
||||
|
||||
const [scaleUnit, setScaleUnit] = useState<ScaleUnit>(getInitialScaleUnit)
|
||||
|
||||
const [cursorLngLat, setCursorLngLat] = useState<{
|
||||
lng: number
|
||||
|
|
|
|||
|
|
@ -9,6 +9,8 @@ import {
|
|||
IconTrash,
|
||||
IconX,
|
||||
} from '@tabler/icons-react'
|
||||
import * as FontAwesomeIcons from 'react-icons/fa'
|
||||
import type { IconType } from 'react-icons'
|
||||
import * as TablerIcons from '@tabler/icons-react'
|
||||
import type { IconProps } from '@tabler/icons-react'
|
||||
import type { ComponentType } from 'react'
|
||||
|
|
@ -104,13 +106,40 @@ const getHueGroupLabel = ({ bucket, hue, lightness }: ColorSortValue) => {
|
|||
if (hue < 270) return 'Blue'
|
||||
return 'Purple'
|
||||
}
|
||||
const getReadableIconName = (icon?: string | null) => {
|
||||
if (!icon) return 'Default pin'
|
||||
|
||||
const resolveMarkerIcon = (icon?: string | null): ComponentType<IconProps> => {
|
||||
return icon
|
||||
.replace(/^fa:/, '')
|
||||
.replace(/^tabler:/, '')
|
||||
.replace(/^Fa/, '')
|
||||
.replace(/^Icon/, '')
|
||||
}
|
||||
const resolveMarkerIcon = (
|
||||
icon?: string | null
|
||||
): ComponentType<IconProps> | IconType => {
|
||||
if (!icon) return IconMapPinFilled
|
||||
|
||||
const Icon = (TablerIcons as Record<string, unknown>)[icon]
|
||||
// Font Awesome
|
||||
if (icon.startsWith('fa:')) {
|
||||
const iconName = icon.replace('fa:', '')
|
||||
const Icon = (FontAwesomeIcons as Record<string, unknown>)[iconName]
|
||||
return Icon ? (Icon as IconType) : IconMapPinFilled
|
||||
}
|
||||
|
||||
return Icon ? (Icon as ComponentType<IconProps>) : IconMapPinFilled
|
||||
// Tabler
|
||||
if (icon.startsWith('tabler:')) {
|
||||
const iconName = icon.replace('tabler:', '')
|
||||
const Icon = (TablerIcons as Record<string, unknown>)[iconName]
|
||||
return Icon ? (Icon as ComponentType<IconProps>) : IconMapPinFilled
|
||||
}
|
||||
|
||||
// Backward compatibility
|
||||
const Icon =
|
||||
(TablerIcons as Record<string, unknown>)[icon] ??
|
||||
(FontAwesomeIcons as Record<string, unknown>)[icon]
|
||||
|
||||
return Icon ? (Icon as ComponentType<IconProps> | IconType) : IconMapPinFilled
|
||||
}
|
||||
|
||||
const getMarkerGroup = (marker: MapMarker, sortField: SortField) => {
|
||||
|
|
@ -130,7 +159,7 @@ const getMarkerGroup = (marker: MapMarker, sortField: SortField) => {
|
|||
}
|
||||
|
||||
if (sortField === 'icon') {
|
||||
const label = marker.icon || 'Default pin'
|
||||
const label = getReadableIconName(marker.icon)
|
||||
return { key: label, label }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,8 @@
|
|||
import { IconCircleFilled } from '@tabler/icons-react'
|
||||
import * as TablerIcons from '@tabler/icons-react'
|
||||
import type { IconProps } from '@tabler/icons-react'
|
||||
import type { IconType } from 'react-icons'
|
||||
import * as FontAwesomeIcons from 'react-icons/fa'
|
||||
import type { ComponentType } from 'react'
|
||||
|
||||
import { PIN_COLORS } from '~/hooks/useMapMarkers'
|
||||
|
|
@ -42,14 +44,30 @@ const getContrastingIconColor = (backgroundColor: string) => {
|
|||
return luminance > 0.55 ? '#111827' : '#ffffff'
|
||||
}
|
||||
|
||||
const resolveIcon = (icon?: string | null): ComponentType<IconProps> => {
|
||||
type MarkerIconComponent =
|
||||
| ComponentType<IconProps>
|
||||
| IconType
|
||||
|
||||
const resolveIcon = (icon?: string | null): MarkerIconComponent => {
|
||||
if (!icon) return IconCircleFilled
|
||||
|
||||
const Icon = (TablerIcons as Record<string, unknown>)[icon]
|
||||
if (icon.startsWith('fa:')) {
|
||||
const iconName = icon.replace('fa:', '')
|
||||
const Icon = (FontAwesomeIcons as Record<string, unknown>)[iconName]
|
||||
return Icon ? (Icon as IconType) : IconCircleFilled
|
||||
}
|
||||
|
||||
if (!Icon) return IconCircleFilled
|
||||
if (icon.startsWith('tabler:')) {
|
||||
const iconName = icon.replace('tabler:', '')
|
||||
const Icon = (TablerIcons as Record<string, unknown>)[iconName]
|
||||
return Icon ? (Icon as ComponentType<IconProps>) : IconCircleFilled
|
||||
}
|
||||
|
||||
return Icon as ComponentType<IconProps>
|
||||
const Icon =
|
||||
(TablerIcons as Record<string, unknown>)[icon] ??
|
||||
(FontAwesomeIcons as Record<string, unknown>)[icon]
|
||||
|
||||
return Icon ? (Icon as MarkerIconComponent) : IconCircleFilled
|
||||
}
|
||||
|
||||
export default function MarkerPin({
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
type ScaleUnit = 'imperial' | 'metric'
|
||||
type ScaleUnit = 'imperial' | 'metric' | 'nautical'
|
||||
|
||||
type ScaleUnitSelectorProps = {
|
||||
scaleUnit: ScaleUnit
|
||||
|
|
@ -7,41 +7,63 @@ type ScaleUnitSelectorProps = {
|
|||
}
|
||||
|
||||
export default function ScaleUnitSelector({
|
||||
scaleUnit,
|
||||
onChange,
|
||||
onMouseEnter,
|
||||
}: ScaleUnitSelectorProps) {
|
||||
const getButtonStyle = (unit: ScaleUnit) => ({
|
||||
background: scaleUnit === unit ? '#424420' : 'white',
|
||||
color: scaleUnit === unit ? 'white' : '#666',
|
||||
cursor: 'pointer',
|
||||
})
|
||||
scaleUnit,
|
||||
onChange,
|
||||
onMouseEnter,
|
||||
}: ScaleUnitSelectorProps) {
|
||||
return (
|
||||
<div className="absolute bottom-[30px] left-[10px] z-[2]" onMouseEnter={onMouseEnter}>
|
||||
<div className="inline-flex overflow-hidden rounded text-[11px] font-semibold leading-none shadow-[0_0_0_2px_rgba(0,0,0,0.1)]">
|
||||
|
||||
return (
|
||||
<div className="absolute bottom-[30px] left-[10px] z-[2]" onMouseEnter={onMouseEnter}>
|
||||
<div className="inline-flex overflow-hidden rounded text-[11px] font-semibold leading-none shadow-[0_0_0_2px_rgba(0,0,0,0.1)]">
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (scaleUnit !== 'metric') onChange('metric')
|
||||
}}
|
||||
className="border-0 px-2 py-1"
|
||||
style={getButtonStyle('metric')}
|
||||
>
|
||||
Metric
|
||||
</button>
|
||||
{/* Metric */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (scaleUnit !== 'metric') onChange('metric')
|
||||
}}
|
||||
className="border-0 px-2 py-1"
|
||||
style={{
|
||||
background: scaleUnit === 'metric' ? '#424420' : 'white',
|
||||
color: scaleUnit === 'metric' ? 'white' : '#666',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
Metric
|
||||
</button>
|
||||
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (scaleUnit !== 'imperial') onChange('imperial')
|
||||
}}
|
||||
className="border-0 px-2 py-1"
|
||||
style={getButtonStyle('imperial')}
|
||||
>
|
||||
Imperial
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
{/* Imperial */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (scaleUnit !== 'imperial') onChange('imperial')
|
||||
}}
|
||||
className="border-0 px-2 py-1"
|
||||
style={{
|
||||
background: scaleUnit === 'imperial' ? '#424420' : 'white',
|
||||
color: scaleUnit === 'imperial' ? 'white' : '#666',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
Imperial
|
||||
</button>
|
||||
|
||||
{/* Nautical */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => {
|
||||
if (scaleUnit !== 'nautical') onChange('nautical')
|
||||
}}
|
||||
className="border-0 px-2 py-1"
|
||||
style={{
|
||||
background: scaleUnit === 'nautical' ? '#424420' : 'white',
|
||||
color: scaleUnit === 'nautical' ? 'white' : '#666',
|
||||
cursor: 'pointer',
|
||||
}}
|
||||
>
|
||||
Nautical
|
||||
</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
180
admin/package-lock.json
generated
180
admin/package-lock.json
generated
|
|
@ -9,94 +9,95 @@
|
|||
"version": "0.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@adonisjs/auth": "^9.4.0",
|
||||
"@adonisjs/core": "^6.18.0",
|
||||
"@adonisjs/cors": "^2.2.1",
|
||||
"@adonisjs/inertia": "^3.1.1",
|
||||
"@adonisjs/lucid": "^21.8.2",
|
||||
"@adonisjs/session": "^7.5.1",
|
||||
"@adonisjs/shield": "^8.2.0",
|
||||
"@adonisjs/static": "^1.1.1",
|
||||
"@adonisjs/transmit": "^2.0.2",
|
||||
"@adonisjs/transmit-client": "^1.0.0",
|
||||
"@adonisjs/vite": "^4.0.0",
|
||||
"@chonkiejs/core": "^0.0.7",
|
||||
"@headlessui/react": "^2.2.4",
|
||||
"@inertiajs/react": "^2.0.13",
|
||||
"@markdoc/markdoc": "^0.5.2",
|
||||
"@openzim/libzim": "^4.0.0",
|
||||
"@protomaps/basemaps": "^5.7.0",
|
||||
"@qdrant/js-client-rest": "^1.16.2",
|
||||
"@tabler/icons-react": "^3.34.0",
|
||||
"@tailwindcss/vite": "^4.1.10",
|
||||
"@tanstack/react-query": "^5.81.5",
|
||||
"@tanstack/react-query-devtools": "^5.83.0",
|
||||
"@tanstack/react-virtual": "^3.13.12",
|
||||
"@uppy/core": "^5.2.0",
|
||||
"@uppy/dashboard": "^5.1.0",
|
||||
"@uppy/react": "^5.1.1",
|
||||
"@vinejs/vine": "^3.0.1",
|
||||
"@vitejs/plugin-react": "^4.6.0",
|
||||
"autoprefixer": "^10.4.21",
|
||||
"axios": "^1.15.0",
|
||||
"better-sqlite3": "^12.1.1",
|
||||
"bullmq": "^5.65.1",
|
||||
"cheerio": "^1.2.0",
|
||||
"compression": "^1.8.1",
|
||||
"dockerode": "^4.0.7",
|
||||
"edge.js": "^6.2.1",
|
||||
"fast-xml-parser": "^5.5.7",
|
||||
"fuse.js": "^7.1.0",
|
||||
"jszip": "^3.10.1",
|
||||
"luxon": "^3.6.1",
|
||||
"maplibre-gl": "^4.7.1",
|
||||
"mysql2": "^3.14.1",
|
||||
"ollama": "^0.6.3",
|
||||
"openai": "^6.27.0",
|
||||
"pdf-parse": "^2.4.5",
|
||||
"pdf2pic": "^3.2.0",
|
||||
"pino-pretty": "^13.0.0",
|
||||
"pmtiles": "^4.4.0",
|
||||
"postcss": "^8.5.6",
|
||||
"react": "^19.1.0",
|
||||
"react-adonis-transmit": "^1.0.1",
|
||||
"react-dom": "^19.1.0",
|
||||
"react-map-gl": "^8.1.0",
|
||||
"react-markdown": "^10.1.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"remark-gfm": "^4.0.1",
|
||||
"sharp": "^0.34.5",
|
||||
"stopword": "^3.1.5",
|
||||
"systeminformation": "^5.31.0",
|
||||
"tailwindcss": "^4.2.1",
|
||||
"tar": "^7.5.11",
|
||||
"tesseract.js": "^7.0.0",
|
||||
"url-join": "^5.0.0",
|
||||
"yaml": "^2.8.3"
|
||||
"@adonisjs/auth": "9.6.0",
|
||||
"@adonisjs/core": "6.19.3",
|
||||
"@adonisjs/cors": "2.2.1",
|
||||
"@adonisjs/inertia": "3.1.1",
|
||||
"@adonisjs/lucid": "21.8.2",
|
||||
"@adonisjs/session": "7.7.1",
|
||||
"@adonisjs/shield": "8.2.0",
|
||||
"@adonisjs/static": "1.1.1",
|
||||
"@adonisjs/transmit": "2.0.2",
|
||||
"@adonisjs/transmit-client": "1.1.0",
|
||||
"@adonisjs/vite": "4.0.0",
|
||||
"@chonkiejs/core": "0.0.7",
|
||||
"@headlessui/react": "2.2.9",
|
||||
"@inertiajs/react": "2.3.13",
|
||||
"@markdoc/markdoc": "0.5.4",
|
||||
"@openzim/libzim": "4.0.0",
|
||||
"@protomaps/basemaps": "5.7.0",
|
||||
"@qdrant/js-client-rest": "1.16.2",
|
||||
"@tabler/icons-react": "3.36.1",
|
||||
"@tailwindcss/vite": "4.1.18",
|
||||
"@tanstack/react-query": "5.90.20",
|
||||
"@tanstack/react-query-devtools": "5.91.3",
|
||||
"@tanstack/react-virtual": "3.13.18",
|
||||
"@uppy/core": "5.2.0",
|
||||
"@uppy/dashboard": "5.1.0",
|
||||
"@uppy/react": "5.1.1",
|
||||
"@vinejs/vine": "3.0.1",
|
||||
"@vitejs/plugin-react": "4.7.0",
|
||||
"autoprefixer": "10.4.24",
|
||||
"axios": "1.15.0",
|
||||
"better-sqlite3": "12.6.2",
|
||||
"bullmq": "5.67.2",
|
||||
"cheerio": "1.2.0",
|
||||
"compression": "1.8.1",
|
||||
"dockerode": "4.0.9",
|
||||
"edge.js": "6.4.0",
|
||||
"fast-xml-parser": "5.5.9",
|
||||
"fuse.js": "7.1.0",
|
||||
"jszip": "3.10.1",
|
||||
"luxon": "3.7.2",
|
||||
"maplibre-gl": "4.7.1",
|
||||
"mysql2": "3.16.2",
|
||||
"ollama": "0.6.3",
|
||||
"openai": "6.27.0",
|
||||
"pdf-parse": "2.4.5",
|
||||
"pdf2pic": "3.2.0",
|
||||
"pino-pretty": "13.1.3",
|
||||
"pmtiles": "4.4.0",
|
||||
"postcss": "8.5.6",
|
||||
"react": "19.2.4",
|
||||
"react-adonis-transmit": "1.0.1",
|
||||
"react-dom": "19.2.4",
|
||||
"react-icons": "5.6.0",
|
||||
"react-map-gl": "8.1.0",
|
||||
"react-markdown": "10.1.0",
|
||||
"reflect-metadata": "0.2.2",
|
||||
"remark-gfm": "4.0.1",
|
||||
"sharp": "0.34.5",
|
||||
"stopword": "3.1.5",
|
||||
"systeminformation": "5.31.0",
|
||||
"tailwindcss": "4.2.2",
|
||||
"tar": "7.5.11",
|
||||
"tesseract.js": "7.0.0",
|
||||
"url-join": "5.0.0",
|
||||
"yaml": "2.8.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@adonisjs/assembler": "^7.8.2",
|
||||
"@adonisjs/eslint-config": "^2.0.0",
|
||||
"@adonisjs/prettier-config": "^1.4.4",
|
||||
"@adonisjs/tsconfig": "^1.4.0",
|
||||
"@japa/assert": "^4.0.1",
|
||||
"@japa/plugin-adonisjs": "^4.0.0",
|
||||
"@japa/runner": "^4.2.0",
|
||||
"@adonisjs/assembler": "7.8.2",
|
||||
"@adonisjs/eslint-config": "2.1.2",
|
||||
"@adonisjs/prettier-config": "1.4.5",
|
||||
"@adonisjs/tsconfig": "1.4.1",
|
||||
"@japa/assert": "4.2.0",
|
||||
"@japa/plugin-adonisjs": "4.0.0",
|
||||
"@japa/runner": "4.5.0",
|
||||
"@swc/core": "1.11.24",
|
||||
"@tanstack/eslint-plugin-query": "^5.81.2",
|
||||
"@types/compression": "^1.8.1",
|
||||
"@types/dockerode": "^4.0.1",
|
||||
"@types/luxon": "^3.6.2",
|
||||
"@types/node": "^22.15.18",
|
||||
"@types/react": "^19.1.8",
|
||||
"@types/react-dom": "^19.1.6",
|
||||
"@types/stopword": "^2.0.3",
|
||||
"eslint": "^9.26.0",
|
||||
"hot-hook": "^0.4.0",
|
||||
"prettier": "^3.5.3",
|
||||
"ts-node-maintained": "^10.9.5",
|
||||
"typescript": "~5.8.3",
|
||||
"vite": "^6.4.2"
|
||||
"@tanstack/eslint-plugin-query": "5.91.4",
|
||||
"@types/compression": "1.8.1",
|
||||
"@types/dockerode": "4.0.1",
|
||||
"@types/luxon": "3.7.1",
|
||||
"@types/node": "22.19.7",
|
||||
"@types/react": "19.2.10",
|
||||
"@types/react-dom": "19.2.3",
|
||||
"@types/stopword": "2.0.3",
|
||||
"eslint": "9.39.2",
|
||||
"hot-hook": "0.4.0",
|
||||
"prettier": "3.8.1",
|
||||
"ts-node-maintained": "10.9.6",
|
||||
"typescript": "5.8.3",
|
||||
"vite": "6.4.2"
|
||||
}
|
||||
},
|
||||
"node_modules/@adobe/css-tools": {
|
||||
|
|
@ -14052,6 +14053,15 @@
|
|||
"react": "^19.2.4"
|
||||
}
|
||||
},
|
||||
"node_modules/react-icons": {
|
||||
"version": "5.6.0",
|
||||
"resolved": "https://registry.npmjs.org/react-icons/-/react-icons-5.6.0.tgz",
|
||||
"integrity": "sha512-RH93p5ki6LfOiIt0UtDyNg/cee+HLVR6cHHtW3wALfo+eOHTp8RnU2kRkI6E+H19zMIs03DyxUG/GfZMOGvmiA==",
|
||||
"license": "MIT",
|
||||
"peerDependencies": {
|
||||
"react": "*"
|
||||
}
|
||||
},
|
||||
"node_modules/react-is": {
|
||||
"version": "18.3.1",
|
||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz",
|
||||
|
|
|
|||
|
|
@ -114,6 +114,7 @@
|
|||
"react": "19.2.4",
|
||||
"react-adonis-transmit": "1.0.1",
|
||||
"react-dom": "19.2.4",
|
||||
"react-icons": "^5.6.0",
|
||||
"react-map-gl": "8.1.0",
|
||||
"react-markdown": "10.1.0",
|
||||
"reflect-metadata": "0.2.2",
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user