diff --git a/packages/apps/docs/src/components/BottomPageSection/BottomPageSection.tsx b/packages/apps/docs/src/components/BottomPageSection/BottomPageSection.tsx index bfe2adad93..06903b22d8 100644 --- a/packages/apps/docs/src/components/BottomPageSection/BottomPageSection.tsx +++ b/packages/apps/docs/src/components/BottomPageSection/BottomPageSection.tsx @@ -5,6 +5,7 @@ import { Subscribe } from './components/Subscribe'; import { bottomWrapperClass, bottomWrapperCodeLayoutClass } from './styles.css'; import { INavigation } from '@/types/Layout'; +import { analyticsEvent, EVENT_NAMES } from '@/utils/analytics'; import classnames from 'classnames'; import Link from 'next/link'; import React, { FC } from 'react'; @@ -24,19 +25,38 @@ export const BottomPageSection: FC = ({ [bottomWrapperCodeLayoutClass]: layout === 'code', }); + const onClickAction = (page: 'prev' | 'next', href?: string): void => { + if (!href) return; + analyticsEvent( + page === 'next' + ? EVENT_NAMES['click:next_page'] + : EVENT_NAMES['click:previous_page'], + { + label: `to: ${href}`, + url: window.location.href, + }, + ); + }; + return (
{navigation?.previous !== undefined && ( - + onClickAction('prev', navigation?.previous?.root)} + href={navigation?.previous.root} + > previous:
{navigation?.previous.title} )} {navigation?.next !== undefined && ( - + onClickAction('next', navigation?.next?.root)} + href={navigation?.next.root} + > next:
{navigation?.next.title} diff --git a/packages/apps/docs/src/components/BottomPageSection/components/EditPage.tsx b/packages/apps/docs/src/components/BottomPageSection/components/EditPage.tsx index f06b7e9c0b..5952e3598a 100644 --- a/packages/apps/docs/src/components/BottomPageSection/components/EditPage.tsx +++ b/packages/apps/docs/src/components/BottomPageSection/components/EditPage.tsx @@ -1,5 +1,6 @@ import { Button } from '@kadena/react-ui'; +import { analyticsEvent, EVENT_NAMES } from '@/utils/analytics'; import React, { FC } from 'react'; interface IProps { @@ -8,11 +9,18 @@ interface IProps { export const EditPage: FC = ({ editLink }) => { if (!editLink) return null; + const onClick = async ( + event: React.MouseEvent, + ): Promise => { + event.preventDefault(); + event.stopPropagation(); + analyticsEvent(EVENT_NAMES['click:edit_page']); + window.open(editLink, '_blank'); + }; return ( + )} + + + ); + }, +}; + +export default meta; diff --git a/packages/libs/react-ui/src/components/NavHeader/NavHeader.test.tsx b/packages/libs/react-ui/src/components/NavHeader/NavHeader.test.tsx new file mode 100644 index 0000000000..700d9cc762 --- /dev/null +++ b/packages/libs/react-ui/src/components/NavHeader/NavHeader.test.tsx @@ -0,0 +1,14 @@ +import { NavHeader } from '@components/NavHeader'; +import { render } from '@testing-library/react'; +import React from 'react'; + +// ? Since we're (likely) using Chromatic soon, I'll hold off on adding more tests here. + +describe('NavHeader', () => { + it('renders correctly', () => { + const { getByTestId } = render(); + + const navHeaderContainer = getByTestId('kda-navheader'); + expect(navHeaderContainer).toBeInTheDocument(); + }); +}); diff --git a/packages/libs/react-ui/src/components/NavHeader/NavHeader.tsx b/packages/libs/react-ui/src/components/NavHeader/NavHeader.tsx new file mode 100644 index 0000000000..a7d85ee7c8 --- /dev/null +++ b/packages/libs/react-ui/src/components/NavHeader/NavHeader.tsx @@ -0,0 +1,42 @@ +import { containerClass, logoClass } from './NavHeader.css'; +import { INavHeaderContentProps } from './NavHeaderContent'; +import { INavHeaderNavigationProps } from './NavHeaderNavigation'; + +import type { LogoVariant } from '@components/BrandLogo'; +import Logo, { logoVariants } from '@components/BrandLogo'; +import { Link } from '@components/Link'; +import React, { FC, FunctionComponentElement } from 'react'; + +export type INavItemTarget = '_self' | '_blank'; +export interface INavItem { + title: string; + href: string; + target?: INavItemTarget; +} +export type INavItems = INavItem[]; + +export interface INavHeaderContainerProps { + brand?: LogoVariant; + children?: FunctionComponentElement< + INavHeaderNavigationProps | INavHeaderContentProps + >[]; + items?: INavItems; +} + +export const NavHeaderContainer: FC = ({ + brand = logoVariants[0], + children, +}) => { + return ( +
+
+ {logoVariants.includes(brand) && ( + + + + )} +
+ {children} +
+ ); +}; diff --git a/packages/libs/react-ui/src/components/NavHeader/NavHeaderContent.tsx b/packages/libs/react-ui/src/components/NavHeader/NavHeaderContent.tsx new file mode 100644 index 0000000000..55e03db519 --- /dev/null +++ b/packages/libs/react-ui/src/components/NavHeader/NavHeaderContent.tsx @@ -0,0 +1,11 @@ +import { childrenClass } from './NavHeader.css'; + +import React, { FC } from 'react'; + +export interface INavHeaderContentProps { + children: React.ReactNode; +} + +export const NavHeaderContent: FC = ({ children }) => { + return
{children}
; +}; diff --git a/packages/libs/react-ui/src/components/NavHeader/NavHeaderLink.tsx b/packages/libs/react-ui/src/components/NavHeader/NavHeaderLink.tsx new file mode 100644 index 0000000000..aa3fcf3908 --- /dev/null +++ b/packages/libs/react-ui/src/components/NavHeader/NavHeaderLink.tsx @@ -0,0 +1,27 @@ +import { activeLinkClass, linkClass } from './NavHeader.css'; + +import classNames from 'classnames'; +import React, { FC } from 'react'; + +export interface INavHeaderLinkProps { + children: string; + href: string; + active?: boolean; +} + +export const NavHeaderLink: FC = ({ + children, + href, + active, +}) => { + return ( + + {children} + + ); +}; diff --git a/packages/libs/react-ui/src/components/NavHeader/NavHeaderNavigation.tsx b/packages/libs/react-ui/src/components/NavHeader/NavHeaderNavigation.tsx new file mode 100644 index 0000000000..758de21275 --- /dev/null +++ b/packages/libs/react-ui/src/components/NavHeader/NavHeaderNavigation.tsx @@ -0,0 +1,56 @@ +import { NavGlow } from './assets/glow'; +import { glowClass, navListClass, navWrapperClass } from './NavHeader.css'; +import { INavHeaderLinkProps } from './NavHeaderLink'; +import useGlow from './useGlow'; + +import React, { FC, FunctionComponentElement } from 'react'; + +export type INavItemTarget = '_self' | '_blank'; +export interface INavItem { + href: string; + target?: INavItemTarget; + title: string; +} +export type INavItems = INavItem[]; + +export interface INavHeaderNavigationProps { + children: FunctionComponentElement[]; +} + +export const NavHeaderNavigation: FC = ({ + children, +}) => { + const { glowX, animationDuration, glowRef, navRef, activeNav, setActiveNav } = + useGlow(); + + return ( + + ); +}; diff --git a/packages/libs/react-ui/src/components/NavHeader/assets/glow.tsx b/packages/libs/react-ui/src/components/NavHeader/assets/glow.tsx new file mode 100644 index 0000000000..b07b9255ae --- /dev/null +++ b/packages/libs/react-ui/src/components/NavHeader/assets/glow.tsx @@ -0,0 +1,53 @@ +import * as React from 'react'; +import { SVGProps } from 'react'; + +export const NavGlow: React.FC> = () => ( + + + + + + + + + + + + + + + + +); diff --git a/packages/libs/react-ui/src/components/NavHeader/index.ts b/packages/libs/react-ui/src/components/NavHeader/index.ts new file mode 100644 index 0000000000..7c09964be9 --- /dev/null +++ b/packages/libs/react-ui/src/components/NavHeader/index.ts @@ -0,0 +1,24 @@ +import type { INavHeaderContainerProps } from './NavHeader'; +import { NavHeaderContainer } from './NavHeader'; +import type { INavHeaderContentProps } from './NavHeaderContent'; +import { NavHeaderContent } from './NavHeaderContent'; +import type { INavHeaderLinkProps } from './NavHeaderLink'; +import { NavHeaderLink } from './NavHeaderLink'; +import type { INavHeaderNavigationProps } from './NavHeaderNavigation'; +import { NavHeaderNavigation } from './NavHeaderNavigation'; + +import { FC } from 'react'; + +export interface INavHeaderProps { + Root: FC; + Navigation: FC; + Link: FC; + Content: FC; +} + +export const NavHeader: INavHeaderProps = { + Root: NavHeaderContainer, + Navigation: NavHeaderNavigation, + Link: NavHeaderLink, + Content: NavHeaderContent, +}; diff --git a/packages/libs/react-ui/src/components/NavHeader/useGlow.ts b/packages/libs/react-ui/src/components/NavHeader/useGlow.ts new file mode 100644 index 0000000000..1e99f68fcd --- /dev/null +++ b/packages/libs/react-ui/src/components/NavHeader/useGlow.ts @@ -0,0 +1,58 @@ +import { useEffect, useRef, useState } from 'react'; + +interface IUseGlowReturn { + activeNav: number; + animationDuration: number; + glowRef: React.RefObject; + glowX: number; + navRef: React.RefObject; + setActiveNav: React.Dispatch>; +} + +const useGlow = (): IUseGlowReturn => { + const glowRef = useRef(null); + const navRef = useRef(null); + + const [glowX, setGlowX] = useState(0); + const [activeNav, setActiveNav] = useState(0); + + const prevGlowX = useRef(glowX); + const glowAnimationSpeed = useRef(0); + + useEffect(() => { + const activeNavElement = navRef.current?.querySelector( + `li:nth-child(${activeNav}) a`, + ); + const activeNavBounds = activeNavElement?.getBoundingClientRect(); + const glowBounds = glowRef.current?.getBoundingClientRect(); + const headerBounds = navRef.current?.parentElement?.getBoundingClientRect(); + + const noActiveNav = activeNav === 0; + + if (noActiveNav && glowBounds) setGlowX(-glowBounds.width / 2); + else if (activeNavBounds && glowBounds && headerBounds) + setGlowX( + activeNavBounds.x - + headerBounds.x - + glowBounds.width / 2 + + activeNavBounds.width / 2, + ); + }, [glowX, activeNav]); + + useEffect(() => { + prevGlowX.current = glowX; + }, [glowX]); + + glowAnimationSpeed.current = Math.abs(glowX - prevGlowX.current) * 2; + + return { + activeNav, + animationDuration: glowAnimationSpeed.current, + glowRef, + glowX, + navRef, + setActiveNav, + }; +}; + +export default useGlow; diff --git a/packages/libs/react-ui/src/components/Pagination/PageNav.tsx b/packages/libs/react-ui/src/components/Pagination/PageNav.tsx new file mode 100644 index 0000000000..aa927ffe90 --- /dev/null +++ b/packages/libs/react-ui/src/components/Pagination/PageNav.tsx @@ -0,0 +1,34 @@ +import { pageNavButtonClass, pageNavLabelClass } from './Pagination.css'; + +import { Box } from '@components/Box'; +import { SystemIcon } from '@components/Icon'; +import React, { FC } from 'react'; + +interface IPageNavProps { + label: string; + direction?: 'prev' | 'next'; + disabled?: boolean; + onClick: () => void; +} + +export const PageNav: FC = ({ + label, + direction, + disabled = false, + onClick, +}) => { + const isPrevious = direction === 'prev'; + const isNext = direction === 'next'; + + return ( + + ); +}; diff --git a/packages/libs/react-ui/src/components/Pagination/PageNum.tsx b/packages/libs/react-ui/src/components/Pagination/PageNum.tsx new file mode 100644 index 0000000000..e3860044dd --- /dev/null +++ b/packages/libs/react-ui/src/components/Pagination/PageNum.tsx @@ -0,0 +1,21 @@ +import { pageNumButtonClass } from './Pagination.css'; + +import classNames from 'classnames'; +import React, { FC } from 'react'; + +interface IPageNumProps { + number: number; + current: boolean; + onClick: () => void; +} + +export const PageNum: FC = ({ number, current, onClick }) => { + return ( + + ); +}; diff --git a/packages/libs/react-ui/src/components/Pagination/Pagination.css.ts b/packages/libs/react-ui/src/components/Pagination/Pagination.css.ts new file mode 100644 index 0000000000..eda7de2611 --- /dev/null +++ b/packages/libs/react-ui/src/components/Pagination/Pagination.css.ts @@ -0,0 +1,58 @@ +import { sprinkles } from '@theme/sprinkles.css'; +import { vars } from '@theme/vars.css'; +import { style } from '@vanilla-extract/css'; + +export const listClass = style([ + sprinkles({ + display: 'flex', + alignItems: 'center', + gap: '$2', + padding: 0, + }), + { listStyleType: 'none' }, +]); + +export const pageNavButtonClass = style([ + sprinkles({ + display: 'flex', + gap: '$3', + alignItems: 'center', + paddingX: '$3', + paddingY: '$2', + color: '$primaryContrast', + border: 'none', + background: 'none', + fontWeight: '$semiBold', + }), + { + ':disabled': { + pointerEvents: 'none', + color: vars.colors.$disabledContrast, + }, + }, +]); + +export const pageNavLabelClass = style([ + sprinkles({ + display: { xs: 'none', sm: 'block' }, + }), +]); + +export const pageNumButtonClass = style([ + sprinkles({ + color: '$primaryContrast', + width: '$8', + paddingY: '$2', + border: 'none', + background: 'none', + fontWeight: '$semiBold', + }), + { + selectors: { + '&.current': { + borderRadius: vars.radii.$sm, + outline: `2px auto ${vars.colors.$primaryAccent}`, + }, + }, + }, +]); diff --git a/packages/libs/react-ui/src/components/Pagination/Pagination.stories.tsx b/packages/libs/react-ui/src/components/Pagination/Pagination.stories.tsx new file mode 100644 index 0000000000..494f230f09 --- /dev/null +++ b/packages/libs/react-ui/src/components/Pagination/Pagination.stories.tsx @@ -0,0 +1,91 @@ +import { SystemIcon } from '@components/Icon'; +import { IPaginationProps, Pagination } from '@components/Pagination'; +import { Stack } from '@components/Stack'; +import { Heading } from '@components/Typography'; +import type { Meta, StoryObj } from '@storybook/react'; +import React from 'react'; + +const meta: Meta< + { + helperText: string; + leadingText: string; + leftIcon: keyof typeof SystemIcon; + rightIcon: keyof typeof SystemIcon; + } & IPaginationProps +> = { + title: 'Components/Pagination', + argTypes: { + label: { + control: { + type: 'text', + }, + }, + totalPages: { + control: { + type: 'number', + min: 0, + max: 20, + step: 2, + }, + }, + visiblePageLimit: { + control: { + type: 'number', + min: 3, + max: 7, + step: 1, + }, + }, + }, +}; + +export default meta; +type Story = StoryObj; + +/* + *👇 Render functions are a framework specific feature to allow you control on how the component renders. + * See https://storybook.js.org/docs/7.0/react/api/csf + * to learn how to use render functions. + */ + +export const Controlled: Story = { + args: { + label: 'Label', + totalPages: 10, + visiblePageLimit: 3, + }, + render: ({ totalPages, label, visiblePageLimit }) => { + const [page, setPage] = React.useState(1); + + return ( + + Controlled Page State: {page} + + + ); + }, +}; + +export const Uncontrolled: Story = { + args: { + label: 'Label', + totalPages: 10, + visiblePageLimit: 3, + }, + render: ({ totalPages, label, visiblePageLimit }) => { + return ( + console.log('Updating Page')} + /> + ); + }, +}; diff --git a/packages/libs/react-ui/src/components/Pagination/Pagination.tsx b/packages/libs/react-ui/src/components/Pagination/Pagination.tsx new file mode 100644 index 0000000000..ea2c340d90 --- /dev/null +++ b/packages/libs/react-ui/src/components/Pagination/Pagination.tsx @@ -0,0 +1,68 @@ +import { PageNav } from './PageNav'; +import { PageNum } from './PageNum'; +import { paginate } from './paginate'; +import { listClass } from './Pagination.css'; + +import React, { FC, useState } from 'react'; + +export interface IPaginationProps { + totalPages: number; + currentPage?: number; + label: string; + visiblePageLimit?: number; + onPageChange: (page: number) => void; +} + +export const Pagination: FC = ({ + totalPages, + currentPage, + label, + visiblePageLimit = 3, + onPageChange, +}) => { + const [_page, setPage] = useState(1); + const page = currentPage || _page; + const pages = paginate({ + page, + total: totalPages, + maxPages: visiblePageLimit, + }); + const enablePrevious = page > 1; + const enableNext = page < totalPages; + + const onClick = (page: number) => { + setPage(page); + onPageChange(page); + }; + + return ( + + ); +}; diff --git a/packages/libs/react-ui/src/components/Pagination/index.tsx b/packages/libs/react-ui/src/components/Pagination/index.tsx new file mode 100644 index 0000000000..28387538e0 --- /dev/null +++ b/packages/libs/react-ui/src/components/Pagination/index.tsx @@ -0,0 +1,2 @@ +export { Pagination } from './Pagination'; +export type { IPaginationProps } from './Pagination'; diff --git a/packages/libs/react-ui/src/components/Pagination/paginate.test.ts b/packages/libs/react-ui/src/components/Pagination/paginate.test.ts new file mode 100644 index 0000000000..baa4d892b8 --- /dev/null +++ b/packages/libs/react-ui/src/components/Pagination/paginate.test.ts @@ -0,0 +1,45 @@ +// Original source: https://github.com/seek-oss/braid-design-system/blob/master/packages/braid-design-system/src/lib/components/Pagination/paginate.ts + +import { paginate } from './paginate'; + +describe('paginate', () => { + it(`should return the required pages when 'total' < 'maxPages'`, () => { + expect(paginate({ page: 1, total: 4, maxPages: 5 })).toEqual([1, 2, 3, 4]); + }); + + it(`should return the max number of pages when 'total' > 'maxPages' and 'page' is '1'`, () => { + expect(paginate({ page: 1, total: 20, maxPages: 5 })).toEqual([ + 1, 2, 3, 4, 5, + ]); + }); + + it(`should return the max number of pages around the selected 'page'`, () => { + expect(paginate({ page: 12, total: 20, maxPages: 5 })).toEqual([ + 10, 11, 12, 13, 14, + ]); + }); + + it(`should return the last pages when 'page' is the 'total'`, () => { + expect(paginate({ page: 20, total: 20, maxPages: 5 })).toEqual([ + 16, 17, 18, 19, 20, + ]); + }); + + it(`should return the correct pages when 'page' is < the middle page`, () => { + expect(paginate({ page: 3, total: 20, maxPages: 5 })).toEqual([ + 1, 2, 3, 4, 5, + ]); + }); + + it(`should return the correct pages when 'page' is the middle page`, () => { + expect(paginate({ page: 4, total: 20, maxPages: 5 })).toEqual([ + 2, 3, 4, 5, 6, + ]); + }); + + it(`should return the correct pages when 'page' is > the middle page`, () => { + expect(paginate({ page: 5, total: 20, maxPages: 5 })).toEqual([ + 3, 4, 5, 6, 7, + ]); + }); +}); diff --git a/packages/libs/react-ui/src/components/Pagination/paginate.ts b/packages/libs/react-ui/src/components/Pagination/paginate.ts new file mode 100644 index 0000000000..a409e47bb6 --- /dev/null +++ b/packages/libs/react-ui/src/components/Pagination/paginate.ts @@ -0,0 +1,26 @@ +// Original source: https://github.com/seek-oss/braid-design-system/blob/master/packages/braid-design-system/src/lib/components/Pagination/paginate.ts + +export const paginate = ({ + page, + total, + maxPages, +}: { + page: number; + total: number; + maxPages: number; +}): number[] => { + const half = (maxPages - 1) / 2; + const smallerHalf = Math.floor(half); + const largerHalf = Math.ceil(half); + const pageCount = Math.min(maxPages, total); + + let minPage = page - smallerHalf; + + if (page - smallerHalf <= 1) { + minPage = 1; + } else if (page + largerHalf >= total) { + minPage = Math.max(1, total - maxPages + 1); + } + + return Array.from(Array(pageCount).keys()).map((p) => p + minPage); +}; diff --git a/packages/libs/react-ui/src/components/Stack/Stack.tsx b/packages/libs/react-ui/src/components/Stack/Stack.tsx index 2340db6821..9f289da7ae 100644 --- a/packages/libs/react-ui/src/components/Stack/Stack.tsx +++ b/packages/libs/react-ui/src/components/Stack/Stack.tsx @@ -25,12 +25,12 @@ export interface IStackProps direction?: Sprinkles['flexDirection']; wrap?: Sprinkles['flexWrap']; spacing?: Sprinkles['gap']; - component?: ElementType; + as?: ElementType; children?: React.ReactNode; } export const Stack = ({ - component = 'div', + as = 'div', margin = undefined, marginX = undefined, marginY = undefined, @@ -54,7 +54,7 @@ export const Stack = ({ children, }: IStackProps): React.ReactElement => { return createElement( - component, + as, { className: sprinkles({ display: 'flex', diff --git a/packages/libs/react-ui/src/components/Tag/Tag.css.ts b/packages/libs/react-ui/src/components/Tag/Tag.css.ts index 7f752d5acd..72f99b6384 100644 --- a/packages/libs/react-ui/src/components/Tag/Tag.css.ts +++ b/packages/libs/react-ui/src/components/Tag/Tag.css.ts @@ -1,18 +1,33 @@ import { sprinkles } from '@theme/sprinkles.css'; +import { vars } from '@theme/vars.css'; import { style } from '@vanilla-extract/css'; export const tagClass = style([ sprinkles({ - backgroundColor: '$foreground', - color: '$background', - borderRadius: '$sm', - paddingX: '$2', - fontSize: '$xs', - fontWeight: '$semiBold', - display: 'inline-block', + backgroundColor: '$neutral1', + color: '$neutral6', + borderRadius: '$xs', + paddingY: '$1', + paddingRight: '$1', + display: 'inline-flex', + alignItems: 'center', }), { - paddingTop: '0.05rem', - paddingBottom: '0.05rem', + border: `1px solid ${vars.colors.$borderSubtle}`, }, ]); + +export const tagLabelClass = style([ + sprinkles({ + paddingX: '$2', + }), +]); + +export const closeButtonClass = style([ + sprinkles({ + border: 'none', + background: 'none', + padding: '$1', + cursor: 'pointer', + }), +]); diff --git a/packages/libs/react-ui/src/components/Tag/Tag.stories.tsx b/packages/libs/react-ui/src/components/Tag/Tag.stories.tsx index b5b058ff48..f7d64f6a65 100644 --- a/packages/libs/react-ui/src/components/Tag/Tag.stories.tsx +++ b/packages/libs/react-ui/src/components/Tag/Tag.stories.tsx @@ -1,9 +1,11 @@ +import { Button } from '@components/Button'; import { ITagProps, Tag } from '@components/Tag'; import type { Meta, StoryObj } from '@storybook/react'; -import React from 'react'; +import React, { useState } from 'react'; const meta: Meta< { + hasClose: boolean; text: string; } & ITagProps > = { @@ -15,6 +17,11 @@ const meta: Meta< type: 'text', }, }, + hasClose: { + control: { + type: 'boolean', + }, + }, }, }; @@ -22,15 +29,22 @@ export default meta; type Story = StoryObj< { text: string; + hasClose: boolean; } & ITagProps >; export const Primary: Story = { name: 'Tag', args: { - text: 'Tag', + text: 'Chain:1', + hasClose: true, }, - render: ({ text }) => { - return {text}; + render: ({ text, hasClose }) => { + const [closed, setClosed] = useState(false); + + if (closed) return ; + return ( + setClosed(true) : undefined}>{text} + ); }, }; diff --git a/packages/libs/react-ui/src/components/Tag/Tag.tsx b/packages/libs/react-ui/src/components/Tag/Tag.tsx index 6eb3dcfc2a..8644d793bf 100644 --- a/packages/libs/react-ui/src/components/Tag/Tag.tsx +++ b/packages/libs/react-ui/src/components/Tag/Tag.tsx @@ -1,16 +1,22 @@ -import { tagClass } from './Tag.css'; +import { closeButtonClass, tagClass, tagLabelClass } from './Tag.css'; +import { SystemIcon } from '@components/Icon'; import React, { FC } from 'react'; export interface ITagProps { - children: React.ReactNode; + children: string; + onClose?: () => void; } -// TODO: Update to accept a color prop -export const Tag: FC = ({ children }) => { +export const Tag: FC = ({ children, onClose }) => { return ( - {children} + {children} + {onClose ? ( + + ) : null} ); }; diff --git a/packages/libs/react-ui/src/components/index.ts b/packages/libs/react-ui/src/components/index.ts index a76b29a1b4..a7c9e8615f 100644 --- a/packages/libs/react-ui/src/components/index.ts +++ b/packages/libs/react-ui/src/components/index.ts @@ -13,6 +13,7 @@ export type { ILinkProps } from './Link'; export type { IMaskedValueProps } from './MaskedValue/MaskedValue'; export type { IModalProps } from './Modal'; export type { IOptionProps } from './Select/Option'; +export type { IPaginationProps } from './Pagination'; export type { IProgressBarProps } from './ProgressBar'; export type { ISelectProps } from './Select/Select'; export type { IStackProps } from './Stack'; @@ -64,6 +65,7 @@ export { MaskedValue } from './MaskedValue/MaskedValue'; export { Modal, ModalProvider, useModal } from './Modal'; export { Notification } from './Notification'; export { Option } from './Select/Option'; +export { Pagination } from './Pagination'; export { ProgressBar } from './ProgressBar'; export { Select } from './Select/Select'; export { Stack } from './Stack'; diff --git a/packages/libs/react-ui/src/index.ts b/packages/libs/react-ui/src/index.ts index 8df12af202..4ac5a572c8 100644 --- a/packages/libs/react-ui/src/index.ts +++ b/packages/libs/react-ui/src/index.ts @@ -32,6 +32,7 @@ export type { INotificationProps, InputWrapperStatus, IOptionProps, + IPaginationProps, IProgressBarProps, ISelectProps, IStackProps, @@ -73,6 +74,7 @@ export { NavFooter, Notification, Option, + Pagination, ProductIcon, ProgressBar, Select, diff --git a/packages/libs/react-ui/src/styles/sprinkles.css.ts b/packages/libs/react-ui/src/styles/sprinkles.css.ts index 7f800e4b4e..18f6b7eca7 100644 --- a/packages/libs/react-ui/src/styles/sprinkles.css.ts +++ b/packages/libs/react-ui/src/styles/sprinkles.css.ts @@ -64,7 +64,15 @@ const responsiveProperties = defineProperties({ defaultCondition: 'xs', properties: { position: ['fixed', 'static', 'absolute', 'relative', 'sticky'], - display: ['none', 'flex', 'block', 'inline', 'inline-block', 'grid'], + display: [ + 'none', + 'flex', + 'block', + 'inline', + 'inline-block', + 'grid', + 'inline-flex', + ], flexDirection: ['row', 'row-reverse', 'column', 'column-reverse'], justifyContent: [ 'flex-start', diff --git a/packages/libs/react-ui/src/styles/vars.css.ts b/packages/libs/react-ui/src/styles/vars.css.ts index 0063e39e80..f64e063b43 100644 --- a/packages/libs/react-ui/src/styles/vars.css.ts +++ b/packages/libs/react-ui/src/styles/vars.css.ts @@ -39,6 +39,7 @@ export const vars = createGlobalTheme(':root', { $bold: '700', }, radii: { + $xs: '2px', $sm: '4px', $md: '6px', $lg: '8px',