refactor: migrate web Tabs to @makeplane/propel (#9752)
* refactor: migrate web Tabs to @makeplane/propel Replace in-repo @plane/propel/tabs with @makeplane/propel/components/tabs in analytics, the image picker popover, and the page navigation pane. Published Tabs omit className on Tabs and TabsPanel, so fill-height layouts use a flex/grid wrapper instead. Tabs.Trigger, Tabs.Content, and Tabs.Indicator become Tab, TabsList, and TabsPanel. * 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. * chore: sync apps/space lockfile to @makeplane/propel 0.3.0 preview's lockfile still resolved apps/space to 0.2.0 after the catalog bump, so pnpm install --frozen-lockfile failed in CI. * fix: restore cover picker tab panel mt-3 p-3 spacing Match the previous scroll wrapper inset so thumbnail grid sizing stays aligned with the 36rem picker layout.
This commit is contained in:
parent
8803088086
commit
439fa3daea
5 changed files with 206 additions and 205 deletions
|
|
@ -11,7 +11,7 @@ import { useRouter } from "next/navigation";
|
|||
import { EUserPermissions, EUserPermissionsLevel } from "@plane/constants";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { EmptyStateDetailed } from "@plane/propel/empty-state";
|
||||
import { Tabs } from "@plane/propel/tabs";
|
||||
import { Tab, Tabs, TabsList, TabsPanel } from "@makeplane/propel/components/tabs";
|
||||
// components
|
||||
import { cn } from "@plane/utils";
|
||||
import AnalyticsFilterActions from "@/components/analytics/analytics-filter-actions";
|
||||
|
|
@ -73,45 +73,32 @@ function AnalyticsPage({ params }: Route.ComponentProps) {
|
|||
<>
|
||||
{workspaceProjectIds.length > 0 || loader === "init-loader" ? (
|
||||
<div className="flex h-full overflow-hidden">
|
||||
<Tabs value={selectedTab} onValueChange={handleTabChange} className="h-full w-full">
|
||||
<Tabs variant="contained" value={selectedTab} onValueChange={handleTabChange}>
|
||||
<div className={"flex h-full w-full flex-col"}>
|
||||
<div
|
||||
className={cn(
|
||||
"flex w-full items-center justify-between gap-4 overflow-hidden border-b border-subtle bg-surface-1 px-6 py-2"
|
||||
)}
|
||||
>
|
||||
<Tabs.List className={"flex h-7 w-fit overflow-x-auto"}>
|
||||
<TabsList>
|
||||
{ANALYTICS_TABS.map((tab) => (
|
||||
<Tabs.Trigger
|
||||
key={tab.key}
|
||||
value={tab.key}
|
||||
disabled={tab.isDisabled}
|
||||
size="md"
|
||||
className="h-6 px-3"
|
||||
onClick={() => {
|
||||
if (!tab.isDisabled) {
|
||||
handleTabChange(tab.key);
|
||||
}
|
||||
}}
|
||||
>
|
||||
{tab.label}
|
||||
</Tabs.Trigger>
|
||||
<Tab key={tab.key} value={tab.key} disabled={tab.isDisabled} label={tab.label} />
|
||||
))}
|
||||
</Tabs.List>
|
||||
</TabsList>
|
||||
|
||||
<div className="flex-shrink-0">
|
||||
<AnalyticsFilterActions />
|
||||
</div>
|
||||
</div>
|
||||
{ANALYTICS_TABS.map((tab) => (
|
||||
<Tabs.Content
|
||||
key={tab.key}
|
||||
value={tab.key}
|
||||
className={"h-full overflow-hidden overflow-y-auto px-2"}
|
||||
>
|
||||
<tab.content />
|
||||
</Tabs.Content>
|
||||
))}
|
||||
{/* Grid wrapper: Propel's TabsPanel omits className, so the single mounted panel
|
||||
gets its fill height from a one-row grid instead. */}
|
||||
<div className="grid min-h-0 w-full flex-1 grid-rows-1 overflow-x-hidden overflow-y-auto px-2">
|
||||
{ANALYTICS_TABS.map((tab) => (
|
||||
<TabsPanel key={tab.key} value={tab.key}>
|
||||
<tab.content />
|
||||
</TabsPanel>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
</Tabs>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -14,9 +14,9 @@ import useSWR from "swr";
|
|||
import { Popover } from "@headlessui/react";
|
||||
// plane imports
|
||||
import { Input, InputGroup } from "@makeplane/propel/components/input";
|
||||
import { Tab, Tabs, TabsList, TabsPanel } from "@makeplane/propel/components/tabs";
|
||||
import { ACCEPTED_COVER_IMAGE_MIME_TYPES_FOR_REACT_DROPZONE, MAX_FILE_SIZE } from "@plane/constants";
|
||||
import { useOutsideClickDetector } from "@plane/hooks";
|
||||
import { Tabs } from "@plane/propel/tabs";
|
||||
import { Button, getButtonStyling } from "@plane/propel/button";
|
||||
import { TOAST_TYPE, setToast } from "@plane/propel/toast";
|
||||
import { EFileAssetType } from "@plane/types";
|
||||
|
|
@ -204,175 +204,183 @@ function ImagePickerPopoverComponent<TFieldValues extends FieldValues = FieldVal
|
|||
>
|
||||
<div
|
||||
ref={imagePickerRef}
|
||||
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]"
|
||||
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]"
|
||||
>
|
||||
<Tabs defaultValue={enabledTabs[0]?.key || "images"} className="flex h-full flex-col p-3">
|
||||
<Tabs.List className="flex rounded bg-layer-3 p-1">
|
||||
{enabledTabs.map((tab) => (
|
||||
<Tabs.Trigger key={tab.key} value={tab.key} size="md">
|
||||
{tab.title}
|
||||
</Tabs.Trigger>
|
||||
))}
|
||||
<Tabs.Indicator />
|
||||
</Tabs.List>
|
||||
<div className="vertical-scrollbar mt-3 scrollbar-sm flex-1 overflow-x-hidden overflow-y-auto p-3">
|
||||
<Tabs.Content value="unsplash" className="h-full w-full space-y-4">
|
||||
{(unsplashImages || !unsplashError) && (
|
||||
<>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Controller
|
||||
control={control}
|
||||
name={"search" as FieldPath<TFieldValues>}
|
||||
render={({ field: { value, ref } }) => (
|
||||
<InputGroup size="2xl">
|
||||
<Input
|
||||
size="2xl"
|
||||
id="search"
|
||||
name="search"
|
||||
type="text"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
setSearchParams(formData.search);
|
||||
}
|
||||
}}
|
||||
value={value}
|
||||
onChange={(e) => setFormData({ ...formData, search: e.target.value })}
|
||||
ref={ref}
|
||||
placeholder="Search for images"
|
||||
/>
|
||||
</InputGroup>
|
||||
)}
|
||||
/>
|
||||
<Button variant="primary" size="xl" onClick={() => setSearchParams(formData.search)}>
|
||||
Search
|
||||
</Button>
|
||||
</div>
|
||||
{unsplashImages ? (
|
||||
unsplashImages.length > 0 ? (
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
{unsplashImages.map((image) => (
|
||||
<div
|
||||
key={image.id}
|
||||
className="relative col-span-2 aspect-video md:col-span-1"
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
onChange(image.urls.regular);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={image.urls.small}
|
||||
alt={image.alt_description}
|
||||
className="absolute top-0 left-0 h-full w-full cursor-pointer rounded-sm object-cover"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="pt-7 text-center text-11 text-secondary">No images found.</p>
|
||||
)
|
||||
) : (
|
||||
<Loader className="grid grid-cols-4 gap-4">
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
</Loader>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="images" className="h-full w-full space-y-4">
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
{Object.values(STATIC_COVER_IMAGES).map((imageUrl, index) => (
|
||||
<div
|
||||
key={imageUrl}
|
||||
className="relative col-span-2 aspect-video md:col-span-1"
|
||||
onClick={() => handleStaticImageSelect(imageUrl)}
|
||||
>
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt={`Cover image ${index + 1}`}
|
||||
className="absolute top-0 left-0 h-full w-full cursor-pointer rounded-sm object-cover transition-opacity hover:opacity-80"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
{/* Row wrapper: published Tabs omits className, so fill height comes from stretch. */}
|
||||
<div className="flex h-full min-h-0 w-full p-3">
|
||||
<Tabs variant="contained" defaultValue={enabledTabs[0]?.key || "images"}>
|
||||
<div className="flex h-full min-h-0 w-full flex-col">
|
||||
{/* Published TabsList is inline-flex; stretch it to the previous full-width bar. */}
|
||||
<div className="w-full min-w-0 [&_[role=tab]]:min-w-0 [&_[role=tab]]:flex-1 [&_[role=tablist]]:flex [&_[role=tablist]]:w-full">
|
||||
<TabsList>
|
||||
{enabledTabs.map((tab) => (
|
||||
<Tab key={tab.key} value={tab.key} label={tab.title} />
|
||||
))}
|
||||
</TabsList>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
<Tabs.Content value="upload" className="h-full w-full">
|
||||
<div className="flex h-full w-full flex-col gap-y-2">
|
||||
<div className="flex w-full flex-1 items-center gap-3">
|
||||
<div
|
||||
{...getRootProps()}
|
||||
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 ${
|
||||
(image === null && isDragActive) || !value
|
||||
? "border-2 border-dashed border-subtle hover:bg-surface-2"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
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"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
{image !== null || (value && value !== "") ? (
|
||||
{/* Grid wrapper: published TabsPanel omits className, so fill height comes from a one-row grid. */}
|
||||
<div className="vertical-scrollbar mt-3 scrollbar-sm grid min-h-0 w-full flex-1 grid-rows-1 overflow-x-hidden overflow-y-auto p-3">
|
||||
<TabsPanel value="unsplash">
|
||||
<div className="space-y-4">
|
||||
{(unsplashImages || !unsplashError) && (
|
||||
<>
|
||||
<img
|
||||
src={image ? URL.createObjectURL(image) : getCoverImageDisplayURL(value, "")}
|
||||
alt="image"
|
||||
className="h-full w-full rounded-lg object-cover"
|
||||
/>
|
||||
<div className="flex items-center gap-x-2">
|
||||
<Controller
|
||||
control={control}
|
||||
name={"search" as FieldPath<TFieldValues>}
|
||||
render={({ field: { value, ref } }) => (
|
||||
<InputGroup size="2xl">
|
||||
<Input
|
||||
size="2xl"
|
||||
id="search"
|
||||
name="search"
|
||||
type="text"
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
setSearchParams(formData.search);
|
||||
}
|
||||
}}
|
||||
value={value}
|
||||
onChange={(e) => setFormData({ ...formData, search: e.target.value })}
|
||||
ref={ref}
|
||||
placeholder="Search for images"
|
||||
/>
|
||||
</InputGroup>
|
||||
)}
|
||||
/>
|
||||
<Button variant="primary" size="xl" onClick={() => setSearchParams(formData.search)}>
|
||||
Search
|
||||
</Button>
|
||||
</div>
|
||||
{unsplashImages ? (
|
||||
unsplashImages.length > 0 ? (
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
{unsplashImages.map((image) => (
|
||||
<div
|
||||
key={image.id}
|
||||
className="relative col-span-2 aspect-video md:col-span-1"
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
onChange(image.urls.regular);
|
||||
}}
|
||||
>
|
||||
<img
|
||||
src={image.urls.small}
|
||||
alt={image.alt_description}
|
||||
className="absolute top-0 left-0 h-full w-full cursor-pointer rounded-sm object-cover"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
) : (
|
||||
<p className="pt-7 text-center text-11 text-secondary">No images found.</p>
|
||||
)
|
||||
) : (
|
||||
<Loader className="grid grid-cols-4 gap-4">
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
<Loader.Item height="80px" width="100%" />
|
||||
</Loader>
|
||||
)}
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
<span className="mt-2 block text-13 font-medium text-secondary">
|
||||
{isDragActive ? "Drop image here to upload" : "Drag & drop image here"}
|
||||
</span>
|
||||
)}
|
||||
</div>
|
||||
</TabsPanel>
|
||||
<TabsPanel value="images">
|
||||
<div className="grid grid-cols-4 gap-4">
|
||||
{Object.values(STATIC_COVER_IMAGES).map((imageUrl, index) => (
|
||||
<div
|
||||
key={imageUrl}
|
||||
className="relative col-span-2 aspect-video md:col-span-1"
|
||||
onClick={() => handleStaticImageSelect(imageUrl)}
|
||||
>
|
||||
<img
|
||||
src={imageUrl}
|
||||
alt={`Cover image ${index + 1}`}
|
||||
className="absolute top-0 left-0 h-full w-full cursor-pointer rounded-sm object-cover transition-opacity hover:opacity-80"
|
||||
/>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</TabsPanel>
|
||||
<TabsPanel value="upload">
|
||||
<div className="flex h-full w-full flex-col gap-y-2">
|
||||
<div className="flex w-full flex-1 items-center gap-3">
|
||||
<div
|
||||
{...getRootProps()}
|
||||
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 ${
|
||||
(image === null && isDragActive) || !value
|
||||
? "border-2 border-dashed border-subtle hover:bg-surface-2"
|
||||
: ""
|
||||
}`}
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
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"
|
||||
>
|
||||
Edit
|
||||
</button>
|
||||
{image !== null || (value && value !== "") ? (
|
||||
<>
|
||||
<img
|
||||
src={image ? URL.createObjectURL(image) : getCoverImageDisplayURL(value, "")}
|
||||
alt="image"
|
||||
className="h-full w-full rounded-lg object-cover"
|
||||
/>
|
||||
</>
|
||||
) : (
|
||||
<div>
|
||||
<span className="mt-2 block text-13 font-medium text-secondary">
|
||||
{isDragActive ? "Drop image here to upload" : "Drag & drop image here"}
|
||||
</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
<input {...getInputProps()} />
|
||||
</div>
|
||||
</div>
|
||||
{fileRejections.length > 0 && (
|
||||
<p className="text-13 text-danger-primary">
|
||||
{fileRejections[0].errors[0].code === "file-too-large"
|
||||
? "The image size cannot exceed 5 MB."
|
||||
: "Please upload a file in a valid format."}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<input {...getInputProps()} />
|
||||
<p className="text-13 text-secondary">File formats supported- .jpeg, .jpg, .png, .webp</p>
|
||||
|
||||
<div className="flex h-12 items-start justify-end gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
setImage(null);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="w-full"
|
||||
onClick={handleSubmit}
|
||||
disabled={!image}
|
||||
loading={isImageUploading}
|
||||
>
|
||||
{isImageUploading ? "Uploading" : "Upload & Save"}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{fileRejections.length > 0 && (
|
||||
<p className="text-13 text-danger-primary">
|
||||
{fileRejections[0].errors[0].code === "file-too-large"
|
||||
? "The image size cannot exceed 5 MB."
|
||||
: "Please upload a file in a valid format."}
|
||||
</p>
|
||||
)}
|
||||
|
||||
<p className="text-13 text-secondary">File formats supported- .jpeg, .jpg, .png, .webp</p>
|
||||
|
||||
<div className="flex h-12 items-start justify-end gap-2">
|
||||
<Button
|
||||
variant="secondary"
|
||||
onClick={() => {
|
||||
setIsOpen(false);
|
||||
setImage(null);
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
<Button
|
||||
variant="primary"
|
||||
className="w-full"
|
||||
onClick={handleSubmit}
|
||||
disabled={!image}
|
||||
loading={isImageUploading}
|
||||
>
|
||||
{isImageUploading ? "Uploading" : "Upload & Save"}
|
||||
</Button>
|
||||
</div>
|
||||
</TabsPanel>
|
||||
</div>
|
||||
</Tabs.Content>
|
||||
</div>
|
||||
</Tabs>
|
||||
</div>
|
||||
</Tabs>
|
||||
</div>
|
||||
</div>
|
||||
</Popover.Panel>
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import { useRouter, useSearchParams } from "next/navigation";
|
|||
import { ArrowRightCircle } from "lucide-react";
|
||||
// plane imports
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Tabs } from "@plane/propel/tabs";
|
||||
import { Tabs } from "@makeplane/propel/components/tabs";
|
||||
import { Tooltip } from "@makeplane/propel/components/tooltip";
|
||||
// hooks
|
||||
import { useQueryParams } from "@/hooks/use-query-params";
|
||||
|
|
@ -110,10 +110,16 @@ export const PageNavigationPaneRoot = observer(function PageNavigationPaneRoot(p
|
|||
{ActiveExtension ? (
|
||||
<ActiveExtension.component page={page} extensionData={ActiveExtension.data} storeType={storeType} />
|
||||
) : showNavigationTabs ? (
|
||||
<Tabs value={activeTab} onValueChange={handleTabChange}>
|
||||
<PageNavigationPaneTabsList />
|
||||
<PageNavigationPaneTabPanelsRoot page={page} versionHistory={versionHistory} />
|
||||
</Tabs>
|
||||
// Row wrapper: Propel's Tabs root omits className, so it takes its full height by
|
||||
// stretching as a flex item.
|
||||
<div className="flex h-full w-full">
|
||||
<Tabs variant="contained" value={activeTab} onValueChange={handleTabChange}>
|
||||
<div className="flex h-full w-full flex-col">
|
||||
<PageNavigationPaneTabsList />
|
||||
<PageNavigationPaneTabPanelsRoot page={page} versionHistory={versionHistory} />
|
||||
</div>
|
||||
</Tabs>
|
||||
</div>
|
||||
) : null}
|
||||
</div>
|
||||
</aside>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,8 @@
|
|||
* See the LICENSE file for details.
|
||||
*/
|
||||
|
||||
// plane imports
|
||||
import { TabsPanel } from "@makeplane/propel/components/tabs";
|
||||
// components
|
||||
import type { TPageRootHandlers } from "@/components/pages/editor/page-root";
|
||||
// store
|
||||
|
|
@ -12,7 +14,6 @@ import type { TPageInstance } from "@/store/pages/base-page";
|
|||
import { PageNavigationPaneAssetsTabPanel } from "./assets";
|
||||
import { PageNavigationPaneInfoTabPanel } from "./info/root";
|
||||
import { PageNavigationPaneOutlineTabPanel } from "./outline";
|
||||
import { Tabs } from "@plane/propel/tabs";
|
||||
import { ORDERED_PAGE_NAVIGATION_TABS_LIST } from ".";
|
||||
|
||||
type Props = {
|
||||
|
|
@ -24,14 +25,16 @@ export function PageNavigationPaneTabPanelsRoot(props: Props) {
|
|||
const { page, versionHistory } = props;
|
||||
|
||||
return (
|
||||
<>
|
||||
// Grid wrapper: Propel's TabsPanel omits className, so the single mounted panel gets its
|
||||
// fill height from a one-row grid instead.
|
||||
<div className="grid min-h-0 w-full flex-1 grid-rows-1 overflow-hidden py-2">
|
||||
{ORDERED_PAGE_NAVIGATION_TABS_LIST.map((tab) => (
|
||||
<Tabs.Content key={tab.key} value={tab.key} className="flex-1 overflow-hidden py-2">
|
||||
<TabsPanel key={tab.key} value={tab.key}>
|
||||
{tab.key === "outline" && <PageNavigationPaneOutlineTabPanel page={page} />}
|
||||
{tab.key === "info" && <PageNavigationPaneInfoTabPanel page={page} versionHistory={versionHistory} />}
|
||||
{tab.key === "assets" && <PageNavigationPaneAssetsTabPanel page={page} />}
|
||||
</Tabs.Content>
|
||||
</TabsPanel>
|
||||
))}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,8 +5,8 @@
|
|||
*/
|
||||
|
||||
// plane imports
|
||||
import { Tab, TabsList } from "@makeplane/propel/components/tabs";
|
||||
import { useTranslation } from "@plane/i18n";
|
||||
import { Tabs } from "@plane/propel/tabs";
|
||||
// plane web components
|
||||
import { ORDERED_PAGE_NAVIGATION_TABS_LIST } from "@/components/pages/navigation-pane/tab-panels";
|
||||
|
||||
|
|
@ -16,14 +16,11 @@ export function PageNavigationPaneTabsList() {
|
|||
|
||||
return (
|
||||
<div className="mx-3.5">
|
||||
<Tabs.List>
|
||||
<TabsList>
|
||||
{ORDERED_PAGE_NAVIGATION_TABS_LIST.map((tab) => (
|
||||
<Tabs.Trigger key={tab.key} value={tab.key}>
|
||||
{t(tab.i18n_label)}
|
||||
</Tabs.Trigger>
|
||||
<Tab key={tab.key} value={tab.key} label={t(tab.i18n_label)} />
|
||||
))}
|
||||
<Tabs.Indicator />
|
||||
</Tabs.List>
|
||||
</TabsList>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue