Skip to content

Commit 5f7449a

Browse files
committed
fix: navbar + select dropdown ui fixes consistency
1 parent 157e18e commit 5f7449a

7 files changed

Lines changed: 141 additions & 101 deletions

File tree

packages/imagekit-editor-dev/src/components/editor/ActionBar.tsx

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ export const ActionBar: FC<ActionBarProps> = ({
6666
size="md"
6767
fontWeight="normal"
6868
leftIcon={<Icon boxSize={4} as={PiImageSquare} />}
69+
_hover={{ bg: "gray.100" }}
6970
onClick={() => setShowOriginal(!showOriginal)}
7071
>
7172
{showOriginal ? "Show Transformed" : "Show Original"}
@@ -105,6 +106,7 @@ export const ActionBar: FC<ActionBarProps> = ({
105106
variant="ghost"
106107
size="md"
107108
fontWeight="normal"
109+
_hover={{ bg: "gray.100" }}
108110
onClick={() => window.open(currentImage, "_blank")}
109111
>
110112
Open image in new tab
Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { Button, type ButtonProps, Icon, IconButton } from "@chakra-ui/react"
22
import type React from "react"
33
import { forwardRef } from "react"
4+
import { chakraAny } from "../../utils"
45

56
interface NavbarItemProps extends Omit<ButtonProps, "variant" | "size"> {
67
icon?: React.ReactElement
@@ -13,6 +14,8 @@ export const NavbarItem = forwardRef<HTMLButtonElement, NavbarItemProps>(
1314
{ icon, label, variant = "button", children, ...props },
1415
ref,
1516
) {
17+
const ButtonAny = chakraAny(Button)
18+
const IconButtonAny = chakraAny(IconButton)
1619
const commonStyles = {
1720
variant: "ghost" as const,
1821
borderRadius: "md" as const,
@@ -21,35 +24,39 @@ export const NavbarItem = forwardRef<HTMLButtonElement, NavbarItemProps>(
2124
mx: "2" as const,
2225
fontSize: "sm" as const,
2326
fontWeight: "medium" as const,
27+
color: "editorBattleshipGrey.700",
2428
_hover: {
25-
bg: "editorBattleshipGrey.50",
29+
bg: "gray.100",
2630
},
2731
}
2832

2933
// If only icon is provided (no children or label to display), use icon variant
3034
if (variant === "icon" || (!children && icon && !label)) {
3135
return (
32-
<IconButton
36+
<IconButtonAny
3337
ref={ref}
3438
aria-label={label}
35-
icon={icon ? <Icon as={icon.type} boxSize={5} /> : undefined}
36-
color="editorBattleshipGrey.500"
39+
icon={
40+
icon ? (
41+
<Icon as={icon.type as React.ElementType} boxSize={5} />
42+
) : undefined
43+
}
3744
{...commonStyles}
38-
{...props}
45+
{...(props as unknown as Record<string, unknown>)}
3946
/>
4047
)
4148
}
4249

4350
return (
44-
<Button
51+
<ButtonAny
4552
ref={ref}
4653
leftIcon={icon}
4754
aria-label={label}
4855
{...commonStyles}
49-
{...props}
56+
{...(props as unknown as Record<string, unknown>)}
5057
>
5158
{children || label}
52-
</Button>
59+
</ButtonAny>
5360
)
5461
},
5562
)

packages/imagekit-editor-dev/src/components/header/TemplateNameInput.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -107,8 +107,8 @@ export function TemplateNameInput() {
107107
px="2"
108108
py="1"
109109
borderRadius="md"
110-
_hover={{ bg: "editorGray.200" }}
111-
_focus={{ bg: "editorGray.200", outline: "none", boxShadow: "none" }}
110+
_hover={{ bg: "gray.100" }}
111+
_focus={{ bg: "gray.100", outline: "none", boxShadow: "none" }}
112112
cursor="text"
113113
/>
114114
)

packages/imagekit-editor-dev/src/components/header/TemplateStatus.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const FlexAny = chakraAny(Flex)
2828
const PopoverContentAny = chakraAny(PopoverContent)
2929
const TooltipAny = chakraAny(Tooltip)
3030
const PopoverBodyAny = chakraAny(PopoverBody)
31+
const ButtonAny = chakraAny(Button)
3132

3233
export function TemplateStatus() {
3334
const syncStatus = useEditorStore((s) => s.syncStatus)
@@ -195,7 +196,7 @@ export function TemplateStatus() {
195196
cursor={isInteractive ? "pointer" : "default"}
196197
pointerEvents={isInteractive ? "auto" : "none"}
197198
_hover={{
198-
bg: isInteractive ? "editorGray.200" : "transparent",
199+
bg: isInteractive ? "gray.100" : "transparent",
199200
}}
200201
>
201202
<Icon
@@ -226,15 +227,15 @@ export function TemplateStatus() {
226227
</TextAny2>
227228
{isUnsavedState && (
228229
<Box mt="3">
229-
<Button
230+
<ButtonAny
230231
size="sm"
231232
colorScheme="blue"
232233
onClick={() => void save()}
233234
isLoading={syncStatus === "saving"}
234235
isDisabled={templateStorageWriteBlocked}
235236
>
236237
Save
237-
</Button>
238+
</ButtonAny>
238239
</Box>
239240
)}
240241
</PopoverBodyAny>

packages/imagekit-editor-dev/src/components/header/TemplatesDropdown.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -300,7 +300,7 @@ export function TemplatesDropdown({
300300
paddingY="2"
301301
height="10"
302302
marginX="2"
303-
_hover={{ bg: "editorGray.200" }}
303+
_hover={{ bg: "gray.100" }}
304304
transition="background-color 0.15s"
305305
aria-label="Open templates dropdown"
306306
>

packages/imagekit-editor-dev/src/components/header/index.tsx

Lines changed: 101 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import {
2121
type RequiredMetadata,
2222
useEditorStore,
2323
} from "../../store"
24+
import { chakraAny } from "../../utils"
2425
import { NavbarItem } from "./NavbarItem"
2526
import { SettingsModal } from "./SettingsModal"
2627
import { TemplateNameInput } from "./TemplateNameInput"
@@ -64,7 +65,13 @@ export const Header = ({
6465
onClose,
6566
exportOptions,
6667
onViewAllTemplates,
67-
}: HeaderProps) => {
68+
}: HeaderProps): React.ReactElement => {
69+
const FlexAny = chakraAny(Flex)
70+
const DividerAny = chakraAny(Divider)
71+
const MenuButtonAny = chakraAny(MenuButton)
72+
const MenuListAny = chakraAny(MenuList)
73+
const MenuItemAny = chakraAny(MenuItem)
74+
6875
const { imageList, originalImageList, currentImage } = useEditorStore()
6976
const templateId = useEditorStore((s) => s.templateId)
7077
const templateIsPrivate = useEditorStore((s) => s.templateIsPrivate)
@@ -102,8 +109,15 @@ export const Header = ({
102109
}
103110
}, [provider, templateId, syncStatus])
104111

112+
const visibleExportOptions =
113+
exportOptions?.filter((exportOption) =>
114+
typeof exportOption.isVisible === "boolean"
115+
? exportOption.isVisible
116+
: exportOption.isVisible(imageList, currentImage),
117+
) ?? []
118+
105119
return (
106-
<Flex
120+
<FlexAny
107121
as="header"
108122
width="full"
109123
h="16"
@@ -117,7 +131,7 @@ export const Header = ({
117131
>
118132
{provider ? (
119133
<>
120-
<Flex alignItems="center" gap="2" px="4" height="full" ml="-4">
134+
<FlexAny alignItems="center" gap="2" px="4" height="full" ml="-4">
121135
{templateId && (
122136
<Icon
123137
as={
@@ -135,8 +149,8 @@ export const Header = ({
135149
/>
136150
)}
137151
<TemplateNameInput />
138-
</Flex>
139-
<Divider
152+
</FlexAny>
153+
<DividerAny
140154
orientation="vertical"
141155
borderColor="editorBattleshipGrey.200"
142156
height="40%"
@@ -147,102 +161,103 @@ export const Header = ({
147161
variant="icon"
148162
onClick={() => setIsSettingsOpen(true)}
149163
/>
150-
<Divider
164+
<DividerAny
151165
orientation="vertical"
152166
borderColor="editorBattleshipGrey.200"
153167
height="40%"
154168
/>
155-
<Flex alignItems="center">
169+
<FlexAny alignItems="center">
156170
<TemplatesDropdown onViewAllTemplates={onViewAllTemplates} />
157-
</Flex>
158-
<Divider
171+
</FlexAny>
172+
<DividerAny
159173
orientation="vertical"
160174
borderColor="editorBattleshipGrey.200"
161175
height="40%"
162176
/>
163177
</>
164178
) : null}
165-
<Flex ml="6">
179+
<FlexAny ml="6">
166180
<TemplateStatus />
167-
</Flex>
181+
</FlexAny>
168182
<Spacer />
169-
{exportOptions
170-
?.filter((exportOption) =>
171-
typeof exportOption.isVisible === "boolean"
172-
? exportOption.isVisible
173-
: exportOption.isVisible(imageList, currentImage),
174-
)
175-
.map((exportOption) => (
176-
<React.Fragment key={`export-option-${exportOption.label}`}>
177-
{exportOption.type === "button" ? (
178-
<NavbarItem
179-
key={`export-button-${exportOption.label}`}
183+
{visibleExportOptions.map((exportOption, exportOptionIndex) => (
184+
<React.Fragment key={`export-option-${exportOption.label}`}>
185+
{exportOption.type === "button" ? (
186+
<NavbarItem
187+
key={`export-button-${exportOption.label}`}
188+
icon={exportOption.icon}
189+
label={exportOption.label}
190+
onClick={() => {
191+
const images = imageList.map((image, index) => ({
192+
url: image,
193+
file: originalImageList[index],
194+
}))
195+
const cImage = images.find(
196+
(image) => image.url === currentImage,
197+
)
198+
exportOption.onClick(images, {
199+
// biome-ignore lint/style/noNonNullAssertion: <required here>
200+
url: cImage!.url,
201+
// biome-ignore lint/style/noNonNullAssertion: <required here>
202+
file: cImage!.file,
203+
})
204+
}}
205+
/>
206+
) : (
207+
<Menu
208+
key={`export-menu-${exportOption.label}`}
209+
placement="bottom-end"
210+
strategy="fixed"
211+
>
212+
<MenuButtonAny
213+
as={NavbarItem}
180214
icon={exportOption.icon}
181215
label={exportOption.label}
182-
onClick={() => {
183-
const images = imageList.map((image, index) => ({
184-
url: image,
185-
file: originalImageList[index],
186-
}))
187-
const cImage = images.find(
188-
(image) => image.url === currentImage,
189-
)
190-
exportOption.onClick(images, {
191-
// biome-ignore lint/style/noNonNullAssertion: <required here>
192-
url: cImage!.url,
193-
// biome-ignore lint/style/noNonNullAssertion: <required here>
194-
file: cImage!.file,
195-
})
196-
}}
197-
/>
198-
) : (
199-
<Menu
200-
key={`export-menu-${exportOption.label}`}
201-
placement="bottom-end"
202-
strategy="fixed"
203216
>
204-
<MenuButton
205-
as={NavbarItem}
206-
icon={exportOption.icon}
207-
label={exportOption.label}
208-
>
209-
{exportOption.label}
210-
</MenuButton>
211-
<MenuList>
212-
{exportOption.options
213-
.filter((option) =>
214-
typeof option.isVisible === "boolean"
215-
? option.isVisible
216-
: option.isVisible(imageList, currentImage),
217-
)
218-
.map((option) => (
219-
<MenuItem
220-
key={`export-menu-option-${option.label}`}
221-
onClick={() => {
222-
const images = imageList.map((image, index) => ({
223-
url: image,
224-
file: originalImageList[index],
225-
}))
226-
const cImage = images.find(
227-
(image) => image.url === currentImage,
228-
)
229-
option.onClick(images, {
230-
// biome-ignore lint/style/noNonNullAssertion: <required here>
231-
url: cImage!.url,
232-
// biome-ignore lint/style/noNonNullAssertion: <required here>
233-
file: cImage!.file,
234-
})
235-
}}
236-
>
237-
{option.label}
238-
</MenuItem>
239-
))}
240-
</MenuList>
241-
</Menu>
242-
)}
243-
</React.Fragment>
244-
))}
245-
<Divider
217+
{exportOption.label}
218+
</MenuButtonAny>
219+
<MenuListAny>
220+
{exportOption.options
221+
.filter((option) =>
222+
typeof option.isVisible === "boolean"
223+
? option.isVisible
224+
: option.isVisible(imageList, currentImage),
225+
)
226+
.map((option) => (
227+
<MenuItemAny
228+
key={`export-menu-option-${option.label}`}
229+
onClick={() => {
230+
const images = imageList.map((image, index) => ({
231+
url: image,
232+
file: originalImageList[index],
233+
}))
234+
const cImage = images.find(
235+
(image) => image.url === currentImage,
236+
)
237+
option.onClick(images, {
238+
// biome-ignore lint/style/noNonNullAssertion: <required here>
239+
url: cImage!.url,
240+
// biome-ignore lint/style/noNonNullAssertion: <required here>
241+
file: cImage!.file,
242+
})
243+
}}
244+
>
245+
{option.label}
246+
</MenuItemAny>
247+
))}
248+
</MenuListAny>
249+
</Menu>
250+
)}
251+
{exportOptionIndex < visibleExportOptions.length - 1 ? (
252+
<DividerAny
253+
orientation="vertical"
254+
borderColor="editorBattleshipGrey.200"
255+
height="40%"
256+
/>
257+
) : null}
258+
</React.Fragment>
259+
))}
260+
<DividerAny
246261
orientation="vertical"
247262
borderColor="editorBattleshipGrey.200"
248263
height="40%"
@@ -270,6 +285,6 @@ export const Header = ({
270285
}}
271286
/>
272287
)}
273-
</Flex>
288+
</FlexAny>
274289
)
275290
}

0 commit comments

Comments
 (0)