feat(dashboard): support RTL in dashboard (#11252)
* fix: add direction attribute to components and adjust styles for RTL support * fix(data-grid): comment it out * Added useDocumentDirection hook * refactor: Integrate useDocumentDirection hook * refactor: Standardize direction prop usage across components * resolve * fix: resolve build errors * fix : remove unused useDocument * Apply RTL styles for some components * Create smooth-gorillas-hide.md * refactor: update some styles for RTL support --------- Co-authored-by: William Bouchard <46496014+willbouch@users.noreply.github.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { EllipsisHorizontal } from "@medusajs/icons"
|
||||
import { PropsWithChildren, ReactNode } from "react"
|
||||
import { Link } from "react-router-dom"
|
||||
import { ConditionalTooltip } from "../conditional-tooltip"
|
||||
import { useDocumentDirection } from "../../../hooks/use-document-direction"
|
||||
|
||||
export type Action = {
|
||||
icon: ReactNode
|
||||
@@ -38,6 +39,7 @@ export const ActionMenu = ({
|
||||
variant = "transparent",
|
||||
children,
|
||||
}: ActionMenuProps) => {
|
||||
const direction = useDocumentDirection()
|
||||
const inner = children ?? (
|
||||
<IconButton size="small" variant={variant}>
|
||||
<EllipsisHorizontal />
|
||||
@@ -45,7 +47,7 @@ export const ActionMenu = ({
|
||||
)
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenu dir={direction}>
|
||||
<DropdownMenu.Trigger asChild>{inner}</DropdownMenu.Trigger>
|
||||
<DropdownMenu.Content>
|
||||
{groups.map((group, index) => {
|
||||
|
||||
@@ -46,7 +46,10 @@ export const JsonViewSection = ({ data }: JsonViewSectionProps) => {
|
||||
<ArrowUpRightOnBox />
|
||||
</IconButton>
|
||||
</Drawer.Trigger>
|
||||
<Drawer.Content className="bg-ui-contrast-bg-base text-ui-code-fg-subtle !shadow-elevation-commandbar overflow-hidden border border-none max-md:inset-x-2 max-md:max-w-[calc(100%-16px)]">
|
||||
<Drawer.Content
|
||||
dir="ltr"
|
||||
className="bg-ui-contrast-bg-base text-ui-code-fg-subtle !shadow-elevation-commandbar overflow-hidden border border-none max-md:inset-x-2 max-md:max-w-[calc(100%-16px)]"
|
||||
>
|
||||
<div className="bg-ui-code-bg-base flex items-center justify-between px-6 py-4">
|
||||
<div className="flex items-center gap-x-4">
|
||||
<Drawer.Title asChild>
|
||||
|
||||
@@ -37,7 +37,7 @@ export const SidebarLink = ({
|
||||
</Text>
|
||||
</div>
|
||||
<div className="flex size-7 items-center justify-center">
|
||||
<TriangleRightMini className="text-ui-fg-muted" />
|
||||
<TriangleRightMini className="text-ui-fg-muted rtl:rotate-180" />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -50,6 +50,8 @@ export const SwitchBox = <
|
||||
<div className="bg-ui-bg-component shadow-elevation-card-rest flex items-start gap-x-3 rounded-lg p-3">
|
||||
<Form.Control>
|
||||
<Switch
|
||||
className="rtl:rotate-180"
|
||||
dir="ltr"
|
||||
{...field}
|
||||
checked={value}
|
||||
onCheckedChange={(e) => {
|
||||
|
||||
@@ -29,6 +29,7 @@ import { FieldValues, UseFormReturn } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { useCommandHistory } from "../../../hooks/use-command-history"
|
||||
import { useDocumentDirection } from "../../../hooks/use-document-direction"
|
||||
import { ConditionalTooltip } from "../../common/conditional-tooltip"
|
||||
import { DataGridContext } from "../context"
|
||||
import {
|
||||
@@ -50,7 +51,7 @@ import { isCellMatch, isSpecialFocusKey } from "../utils"
|
||||
import { DataGridKeyboardShortcutModal } from "./data-grid-keyboard-shortcut-modal"
|
||||
export interface DataGridRootProps<
|
||||
TData,
|
||||
TFieldValues extends FieldValues = FieldValues
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
> {
|
||||
data?: TData[]
|
||||
columns: ColumnDef<TData>[]
|
||||
@@ -96,7 +97,7 @@ const getCommonPinningStyles = <TData,>(
|
||||
|
||||
export const DataGridRoot = <
|
||||
TData,
|
||||
TFieldValues extends FieldValues = FieldValues
|
||||
TFieldValues extends FieldValues = FieldValues,
|
||||
>({
|
||||
data = [],
|
||||
columns,
|
||||
@@ -700,6 +701,7 @@ const DataGridHeader = ({
|
||||
const [shortcutsOpen, setShortcutsOpen] = useState(false)
|
||||
const [columnsOpen, setColumnsOpen] = useState(false)
|
||||
const { t } = useTranslation()
|
||||
const direction = useDocumentDirection()
|
||||
|
||||
// Since all columns are checked by default, we can check if any column is unchecked
|
||||
const hasChanged = columnOptions.some((column) => !column.checked)
|
||||
@@ -716,7 +718,11 @@ const DataGridHeader = ({
|
||||
return (
|
||||
<div className="bg-ui-bg-base flex items-center justify-between border-b p-4">
|
||||
<div className="flex items-center gap-x-2">
|
||||
<DropdownMenu open={columnsOpen} onOpenChange={handleColumnsOpenChange}>
|
||||
<DropdownMenu
|
||||
dir={direction}
|
||||
open={columnsOpen}
|
||||
onOpenChange={handleColumnsOpenChange}
|
||||
>
|
||||
<ConditionalTooltip
|
||||
showTooltip={isDisabled}
|
||||
content={t("dataGrid.columns.disabled")}
|
||||
|
||||
@@ -106,9 +106,10 @@ const OuterComponent = ({
|
||||
>
|
||||
<div className="absolute inset-y-0 left-4 z-[3] flex w-fit items-center justify-center">
|
||||
<Switch
|
||||
dir="ltr"
|
||||
ref={buttonRef}
|
||||
size="small"
|
||||
className="shrink-0"
|
||||
className="shrink-0 rtl:rotate-180"
|
||||
checked={localValue.checked}
|
||||
disabled={localValue.disabledToggle}
|
||||
onCheckedChange={handleCheckedChange}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Button, DropdownMenu } from "@medusajs/ui"
|
||||
import { ReactNode } from "react"
|
||||
import { useSearchParams } from "react-router-dom"
|
||||
import { useDocumentDirection } from "../../../hooks/use-document-direction"
|
||||
|
||||
type FilterGroupProps = {
|
||||
filters: {
|
||||
@@ -37,8 +38,11 @@ type AddFilterMenuProps = {
|
||||
}
|
||||
|
||||
const AddFilterMenu = ({ availableKeys }: AddFilterMenuProps) => {
|
||||
const direction = useDocumentDirection()
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenu
|
||||
dir={direction}
|
||||
>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<Button variant="secondary" size="small">
|
||||
Add filter
|
||||
|
||||
@@ -4,6 +4,8 @@ import { useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useSearchParams } from "react-router-dom"
|
||||
|
||||
import { useDocumentDirection } from "../../../hooks/use-document-direction"
|
||||
|
||||
type OrderByProps = {
|
||||
keys: string[]
|
||||
}
|
||||
@@ -56,6 +58,7 @@ export const OrderBy = ({ keys }: OrderByProps) => {
|
||||
}>(initState(searchParams))
|
||||
|
||||
const { t } = useTranslation()
|
||||
const direction = useDocumentDirection()
|
||||
|
||||
const handleDirChange = (dir: string) => {
|
||||
setState((prev) => ({
|
||||
@@ -99,7 +102,7 @@ export const OrderBy = ({ keys }: OrderByProps) => {
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenu dir={direction}>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<IconButton size="small">
|
||||
<ArrowUpDown />
|
||||
|
||||
@@ -7,6 +7,7 @@ import { Control } from "react-hook-form"
|
||||
import { AddressSchema } from "../../../lib/schemas"
|
||||
import { Form } from "../../common/form"
|
||||
import { CountrySelect } from "../../inputs/country-select"
|
||||
import { useDocumentDirection } from "../../../hooks/use-document-direction"
|
||||
|
||||
type AddressFieldValues = z.infer<typeof AddressSchema>
|
||||
|
||||
@@ -22,7 +23,7 @@ export const AddressForm = ({
|
||||
layout,
|
||||
}: AddressFormProps) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const direction = useDocumentDirection()
|
||||
const style = clx("gap-4", {
|
||||
"flex flex-col": layout === "stack",
|
||||
"grid grid-cols-2": layout === "grid",
|
||||
@@ -182,7 +183,11 @@ export const AddressForm = ({
|
||||
<Form.Label>{t("fields.country")}</Form.Label>
|
||||
<Form.Control>
|
||||
{countries ? (
|
||||
<Select {...field} onValueChange={onChange}>
|
||||
<Select
|
||||
dir={direction}
|
||||
{...field}
|
||||
onValueChange={onChange}
|
||||
>
|
||||
<Select.Trigger ref={ref}>
|
||||
<Select.Value />
|
||||
</Select.Trigger>
|
||||
|
||||
@@ -25,6 +25,7 @@ import { Form } from "../../common/form"
|
||||
import { Skeleton } from "../../common/skeleton"
|
||||
import { RouteDrawer, useRouteModal } from "../../modals"
|
||||
import { KeyboundForm } from "../../utilities/keybound-form"
|
||||
import { useDocumentDirection } from "../../../hooks/use-document-direction"
|
||||
|
||||
type MetaDataSubmitHook<TRes> = (
|
||||
params: { metadata?: Record<string, any> | null },
|
||||
@@ -77,7 +78,7 @@ const InnerForm = <TRes,>({
|
||||
}: Omit<MetadataFormProps<TRes>, "isPending">) => {
|
||||
const { t } = useTranslation()
|
||||
const { handleSuccess } = useRouteModal()
|
||||
|
||||
const direction = useDocumentDirection()
|
||||
const hasUneditableRows = getHasUneditableRows(metadata)
|
||||
|
||||
const form = useForm<z.infer<typeof MetadataSchema>>({
|
||||
@@ -215,10 +216,12 @@ const InnerForm = <TRes,>({
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<DropdownMenu>
|
||||
<DropdownMenu
|
||||
dir={direction}
|
||||
>
|
||||
<DropdownMenu.Trigger
|
||||
className={clx(
|
||||
"invisible absolute inset-y-0 -right-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
|
||||
"invisible absolute inset-y-0 -end-2.5 my-auto group-hover/table:visible data-[state='open']:visible",
|
||||
{
|
||||
hidden: isDisabled,
|
||||
}
|
||||
|
||||
@@ -251,7 +251,7 @@ const ComboboxImpl = <T extends Value = string>(
|
||||
e.preventDefault()
|
||||
handleValueChange(isArrayValue ? ([] as unknown as T) : undefined)
|
||||
}}
|
||||
className="bg-ui-bg-base hover:bg-ui-bg-base-hover txt-compact-small-plus text-ui-fg-subtle focus-within:border-ui-fg-interactive transition-fg absolute left-0.5 top-0.5 z-[1] flex h-[28px] items-center rounded-[4px] border py-[3px] pl-1.5 pr-1 outline-none"
|
||||
className="bg-ui-bg-base hover:bg-ui-bg-base-hover txt-compact-small-plus text-ui-fg-subtle focus-within:border-ui-fg-interactive transition-fg absolute start-0.5 top-0.5 z-[1] flex h-[28px] items-center rounded-[4px] border py-[3px] ps-1.5 pe-1 outline-none"
|
||||
>
|
||||
<span className="tabular-nums">{selectedValues.length}</span>
|
||||
<XMarkMini className="text-ui-fg-muted" />
|
||||
@@ -263,8 +263,8 @@ const ComboboxImpl = <T extends Value = string>(
|
||||
className={clx(
|
||||
"pointer-events-none absolute inset-y-0 flex size-full items-center",
|
||||
{
|
||||
"left-[calc(var(--tag-width)+8px)]": showTag,
|
||||
"left-2": !showTag,
|
||||
"start-[calc(var(--tag-width)+8px)]": showTag,
|
||||
"start-2": !showTag,
|
||||
}
|
||||
)}
|
||||
>
|
||||
@@ -278,8 +278,8 @@ const ComboboxImpl = <T extends Value = string>(
|
||||
className={clx(
|
||||
"pointer-events-none absolute inset-y-0 flex size-full items-center overflow-hidden",
|
||||
{
|
||||
"left-[calc(var(--tag-width)+8px)]": showTag,
|
||||
"left-2": !showTag,
|
||||
"start-[calc(var(--tag-width)+8px)]": showTag,
|
||||
"start-2": !showTag,
|
||||
}
|
||||
)}
|
||||
>
|
||||
@@ -293,12 +293,12 @@ const ComboboxImpl = <T extends Value = string>(
|
||||
ref={comboboxRef}
|
||||
onFocus={() => setOpen(true)}
|
||||
className={clx(
|
||||
"txt-compact-small text-ui-fg-base !placeholder:text-ui-fg-muted transition-fg size-full cursor-pointer bg-transparent pl-2 pr-8 outline-none focus:cursor-text",
|
||||
"txt-compact-small text-ui-fg-base !placeholder:text-ui-fg-muted transition-fg size-full cursor-pointer bg-transparent ps-2 pe-8 outline-none focus:cursor-text",
|
||||
"hover:bg-ui-bg-field-hover",
|
||||
{
|
||||
"opacity-0": hideInput,
|
||||
"pl-2": !showTag,
|
||||
"pl-[calc(var(--tag-width)+8px)]": showTag,
|
||||
"ps-2": !showTag,
|
||||
"ps-[calc(var(--tag-width)+8px)]": showTag,
|
||||
}
|
||||
)}
|
||||
placeholder={hidePlaceholder ? undefined : placeholder}
|
||||
@@ -312,7 +312,7 @@ const ComboboxImpl = <T extends Value = string>(
|
||||
e.preventDefault()
|
||||
handleValueChange(undefined)
|
||||
}}
|
||||
className="bg-ui-bg-base hover:bg-ui-bg-base-hover txt-compact-small-plus text-ui-fg-subtle focus-within:border-ui-fg-interactive transition-fg absolute right-[28px] top-0.5 z-[1] flex h-[28px] items-center rounded-[4px] border px-1.5 py-[2px] outline-none"
|
||||
className="bg-ui-bg-base hover:bg-ui-bg-base-hover txt-compact-small-plus text-ui-fg-subtle focus-within:border-ui-fg-interactive transition-fg absolute end-[28px] top-0.5 z-[1] flex h-[28px] items-center rounded-[4px] border px-1.5 py-[2px] outline-none"
|
||||
>
|
||||
<XMarkMini className="text-ui-fg-muted" />
|
||||
</button>
|
||||
@@ -323,7 +323,7 @@ const ComboboxImpl = <T extends Value = string>(
|
||||
<button
|
||||
{...props}
|
||||
type="button"
|
||||
className="text-ui-fg-muted transition-fg hover:bg-ui-bg-field-hover absolute right-0 flex size-8 items-center justify-center rounded-r outline-none"
|
||||
className="text-ui-fg-muted transition-fg hover:bg-ui-bg-field-hover absolute end-0 flex size-8 items-center justify-center rounded-r outline-none"
|
||||
>
|
||||
<TrianglesMini />
|
||||
</button>
|
||||
@@ -341,7 +341,7 @@ const ComboboxImpl = <T extends Value = string>(
|
||||
"max-h-[200px] overflow-y-auto",
|
||||
"data-[state=open]:animate-in data-[state=open]:fade-in-0 data-[state=open]:zoom-in-95",
|
||||
"data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95",
|
||||
"data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2"
|
||||
"data-[side=bottom]:slide-in-from-top-2 data-[side=start]:slide-in-from-end-2 data-[side=end]:slide-in-from-start-2 data-[side=top]:slide-in-from-bottom-2"
|
||||
)}
|
||||
style={{
|
||||
pointerEvents: open ? "auto" : "none",
|
||||
|
||||
@@ -33,7 +33,7 @@ export const CountrySelect = forwardRef<
|
||||
<div className="relative">
|
||||
<TrianglesMini
|
||||
className={clx(
|
||||
"text-ui-fg-muted transition-fg pointer-events-none absolute right-2 top-1/2 -translate-y-1/2",
|
||||
"text-ui-fg-muted transition-fg pointer-events-none absolute end-2 top-1/2 -translate-y-1/2",
|
||||
{
|
||||
"text-ui-fg-disabled": disabled,
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ export const ProvinceSelect = forwardRef<
|
||||
<div className="relative">
|
||||
<TrianglesMini
|
||||
className={clx(
|
||||
"text-ui-fg-muted transition-fg pointer-events-none absolute right-2 top-1/2 -translate-y-1/2",
|
||||
"text-ui-fg-muted transition-fg pointer-events-none absolute end-2 top-1/2 -translate-y-1/2",
|
||||
{
|
||||
"text-ui-fg-disabled": disabled,
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ import { queryClient } from "../../../lib/query-client"
|
||||
import { useExtension } from "../../../providers/extension-provider"
|
||||
import { useSearch } from "../../../providers/search-provider"
|
||||
import { UserMenu } from "../user-menu"
|
||||
import { useDocumentDirection } from "../../../hooks/use-document-direction"
|
||||
|
||||
export const MainLayout = () => {
|
||||
return (
|
||||
@@ -94,7 +95,7 @@ const Logout = () => {
|
||||
const Header = () => {
|
||||
const { t } = useTranslation()
|
||||
const { store, isPending, isError, error } = useStore()
|
||||
|
||||
const direction = useDocumentDirection()
|
||||
const name = store?.name
|
||||
const fallback = store?.name?.slice(0, 1).toUpperCase()
|
||||
|
||||
@@ -106,11 +107,12 @@ const Header = () => {
|
||||
|
||||
return (
|
||||
<div className="w-full p-3">
|
||||
<DropdownMenu>
|
||||
<DropdownMenu
|
||||
dir={direction}>
|
||||
<DropdownMenu.Trigger
|
||||
disabled={!isLoaded}
|
||||
className={clx(
|
||||
"bg-ui-bg-subtle transition-fg grid w-full grid-cols-[24px_1fr_15px] items-center gap-x-3 rounded-md p-0.5 pr-2 outline-none",
|
||||
"bg-ui-bg-subtle transition-fg grid w-full grid-cols-[24px_1fr_15px] items-center gap-x-3 rounded-md p-0.5 pe-2 outline-none",
|
||||
"hover:bg-ui-bg-subtle-hover",
|
||||
"data-[state=open]:bg-ui-bg-subtle-hover",
|
||||
"focus-visible:shadow-borders-focus"
|
||||
@@ -121,7 +123,7 @@ const Header = () => {
|
||||
) : (
|
||||
<Skeleton className="h-6 w-6 rounded-md" />
|
||||
)}
|
||||
<div className="block overflow-hidden text-left">
|
||||
<div className="block overflow-hidden text-start">
|
||||
{name ? (
|
||||
<Text
|
||||
size="small"
|
||||
@@ -267,7 +269,7 @@ const Searchbar = () => {
|
||||
)}
|
||||
>
|
||||
<MagnifyingGlass />
|
||||
<div className="flex-1 text-left">
|
||||
<div className="flex-1 text-start">
|
||||
<Text size="small" leading="compact" weight="plus">
|
||||
{t("app.search.label")}
|
||||
</Text>
|
||||
|
||||
@@ -156,7 +156,7 @@ const Breadcrumbs = () => {
|
||||
)}
|
||||
{!isLast && (
|
||||
<span className="mx-2">
|
||||
<TriangleRightMini />
|
||||
<TriangleRightMini className="rtl:rotate-180" />
|
||||
</span>
|
||||
)}
|
||||
</li>
|
||||
@@ -177,7 +177,7 @@ const ToggleSidebar = () => {
|
||||
onClick={() => toggle("desktop")}
|
||||
size="small"
|
||||
>
|
||||
<SidebarLeft className="text-ui-fg-muted" />
|
||||
<SidebarLeft className="text-ui-fg-muted rtl:rotate-180" />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
className="hidden max-lg:flex"
|
||||
@@ -185,7 +185,7 @@ const ToggleSidebar = () => {
|
||||
onClick={() => toggle("mobile")}
|
||||
size="small"
|
||||
>
|
||||
<SidebarLeft className="text-ui-fg-muted" />
|
||||
<SidebarLeft className="text-ui-fg-muted rtl:rotate-180" />
|
||||
</IconButton>
|
||||
</div>
|
||||
)
|
||||
@@ -210,7 +210,7 @@ const DesktopSidebarContainer = ({ children }: PropsWithChildren) => {
|
||||
|
||||
return (
|
||||
<div
|
||||
className={clx("hidden h-screen w-[220px] border-r", {
|
||||
className={clx("hidden h-screen w-[220px] border-e", {
|
||||
"lg:flex": desktop,
|
||||
})}
|
||||
>
|
||||
@@ -234,8 +234,8 @@ const MobileSidebarContainer = ({ children }: PropsWithChildren) => {
|
||||
/>
|
||||
<RadixDialog.Content
|
||||
className={clx(
|
||||
"bg-ui-bg-subtle shadow-elevation-modal fixed inset-y-2 left-2 flex w-full max-w-[304px] flex-col overflow-hidden rounded-lg border-r",
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:slide-out-to-left-1/2 data-[state=open]:slide-in-from-left-1/2 duration-200"
|
||||
"bg-ui-bg-subtle shadow-elevation-modal fixed inset-y-2 start-2 flex w-full max-w-[304px] flex-col overflow-hidden rounded-lg border-r",
|
||||
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:slide-out-to-start-1/2 data-[state=open]:slide-in-from-start-1/2 duration-200"
|
||||
)}
|
||||
>
|
||||
<div className="p-3">
|
||||
|
||||
@@ -29,10 +29,12 @@ import { useLogout, useMe } from "../../../hooks/api"
|
||||
import { queryClient } from "../../../lib/query-client"
|
||||
import { useGlobalShortcuts } from "../../../providers/keybind-provider/hooks"
|
||||
import { useTheme } from "../../../providers/theme-provider"
|
||||
import { useDocumentDirection } from "../../../hooks/use-document-direction"
|
||||
|
||||
export const UserMenu = () => {
|
||||
const { t } = useTranslation()
|
||||
const location = useLocation()
|
||||
const direction = useDocumentDirection()
|
||||
|
||||
const [openMenu, setOpenMenu] = useState(false)
|
||||
const [openModal, setOpenModal] = useState(false)
|
||||
@@ -44,33 +46,33 @@ export const UserMenu = () => {
|
||||
|
||||
return (
|
||||
<div>
|
||||
<DropdownMenu open={openMenu} onOpenChange={setOpenMenu}>
|
||||
<DropdownMenu dir={direction} open={openMenu} onOpenChange={setOpenMenu}>
|
||||
<UserBadge />
|
||||
<DropdownMenu.Content className="min-w-[var(--radix-dropdown-menu-trigger-width)] max-w-[var(--radix-dropdown-menu-trigger-width)]">
|
||||
<UserItem />
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item asChild>
|
||||
<Link to="/settings/profile" state={{ from: location.pathname }}>
|
||||
<UserIcon className="text-ui-fg-subtle mr-2" />
|
||||
<UserIcon className="text-ui-fg-subtle me-2" />
|
||||
{t("app.menus.user.profileSettings")}
|
||||
</Link>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item asChild>
|
||||
<Link to="https://docs.medusajs.com" target="_blank">
|
||||
<BookOpen className="text-ui-fg-subtle mr-2" />
|
||||
<BookOpen className="text-ui-fg-subtle me-2" />
|
||||
{t("app.menus.user.documentation")}
|
||||
</Link>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Item asChild>
|
||||
<Link to="https://medusajs.com/changelog/" target="_blank">
|
||||
<TimelineVertical className="text-ui-fg-subtle mr-2" />
|
||||
<TimelineVertical className="text-ui-fg-subtle me-2" />
|
||||
{t("app.menus.user.changelog")}
|
||||
</Link>
|
||||
</DropdownMenu.Item>
|
||||
<DropdownMenu.Separator />
|
||||
<DropdownMenu.Item onClick={toggleModal}>
|
||||
<Keyboard className="text-ui-fg-subtle mr-2" />
|
||||
<Keyboard className="text-ui-fg-subtle me-2" />
|
||||
{t("app.menus.user.shortcuts")}
|
||||
</DropdownMenu.Item>
|
||||
<ThemeToggle />
|
||||
@@ -93,7 +95,7 @@ const UserBadge = () => {
|
||||
|
||||
if (isPending) {
|
||||
return (
|
||||
<button className="shadow-borders-base flex max-w-[192px] select-none items-center gap-x-2 overflow-hidden text-ellipsis whitespace-nowrap rounded-full py-1 pl-1 pr-2.5">
|
||||
<button className="shadow-borders-base flex max-w-[192px] select-none items-center gap-x-2 overflow-hidden text-ellipsis whitespace-nowrap rounded-full py-1 ps-1 pe-2.5">
|
||||
<Skeleton className="h-5 w-5 rounded-full" />
|
||||
<Skeleton className="h-[9px] w-[70px]" />
|
||||
</button>
|
||||
@@ -109,7 +111,7 @@ const UserBadge = () => {
|
||||
<DropdownMenu.Trigger
|
||||
disabled={!user}
|
||||
className={clx(
|
||||
"bg-ui-bg-subtle grid w-full cursor-pointer grid-cols-[24px_1fr_15px] items-center gap-2 rounded-md py-1 pl-0.5 pr-2 outline-none",
|
||||
"bg-ui-bg-subtle grid w-full cursor-pointer grid-cols-[24px_1fr_15px] items-center gap-2 rounded-md py-1 ps-0.5 pe-2 outline-none",
|
||||
"hover:bg-ui-bg-subtle-hover",
|
||||
"data-[state=open]:bg-ui-bg-subtle-hover",
|
||||
"focus-visible:shadow-borders-focus"
|
||||
@@ -148,9 +150,9 @@ const ThemeToggle = () => {
|
||||
|
||||
return (
|
||||
<DropdownMenu.SubMenu>
|
||||
<DropdownMenu.SubMenuTrigger className="rounded-md">
|
||||
<CircleHalfSolid className="text-ui-fg-subtle mr-2" />
|
||||
{t("app.menus.user.theme.label")}
|
||||
<DropdownMenu.SubMenuTrigger dir="ltr" className="rounded-md rtl:rotate-180">
|
||||
<CircleHalfSolid className="text-ui-fg-subtle me-2" />
|
||||
<span className="rtl:rotate-180">{t("app.menus.user.theme.label")}</span>
|
||||
</DropdownMenu.SubMenuTrigger>
|
||||
<DropdownMenu.SubMenuContent>
|
||||
<DropdownMenu.RadioGroup value={theme}>
|
||||
|
||||
@@ -44,6 +44,7 @@ import {
|
||||
} from "./constants"
|
||||
import { SearchArea } from "./types"
|
||||
import { useSearchResults } from "./use-search-results"
|
||||
import { useDocumentDirection } from "../../hooks/use-document-direction"
|
||||
|
||||
export const Search = () => {
|
||||
const [area, setArea] = useState<SearchArea>("all")
|
||||
@@ -54,6 +55,7 @@ export const Search = () => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
|
||||
|
||||
const inputRef = useRef<HTMLInputElement>(null)
|
||||
const listRef = useRef<HTMLDivElement>(null)
|
||||
|
||||
@@ -367,7 +369,7 @@ const CommandInput = forwardRef<
|
||||
) => {
|
||||
const { t } = useTranslation()
|
||||
const innerRef = useRef<HTMLInputElement>(null)
|
||||
|
||||
const direction = useDocumentDirection()
|
||||
useImperativeHandle<HTMLInputElement | null, HTMLInputElement | null>(
|
||||
ref,
|
||||
() => innerRef.current
|
||||
@@ -376,7 +378,7 @@ const CommandInput = forwardRef<
|
||||
return (
|
||||
<div className="flex flex-col border-b">
|
||||
<div className="px-4 pt-4">
|
||||
<DropdownMenu>
|
||||
<DropdownMenu dir={direction}>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<Badge
|
||||
size="2xsmall"
|
||||
@@ -432,7 +434,7 @@ const CommandInput = forwardRef<
|
||||
)}
|
||||
{...props}
|
||||
/>
|
||||
<div className="absolute right-4 top-1/2 flex -translate-y-1/2 items-center justify-end gap-x-2">
|
||||
<div className="absolute end-4 top-1/2 flex -translate-y-1/2 items-center justify-end gap-x-2">
|
||||
{isFetching && (
|
||||
<Spinner className="text-ui-fg-muted animate-spin" />
|
||||
)}
|
||||
|
||||
@@ -4,6 +4,8 @@ import { useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useSearchParams } from "react-router-dom"
|
||||
|
||||
import { useDocumentDirection } from "../../../../hooks/use-document-direction"
|
||||
|
||||
export type DataTableOrderByKey<TData> = {
|
||||
key: keyof TData
|
||||
label: string
|
||||
@@ -54,6 +56,7 @@ export const DataTableOrderBy = <TData,>({
|
||||
}>(initState(searchParams, prefix))
|
||||
const param = prefix ? `${prefix}_order` : "order"
|
||||
const { t } = useTranslation()
|
||||
const direction = useDocumentDirection()
|
||||
|
||||
const handleDirChange = (dir: string) => {
|
||||
setState((prev) => ({
|
||||
@@ -97,7 +100,7 @@ export const DataTableOrderBy = <TData,>({
|
||||
}
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenu dir={direction}>
|
||||
<DropdownMenu.Trigger asChild>
|
||||
<IconButton size="small">
|
||||
<DescendingSorting />
|
||||
|
||||
@@ -262,7 +262,7 @@ export const DataTableRoot = <TData,>({
|
||||
<Table.Cell
|
||||
key={cell.id}
|
||||
className={clx({
|
||||
"!pl-0 !pr-0": shouldRenderAsLink,
|
||||
"!ps-0 !pe-0": shouldRenderAsLink,
|
||||
"bg-ui-bg-base group-data-[selected=true]/row:bg-ui-bg-highlight group-data-[selected=true]/row:group-hover/row:bg-ui-bg-highlight-hover group-hover/row:bg-ui-bg-base-hover transition-fg group-has-[[data-row-link]:focus-visible]:bg-ui-bg-base-hover sticky left-0 after:absolute after:inset-y-0 after:right-0 after:h-full after:w-px after:bg-transparent after:content-['']":
|
||||
isStickyCell,
|
||||
"bg-ui-bg-subtle group-hover/row:bg-ui-bg-subtle-hover":
|
||||
@@ -288,9 +288,9 @@ export const DataTableRoot = <TData,>({
|
||||
>
|
||||
<div
|
||||
className={clx(
|
||||
"flex size-full items-center pr-6",
|
||||
"flex size-full items-center pe-6",
|
||||
{
|
||||
"pl-6": isTabableLink && !hasLeftOffset,
|
||||
"ps-6": isTabableLink && !hasLeftOffset,
|
||||
}
|
||||
)}
|
||||
>
|
||||
|
||||
Reference in New Issue
Block a user