Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 53 additions & 9 deletions apps/web/src/components/editor/media-panel/views/stickers.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -111,16 +111,22 @@ function StickerGrid({
icons,
onAdd,
addingSticker,
capSize = false,
}: {
icons: string[];
onAdd: (iconName: string) => void;
addingSticker: string | null;
capSize?: boolean;
}) {
return (
<div
className="grid gap-2"
style={{
gridTemplateColumns: "repeat(auto-fill, 112px)",
gridTemplateColumns: capSize
? "repeat(auto-fill, minmax(var(--sticker-min, 96px), var(--sticker-max, 160px)))"
: "repeat(auto-fit, minmax(var(--sticker-min, 96px), 1fr))",
["--sticker-min" as any]: "96px",
...(capSize ? ({ ["--sticker-max"]: "160px" } as any) : {}),
Comment on lines +128 to +129
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

CSS custom properties are using as any type casting, bypassing TypeScript's type safety when a proper typed approach is available in the codebase.

View Details
📝 Patch Details
diff --git a/apps/web/src/components/editor/media-panel/views/stickers.tsx b/apps/web/src/components/editor/media-panel/views/stickers.tsx
index 7d3e073..e4ddcef 100644
--- a/apps/web/src/components/editor/media-panel/views/stickers.tsx
+++ b/apps/web/src/components/editor/media-panel/views/stickers.tsx
@@ -125,9 +125,9 @@ function StickerGrid({
         gridTemplateColumns: capSize
           ? "repeat(auto-fill, minmax(var(--sticker-min, 96px), var(--sticker-max, 160px)))"
           : "repeat(auto-fit, minmax(var(--sticker-min, 96px), 1fr))",
-        ["--sticker-min" as any]: "96px",
-        ...(capSize ? ({ ["--sticker-max"]: "160px" } as any) : {}),
-      }}
+        "--sticker-min": "96px",
+        ...(capSize ? { "--sticker-max": "160px" } : {}),
+      } as React.CSSProperties}
     >
       {icons.map((iconName) => (
         <StickerItem

Analysis

The code uses as any type casting for CSS custom properties on lines 128-129:

[\"--sticker-min\" as any]: \"96px\",
...(capSize ? ({ [\"--sticker-max\"]: \"160px\" } as any) : {}),

However, examining other files in the codebase (like sidebar.tsx), there's a more type-safe pattern being used where CSS custom properties are properly typed by casting the entire style object as React.CSSProperties. The as any casting bypasses TypeScript's type checking and could hide potential issues, while the React.CSSProperties approach maintains type safety for the style object structure.

This inconsistency in typing patterns could lead to maintenance issues and reduced type safety across the codebase.


Recommendation

Remove the individual as any castings and instead cast the entire style object as React.CSSProperties, following the pattern used elsewhere in the codebase:

style={{
  gridTemplateColumns: capSize
    ? "repeat(auto-fill, minmax(var(--sticker-min, 96px), var(--sticker-max, 160px)))"
    : "repeat(auto-fit, minmax(var(--sticker-min, 96px), 1fr))",
  "--sticker-min": "96px",
  ...(capSize ? { "--sticker-max": "160px" } : {}),
} as React.CSSProperties}

}}
>
{icons.map((iconName) => (
Expand All @@ -129,6 +135,7 @@ function StickerGrid({
iconName={iconName}
onAdd={onAdd}
isAdding={addingSticker === iconName}
capSize={capSize}
/>
))}
</div>
Expand All @@ -148,7 +155,7 @@ function CollectionGrid({
onSelectCollection: (prefix: string) => void;
}) {
return (
<div className="grid grid-cols-1 gap-2 h-full overflow-hidden">
<div className="grid grid-cols-1 gap-2">
{collections.map((collection) => (
<CollectionItem
key={collection.prefix}
Expand Down Expand Up @@ -198,6 +205,7 @@ function StickersContentView({ category }: { category: StickerCategory }) {
searchStickers,
downloadSticker,
clearRecentStickers,
setSelectedCategory,
} = useStickersStore();
Comment on lines 206 to 209
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Good integration of setSelectedCategory; add type="button" and align wording with “All Collections”

  • The store setter is correctly wired to enable switching to the All tab.
  • Add type="button" to the new Button to satisfy the button-type guideline and avoid accidental form submission if placed inside a form.
  • Optional: Align the CTA text with the PR language (“All Collections”) for consistency.

Apply this diff:

-                    {category !== "all" && (
+                    {category !== "all" && (
                       <Button
                         variant="outline"
+                        type="button"
                         onClick={() => {
                           const q = localSearchQuery || searchQuery;
                           if (q) {
                             setSearchQuery(q);
                           }
                           setSelectedCategory("all");
                           if (q) {
                             searchStickers(q);
                           }
                         }}
                       >
-                        Search in all icons
+                        Search in all collections
                       </Button>
                     )}

Also applies to: 470-486

🤖 Prompt for AI Agents
In apps/web/src/components/editor/media-panel/views/stickers.tsx around lines
206-209 (and similarly at lines 470-486), the new Button that triggers
setSelectedCategory is missing an explicit type and the CTA text differs from
the PR language; update the Button element(s) to include type="button" to
prevent accidental form submission and change the button label text to "All
Collections" to match the PR wording, preserving the existing onClick handler
that calls setSelectedCategory.


const [addingSticker, setAddingSticker] = useState<string | null>(null);
Expand Down Expand Up @@ -355,15 +363,23 @@ function StickersContentView({ category }: { category: StickerCategory }) {

return (
<div className="flex flex-col gap-5 mt-1 h-full">
<div className="space-y-3">
<div className="space-y-3 pl-2">
<InputWithBack
isExpanded={isInCollection}
setIsExpanded={(expanded) => {
if (!expanded && isInCollection) {
setSelectedCollection(null);
}
}}
placeholder="Search icons..."
placeholder={
category === "all"
? "Search all stickers"
: category === "general"
? "Search icons"
: category === "brands"
? "Search brands"
: "Search Emojis"
}
value={localSearchQuery}
onChange={setLocalSearchQuery}
/>
Expand All @@ -375,7 +391,7 @@ function StickersContentView({ category }: { category: StickerCategory }) {
ref={scrollAreaRef}
onScrollCapture={handleScroll}
>
<div className="flex flex-col gap-4 h-full">
<div className="flex flex-col gap-4 h-full px-2">
{recentStickers.length > 0 && viewMode === "browse" && (
<div className="h-full">
<div className="flex items-center gap-2 mb-2">
Expand All @@ -401,6 +417,7 @@ function StickersContentView({ category }: { category: StickerCategory }) {
icons={recentStickers.slice(0, 12)}
onAdd={handleAddSticker}
addingSticker={addingSticker}
capSize
/>
</div>
)}
Expand Down Expand Up @@ -442,12 +459,32 @@ function StickersContentView({ category }: { category: StickerCategory }) {
icons={iconsToDisplay}
onAdd={handleAddSticker}
addingSticker={addingSticker}
capSize
/>
</>
) : searchQuery ? (
<EmptyView
message={`No stickers found for "${searchQuery}"`}
/>
<div className="flex flex-col items-center justify-center py-8 gap-3">
<EmptyView
message={`No stickers found for "${searchQuery}"`}
/>
{category !== "all" && (
<Button
variant="outline"
onClick={() => {
const q = localSearchQuery || searchQuery;
if (q) {
setSearchQuery(q);
}
setSelectedCategory("all");
if (q) {
searchStickers(q);
}
}}
>
Search in all icons
</Button>
)}
</div>
) : null}
</div>
)}
Expand Down Expand Up @@ -525,9 +562,10 @@ interface StickerItemProps {
iconName: string;
onAdd: (iconName: string) => void;
isAdding?: boolean;
capSize?: boolean;
}

function StickerItem({ iconName, onAdd, isAdding }: StickerItemProps) {
function StickerItem({ iconName, onAdd, isAdding, capSize = false }: StickerItemProps) {
const [imageError, setImageError] = useState(false);
const [hostIndex, setHostIndex] = useState(0);

Expand Down Expand Up @@ -561,6 +599,11 @@ function StickerItem({ iconName, onAdd, isAdding }: StickerItemProps) {
width={64}
height={64}
className="w-full h-full object-contain"
style={
capSize
? { maxWidth: "var(--sticker-max, 160px)", maxHeight: "var(--sticker-max, 160px)" }
: undefined
}
onError={() => {
const next = hostIndex + 1;
if (next < ICONIFY_HOSTS.length) {
Expand Down Expand Up @@ -598,6 +641,7 @@ function StickerItem({ iconName, onAdd, isAdding }: StickerItemProps) {
rounded={true}
variant="card"
className=""
containerClassName="w-full"
isDraggable={false}
/>
{isAdding && (
Expand Down
7 changes: 6 additions & 1 deletion apps/web/src/components/ui/draggable-item.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ export interface DraggableMediaItemProps {
onAddToTimeline?: (currentTime: number) => void;
aspectRatio?: number;
className?: string;
containerClassName?: string;
showPlusOnDrag?: boolean;
showLabel?: boolean;
rounded?: boolean;
Expand All @@ -36,6 +37,7 @@ export function DraggableMediaItem({
onAddToTimeline,
aspectRatio = 16 / 9,
className = "",
containerClassName,
showPlusOnDrag = true,
showLabel = true,
rounded = true,
Expand Down Expand Up @@ -95,7 +97,10 @@ export function DraggableMediaItem({
return (
<>
{variant === "card" ? (
<div ref={dragRef} className="relative group w-28 h-28">
<div
ref={dragRef}
className={cn("relative group", containerClassName ?? "w-28 h-28")}
>
<div
className={`flex flex-col gap-1 p-0 h-auto w-full relative cursor-default ${className}`}
>
Expand Down
Loading