@orsetra/shared-ui 1.0.12 → 1.0.13
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useState, useRef } from "react"
|
|
4
|
+
import { FileCode, Package, Boxes, Pencil } from "lucide-react"
|
|
5
|
+
import { ImageCropDialog } from "./image-crop-dialog"
|
|
6
|
+
|
|
7
|
+
// Get icon for asset type
|
|
8
|
+
export const getAssetIcon = (type?: string) => {
|
|
9
|
+
if (!type) return Boxes
|
|
10
|
+
if (type.startsWith('operation')) return FileCode
|
|
11
|
+
if (type.startsWith('component')) return Package
|
|
12
|
+
return Boxes
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
// Get color for asset type
|
|
16
|
+
export const getAssetColor = (type?: string) => {
|
|
17
|
+
if (!type) return 'text-purple-600 bg-purple-50'
|
|
18
|
+
if (type.startsWith('operation')) return 'text-blue-600 bg-blue-50'
|
|
19
|
+
if (type.startsWith('component')) return 'text-green-600 bg-green-50'
|
|
20
|
+
return 'text-purple-600 bg-purple-50'
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface AssetLogoProps {
|
|
24
|
+
type: string
|
|
25
|
+
size?: 'sm' | 'md' | 'lg'
|
|
26
|
+
editable?: boolean
|
|
27
|
+
logoUrl?: string | null
|
|
28
|
+
onLogoChange?: (logoBase64: string) => void
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function AssetLogo({ type, size = 'md', editable = false, logoUrl, onLogoChange }: AssetLogoProps) {
|
|
32
|
+
const [isHovered, setIsHovered] = useState(false)
|
|
33
|
+
const [showCropDialog, setShowCropDialog] = useState(false)
|
|
34
|
+
const [tempImageSrc, setTempImageSrc] = useState<string>("")
|
|
35
|
+
const fileInputRef = useRef<HTMLInputElement>(null)
|
|
36
|
+
|
|
37
|
+
const Icon = getAssetIcon(type)
|
|
38
|
+
const colorClass = getAssetColor(type)
|
|
39
|
+
|
|
40
|
+
const sizeClasses = {
|
|
41
|
+
sm: 'w-8 h-8',
|
|
42
|
+
md: 'w-12 h-12',
|
|
43
|
+
lg: 'w-16 h-16'
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const iconSizes = {
|
|
47
|
+
sm: 'h-4 w-4',
|
|
48
|
+
md: 'h-6 w-6',
|
|
49
|
+
lg: 'h-8 w-8'
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const handleFileSelect = (e: React.ChangeEvent<HTMLInputElement>) => {
|
|
53
|
+
const file = e.target.files?.[0]
|
|
54
|
+
if (file) {
|
|
55
|
+
const reader = new FileReader()
|
|
56
|
+
reader.onload = () => {
|
|
57
|
+
setTempImageSrc(reader.result as string)
|
|
58
|
+
setShowCropDialog(true)
|
|
59
|
+
}
|
|
60
|
+
reader.readAsDataURL(file)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const handleCropComplete = (croppedImageBase64: string) => {
|
|
65
|
+
if (onLogoChange) {
|
|
66
|
+
onLogoChange(croppedImageBase64)
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const handleEditClick = () => {
|
|
71
|
+
fileInputRef.current?.click()
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
return (
|
|
75
|
+
<>
|
|
76
|
+
<div
|
|
77
|
+
className={`${sizeClasses[size]} rounded-full ${!logoUrl ? colorClass : 'bg-white border-2 border-gray-200'} flex items-center justify-center flex-shrink-0 relative group cursor-pointer`}
|
|
78
|
+
onMouseEnter={() => editable && setIsHovered(true)}
|
|
79
|
+
onMouseLeave={() => editable && setIsHovered(false)}
|
|
80
|
+
onClick={editable ? handleEditClick : undefined}
|
|
81
|
+
>
|
|
82
|
+
{logoUrl ? (
|
|
83
|
+
<img
|
|
84
|
+
src={logoUrl}
|
|
85
|
+
alt="Asset logo"
|
|
86
|
+
className="w-full h-full rounded-full object-cover"
|
|
87
|
+
/>
|
|
88
|
+
) : (
|
|
89
|
+
<Icon className={iconSizes[size]} />
|
|
90
|
+
)}
|
|
91
|
+
|
|
92
|
+
{editable && isHovered && (
|
|
93
|
+
<div className="absolute inset-0 bg-black bg-opacity-50 rounded-full flex items-center justify-center">
|
|
94
|
+
<Pencil className="h-4 w-4 text-white" />
|
|
95
|
+
</div>
|
|
96
|
+
)}
|
|
97
|
+
</div>
|
|
98
|
+
|
|
99
|
+
<input
|
|
100
|
+
ref={fileInputRef}
|
|
101
|
+
type="file"
|
|
102
|
+
accept="image/*"
|
|
103
|
+
onChange={handleFileSelect}
|
|
104
|
+
className="hidden"
|
|
105
|
+
/>
|
|
106
|
+
|
|
107
|
+
{showCropDialog && (
|
|
108
|
+
<ImageCropDialog
|
|
109
|
+
open={showCropDialog}
|
|
110
|
+
onClose={() => setShowCropDialog(false)}
|
|
111
|
+
imageSrc={tempImageSrc}
|
|
112
|
+
onCropComplete={handleCropComplete}
|
|
113
|
+
/>
|
|
114
|
+
)}
|
|
115
|
+
</>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
@@ -20,30 +20,33 @@ export function AssetsHeader({
|
|
|
20
20
|
subtitle,
|
|
21
21
|
}: AssetsHeaderProps) {
|
|
22
22
|
return (
|
|
23
|
-
<div className="border-b bg-white
|
|
24
|
-
{/*
|
|
25
|
-
<div className="flex items-center gap-3">
|
|
26
|
-
{
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
<div className="flex flex-col">
|
|
33
|
-
{typeof title === 'string' ? (
|
|
34
|
-
<h2 className="text-base font-semibold text-text-primary">{title}</h2>
|
|
35
|
-
) : (
|
|
36
|
-
title
|
|
37
|
-
)}
|
|
38
|
-
{subtitle && (
|
|
39
|
-
<p className="text-xs text-gray-500 mt-0.5">{subtitle}</p>
|
|
23
|
+
<div className="border-b bg-white sticky top-0 z-10">
|
|
24
|
+
{/* Mobile: Stack vertically, Desktop: Horizontal */}
|
|
25
|
+
<div className="flex flex-col sm:flex-row sm:items-center sm:justify-between px-3 sm:px-4 py-3 gap-3 sm:gap-0">
|
|
26
|
+
{/* Left side - Back link, Logo and Title */}
|
|
27
|
+
<div className="flex items-center gap-3 min-w-0">
|
|
28
|
+
{backLink && (
|
|
29
|
+
<Link href={backLink} className="flex-shrink-0">
|
|
30
|
+
<ArrowLeft className="h-5 w-5 text-text-secondary" />
|
|
31
|
+
</Link>
|
|
40
32
|
)}
|
|
33
|
+
{logo && <div className="flex-shrink-0">{logo}</div>}
|
|
34
|
+
<div className="flex flex-col min-w-0">
|
|
35
|
+
{typeof title === 'string' ? (
|
|
36
|
+
<h2 className="text-sm sm:text-base font-semibold text-text-primary truncate">{title}</h2>
|
|
37
|
+
) : (
|
|
38
|
+
title
|
|
39
|
+
)}
|
|
40
|
+
{subtitle && (
|
|
41
|
+
<p className="text-xs text-gray-500 mt-0.5 truncate">{subtitle}</p>
|
|
42
|
+
)}
|
|
43
|
+
</div>
|
|
41
44
|
</div>
|
|
42
|
-
</div>
|
|
43
45
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
46
|
+
{/* Right side - Actions */}
|
|
47
|
+
<div className="flex items-center justify-start sm:justify-end gap-2 flex-wrap">
|
|
48
|
+
{actions}
|
|
49
|
+
</div>
|
|
47
50
|
</div>
|
|
48
51
|
</div>
|
|
49
52
|
)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useState, useRef, useEffect } from "react"
|
|
4
|
+
import { Pencil, Check, X } from "lucide-react"
|
|
5
|
+
import { Input } from "./input"
|
|
6
|
+
import { Button } from "./button"
|
|
7
|
+
|
|
8
|
+
interface EditableTitleProps {
|
|
9
|
+
value: string
|
|
10
|
+
onSave: (newValue: string) => void
|
|
11
|
+
className?: string
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function EditableTitle({ value, onSave, className = "" }: EditableTitleProps) {
|
|
15
|
+
const [isEditing, setIsEditing] = useState(false)
|
|
16
|
+
const [isHovered, setIsHovered] = useState(false)
|
|
17
|
+
const [editValue, setEditValue] = useState(value)
|
|
18
|
+
const inputRef = useRef<HTMLInputElement>(null)
|
|
19
|
+
|
|
20
|
+
useEffect(() => {
|
|
21
|
+
if (isEditing && inputRef.current) {
|
|
22
|
+
inputRef.current.focus()
|
|
23
|
+
inputRef.current.select()
|
|
24
|
+
}
|
|
25
|
+
}, [isEditing])
|
|
26
|
+
|
|
27
|
+
const handleSave = () => {
|
|
28
|
+
if (editValue.trim() && editValue !== value) {
|
|
29
|
+
onSave(editValue.trim())
|
|
30
|
+
}
|
|
31
|
+
setIsEditing(false)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const handleCancel = () => {
|
|
35
|
+
setEditValue(value)
|
|
36
|
+
setIsEditing(false)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
40
|
+
if (e.key === 'Enter') {
|
|
41
|
+
handleSave()
|
|
42
|
+
} else if (e.key === 'Escape') {
|
|
43
|
+
handleCancel()
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
if (isEditing) {
|
|
48
|
+
return (
|
|
49
|
+
<div className="flex items-center gap-2">
|
|
50
|
+
<Input
|
|
51
|
+
ref={inputRef}
|
|
52
|
+
value={editValue}
|
|
53
|
+
onChange={(e) => setEditValue(e.target.value)}
|
|
54
|
+
onKeyDown={handleKeyDown}
|
|
55
|
+
className="h-8 min-w-[300px]"
|
|
56
|
+
/>
|
|
57
|
+
<Button
|
|
58
|
+
size="sm"
|
|
59
|
+
variant="default"
|
|
60
|
+
onClick={handleSave}
|
|
61
|
+
className="h-8 w-8 p-0"
|
|
62
|
+
>
|
|
63
|
+
<Check className="h-4 w-4" />
|
|
64
|
+
</Button>
|
|
65
|
+
<Button
|
|
66
|
+
size="sm"
|
|
67
|
+
variant="secondary"
|
|
68
|
+
onClick={handleCancel}
|
|
69
|
+
className="h-8 w-8 p-0"
|
|
70
|
+
>
|
|
71
|
+
<X className="h-4 w-4" />
|
|
72
|
+
</Button>
|
|
73
|
+
</div>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
return (
|
|
78
|
+
<div
|
|
79
|
+
className={`flex items-center gap-2 group cursor-pointer ${className}`}
|
|
80
|
+
onMouseEnter={() => setIsHovered(true)}
|
|
81
|
+
onMouseLeave={() => setIsHovered(false)}
|
|
82
|
+
onClick={() => setIsEditing(true)}
|
|
83
|
+
>
|
|
84
|
+
<h2 className="text-base font-semibold text-text-primary">{value}</h2>
|
|
85
|
+
{isHovered && (
|
|
86
|
+
<Pencil className="h-3 w-3 text-gray-400 opacity-0 group-hover:opacity-100 transition-opacity" />
|
|
87
|
+
)}
|
|
88
|
+
</div>
|
|
89
|
+
)
|
|
90
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
"use client"
|
|
2
|
+
|
|
3
|
+
import { useState, useCallback } from "react"
|
|
4
|
+
import Cropper from "react-easy-crop"
|
|
5
|
+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "./dialog"
|
|
6
|
+
import { Button } from "./button"
|
|
7
|
+
import { Slider } from "./slider"
|
|
8
|
+
|
|
9
|
+
interface ImageCropDialogProps {
|
|
10
|
+
open: boolean
|
|
11
|
+
onClose: () => void
|
|
12
|
+
imageSrc: string
|
|
13
|
+
onCropComplete: (croppedImageBase64: string) => void
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
interface Area {
|
|
17
|
+
x: number
|
|
18
|
+
y: number
|
|
19
|
+
width: number
|
|
20
|
+
height: number
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
const createImage = (url: string): Promise<HTMLImageElement> =>
|
|
24
|
+
new Promise((resolve, reject) => {
|
|
25
|
+
const image = new Image()
|
|
26
|
+
image.addEventListener('load', () => resolve(image))
|
|
27
|
+
image.addEventListener('error', (error) => reject(error))
|
|
28
|
+
image.src = url
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
async function getCroppedImg(imageSrc: string, pixelCrop: Area): Promise<string> {
|
|
32
|
+
const image = await createImage(imageSrc)
|
|
33
|
+
const canvas = document.createElement('canvas')
|
|
34
|
+
const ctx = canvas.getContext('2d')
|
|
35
|
+
|
|
36
|
+
if (!ctx) {
|
|
37
|
+
throw new Error('No 2d context')
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Set canvas size to a square (optimized size for logo)
|
|
41
|
+
const size = 128
|
|
42
|
+
canvas.width = size
|
|
43
|
+
canvas.height = size
|
|
44
|
+
|
|
45
|
+
ctx.drawImage(
|
|
46
|
+
image,
|
|
47
|
+
pixelCrop.x,
|
|
48
|
+
pixelCrop.y,
|
|
49
|
+
pixelCrop.width,
|
|
50
|
+
pixelCrop.height,
|
|
51
|
+
0,
|
|
52
|
+
0,
|
|
53
|
+
size,
|
|
54
|
+
size
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
// Use JPEG with quality 0.8 for better compression
|
|
58
|
+
// Falls back to PNG if the image has transparency
|
|
59
|
+
let dataUrl = canvas.toDataURL('image/jpeg', 0.8)
|
|
60
|
+
|
|
61
|
+
// If the compressed image is still too large (>50KB), reduce quality further
|
|
62
|
+
if (dataUrl.length > 50000) {
|
|
63
|
+
dataUrl = canvas.toDataURL('image/jpeg', 0.6)
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return dataUrl
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function ImageCropDialog({ open, onClose, imageSrc, onCropComplete }: ImageCropDialogProps) {
|
|
70
|
+
const [crop, setCrop] = useState({ x: 0, y: 0 })
|
|
71
|
+
const [zoom, setZoom] = useState(1)
|
|
72
|
+
const [croppedAreaPixels, setCroppedAreaPixels] = useState<Area | null>(null)
|
|
73
|
+
const [isSaving, setIsSaving] = useState(false)
|
|
74
|
+
|
|
75
|
+
const onCropChange = useCallback((crop: { x: number; y: number }) => {
|
|
76
|
+
setCrop(crop)
|
|
77
|
+
}, [])
|
|
78
|
+
|
|
79
|
+
const onZoomChange = useCallback((zoom: number) => {
|
|
80
|
+
setZoom(zoom)
|
|
81
|
+
}, [])
|
|
82
|
+
|
|
83
|
+
const onCropCompleteCallback = useCallback(
|
|
84
|
+
(_croppedArea: Area, croppedAreaPixels: Area) => {
|
|
85
|
+
setCroppedAreaPixels(croppedAreaPixels)
|
|
86
|
+
},
|
|
87
|
+
[]
|
|
88
|
+
)
|
|
89
|
+
|
|
90
|
+
const handleSave = async () => {
|
|
91
|
+
if (!croppedAreaPixels) return
|
|
92
|
+
|
|
93
|
+
setIsSaving(true)
|
|
94
|
+
try {
|
|
95
|
+
const croppedImage = await getCroppedImg(imageSrc, croppedAreaPixels)
|
|
96
|
+
onCropComplete(croppedImage)
|
|
97
|
+
onClose()
|
|
98
|
+
} catch (error) {
|
|
99
|
+
console.error('Error cropping image:', error)
|
|
100
|
+
} finally {
|
|
101
|
+
setIsSaving(false)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
return (
|
|
106
|
+
<Dialog open={open} onOpenChange={onClose}>
|
|
107
|
+
<DialogContent className="sm:max-w-[600px]">
|
|
108
|
+
<DialogHeader>
|
|
109
|
+
<DialogTitle>Crop Image</DialogTitle>
|
|
110
|
+
</DialogHeader>
|
|
111
|
+
<div className="space-y-4">
|
|
112
|
+
<div className="relative h-[400px] bg-ibm-gray-20 rounded-lg">
|
|
113
|
+
<Cropper
|
|
114
|
+
image={imageSrc}
|
|
115
|
+
crop={crop}
|
|
116
|
+
zoom={zoom}
|
|
117
|
+
aspect={1}
|
|
118
|
+
cropShape="round"
|
|
119
|
+
showGrid={false}
|
|
120
|
+
onCropChange={onCropChange}
|
|
121
|
+
onZoomChange={onZoomChange}
|
|
122
|
+
onCropComplete={onCropCompleteCallback}
|
|
123
|
+
/>
|
|
124
|
+
</div>
|
|
125
|
+
<div className="space-y-2">
|
|
126
|
+
<label className="text-sm font-medium">Zoom</label>
|
|
127
|
+
<Slider
|
|
128
|
+
value={[zoom]}
|
|
129
|
+
onValueChange={(value) => setZoom(value[0])}
|
|
130
|
+
min={1}
|
|
131
|
+
max={3}
|
|
132
|
+
step={0.1}
|
|
133
|
+
className="w-full"
|
|
134
|
+
/>
|
|
135
|
+
</div>
|
|
136
|
+
</div>
|
|
137
|
+
<DialogFooter>
|
|
138
|
+
<Button
|
|
139
|
+
variant="secondary"
|
|
140
|
+
onClick={onClose}
|
|
141
|
+
disabled={isSaving}
|
|
142
|
+
className="rounded-none"
|
|
143
|
+
>
|
|
144
|
+
Cancel
|
|
145
|
+
</Button>
|
|
146
|
+
<Button
|
|
147
|
+
onClick={handleSave}
|
|
148
|
+
disabled={isSaving}
|
|
149
|
+
className="rounded-none"
|
|
150
|
+
>
|
|
151
|
+
{isSaving ? "Saving..." : "OK"}
|
|
152
|
+
</Button>
|
|
153
|
+
</DialogFooter>
|
|
154
|
+
</DialogContent>
|
|
155
|
+
</Dialog>
|
|
156
|
+
)
|
|
157
|
+
}
|
package/components/ui/index.ts
CHANGED
|
@@ -11,8 +11,12 @@ export { Tooltip, TooltipTrigger, TooltipContent, TooltipProvider } from './tool
|
|
|
11
11
|
export { PageHeader } from './page-header'
|
|
12
12
|
export { ChartContainer, ChartTooltip, ChartTooltipContent, ChartLegend, ChartLegendContent, type ChartConfig } from './chart'
|
|
13
13
|
export { SearchInput } from './search-input'
|
|
14
|
+
export { AssetsHeader } from './assets-header'
|
|
15
|
+
export { AssetLogo, getAssetIcon, getAssetColor } from './asset-logo'
|
|
16
|
+
export { EditableTitle } from './editable-title'
|
|
17
|
+
export { ImageCropDialog } from './image-crop-dialog'
|
|
14
18
|
// Note: The following components are not exported as they depend on app-specific services or models:
|
|
15
|
-
// -
|
|
19
|
+
// - CertificateEditor, EnvironmentSettings, EnvironmentVariablesConfig
|
|
16
20
|
// - FileImport, ProcessStatus, ResourceSettings, SecretExplorer, SecretPropertiesEditor, SelectedAsset
|
|
17
21
|
export { Accordion, AccordionItem, AccordionTrigger, AccordionContent } from './accordion'
|
|
18
22
|
export { AlertDialog, AlertDialogTrigger, AlertDialogContent, AlertDialogHeader, AlertDialogFooter, AlertDialogTitle, AlertDialogDescription, AlertDialogAction, AlertDialogCancel } from './alert-dialog'
|
package/package.json
CHANGED
|
@@ -1,94 +1,95 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "@orsetra/shared-ui",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Shared UI components for Orsetra platform",
|
|
5
|
-
"main": "./index.ts",
|
|
6
|
-
"types": "./index.ts",
|
|
7
|
-
"exports": {
|
|
8
|
-
".": "./index.ts",
|
|
9
|
-
"./components/ui": "./components/ui/index.ts",
|
|
10
|
-
"./components/layout": "./components/layout/index.ts",
|
|
11
|
-
"./hooks": "./hooks/index.ts",
|
|
12
|
-
"./lib/*": "./lib/*.ts"
|
|
13
|
-
},
|
|
14
|
-
"files": [
|
|
15
|
-
"index.ts",
|
|
16
|
-
"components",
|
|
17
|
-
"hooks",
|
|
18
|
-
"lib",
|
|
19
|
-
"README.md"
|
|
20
|
-
],
|
|
21
|
-
"publishConfig": {
|
|
22
|
-
"access": "public"
|
|
23
|
-
},
|
|
24
|
-
"repository": {
|
|
25
|
-
"type": "git",
|
|
26
|
-
"url": "https://github.com/orsetra/console-ui.git",
|
|
27
|
-
"directory": "packages/shared-ui"
|
|
28
|
-
},
|
|
29
|
-
"keywords": [
|
|
30
|
-
"react",
|
|
31
|
-
"components",
|
|
32
|
-
"ui",
|
|
33
|
-
"orsetra",
|
|
34
|
-
"radix-ui",
|
|
35
|
-
"tailwind"
|
|
36
|
-
],
|
|
37
|
-
"peerDependencies": {
|
|
38
|
-
"react": "^18.0.0 || ^19.0.0",
|
|
39
|
-
"react-dom": "^18.0.0 || ^19.0.0",
|
|
40
|
-
"next": "^14.0.0 || ^15.0.0"
|
|
41
|
-
},
|
|
42
|
-
"dependencies": {
|
|
43
|
-
"react-avatar": "^5.0.3",
|
|
44
|
-
"react-
|
|
45
|
-
"react-
|
|
46
|
-
"
|
|
47
|
-
"
|
|
48
|
-
"
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"react
|
|
53
|
-
"
|
|
54
|
-
"
|
|
55
|
-
"
|
|
56
|
-
"
|
|
57
|
-
"
|
|
58
|
-
"
|
|
59
|
-
"
|
|
60
|
-
"
|
|
61
|
-
"
|
|
62
|
-
"@radix-ui/react-
|
|
63
|
-
"@radix-ui/react-
|
|
64
|
-
"@radix-ui/react-
|
|
65
|
-
"@radix-ui/react-
|
|
66
|
-
"@radix-ui/react-
|
|
67
|
-
"@radix-ui/react-
|
|
68
|
-
"@radix-ui/react-
|
|
69
|
-
"@radix-ui/react-
|
|
70
|
-
"@radix-ui/react-
|
|
71
|
-
"@radix-ui/react-
|
|
72
|
-
"@radix-ui/react-
|
|
73
|
-
"@radix-ui/react-
|
|
74
|
-
"@radix-ui/react-
|
|
75
|
-
"@radix-ui/react-
|
|
76
|
-
"@radix-ui/react-
|
|
77
|
-
"@radix-ui/react-
|
|
78
|
-
"@radix-ui/react-
|
|
79
|
-
"@radix-ui/react-
|
|
80
|
-
"@radix-ui/react-
|
|
81
|
-
"@radix-ui/react-
|
|
82
|
-
"@radix-ui/react-
|
|
83
|
-
"@radix-ui/react-
|
|
84
|
-
"@radix-ui/react-
|
|
85
|
-
"@radix-ui/react-
|
|
86
|
-
"@radix-ui/react-toggle
|
|
87
|
-
"@radix-ui/react-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
|
|
94
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "@orsetra/shared-ui",
|
|
3
|
+
"version": "1.0.13",
|
|
4
|
+
"description": "Shared UI components for Orsetra platform",
|
|
5
|
+
"main": "./index.ts",
|
|
6
|
+
"types": "./index.ts",
|
|
7
|
+
"exports": {
|
|
8
|
+
".": "./index.ts",
|
|
9
|
+
"./components/ui": "./components/ui/index.ts",
|
|
10
|
+
"./components/layout": "./components/layout/index.ts",
|
|
11
|
+
"./hooks": "./hooks/index.ts",
|
|
12
|
+
"./lib/*": "./lib/*.ts"
|
|
13
|
+
},
|
|
14
|
+
"files": [
|
|
15
|
+
"index.ts",
|
|
16
|
+
"components",
|
|
17
|
+
"hooks",
|
|
18
|
+
"lib",
|
|
19
|
+
"README.md"
|
|
20
|
+
],
|
|
21
|
+
"publishConfig": {
|
|
22
|
+
"access": "public"
|
|
23
|
+
},
|
|
24
|
+
"repository": {
|
|
25
|
+
"type": "git",
|
|
26
|
+
"url": "https://github.com/orsetra/console-ui.git",
|
|
27
|
+
"directory": "packages/shared-ui"
|
|
28
|
+
},
|
|
29
|
+
"keywords": [
|
|
30
|
+
"react",
|
|
31
|
+
"components",
|
|
32
|
+
"ui",
|
|
33
|
+
"orsetra",
|
|
34
|
+
"radix-ui",
|
|
35
|
+
"tailwind"
|
|
36
|
+
],
|
|
37
|
+
"peerDependencies": {
|
|
38
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
39
|
+
"react-dom": "^18.0.0 || ^19.0.0",
|
|
40
|
+
"next": "^14.0.0 || ^15.0.0"
|
|
41
|
+
},
|
|
42
|
+
"dependencies": {
|
|
43
|
+
"react-avatar": "^5.0.3",
|
|
44
|
+
"react-easy-crop": "^5.0.8",
|
|
45
|
+
"react-hook-form": "^7.54.0",
|
|
46
|
+
"react-resizable-panels": "^2.1.7",
|
|
47
|
+
"@hookform/resolvers": "^3.9.1",
|
|
48
|
+
"zod": "^3.24.1",
|
|
49
|
+
"clsx": "^2.1.1",
|
|
50
|
+
"tailwind-merge": "^2.5.5",
|
|
51
|
+
"class-variance-authority": "^0.7.1",
|
|
52
|
+
"lucide-react": "^0.454.0",
|
|
53
|
+
"react-day-picker": "8.10.1",
|
|
54
|
+
"embla-carousel-react": "8.5.1",
|
|
55
|
+
"cmdk": "1.0.4",
|
|
56
|
+
"recharts": "^2.15.0",
|
|
57
|
+
"date-fns": "4.1.0",
|
|
58
|
+
"input-otp": "1.4.1",
|
|
59
|
+
"vaul": "^1.1.1",
|
|
60
|
+
"next-themes": "^0.4.4",
|
|
61
|
+
"sonner": "^1.7.1",
|
|
62
|
+
"@radix-ui/react-accordion": "1.2.2",
|
|
63
|
+
"@radix-ui/react-alert-dialog": "1.1.4",
|
|
64
|
+
"@radix-ui/react-aspect-ratio": "1.1.1",
|
|
65
|
+
"@radix-ui/react-avatar": "1.1.2",
|
|
66
|
+
"@radix-ui/react-checkbox": "1.1.3",
|
|
67
|
+
"@radix-ui/react-collapsible": "1.1.2",
|
|
68
|
+
"@radix-ui/react-context-menu": "2.2.4",
|
|
69
|
+
"@radix-ui/react-dialog": "1.1.4",
|
|
70
|
+
"@radix-ui/react-dropdown-menu": "2.1.4",
|
|
71
|
+
"@radix-ui/react-hover-card": "1.1.4",
|
|
72
|
+
"@radix-ui/react-label": "2.1.1",
|
|
73
|
+
"@radix-ui/react-menubar": "1.1.4",
|
|
74
|
+
"@radix-ui/react-navigation-menu": "1.2.3",
|
|
75
|
+
"@radix-ui/react-popover": "1.1.4",
|
|
76
|
+
"@radix-ui/react-progress": "1.1.1",
|
|
77
|
+
"@radix-ui/react-radio-group": "1.2.2",
|
|
78
|
+
"@radix-ui/react-scroll-area": "1.2.2",
|
|
79
|
+
"@radix-ui/react-select": "2.1.4",
|
|
80
|
+
"@radix-ui/react-separator": "1.1.1",
|
|
81
|
+
"@radix-ui/react-slider": "1.2.2",
|
|
82
|
+
"@radix-ui/react-slot": "1.1.1",
|
|
83
|
+
"@radix-ui/react-switch": "1.1.2",
|
|
84
|
+
"@radix-ui/react-tabs": "1.1.2",
|
|
85
|
+
"@radix-ui/react-toast": "1.2.4",
|
|
86
|
+
"@radix-ui/react-toggle": "1.1.1",
|
|
87
|
+
"@radix-ui/react-toggle-group": "1.1.1",
|
|
88
|
+
"@radix-ui/react-tooltip": "1.1.6"
|
|
89
|
+
},
|
|
90
|
+
"devDependencies": {
|
|
91
|
+
"@types/react": "^19",
|
|
92
|
+
"next": "^15.0.0",
|
|
93
|
+
"typescript": "^5"
|
|
94
|
+
}
|
|
95
|
+
}
|