Skip to content

Commit 58c8660

Browse files
fix: restore cover picker tab strip to full width
Published TabsList is inline-flex, so Images/Upload hugged content instead of spanning the 36rem panel. Stretch the list to the previous full-width segmented bar.
1 parent 71a592c commit 58c8660

1 file changed

Lines changed: 164 additions & 161 deletions

File tree

apps/web/core/components/core/image-picker-popover.tsx

Lines changed: 164 additions & 161 deletions
Original file line numberDiff line numberDiff line change
@@ -204,177 +204,180 @@ function ImagePickerPopoverComponent<TFieldValues extends FieldValues = FieldVal
204204
>
205205
<div
206206
ref={imagePickerRef}
207-
className="flex h-96 w-80 flex-col overflow-auto rounded border border-subtle bg-surface-1 shadow-raised-200 md:h-[36rem] md:w-[36rem]"
207+
className="flex h-96 w-80 flex-col overflow-hidden rounded border border-subtle bg-surface-1 shadow-raised-200 md:h-[36rem] md:w-[36rem]"
208208
>
209-
{/* Row wrapper: Propel's Tabs root omits className, so it takes its full height by
210-
stretching as a flex item. */}
211-
<div className="flex h-full p-3">
209+
{/* Row wrapper: published Tabs omits className, so fill height comes from stretch. */}
210+
<div className="flex h-full min-h-0 w-full p-3">
212211
<Tabs variant="contained" defaultValue={enabledTabs[0]?.key || "images"}>
213-
<TabsList>
214-
{enabledTabs.map((tab) => (
215-
<Tab key={tab.key} value={tab.key} label={tab.title} />
216-
))}
217-
</TabsList>
218-
{/* Grid wrapper: Propel's TabsPanel omits className, so the single mounted panel
219-
gets its fill height from a one-row grid instead. */}
220-
<div className="vertical-scrollbar scrollbar-sm grid min-h-0 w-full flex-1 grid-rows-1 overflow-x-hidden overflow-y-auto p-3">
221-
<TabsPanel value="unsplash">
222-
<div className="space-y-4">
223-
{(unsplashImages || !unsplashError) && (
224-
<>
225-
<div className="flex items-center gap-x-2">
226-
<Controller
227-
control={control}
228-
name={"search" as FieldPath<TFieldValues>}
229-
render={({ field: { value, ref } }) => (
230-
<InputGroup size="2xl">
231-
<Input
232-
size="2xl"
233-
id="search"
234-
name="search"
235-
type="text"
236-
onKeyDown={(e) => {
237-
if (e.key === "Enter") {
238-
e.preventDefault();
239-
setSearchParams(formData.search);
240-
}
241-
}}
242-
value={value}
243-
onChange={(e) => setFormData({ ...formData, search: e.target.value })}
244-
ref={ref}
245-
placeholder="Search for images"
246-
/>
247-
</InputGroup>
248-
)}
249-
/>
250-
<Button variant="primary" size="xl" onClick={() => setSearchParams(formData.search)}>
251-
Search
252-
</Button>
253-
</div>
254-
{unsplashImages ? (
255-
unsplashImages.length > 0 ? (
256-
<div className="grid grid-cols-4 gap-4">
257-
{unsplashImages.map((image) => (
258-
<div
259-
key={image.id}
260-
className="relative col-span-2 aspect-video md:col-span-1"
261-
onClick={() => {
262-
setIsOpen(false);
263-
onChange(image.urls.regular);
264-
}}
265-
>
266-
<img
267-
src={image.urls.small}
268-
alt={image.alt_description}
269-
className="absolute top-0 left-0 h-full w-full cursor-pointer rounded-sm object-cover"
270-
/>
271-
</div>
272-
))}
273-
</div>
274-
) : (
275-
<p className="pt-7 text-center text-11 text-secondary">No images found.</p>
276-
)
277-
) : (
278-
<Loader className="grid grid-cols-4 gap-4">
279-
<Loader.Item height="80px" width="100%" />
280-
<Loader.Item height="80px" width="100%" />
281-
<Loader.Item height="80px" width="100%" />
282-
<Loader.Item height="80px" width="100%" />
283-
<Loader.Item height="80px" width="100%" />
284-
<Loader.Item height="80px" width="100%" />
285-
<Loader.Item height="80px" width="100%" />
286-
<Loader.Item height="80px" width="100%" />
287-
</Loader>
288-
)}
289-
</>
290-
)}
291-
</div>
292-
</TabsPanel>
293-
<TabsPanel value="images">
294-
<div className="grid grid-cols-4 gap-4">
295-
{Object.values(STATIC_COVER_IMAGES).map((imageUrl, index) => (
296-
<div
297-
key={imageUrl}
298-
className="relative col-span-2 aspect-video md:col-span-1"
299-
onClick={() => handleStaticImageSelect(imageUrl)}
300-
>
301-
<img
302-
src={imageUrl}
303-
alt={`Cover image ${index + 1}`}
304-
className="absolute top-0 left-0 h-full w-full cursor-pointer rounded-sm object-cover transition-opacity hover:opacity-80"
305-
/>
306-
</div>
212+
<div className="flex h-full min-h-0 w-full flex-col">
213+
{/* Published TabsList is inline-flex; stretch it to the previous full-width bar. */}
214+
<div className="w-full min-w-0 [&_[role=tab]]:min-w-0 [&_[role=tab]]:flex-1 [&_[role=tablist]]:flex [&_[role=tablist]]:w-full">
215+
<TabsList>
216+
{enabledTabs.map((tab) => (
217+
<Tab key={tab.key} value={tab.key} label={tab.title} />
307218
))}
308-
</div>
309-
</TabsPanel>
310-
<TabsPanel value="upload">
311-
<div className="flex h-full w-full flex-col gap-y-2">
312-
<div className="flex w-full flex-1 items-center gap-3">
313-
<div
314-
{...getRootProps()}
315-
className={`relative grid h-full w-full cursor-pointer place-items-center rounded-lg p-12 text-center focus:ring-2 focus:ring-accent-strong focus:ring-offset-2 focus:outline-none ${
316-
(image === null && isDragActive) || !value
317-
? "border-2 border-dashed border-subtle hover:bg-surface-2"
318-
: ""
319-
}`}
320-
>
321-
<button
322-
type="button"
323-
className="absolute top-0 right-0 z-40 -translate-y-1/2 rounded-sm bg-surface-2 px-2 py-0.5 text-11 font-medium text-secondary"
324-
>
325-
Edit
326-
</button>
327-
{image !== null || (value && value !== "") ? (
328-
<>
329-
<img
330-
src={image ? URL.createObjectURL(image) : getCoverImageDisplayURL(value, "")}
331-
alt="image"
332-
className="h-full w-full rounded-lg object-cover"
219+
</TabsList>
220+
</div>
221+
{/* Grid wrapper: published TabsPanel omits className, so fill height comes from a one-row grid. */}
222+
<div className="vertical-scrollbar scrollbar-sm grid min-h-0 w-full flex-1 grid-rows-1 overflow-x-hidden overflow-y-auto pt-1">
223+
<TabsPanel value="unsplash">
224+
<div className="space-y-4">
225+
{(unsplashImages || !unsplashError) && (
226+
<>
227+
<div className="flex items-center gap-x-2">
228+
<Controller
229+
control={control}
230+
name={"search" as FieldPath<TFieldValues>}
231+
render={({ field: { value, ref } }) => (
232+
<InputGroup size="2xl">
233+
<Input
234+
size="2xl"
235+
id="search"
236+
name="search"
237+
type="text"
238+
onKeyDown={(e) => {
239+
if (e.key === "Enter") {
240+
e.preventDefault();
241+
setSearchParams(formData.search);
242+
}
243+
}}
244+
value={value}
245+
onChange={(e) => setFormData({ ...formData, search: e.target.value })}
246+
ref={ref}
247+
placeholder="Search for images"
248+
/>
249+
</InputGroup>
250+
)}
333251
/>
334-
</>
335-
) : (
336-
<div>
337-
<span className="mt-2 block text-13 font-medium text-secondary">
338-
{isDragActive ? "Drop image here to upload" : "Drag & drop image here"}
339-
</span>
252+
<Button variant="primary" size="xl" onClick={() => setSearchParams(formData.search)}>
253+
Search
254+
</Button>
340255
</div>
341-
)}
256+
{unsplashImages ? (
257+
unsplashImages.length > 0 ? (
258+
<div className="grid grid-cols-4 gap-4">
259+
{unsplashImages.map((image) => (
260+
<div
261+
key={image.id}
262+
className="relative col-span-2 aspect-video md:col-span-1"
263+
onClick={() => {
264+
setIsOpen(false);
265+
onChange(image.urls.regular);
266+
}}
267+
>
268+
<img
269+
src={image.urls.small}
270+
alt={image.alt_description}
271+
className="absolute top-0 left-0 h-full w-full cursor-pointer rounded-sm object-cover"
272+
/>
273+
</div>
274+
))}
275+
</div>
276+
) : (
277+
<p className="pt-7 text-center text-11 text-secondary">No images found.</p>
278+
)
279+
) : (
280+
<Loader className="grid grid-cols-4 gap-4">
281+
<Loader.Item height="80px" width="100%" />
282+
<Loader.Item height="80px" width="100%" />
283+
<Loader.Item height="80px" width="100%" />
284+
<Loader.Item height="80px" width="100%" />
285+
<Loader.Item height="80px" width="100%" />
286+
<Loader.Item height="80px" width="100%" />
287+
<Loader.Item height="80px" width="100%" />
288+
<Loader.Item height="80px" width="100%" />
289+
</Loader>
290+
)}
291+
</>
292+
)}
293+
</div>
294+
</TabsPanel>
295+
<TabsPanel value="images">
296+
<div className="grid grid-cols-4 gap-4">
297+
{Object.values(STATIC_COVER_IMAGES).map((imageUrl, index) => (
298+
<div
299+
key={imageUrl}
300+
className="relative col-span-2 aspect-video md:col-span-1"
301+
onClick={() => handleStaticImageSelect(imageUrl)}
302+
>
303+
<img
304+
src={imageUrl}
305+
alt={`Cover image ${index + 1}`}
306+
className="absolute top-0 left-0 h-full w-full cursor-pointer rounded-sm object-cover transition-opacity hover:opacity-80"
307+
/>
308+
</div>
309+
))}
310+
</div>
311+
</TabsPanel>
312+
<TabsPanel value="upload">
313+
<div className="flex h-full w-full flex-col gap-y-2">
314+
<div className="flex w-full flex-1 items-center gap-3">
315+
<div
316+
{...getRootProps()}
317+
className={`relative grid h-full w-full cursor-pointer place-items-center rounded-lg p-12 text-center focus:ring-2 focus:ring-accent-strong focus:ring-offset-2 focus:outline-none ${
318+
(image === null && isDragActive) || !value
319+
? "border-2 border-dashed border-subtle hover:bg-surface-2"
320+
: ""
321+
}`}
322+
>
323+
<button
324+
type="button"
325+
className="absolute top-0 right-0 z-40 -translate-y-1/2 rounded-sm bg-surface-2 px-2 py-0.5 text-11 font-medium text-secondary"
326+
>
327+
Edit
328+
</button>
329+
{image !== null || (value && value !== "") ? (
330+
<>
331+
<img
332+
src={image ? URL.createObjectURL(image) : getCoverImageDisplayURL(value, "")}
333+
alt="image"
334+
className="h-full w-full rounded-lg object-cover"
335+
/>
336+
</>
337+
) : (
338+
<div>
339+
<span className="mt-2 block text-13 font-medium text-secondary">
340+
{isDragActive ? "Drop image here to upload" : "Drag & drop image here"}
341+
</span>
342+
</div>
343+
)}
342344

343-
<input {...getInputProps()} />
345+
<input {...getInputProps()} />
346+
</div>
344347
</div>
345-
</div>
346-
{fileRejections.length > 0 && (
347-
<p className="text-13 text-danger-primary">
348-
{fileRejections[0].errors[0].code === "file-too-large"
349-
? "The image size cannot exceed 5 MB."
350-
: "Please upload a file in a valid format."}
351-
</p>
352-
)}
348+
{fileRejections.length > 0 && (
349+
<p className="text-13 text-danger-primary">
350+
{fileRejections[0].errors[0].code === "file-too-large"
351+
? "The image size cannot exceed 5 MB."
352+
: "Please upload a file in a valid format."}
353+
</p>
354+
)}
353355

354-
<p className="text-13 text-secondary">File formats supported- .jpeg, .jpg, .png, .webp</p>
356+
<p className="text-13 text-secondary">File formats supported- .jpeg, .jpg, .png, .webp</p>
355357

356-
<div className="flex h-12 items-start justify-end gap-2">
357-
<Button
358-
variant="secondary"
359-
onClick={() => {
360-
setIsOpen(false);
361-
setImage(null);
362-
}}
363-
>
364-
Cancel
365-
</Button>
366-
<Button
367-
variant="primary"
368-
className="w-full"
369-
onClick={handleSubmit}
370-
disabled={!image}
371-
loading={isImageUploading}
372-
>
373-
{isImageUploading ? "Uploading" : "Upload & Save"}
374-
</Button>
358+
<div className="flex h-12 items-start justify-end gap-2">
359+
<Button
360+
variant="secondary"
361+
onClick={() => {
362+
setIsOpen(false);
363+
setImage(null);
364+
}}
365+
>
366+
Cancel
367+
</Button>
368+
<Button
369+
variant="primary"
370+
className="w-full"
371+
onClick={handleSubmit}
372+
disabled={!image}
373+
loading={isImageUploading}
374+
>
375+
{isImageUploading ? "Uploading" : "Upload & Save"}
376+
</Button>
377+
</div>
375378
</div>
376-
</div>
377-
</TabsPanel>
379+
</TabsPanel>
380+
</div>
378381
</div>
379382
</Tabs>
380383
</div>

0 commit comments

Comments
 (0)