betterstart-cli 0.0.17 → 0.0.19
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.
- package/dist/assets/adapters/next/plugins/blog/schemas/menus.json +1 -0
- package/dist/assets/adapters/next/plugins/blog/schemas/posts.json +1 -0
- package/dist/assets/adapters/next/plugins/portfolio/plugin.ts +17 -0
- package/dist/assets/adapters/next/plugins/portfolio/schemas/portfolio.json +128 -0
- package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/dev-mode-types.ts +2 -2
- package/dist/assets/adapters/next/templates/init/components/shared/dev-mode/snippets-tab.tsx +1 -0
- package/dist/assets/adapters/next/templates/init/components/shared/dev-mode-integrate.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/media/media-delete-dialog.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/media/media-delete-drawer.tsx +1 -1
- package/dist/assets/adapters/next/templates/init/components/shared/media/media-grid-item.tsx +17 -7
- package/dist/assets/adapters/next/templates/init/components/shared/media/media-preview.tsx +1 -0
- package/dist/assets/adapters/next/templates/init/data/navigation.ts +2 -1
- package/dist/assets/adapters/next/templates/init/hooks/content-editor/use-content-editor-source-mode.tsx +3 -3
- package/dist/assets/adapters/next/templates/init/hooks/use-media.ts +2 -1
- package/dist/assets/adapters/next/templates/init/pages/profile/profile-form.tsx +3 -3
- package/dist/assets/shared-assets/react-admin/custom/content-editor/media-gallery-block.tsx +2 -2
- package/dist/assets/shared-assets/react-admin/custom/content-editor/table-add-controls.tsx +2 -2
- package/dist/assets/shared-assets/react-admin/custom/gallery-field.tsx +219 -40
- package/dist/assets/shared-assets/react-admin/custom/media-field-empty-state.tsx +99 -0
- package/dist/assets/shared-assets/react-admin/custom/media-field.tsx +181 -0
- package/dist/assets/shared-assets/react-admin/custom/sortable-gallery-item.tsx +38 -38
- package/dist/assets/shared-assets/react-admin/schema.json +64 -2
- package/dist/assets/shared-assets/react-admin/ui/pagination.tsx +1 -1
- package/dist/cli.js +474 -143
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
- package/dist/assets/shared-assets/react-admin/custom/gallery-thumbnail.tsx +0 -39
- package/dist/assets/shared-assets/react-admin/custom/media-gallery-field.tsx +0 -182
package/package.json
CHANGED
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import type { AdminMedia } from '@admin/types'
|
|
2
|
-
import { FileText, Film } from 'lucide-react'
|
|
3
|
-
import Image from 'next/image'
|
|
4
|
-
|
|
5
|
-
export function GalleryThumbnail({ media }: { media: AdminMedia }) {
|
|
6
|
-
if (media.contentType.startsWith('image/')) {
|
|
7
|
-
return (
|
|
8
|
-
<Image
|
|
9
|
-
src={media.url}
|
|
10
|
-
alt={media.alt || media.filename}
|
|
11
|
-
fill
|
|
12
|
-
sizes="(max-width: 640px) 33vw, (max-width: 768px) 25vw, 160px"
|
|
13
|
-
className="object-cover"
|
|
14
|
-
loading="lazy"
|
|
15
|
-
draggable={false}
|
|
16
|
-
/>
|
|
17
|
-
)
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
if (media.contentType.startsWith('video/')) {
|
|
21
|
-
return (
|
|
22
|
-
<div className="flex size-full flex-col items-center justify-center gap-1">
|
|
23
|
-
<Film className="size-6 text-foreground" />
|
|
24
|
-
<span className="max-w-full truncate px-1 text-[9px] text-muted-foreground">
|
|
25
|
-
{media.filename}
|
|
26
|
-
</span>
|
|
27
|
-
</div>
|
|
28
|
-
)
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return (
|
|
32
|
-
<div className="flex size-full flex-col items-center justify-center gap-1">
|
|
33
|
-
<FileText className="size-6 text-foreground" />
|
|
34
|
-
<span className="max-w-full truncate px-1 text-[9px] text-muted-foreground">
|
|
35
|
-
{media.filename}
|
|
36
|
-
</span>
|
|
37
|
-
</div>
|
|
38
|
-
)
|
|
39
|
-
}
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
'use client'
|
|
2
|
-
|
|
3
|
-
import { MediaGalleryDialog } from '@admin/components/shared/media/media-gallery-dialog'
|
|
4
|
-
import { MediaPreview } from '@admin/components/shared/media/media-preview'
|
|
5
|
-
import { Button } from '@admin/components/ui/button'
|
|
6
|
-
import { Card, CardContent, CardFooter } from '@admin/components/ui/card'
|
|
7
|
-
import { useCopyToClipboard } from '@admin/hooks/use-copy-to-clipboard'
|
|
8
|
-
import { useMediaById } from '@admin/hooks/use-media'
|
|
9
|
-
import type { AdminMedia } from '@admin/types'
|
|
10
|
-
import { createAdminMediaFromUrl } from '@admin/utils/media/fallback'
|
|
11
|
-
import { cn } from '@admin/utils/shared/cn'
|
|
12
|
-
import { Check, Copy, ImageIcon, Link2 } from 'lucide-react'
|
|
13
|
-
import Link from 'next/link'
|
|
14
|
-
import * as React from 'react'
|
|
15
|
-
|
|
16
|
-
type MediaGalleryFieldValueMode = 'id' | 'url'
|
|
17
|
-
|
|
18
|
-
interface MediaGalleryFieldProps {
|
|
19
|
-
value?: string
|
|
20
|
-
onChange: (value: string) => void
|
|
21
|
-
onBlur?: () => void
|
|
22
|
-
onRemove?: () => void
|
|
23
|
-
accept?: string
|
|
24
|
-
disabled?: boolean
|
|
25
|
-
className?: string
|
|
26
|
-
isRemovable?: boolean
|
|
27
|
-
valueMode?: MediaGalleryFieldValueMode
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export function MediaGalleryField({
|
|
31
|
-
value,
|
|
32
|
-
onChange,
|
|
33
|
-
onBlur,
|
|
34
|
-
onRemove,
|
|
35
|
-
accept,
|
|
36
|
-
disabled,
|
|
37
|
-
className,
|
|
38
|
-
isRemovable = false,
|
|
39
|
-
valueMode = 'id'
|
|
40
|
-
}: MediaGalleryFieldProps) {
|
|
41
|
-
const [open, setOpen] = React.useState(false)
|
|
42
|
-
const { data: media } = useMediaById(valueMode === 'id' ? value : undefined)
|
|
43
|
-
const [copyMediaUrl, hasCopiedMediaUrl] = useCopyToClipboard()
|
|
44
|
-
const selectedMedia =
|
|
45
|
-
media ?? (valueMode === 'url' && value ? createAdminMediaFromUrl(value, accept) : null)
|
|
46
|
-
|
|
47
|
-
function handleSelect(selected: AdminMedia | AdminMedia[]) {
|
|
48
|
-
const item = Array.isArray(selected) ? selected[0] : selected
|
|
49
|
-
if (item) {
|
|
50
|
-
onChange(valueMode === 'url' ? item.url : item.id)
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function handleRemove(e: React.MouseEvent) {
|
|
55
|
-
e.preventDefault()
|
|
56
|
-
e.stopPropagation()
|
|
57
|
-
if (isRemovable && onRemove) {
|
|
58
|
-
onRemove()
|
|
59
|
-
return
|
|
60
|
-
}
|
|
61
|
-
onChange('')
|
|
62
|
-
onBlur?.()
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
function handleAddImage(e: React.MouseEvent) {
|
|
66
|
-
e.preventDefault()
|
|
67
|
-
e.stopPropagation()
|
|
68
|
-
setOpen(true)
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
if (selectedMedia?.url) {
|
|
72
|
-
return (
|
|
73
|
-
<div className={cn('group relative', className)}>
|
|
74
|
-
<Card className="p-0">
|
|
75
|
-
<CardContent className="p-0! relative">
|
|
76
|
-
<div className="relative aspect-video rounded-lg bg-background overflow-hidden">
|
|
77
|
-
<MediaPreview media={selectedMedia} showMetadata={Boolean(media)} />
|
|
78
|
-
</div>
|
|
79
|
-
|
|
80
|
-
{!disabled && (
|
|
81
|
-
<>
|
|
82
|
-
<div
|
|
83
|
-
aria-hidden="true"
|
|
84
|
-
className="pointer-events-none absolute inset-0 bg-black/10 opacity-0 backdrop-blur-xs transition-opacity duration-300 ease-out group-hover:opacity-100 rounded-lg"
|
|
85
|
-
/>
|
|
86
|
-
<div className="absolute inset-0 flex items-center justify-center gap-1 opacity-0 transition-opacity duration-200 ease-out group-hover:opacity-100">
|
|
87
|
-
<Button
|
|
88
|
-
type="button"
|
|
89
|
-
variant="outline"
|
|
90
|
-
onClick={() => setOpen(true)}
|
|
91
|
-
className="border-primary/10"
|
|
92
|
-
>
|
|
93
|
-
Change
|
|
94
|
-
</Button>
|
|
95
|
-
<Button type="button" variant="default" onClick={handleRemove}>
|
|
96
|
-
Remove
|
|
97
|
-
</Button>
|
|
98
|
-
</div>
|
|
99
|
-
</>
|
|
100
|
-
)}
|
|
101
|
-
</CardContent>
|
|
102
|
-
<CardFooter className="grid w-full gap-0.5 px-4">
|
|
103
|
-
<div className="flex w-full min-w-0 items-center gap-x-2">
|
|
104
|
-
<div className="flex min-w-0 flex-1 items-center gap-x-2 text-muted-foreground">
|
|
105
|
-
<Link2 className="size-4 shrink-0" />
|
|
106
|
-
<Link
|
|
107
|
-
href={selectedMedia.url}
|
|
108
|
-
target="_blank"
|
|
109
|
-
rel="noopener noreferrer"
|
|
110
|
-
className="min-w-0 truncate text-foreground hover:underline underline-offset-4"
|
|
111
|
-
>
|
|
112
|
-
{selectedMedia.url}
|
|
113
|
-
</Link>
|
|
114
|
-
</div>
|
|
115
|
-
<Button
|
|
116
|
-
type="button"
|
|
117
|
-
aria-label={hasCopiedMediaUrl ? 'Media URL copied' : 'Copy media URL'}
|
|
118
|
-
variant="ghost"
|
|
119
|
-
title={hasCopiedMediaUrl ? 'Copied' : 'Copy media URL'}
|
|
120
|
-
onClick={() => copyMediaUrl(selectedMedia.url)}
|
|
121
|
-
className="shrink-0 size-7"
|
|
122
|
-
>
|
|
123
|
-
{hasCopiedMediaUrl ? <Check className="text-foreground" /> : <Copy />}
|
|
124
|
-
</Button>
|
|
125
|
-
</div>
|
|
126
|
-
</CardFooter>
|
|
127
|
-
</Card>
|
|
128
|
-
|
|
129
|
-
<MediaGalleryDialog
|
|
130
|
-
open={open}
|
|
131
|
-
onOpenChange={setOpen}
|
|
132
|
-
onSelect={handleSelect}
|
|
133
|
-
mode="single"
|
|
134
|
-
accept={accept}
|
|
135
|
-
/>
|
|
136
|
-
</div>
|
|
137
|
-
)
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
return (
|
|
141
|
-
<div className={className}>
|
|
142
|
-
<Card
|
|
143
|
-
className={cn('group p-0 gap-0', {
|
|
144
|
-
'cursor-not-allowed opacity-50 pointer-events-none': disabled
|
|
145
|
-
})}
|
|
146
|
-
onClick={() => !disabled && setOpen(true)}
|
|
147
|
-
>
|
|
148
|
-
<CardContent className="relative p-0 justify-center items-center flex flex-col h-40 hover:bg-muted/50 transition-colors duration-200">
|
|
149
|
-
<ImageIcon
|
|
150
|
-
className={cn('text-muted-foreground transition-opacity', {
|
|
151
|
-
'group-hover:opacity-0 group-focus-within:opacity-0': isRemovable && !disabled
|
|
152
|
-
})}
|
|
153
|
-
strokeWidth={1}
|
|
154
|
-
/>
|
|
155
|
-
{isRemovable && !disabled && (
|
|
156
|
-
<div className="absolute inset-0 flex items-center justify-center gap-1 opacity-0 transition-opacity duration-200 ease-out group-hover:opacity-100 group-focus-within:opacity-100">
|
|
157
|
-
<Button
|
|
158
|
-
type="button"
|
|
159
|
-
variant="outline"
|
|
160
|
-
onClick={handleAddImage}
|
|
161
|
-
className="border-primary/10"
|
|
162
|
-
>
|
|
163
|
-
Add Image
|
|
164
|
-
</Button>
|
|
165
|
-
<Button type="button" variant="default" onClick={handleRemove}>
|
|
166
|
-
Remove
|
|
167
|
-
</Button>
|
|
168
|
-
</div>
|
|
169
|
-
)}
|
|
170
|
-
</CardContent>
|
|
171
|
-
</Card>
|
|
172
|
-
|
|
173
|
-
<MediaGalleryDialog
|
|
174
|
-
open={open}
|
|
175
|
-
onOpenChange={setOpen}
|
|
176
|
-
onSelect={handleSelect}
|
|
177
|
-
mode="single"
|
|
178
|
-
accept={accept}
|
|
179
|
-
/>
|
|
180
|
-
</div>
|
|
181
|
-
)
|
|
182
|
-
}
|