Continue inventory updates

This commit is contained in:
Bill 2024-04-16 22:54:31 -04:00
parent 4e848fd3f5
commit 69e90bd1d3
16 changed files with 232 additions and 128 deletions

View File

@ -1,16 +1,12 @@
import { GetRenderer, GetTicker, NitroTicker, RoomPreviewer, TextureUtils } from '@nitrots/nitro-renderer';
import { FC, MouseEvent, ReactNode, useEffect, useRef } from 'react';
import { FC, MouseEvent, useEffect, useRef } from 'react';
export interface LayoutRoomPreviewerViewProps
{
export const LayoutRoomPreviewerView: FC<{
roomPreviewer: RoomPreviewer;
height?: number;
children?: ReactNode;
}
export const LayoutRoomPreviewerView: FC<LayoutRoomPreviewerViewProps> = props =>
}> = props =>
{
const { roomPreviewer = null, height = 0, children = null } = props;
const { roomPreviewer = null, height = 0 } = props;
const elementRef = useRef<HTMLDivElement>();
const onClick = (event: MouseEvent<HTMLDivElement>) =>
@ -80,9 +76,14 @@ export const LayoutRoomPreviewerView: FC<LayoutRoomPreviewerViewProps> = props =
}, [ roomPreviewer, elementRef, height ]);
return (
<div className="relative w-full">
<div ref={ elementRef } className="rounded-md shadow" style={ { height } } onClick={ onClick } />
{ children }
</div>
<div
ref={ elementRef }
className="relative w-full rounded-md shadow-room-previewer"
style={ {
height,
minHeight: height,
maxHeight: height
} }
onClick={ onClick } />
);
}

View File

@ -1,10 +1,13 @@
import { GetEventDispatcher, NitroToolbarAnimateIconEvent, TextureUtils, ToolbarIconEnum } from '@nitrots/nitro-renderer';
import { GetEventDispatcher, NitroToolbarAnimateIconEvent, RoomPreviewer, TextureUtils, ToolbarIconEnum } from '@nitrots/nitro-renderer';
import { FC, useRef } from 'react';
import { LayoutRoomPreviewerView, LayoutRoomPreviewerViewProps } from '../../../../common';
import { LayoutRoomPreviewerView } from '../../../../common';
import { CatalogPurchasedEvent } from '../../../../events';
import { useUiEvent } from '../../../../hooks';
export const CatalogRoomPreviewerView: FC<LayoutRoomPreviewerViewProps> = props =>
export const CatalogRoomPreviewerView: FC<{
roomPreviewer: RoomPreviewer;
height?: number;
}> = props =>
{
const { roomPreviewer = null } = props;
const elementRef = useRef<HTMLDivElement>(null);

View File

@ -1,7 +1,8 @@
import { FC, PropsWithChildren } from 'react';
import { UnseenItemCategory } from '../../../../api';
import { LayoutBadgeImageView, LayoutGridItem } from '../../../../common';
import { LayoutBadgeImageView } from '../../../../common';
import { useInventoryBadges, useInventoryUnseenTracker } from '../../../../hooks';
import { InfiniteGrid } from '../../../../layout';
export const InventoryBadgeItemView: FC<PropsWithChildren<{ badgeCode: string }>> = props =>
{
@ -11,9 +12,9 @@ export const InventoryBadgeItemView: FC<PropsWithChildren<{ badgeCode: string }>
const unseen = isUnseen(UnseenItemCategory.BADGE, getBadgeId(badgeCode));
return (
<LayoutGridItem itemActive={ (selectedBadgeCode === badgeCode) } itemUnseen={ unseen } onDoubleClick={ event => toggleBadge(selectedBadgeCode) } onMouseDown={ event => setSelectedBadgeCode(badgeCode) } { ...rest }>
<InfiniteGrid.Item itemActive={ (selectedBadgeCode === badgeCode) } itemUnseen={ unseen } onDoubleClick={ event => toggleBadge(selectedBadgeCode) } onMouseDown={ event => setSelectedBadgeCode(badgeCode) } { ...rest }>
<LayoutBadgeImageView badgeCode={ badgeCode } />
{ children }
</LayoutGridItem>
</InfiniteGrid.Item>
);
}
}

View File

@ -1,7 +1,8 @@
import { FC, useEffect, useState } from 'react';
import { LocalizeBadgeName, LocalizeText, UnseenItemCategory } from '../../../../api';
import { AutoGrid, Button, Column, Grid, LayoutBadgeImageView, Text } from '../../../../common';
import { LayoutBadgeImageView } from '../../../../common';
import { useInventoryBadges, useInventoryUnseenTracker } from '../../../../hooks';
import { InfiniteGrid, NitroButton } from '../../../../layout';
import { InventoryBadgeItemView } from './InventoryBadgeItemView';
export const InventoryBadgeView: FC<{}> = props =>
@ -34,33 +35,36 @@ export const InventoryBadgeView: FC<{}> = props =>
}, []);
return (
<Grid>
<Column overflow="hidden" size={ 7 }>
<AutoGrid columnCount={ 4 }>
{ badgeCodes && (badgeCodes.length > 0) && badgeCodes.map((badgeCode, index) =>
{
if(isWearingBadge(badgeCode)) return null;
return <InventoryBadgeItemView key={ index } badgeCode={ badgeCode } />
}) }
</AutoGrid>
</Column>
<Column className="justify-content-between" overflow="auto" size={ 5 }>
<Column gap={ 2 } overflow="hidden">
<Text>{ LocalizeText('inventory.badges.activebadges') }</Text>
<AutoGrid columnCount={ 3 }>
{ activeBadgeCodes && (activeBadgeCodes.length > 0) && activeBadgeCodes.map((badgeCode, index) => <InventoryBadgeItemView key={ index } badgeCode={ badgeCode } />) }
</AutoGrid>
</Column>
<div className="grid h-full grid-cols-12 gap-2">
<div className="flex flex-col col-span-7 gap-1 overflow-hidden">
<InfiniteGrid<string>
columnCount={ 5 }
estimateSize={ 50 }
itemRender={ item => <InventoryBadgeItemView badgeCode={ item } /> }
items={ badgeCodes.filter(code => !isWearingBadge(code)) } />
</div>
<div className="flex flex-col justify-between col-span-5 overflow-auto">
<div className="flex flex-col gap-2 overflow-hidden">
<span className="text-sm truncate grow">{ LocalizeText('inventory.badges.activebadges') }</span>
<InfiniteGrid<string>
columnCount={ 3 }
estimateSize={ 50 }
itemRender={ item => <InventoryBadgeItemView badgeCode={ item } /> }
items={ activeBadgeCodes } />
</div>
{ !!selectedBadgeCode &&
<Column grow gap={ 2 } justifyContent="end">
<div className="flex flex-col gap-2">
<div className="items-center gap-2">
<LayoutBadgeImageView shrink badgeCode={ selectedBadgeCode } />
<Text>{ LocalizeBadgeName(selectedBadgeCode) }</Text>
<span className="text-sm truncate grow">{ LocalizeBadgeName(selectedBadgeCode) }</span>
</div>
<Button disabled={ !isWearingBadge(selectedBadgeCode) && !canWearBadges() } variant={ (isWearingBadge(selectedBadgeCode) ? 'danger' : 'success') } onClick={ event => toggleBadge(selectedBadgeCode) }>{ LocalizeText(isWearingBadge(selectedBadgeCode) ? 'inventory.badges.clearbadge' : 'inventory.badges.wearbadge') }</Button>
</Column> }
</Column>
</Grid>
<NitroButton
disabled={ !isWearingBadge(selectedBadgeCode) && !canWearBadges() }
onClick={ event => toggleBadge(selectedBadgeCode) }>
{ LocalizeText(isWearingBadge(selectedBadgeCode) ? 'inventory.badges.clearbadge' : 'inventory.badges.wearbadge') }
</NitroButton>
</div> }
</div>
</div>
);
}

View File

@ -1,10 +1,13 @@
import { MouseEventType } from '@nitrots/nitro-renderer';
import { FC, MouseEvent, PropsWithChildren, useState } from 'react';
import { attemptBotPlacement, IBotItem, UnseenItemCategory } from '../../../../api';
import { LayoutAvatarImageView, LayoutGridItem } from '../../../../common';
import { IBotItem, UnseenItemCategory, attemptBotPlacement } from '../../../../api';
import { LayoutAvatarImageView } from '../../../../common';
import { useInventoryBots, useInventoryUnseenTracker } from '../../../../hooks';
import { InfiniteGrid } from '../../../../layout';
export const InventoryBotItemView: FC<PropsWithChildren<{ botItem: IBotItem }>> = props =>
export const InventoryBotItemView: FC<PropsWithChildren<{
botItem: IBotItem
}>> = props =>
{
const { botItem = null, children = null, ...rest } = props;
const [ isMouseDown, setMouseDown ] = useState(false);
@ -35,9 +38,9 @@ export const InventoryBotItemView: FC<PropsWithChildren<{ botItem: IBotItem }>>
}
return (
<LayoutGridItem itemActive={ (selectedBot === botItem) } itemUnseen={ unseen } onDoubleClick={ onMouseEvent } onMouseDown={ onMouseEvent } onMouseOut={ onMouseEvent } onMouseUp={ onMouseEvent } { ...rest }>
<InfiniteGrid.Item itemActive={ (selectedBot === botItem) } itemUnseen={ unseen } onDoubleClick={ onMouseEvent } onMouseDown={ onMouseEvent } onMouseOut={ onMouseEvent } onMouseUp={ onMouseEvent } { ...rest } className="*:[background-position-y:-32px]">
<LayoutAvatarImageView direction={ 3 } figure={ botItem.botData.figure } headOnly={ true } />
{ children }
</LayoutGridItem>
</InfiniteGrid.Item>
);
}

View File

@ -1,18 +1,16 @@
import { GetRoomEngine, IRoomSession, RoomObjectVariable, RoomPreviewer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react';
import { LocalizeText, UnseenItemCategory, attemptBotPlacement } from '../../../../api';
import { AutoGrid, Button, Column, Grid, LayoutRoomPreviewerView, Text } from '../../../../common';
import { IBotItem, LocalizeText, UnseenItemCategory, attemptBotPlacement } from '../../../../api';
import { LayoutRoomPreviewerView } from '../../../../common';
import { useInventoryBots, useInventoryUnseenTracker } from '../../../../hooks';
import { InfiniteGrid, NitroButton } from '../../../../layout';
import { InventoryCategoryEmptyView } from '../InventoryCategoryEmptyView';
import { InventoryBotItemView } from './InventoryBotItemView';
interface InventoryBotViewProps
{
export const InventoryBotView: FC<{
roomSession: IRoomSession;
roomPreviewer: RoomPreviewer;
}
export const InventoryBotView: FC<InventoryBotViewProps> = props =>
}> = props =>
{
const { roomSession = null, roomPreviewer = null } = props;
const [ isVisible, setIsVisible ] = useState(false);
@ -67,25 +65,26 @@ export const InventoryBotView: FC<InventoryBotViewProps> = props =>
if(!botItems || !botItems.length) return <InventoryCategoryEmptyView desc={ LocalizeText('inventory.empty.bots.desc') } title={ LocalizeText('inventory.empty.bots.title') } />;
return (
<Grid>
<Column overflow="hidden" size={ 7 }>
<AutoGrid columnCount={ 5 }>
{ botItems && (botItems.length > 0) && botItems.map(item => <InventoryBotItemView key={ item.botData.id } botItem={ item } />) }
</AutoGrid>
</Column>
<Column overflow="auto" size={ 5 }>
<Column overflow="hidden" position="relative">
<div className="grid h-full grid-cols-12 gap-2">
<div className="flex flex-col col-span-7 gap-1 overflow-hidden">
<InfiniteGrid<IBotItem>
columnCount={ 6 }
itemRender={ item => <InventoryBotItemView botItem={ item } /> }
items={ botItems } />
</div>
<div className="flex flex-col col-span-5">
<div className="relative flex flex-col">
<LayoutRoomPreviewerView height={ 140 } roomPreviewer={ roomPreviewer } />
</Column>
</div>
{ selectedBot &&
<Column grow gap={ 2 } justifyContent="between">
<Text grow truncate>{ selectedBot.botData.name }</Text>
<div className="flex flex-col justify-between gap-2 grow">
<span className="truncate grow">{ selectedBot.botData.name }</span>
{ !!roomSession &&
<Button variant="success" onClick={ event => attemptBotPlacement(selectedBot) }>
<NitroButton onClick={ event => attemptBotPlacement(selectedBot) }>
{ LocalizeText('inventory.furni.placetoroom') }
</Button> }
</Column> }
</Column>
</Grid>
</NitroButton> }
</div> }
</div>
</div>
);
}

View File

@ -4,7 +4,9 @@ import { GroupItem, attemptItemPlacement } from '../../../../api';
import { useInventoryFurni } from '../../../../hooks';
import { InfiniteGrid, classNames } from '../../../../layout';
export const InventoryFurnitureItemView: FC<{ groupItem: GroupItem }> = props =>
export const InventoryFurnitureItemView: FC<{
groupItem: GroupItem
}> = props =>
{
const { groupItem = null, ...rest } = props;
const [ isMouseDown, setMouseDown ] = useState(false);

View File

@ -1,15 +1,12 @@
import { Dispatch, FC, SetStateAction, useEffect, useState } from 'react';
import { FaSearch } from 'react-icons/fa';
import { GroupItem, LocalizeText } from '../../../../api';
import { Button } from '../../../../common';
import { NitroButton, NitroInput } from '../../../../layout';
export interface InventoryFurnitureSearchViewProps
{
export const InventoryFurnitureSearchView: FC<{
groupItems: GroupItem[];
setGroupItems: Dispatch<SetStateAction<GroupItem[]>>;
}
export const InventoryFurnitureSearchView: FC<InventoryFurnitureSearchViewProps> = props =>
}> = props =>
{
const { groupItems = [], setGroupItems = null } = props;
const [ searchValue, setSearchValue ] = useState('');
@ -38,10 +35,13 @@ export const InventoryFurnitureSearchView: FC<InventoryFurnitureSearchViewProps>
return (
<div className="flex gap-1">
<input className="form-control form-control-sm" placeholder={ LocalizeText('generic.search') } type="text" value={ searchValue } onChange={ event => setSearchValue(event.target.value) } />
<Button variant="primary">
<NitroInput
placeholder={ LocalizeText('generic.search') }
value={ searchValue }
onChange={ event => setSearchValue(event.target.value) } />
<NitroButton>
<FaSearch className="fa-icon" />
</Button>
</NitroButton>
</div>
);
}

View File

@ -2,19 +2,14 @@ import { InfiniteGrid } from '@layout/InfiniteGrid';
import { GetRoomEngine, GetSessionDataManager, IRoomSession, RoomObjectVariable, RoomPreviewer, Vector3d } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react';
import { DispatchUiEvent, FurniCategory, GroupItem, LocalizeText, UnseenItemCategory, attemptItemPlacement } from '../../../../api';
import { Button, LayoutLimitedEditionCompactPlateView, LayoutRarityLevelView, LayoutRoomPreviewerView } from '../../../../common';
import { LayoutLimitedEditionCompactPlateView, LayoutRarityLevelView, LayoutRoomPreviewerView } from '../../../../common';
import { CatalogPostMarketplaceOfferEvent } from '../../../../events';
import { useInventoryFurni, useInventoryUnseenTracker } from '../../../../hooks';
import { NitroButton } from '../../../../layout';
import { InventoryCategoryEmptyView } from '../InventoryCategoryEmptyView';
import { InventoryFurnitureItemView } from './InventoryFurnitureItemView';
import { InventoryFurnitureSearchView } from './InventoryFurnitureSearchView';
interface InventoryFurnitureViewProps
{
roomSession: IRoomSession;
roomPreviewer: RoomPreviewer;
}
const attemptPlaceMarketplaceOffer = (groupItem: GroupItem) =>
{
const item = groupItem.getLastItem();
@ -26,7 +21,10 @@ const attemptPlaceMarketplaceOffer = (groupItem: GroupItem) =>
DispatchUiEvent(new CatalogPostMarketplaceOfferEvent(item));
}
export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props =>
export const InventoryFurnitureView: FC<{
roomSession: IRoomSession;
roomPreviewer: RoomPreviewer;
}> = props =>
{
const { roomSession = null, roomPreviewer = null } = props;
const [ isVisible, setIsVisible ] = useState(false);
@ -112,7 +110,7 @@ export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props =>
if(!groupItems || !groupItems.length) return <InventoryCategoryEmptyView desc={ LocalizeText('inventory.empty.desc') } title={ LocalizeText('inventory.empty.title') } />;
return (
<div className="grid h-full grid-cols-12 gap-2 overflow-hidden">
<div className="grid h-full grid-cols-12 gap-2">
<div className="flex flex-col col-span-7 gap-1 overflow-hidden">
<InventoryFurnitureSearchView groupItems={ groupItems } setGroupItems={ setFilteredGroupItems } />
<InfiniteGrid<GroupItem>
@ -120,8 +118,8 @@ export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props =>
itemRender={ item => <InventoryFurnitureItemView groupItem={ item } /> }
items={ filteredGroupItems } />
</div>
<div className="flex flex-col col-span-5 overflow-hidden">
<div className="relative flex flex-col overflow-hidden">
<div className="flex flex-col col-span-5">
<div className="relative flex flex-col">
<LayoutRoomPreviewerView height={ 140 } roomPreviewer={ roomPreviewer } />
{ selectedItem && selectedItem.stuffData.isUnique &&
<LayoutLimitedEditionCompactPlateView className="top-2 end-2" position="absolute" uniqueNumber={ selectedItem.stuffData.uniqueNumber } uniqueSeries={ selectedItem.stuffData.uniqueSeries } /> }
@ -130,16 +128,16 @@ export const InventoryFurnitureView: FC<InventoryFurnitureViewProps> = props =>
</div>
{ selectedItem &&
<div className="flex flex-col justify-between gap-2 grow">
<span className="truncate grow">{ selectedItem.name }</span>
<span className="text-sm truncate grow">{ selectedItem.name }</span>
<div className="flex flex-col gap-1">
{ !!roomSession &&
<Button variant="success" onClick={ event => attemptItemPlacement(selectedItem) }>
<NitroButton onClick={ event => attemptItemPlacement(selectedItem) }>
{ LocalizeText('inventory.furni.placetoroom') }
</Button> }
</NitroButton> }
{ (selectedItem && selectedItem.isSellable) &&
<Button onClick={ event => attemptPlaceMarketplaceOffer(selectedItem) }>
<NitroButton onClick={ event => attemptPlaceMarketplaceOffer(selectedItem) }>
{ LocalizeText('inventory.marketplace.sell') }
</Button> }
</NitroButton> }
</div>
</div> }
</div>

View File

@ -1,8 +1,9 @@
import { MouseEventType } from '@nitrots/nitro-renderer';
import { FC, MouseEvent, PropsWithChildren, useState } from 'react';
import { attemptPetPlacement, IPetItem, UnseenItemCategory } from '../../../../api';
import { LayoutGridItem, LayoutPetImageView } from '../../../../common';
import { IPetItem, UnseenItemCategory, attemptPetPlacement } from '../../../../api';
import { LayoutPetImageView } from '../../../../common';
import { useInventoryPets, useInventoryUnseenTracker } from '../../../../hooks';
import { InfiniteGrid } from '../../../../layout';
export const InventoryPetItemView: FC<PropsWithChildren<{ petItem: IPetItem }>> = props =>
{
@ -35,9 +36,9 @@ export const InventoryPetItemView: FC<PropsWithChildren<{ petItem: IPetItem }>>
}
return (
<LayoutGridItem itemActive={ (petItem === selectedPet) } itemUnseen={ unseen } onDoubleClick={ onMouseEvent } onMouseDown={ onMouseEvent } onMouseOut={ onMouseEvent } onMouseUp={ onMouseEvent } { ...rest }>
<InfiniteGrid.Item itemActive={ (petItem === selectedPet) } itemUnseen={ unseen } onDoubleClick={ onMouseEvent } onMouseDown={ onMouseEvent } onMouseOut={ onMouseEvent } onMouseUp={ onMouseEvent } { ...rest }>
<LayoutPetImageView direction={ 3 } figure={ petItem.petData.figureData.figuredata } headOnly={ true } />
{ children }
</LayoutGridItem>
</InfiniteGrid.Item>
);
}

View File

@ -1,18 +1,16 @@
import { GetRoomEngine, IRoomSession, RoomObjectVariable, RoomPreviewer } from '@nitrots/nitro-renderer';
import { FC, useEffect, useState } from 'react';
import { LocalizeText, UnseenItemCategory, attemptPetPlacement } from '../../../../api';
import { AutoGrid, Button, Column, Grid, LayoutRoomPreviewerView, Text } from '../../../../common';
import { IPetItem, LocalizeText, UnseenItemCategory, attemptPetPlacement } from '../../../../api';
import { LayoutRoomPreviewerView } from '../../../../common';
import { useInventoryPets, useInventoryUnseenTracker } from '../../../../hooks';
import { InfiniteGrid, NitroButton } from '../../../../layout';
import { InventoryCategoryEmptyView } from '../InventoryCategoryEmptyView';
import { InventoryPetItemView } from './InventoryPetItemView';
interface InventoryPetViewProps
{
export const InventoryPetView: FC<{
roomSession: IRoomSession;
roomPreviewer: RoomPreviewer;
}
export const InventoryPetView: FC<InventoryPetViewProps> = props =>
}> = props =>
{
const { roomSession = null, roomPreviewer = null } = props;
const [ isVisible, setIsVisible ] = useState(false);
@ -66,25 +64,26 @@ export const InventoryPetView: FC<InventoryPetViewProps> = props =>
if(!petItems || !petItems.length) return <InventoryCategoryEmptyView desc={ LocalizeText('inventory.empty.pets.desc') } title={ LocalizeText('inventory.empty.pets.title') } />;
return (
<Grid>
<Column overflow="hidden" size={ 7 }>
<AutoGrid columnCount={ 5 }>
{ petItems && (petItems.length > 0) && petItems.map(item => <InventoryPetItemView key={ item.petData.id } petItem={ item } />) }
</AutoGrid>
</Column>
<Column overflow="auto" size={ 5 }>
<Column overflow="hidden" position="relative">
<div className="grid h-full grid-cols-12 gap-2">
<div className="flex flex-col col-span-7 gap-1 overflow-hidden">
<InfiniteGrid<IPetItem>
columnCount={ 6 }
itemRender={ item => <InventoryPetItemView petItem={ item } /> }
items={ petItems } />
</div>
<div className="flex flex-col col-span-5">
<div className="relative flex flex-col">
<LayoutRoomPreviewerView height={ 140 } roomPreviewer={ roomPreviewer } />
</Column>
</div>
{ selectedPet && selectedPet.petData &&
<Column grow gap={ 2 } justifyContent="between">
<Text grow truncate>{ selectedPet.petData.name }</Text>
<div className="flex flex-col justify-between gap-2 grow">
<span className="text-sm truncate grow">{ selectedPet.petData.name }</span>
{ !!roomSession &&
<Button variant="success" onClick={ event => attemptPetPlacement(selectedPet) }>
<NitroButton onClick={ event => attemptPetPlacement(selectedPet) }>
{ LocalizeText('inventory.furni.placetoroom') }
</Button> }
</Column> }
</Column>
</Grid>
</NitroButton> }
</div> }
</div>
</div>
);
}

View File

@ -69,9 +69,10 @@ const InfiniteGridRoot = <T,>(props: Props<T>) =>
<div
key={ virtualRow.key + 'a' }
ref={ virtualizer.measureElement }
className={ `grid grid-cols-${ columnCount } gap-1 absolute top-0 left-0 h-[45px] last:pb-0 w-full` }
className={ `grid grid-cols-${ columnCount } gap-1 absolute top-0 left-0 last:pb-0 w-full` }
data-index={ virtualRow.index }
style={ {
height: virtualRow.size,
transform: `translateY(${ virtualRow.start }px)`
} }>
{ Array.from(Array(columnCount)).map((e,i) =>
@ -142,7 +143,7 @@ const InfiniteGridItem = forwardRef<HTMLDivElement, {
ref={ ref }
className={ classNames(
'flex flex-col items-center justify-center cursor-pointer overflow-hidden relative bg-center bg-no-repeat w-full rounded-md border-2',
(!backgroundImageUrl || !backgroundImageUrl.length) && 'nitro-icon icon-loading',
(itemImage && (!backgroundImageUrl || !backgroundImageUrl.length)) && 'nitro-icon icon-loading',
itemActive ? 'border-card-grid-item-active bg-card-grid-item-active' : 'border-card-grid-item-border bg-card-grid-item',
(itemUniqueSoldout || (itemUniqueNumber > 0)) && 'unique-item',
itemUniqueSoldout && 'sold-out',

View File

@ -0,0 +1,44 @@
import { ButtonHTMLAttributes, DetailedHTMLProps, forwardRef, PropsWithChildren } from 'react';
import { classNames } from './classNames';
const classes = {
base: 'inline-flex justify-center items-center gap-2 transition-[background-color] duration-300 transform tracking-wide rounded-md',
disabled: '',
size: {
default: 'px-2 py-0.5 text-sm font-medium',
lg: 'px-5 py-3 text-base font-medium',
xl: 'px-6 py-3.5 text-base font-medium',
},
outline: {
default: 'text-blue-700 hover:text-white border border-blue-700 hover:bg-blue-800 focus:ring-4 focus:outline-none focus:ring-blue-300 dark:border-blue-500 dark:text-blue-500 dark:hover:text-white dark:hover:bg-blue-600 dark:focus:ring-blue-800'
},
color: {
default: 'bg-button-gradient-gray border border-gray-500',
}
}
export const NitroButton = forwardRef<HTMLButtonElement, PropsWithChildren<{
color?: 'default' | 'dark' | 'ghost';
size?: 'default' | 'lg' | 'xl';
outline?: boolean;
}> & DetailedHTMLProps<ButtonHTMLAttributes<HTMLButtonElement>, HTMLButtonElement>>((props, ref) =>
{
const { color = 'default', size = 'default', outline = false, disabled = false, type = 'button', className = null, ...rest } = props;
return (
<button
ref={ ref }
className={ classNames(
classes.base,
classes.size[size],
outline ? classes.outline[color] : classes.color[color],
disabled && classes.disabled,
className
) }
disabled={ disabled }
type={ type }
{ ...rest } />
);
});
NitroButton.displayName = 'NitroButton';

42
src/layout/NitroInput.tsx Normal file
View File

@ -0,0 +1,42 @@
import { DetailedHTMLProps, forwardRef, InputHTMLAttributes, PropsWithChildren } from 'react';
import { classNames } from './classNames';
const classes = {
base: 'block w-full placeholder-gray-400 border border-gray-300 shadow-sm appearance-none',
disabled: '',
size: {
default: 'px-2 py-2 text-sm font-medium',
},
rounded: 'rounded-md',
color: {
default: 'focus:outline-none focus:ring-indigo-500 focus:border-indigo-500',
}
}
export const NitroInput = forwardRef<HTMLInputElement, PropsWithChildren<{
color?: 'default' | 'dark' | 'ghost';
inputSize?: 'xs' | 'sm' | 'default' | 'lg' | 'xl';
rounded?: boolean;
}> & DetailedHTMLProps<InputHTMLAttributes<HTMLInputElement>, HTMLInputElement>>((props, ref) =>
{
const { color = 'default', inputSize = 'default', rounded = true, disabled = false, type = 'text', autoComplete = 'off', className = null, ...rest } = props;
return (
<input
ref={ ref }
autoComplete={ autoComplete }
className={ classNames(
classes.base,
classes.size[inputSize],
rounded && classes.rounded,
classes.color[color],
disabled && classes.disabled,
className
) }
disabled={ disabled }
type={ type }
{ ...rest } />
);
});
NitroInput.displayName = 'NitroInput';

View File

@ -1,5 +1,7 @@
export * from './InfiniteGrid';
export * from './NitroButton';
export * from './NitroCard';
export * from './NitroInput';
export * from './NitroItemCountBadge';
export * from './classNames';
export * from './limited-edition';

View File

@ -19,7 +19,8 @@ const colors = {
};
const boxShadow = {
'inner1px': 'inset 0 0 0 1px rgba(255,255,255,.3)'
'inner1px': 'inset 0 0 0 1px rgba(255,255,255,.3)',
'room-previewer': '-2px -2px rgba(0, 0, 0, 0.4), inset 3px 3px rgba(0, 0, 0, 0.2);'
};
@ -31,6 +32,9 @@ module.exports = {
},
colors: generateShades(colors),
boxShadow,
backgroundImage: {
'button-gradient-gray': 'linear-gradient(to bottom, #e2e2e2 50%, #c8c8c8 50%)',
},
spacing: {
'card-header': '33px',
'card-tabs': '33px',