/* ! * (c) Copyright 2026 Palantir Technologies Inc. All rights reserved. */ import { type Meta, type StoryObj } from "@storybook/react-vite"; import { StoryLabel } from "@storybook-common"; // eslint-disable-next-line import/no-extraneous-dependencies -- Storybook-only; mirrors icons package generator casing import { pascalCase } from "change-case"; import React, { type ComponentType, type ReactElement, useCallback, useState } from "react"; import * as NextIcons from "@blueprintjs/icons/next"; import { nextIconManifest, type NextIconManifestEntry } from "@blueprintjs/icons/next"; import { Flex } from "@blueprintjs/labs"; import { Card } from "../card/card"; // ----------------------------------------------------------------------------- // Constants & data const GALLERY_MAX_WIDTH = 1024; const DISPLAY_ICON_SIZE = 32; const ICON_GRID_CARD_STYLE: React.CSSProperties = { aspectRatio: "1 / 1", minWidth: 72, }; const OUTLINED_ICONS = [...nextIconManifest].sort((a, b) => a.name.localeCompare(b.name)); const FILLED_ICONS = nextIconManifest.filter(e => e.hasFilled).sort((a, b) => a.name.localeCompare(b.name)); // ----------------------------------------------------------------------------- // Storybook meta & stories const meta = { title: "Icons/Next Icon Gallery", decorators: [galleryLayoutDecorator], argTypes: { size: { control: { type: "range", min: 12, max: 64, step: 4 } }, }, args: { size: DISPLAY_ICON_SIZE, }, parameters: { actions: { disable: true }, controls: { disableSaveFromUI: true }, interactions: { disable: true }, layout: "centered", }, } satisfies Meta<{ size: number }>; export default meta; type Story = StoryObj; export const Outlined: Story = { render: ({ size }) => , }; export const Filled: Story = { render: ({ size }) => , }; export const Compare: Story = { render: ({ size }) => , }; // ----------------------------------------------------------------------------- // Layout function galleryLayoutDecorator(Story: ComponentType) { return (
); } function NextIconGallery({ icons, size, variant, }: { icons: NextIconManifestEntry[]; size: number; variant: "outlined" | "filled"; }) { return ( {icons.map(entry => ( {renderNextIcon(entry.name, size, variant)} ))} ); } function CompareGallery({ icons, size }: { icons: NextIconManifestEntry[]; size: number }) { return ( {icons.map(entry => ( ))} ); } function CompareCard({ iconName, size }: { iconName: string; size: number }) { const [showFilled, setShowFilled] = useState(false); const handleClick = useCallback(() => setShowFilled(prev => !prev), []); return ( {renderNextIcon(iconName, size, showFilled ? "filled" : "outlined")} ); } // ----------------------------------------------------------------------------- // Icon rendering function renderNextIcon(iconName: string, pixelSize: number, variant: "outlined" | "filled"): ReactElement { const exportName = variant === "outlined" ? `${pascalCase(iconName)}Icon` : `${pascalCase(iconName)}FilledIcon`; const IconComponent = (NextIcons as unknown as Record>)[exportName]; if (IconComponent == null) { return ; } return ; }