-
Notifications
You must be signed in to change notification settings - Fork 22
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Statistics, Search and Logo Components (Landing Page) (#2116)
* wip: statistics stack component and main page layout * feat: styled statistics component and kadena logo * chore: changeset file * fix: lint fixes * fix: small corrections and fixes * Search Combobox Component (#2166) * wip: introduced dropdown * wip: search combobox * feast: changes in statistics stack and index * wip: search combobox * feat: search component * fix: linting fixes * chore: changeset file * feat: implement polling on statistics data * fix: small design fixes * Delete .changeset/forty-frogs-fly.md * Update brave-frogs-argue.md * fix: typo fix * feat: remove border from select * wip: added statistics grid for mobile view and added react-responsive * wip: apply getMediaQuery * feat: tweak responsive values for search input and statistics grid * fix: linting * feat: adjust responsiveness * feat: replace library causing hydration issue to another alternative compatible with ssr * fix: lint formatting * fix: remove react-responsive * feat: apply media styling with updated variables * fix: remove lib from devDependencies
- Loading branch information
1 parent
42b0a1b
commit 9231982
Showing
13 changed files
with
543 additions
and
7 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
--- | ||
"@kadena/explorer": patch | ||
--- | ||
|
||
Implemented statistics, search and logo components and necessary logic |
34 changes: 34 additions & 0 deletions
34
packages/apps/explorer/src/components/search/search.css.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { atoms, responsiveStyle } from '@kadena/react-ui/styles'; | ||
import { style } from '@vanilla-extract/css'; | ||
|
||
export const searchBoxClass = style({ | ||
...responsiveStyle({ | ||
md: { | ||
width: 525, | ||
}, | ||
sm: { | ||
width: 475, | ||
}, | ||
xs: { | ||
width: 325, | ||
}, | ||
}), | ||
}); | ||
|
||
export const searchInputClass = style([ | ||
atoms({ | ||
backgroundColor: 'base.default', | ||
fontSize: 'md', | ||
fontFamily: 'primaryFont', | ||
outline: 'none', | ||
}), | ||
{ | ||
height: 55, | ||
border: 'none', | ||
width: '75%', | ||
}, | ||
]); | ||
|
||
export const searchBadgeBoxClass = style({ | ||
width: '20%', | ||
}); |
229 changes: 229 additions & 0 deletions
229
packages/apps/explorer/src/components/search/search.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,229 @@ | ||
import { truncateValues } from '@/services/format'; | ||
import { MonoSearch } from '@kadena/react-icons/system'; | ||
import { Badge, Box } from '@kadena/react-ui'; | ||
import { atoms } from '@kadena/react-ui/styles'; | ||
import React, { useState } from 'react'; | ||
import { | ||
searchBadgeBoxClass, | ||
searchBoxClass, | ||
searchInputClass, | ||
} from './search.css'; | ||
|
||
export type SearchItemTitle = | ||
| 'Account' | ||
| 'Request Key' | ||
| 'Block Height' | ||
| 'Block Hash' | ||
| 'Events'; | ||
|
||
export interface ISearchItem { | ||
title: SearchItemTitle; | ||
disabled?: boolean; | ||
} | ||
interface ISearchComponentProps { | ||
placeholder: string; | ||
searchItems: ISearchItem[]; | ||
} | ||
|
||
const SearchCombobox: React.FC<ISearchComponentProps> = ({ | ||
placeholder, | ||
searchItems, | ||
}) => { | ||
const [isEditing, setIsEditing] = useState(false); | ||
const [searchOption, setSearchOption] = useState<number | null>(null); | ||
const [searchValue, setSearchValue] = useState<string>(''); | ||
const [optionClicked, setOptionClicked] = useState(false); | ||
const [escapePressed, setEscapePressed] = useState(false); | ||
|
||
const setOptionsDisabledExcept = (exceptIndex: number): void => { | ||
searchItems.forEach((item, index) => { | ||
if (index !== exceptIndex) { | ||
item.disabled = true; | ||
} | ||
}); | ||
}; | ||
|
||
const inferOption = (value: string): SearchItemTitle | undefined => { | ||
if ( | ||
value.toLocaleLowerCase().startsWith('k:') || | ||
value.toLocaleLowerCase().startsWith('w:') | ||
) { | ||
return 'Account'; | ||
} else if (value.includes('.')) { | ||
return 'Events'; | ||
} else if (value.length === 43) { | ||
return 'Request Key'; | ||
} else if (/^\d+$/.test(value)) { | ||
return 'Block Height'; | ||
} | ||
|
||
return undefined; | ||
}; | ||
|
||
const enableAllOptions = (): void => { | ||
searchItems.forEach((item) => { | ||
item.disabled = false; | ||
}); | ||
}; | ||
|
||
const handleSearch = (value: string, option: number | null): void => {}; | ||
|
||
const handleSearchValueChange = ( | ||
e: React.ChangeEvent<HTMLInputElement>, | ||
): void => { | ||
setSearchValue(e.target.value); | ||
|
||
if (escapePressed || optionClicked) return; | ||
|
||
const inferedOption = inferOption(e.target.value); | ||
if (inferedOption === 'Account') { | ||
setSearchOption(0); | ||
setOptionsDisabledExcept(0); | ||
} | ||
if (inferedOption === 'Request Key') { | ||
setSearchOption(1); | ||
setOptionsDisabledExcept(1); | ||
} | ||
|
||
if (inferedOption === 'Block Height') { | ||
setSearchOption(2); | ||
setOptionsDisabledExcept(2); | ||
} | ||
|
||
if (!inferedOption || inferedOption === undefined) { | ||
setSearchOption(null); | ||
enableAllOptions(); | ||
} | ||
}; | ||
|
||
const handleSearchValueKeyDown = ( | ||
e: React.KeyboardEvent<HTMLDivElement>, | ||
): void => { | ||
if (e.key === 'ArrowDown') { | ||
e.preventDefault(); | ||
setSearchOption((prev) => | ||
prev === null ? 0 : Math.min(prev + 1, searchItems.length - 1), | ||
); | ||
} else if (e.key === 'ArrowUp') { | ||
e.preventDefault(); | ||
setSearchOption((prev) => (prev === null ? 0 : Math.max(prev - 1, 0))); | ||
} else if (e.key === 'Enter') { | ||
e.preventDefault(); | ||
setIsEditing(false); | ||
setEscapePressed(false); | ||
setOptionClicked(false); | ||
handleSearch(searchValue, searchOption); | ||
} else if (e.key === 'Escape') { | ||
setOptionClicked(false); | ||
setSearchOption(null); | ||
setEscapePressed(true); | ||
enableAllOptions(); | ||
} | ||
}; | ||
|
||
return ( | ||
<> | ||
<Box | ||
paddingInline={'xxl'} | ||
display={'flex'} | ||
flexDirection={'column'} | ||
onKeyDown={(e) => handleSearchValueKeyDown(e)} | ||
onBlur={() => { | ||
if (!optionClicked) { | ||
setIsEditing(false); | ||
} | ||
}} | ||
> | ||
<Box | ||
display={'inline-flex'} | ||
flexDirection={'row'} | ||
alignItems={'center'} | ||
borderStyle="solid" | ||
borderWidth="hairline" | ||
backgroundColor="base.default" | ||
gap={'sm'} | ||
paddingInlineStart={'sm'} | ||
paddingInlineEnd={'sm'} | ||
className={searchBoxClass} | ||
> | ||
<MonoSearch /> | ||
|
||
<input | ||
type="text" | ||
placeholder={placeholder} | ||
value={searchValue} | ||
onChange={(e) => handleSearchValueChange(e)} | ||
onFocus={() => setIsEditing(true)} | ||
className={searchInputClass} | ||
/> | ||
|
||
{searchOption !== null && ( | ||
<Box | ||
display={'flex'} | ||
justifyContent={'flex-end'} | ||
className={searchBadgeBoxClass} | ||
> | ||
<Badge size="lg">{searchItems[searchOption].title}</Badge> | ||
</Box> | ||
)} | ||
</Box> | ||
|
||
{isEditing && ( | ||
<div | ||
className={atoms({ | ||
display: 'grid', | ||
borderStyle: 'solid', | ||
borderWidth: 'hairline', | ||
backgroundColor: 'base.@active', | ||
fontSize: 'sm', | ||
fontFamily: 'primaryFont', | ||
})} | ||
> | ||
{searchItems.map((item, index) => ( | ||
<Box | ||
key={index} | ||
onMouseDown={() => setOptionClicked(true)} | ||
onClick={() => { | ||
if (!item.disabled) { | ||
setSearchOption(index); | ||
setIsEditing(false); | ||
} | ||
}} | ||
style={{ | ||
gridTemplateColumns: '1fr 3fr', | ||
borderLeft: index === searchOption ? 'solid' : 'none', | ||
}} | ||
className={atoms({ | ||
display: 'grid', | ||
alignItems: 'flex-start', | ||
paddingInlineStart: 'md', | ||
cursor: item.disabled ? 'not-allowed' : 'pointer', | ||
backgroundColor: | ||
index === searchOption ? 'base.@active' : 'base.default', | ||
width: '100%', | ||
})} | ||
> | ||
<div | ||
className={atoms({ | ||
alignItems: 'flex-start', | ||
})} | ||
> | ||
{item.title} | ||
</div> | ||
<div | ||
className={atoms({ | ||
alignItems: 'flex-end', | ||
})} | ||
> | ||
{truncateValues(searchValue)} | ||
</div> | ||
</Box> | ||
))} | ||
</div> | ||
)} | ||
</Box> | ||
</> | ||
); | ||
}; | ||
|
||
export default SearchCombobox; |
38 changes: 38 additions & 0 deletions
38
packages/apps/explorer/src/components/statistics/statistics-grid.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { Grid, Stack, Text } from '@kadena/react-ui'; | ||
import { atoms } from '@kadena/react-ui/styles'; | ||
import React from 'react'; | ||
|
||
interface ISearchComponentProps { | ||
data: { label: string; value: string }[]; | ||
} | ||
|
||
const StatisticsGrid: React.FC<ISearchComponentProps> = ({ data }) => { | ||
return ( | ||
<Grid columns={2} borderStyle="solid" borderWidth="hairline"> | ||
{data.map((item) => ( | ||
<Stack | ||
flexDirection={'column'} | ||
alignItems={'center'} | ||
padding={'sm'} | ||
borderStyle="solid" | ||
borderWidth="hairline" | ||
key={`statistic-stack-${item.label}`} | ||
> | ||
<Text variant="code">{item.value}</Text> | ||
<Text | ||
variant="code" | ||
bold | ||
size="smallest" | ||
className={atoms({ | ||
flexWrap: 'nowrap', | ||
})} | ||
> | ||
{item.label} | ||
</Text> | ||
</Stack> | ||
))} | ||
</Grid> | ||
); | ||
}; | ||
|
||
export default StatisticsGrid; |
79 changes: 79 additions & 0 deletions
79
packages/apps/explorer/src/components/statistics/statistics-stack.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,79 @@ | ||
import { SpireKeyKdacolorLogoWhite } from '@kadena/react-icons/product'; | ||
import { MonoHub } from '@kadena/react-icons/system'; | ||
import { Button, Select, SelectItem, Stack, Text } from '@kadena/react-ui'; | ||
import { atoms } from '@kadena/react-ui/styles'; | ||
import React, { useState } from 'react'; | ||
import { Media } from '../layout/media'; | ||
import { borderStyleClass, statisticsSpireKeyClass } from './statistics.css'; | ||
|
||
interface IStatisticsStackProps { | ||
data: { label: string; value: string }[]; | ||
} | ||
|
||
const StatisticsStack: React.FC<IStatisticsStackProps> = ({ data }) => { | ||
const [selectedNetwork, setSelectedNetwork] = useState('Mainnet'); | ||
|
||
return ( | ||
<Stack flexDirection={'row'}> | ||
<Stack flexDirection={'row'}> | ||
{data.map((item) => ( | ||
<Stack | ||
flexDirection={'column'} | ||
alignItems={'center'} | ||
padding={'sm'} | ||
borderStyle="solid" | ||
borderWidth="hairline" | ||
key={`statistic-stack-${item.label}`} | ||
> | ||
<Text variant="code">{item.value}</Text> | ||
<Text variant="code" bold size="smallest"> | ||
{item.label} | ||
</Text> | ||
</Stack> | ||
))} | ||
|
||
<Stack flexDirection={'row'}> | ||
<div className={borderStyleClass}> | ||
<Media greaterThanOrEqual="md"> | ||
<Button variant="transparent" endVisual={<MonoHub />}> | ||
Graph | ||
</Button> | ||
</Media> | ||
<Media lessThan="md"> | ||
<Button variant="transparent" endVisual={<MonoHub />} /> | ||
</Media> | ||
</div> | ||
|
||
<div className={borderStyleClass}> | ||
<Select | ||
defaultSelectedKey={selectedNetwork} | ||
fontType="code" | ||
size="lg" | ||
className={atoms({ | ||
height: '100%', | ||
})} | ||
onSelectionChange={(value) => | ||
setSelectedNetwork(value.toString()) | ||
} | ||
> | ||
<SelectItem key={'Mainnet'} textValue="Mainnet"> | ||
Mainnet | ||
</SelectItem> | ||
<SelectItem key={'Testnet'} textValue="Testnet"> | ||
Testnet | ||
</SelectItem> | ||
</Select> | ||
</div> | ||
<div className={statisticsSpireKeyClass}> | ||
<Button | ||
variant="transparent" | ||
startVisual={<SpireKeyKdacolorLogoWhite />} | ||
/> | ||
</div> | ||
</Stack> | ||
</Stack> | ||
</Stack> | ||
); | ||
}; | ||
|
||
export default StatisticsStack; |
Oops, something went wrong.