Skip to content

Commit

Permalink
Merge pull request #63 from omnifed/62-feature-add-icons-page
Browse files Browse the repository at this point in the history
62 feature add icons page
  • Loading branch information
caseybaggz committed May 16, 2024
2 parents 34efe9a + 42da42f commit f913c39
Show file tree
Hide file tree
Showing 12 changed files with 447 additions and 32 deletions.
1 change: 0 additions & 1 deletion docs/app/components/Nav.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ export function Nav() {
columns: 3,
gridTemplateRows: '1fr 1fr',
gap: '0',
mb: '12',
position: 'sticky',
md: {
gridTemplateRows: '1fr',
Expand Down
1 change: 1 addition & 0 deletions docs/app/components/PageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ export function PageLayout(props: PropsWithChildren<PageLayoutProps>) {
h: PAGE_MINUS_HEADER,
overflowX: 'hidden',
position: 'relative',
pt: '12',
md: {
overflowX: 'initial',
},
Expand Down
61 changes: 61 additions & 0 deletions docs/app/icons/[name]/components/CopyToClipboard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
'use client'

import { css } from '@/styled-system/css'
import { Copy } from '@cerberus-design/icons'
import { Show } from '@cerberus-design/react'
import { useEffect, useState } from 'react'

interface CopyToClipboardProps {
componentName: string
}

export default function CopyToClipboard(props: CopyToClipboardProps) {
const [copied, setCopied] = useState<boolean>(false)

function handleCopy() {
navigator.clipboard.writeText(`<${props.componentName} />`)
setCopied(true)
}

useEffect(() => {
if (copied) {
const timeout = setTimeout(() => {
setCopied(false)
}, 2000)

return () => clearTimeout(timeout)
}
}, [copied])

return (
<button
className={css({
alignItems: 'center',
appearance: 'none',
display: 'inline-flex',
bg: 'action.bg.initial',
border: 'none',
color: 'action.text.initial',
gap: '2',
h: '3.3rem',
pxi: '6',
rounded: 'full',
transition: 'background-color 0.2s ease-in-out',
'&:hover': {
bg: 'action.bg.hover',
},
})}
onClick={handleCopy}
type="button"
>
<Copy
className={css({
opacity: '0.5',
})}
/>
<Show when={copied} fallback={<>Copy JSX</>}>
Copied!
</Show>
</button>
)
}
107 changes: 107 additions & 0 deletions docs/app/icons/[name]/components/IconDetails.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,107 @@
import * as Icons from '@cerberus-design/icons'
import { container, grid, gridItem, vstack } from '@/styled-system/patterns'
import CopyToClipboard from './CopyToClipboard'

const sizes = [16, 20, 24, 32]
const PREVIEW_SIZE = '15rem'

interface IconDetailsProps {
iconName: string
}

export default function IconDetails(props: IconDetailsProps) {
const Icon = Icons[props.iconName as keyof typeof Icons]

return (
<div className={container()}>
<section
className={grid({
columns: 12,
lg: {
borderBottom: '1px solid',
borderColor: 'neutral.border.initial',
mb: '8',
pb: '8',
},
})}
>
<div
className={gridItem({
gridColumnStart: 1,
gridColumnEnd: 10,
lg: {
gridColumnStart: 1,
gridColumnEnd: 5,
},
})}
>
<div
className={vstack({
justify: 'center',
bgColor: 'neutral.surface.200',
h: PREVIEW_SIZE,
rounded: 'md',
})}
>
<Icon size="12rem" />
</div>
</div>

<div
className={gridItem({
gridColumnStart: 10,
gridColumnEnd: 13,
lg: {
gridColumnStart: 5,
gridColumnEnd: 6,
},
})}
>
<ul
className={vstack({
justify: 'center',
gap: '2',
h: PREVIEW_SIZE,
})}
>
{sizes.map((size) => (
<li
className={vstack({
bgColor: 'neutral.surface.200',
h: `calc(${PREVIEW_SIZE} / 4)`,
justify: 'center',
rounded: 'md',
w: 'full',
})}
key={size}
title={`Size ${size}`}
>
<Icon size={size} />
</li>
))}
</ul>
</div>

<div
className={gridItem({
gridColumnStart: 1,
gridColumnEnd: 13,
mt: '4',
lg: {
gridColumnStart: 7,
},
})}
>
<h2>{props.iconName}</h2>
<p>
Available in {sizes.length} sizes: {sizes.join(' ')}.
</p>

<div>
<CopyToClipboard componentName={props.iconName} />
</div>
</div>
</section>
</div>
)
}
23 changes: 23 additions & 0 deletions docs/app/icons/[name]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import OnThisPage from '../../components/OnThisPage'
import { PageMainContent, PageSections } from '../../components/PageLayout'
import IconDetails from './components/IconDetails'

interface IconDetailsPageProps {
params: {
name: string
}
}

export default function IconDetailsPage(props: IconDetailsPageProps) {
return (
<>
<PageMainContent>
<IconDetails iconName={props.params.name} />
</PageMainContent>

<PageSections>
<OnThisPage />
</PageSections>
</>
)
}
68 changes: 68 additions & 0 deletions docs/app/icons/all/components/icons-list.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
import { css, cx } from '@/styled-system/css'
import { grid, gridItem, vstack } from '@/styled-system/patterns'
import * as Icons from '@cerberus-design/icons'
import Link from 'next/link'

const listOfIcons = Object.entries(Icons)

interface IconsListProps {
filter?: string
}

export default function IconsList(props: IconsListProps) {
const { filter } = props
const filteredIcons = listOfIcons.filter(([name]) =>
name.toLowerCase().includes(filter?.toLocaleLowerCase() ?? ''),
)

return (
<ul
className={cx(
css({
marginInlineStart: 'auto',
marginInlineEnd: 'auto',
w: 'max-content',
md: {
gridTemplateColumns: 'repeat(13, 4rem)',
gap: '3.5',
},
}),
grid({
columns: 6,
mt: '8',
}),
)}
>
{filteredIcons.map(([name, Icon]) => (
<li
className={gridItem({
h: '3rem',
md: {
h: '4rem',
},
})}
key={name}
title={name}
>
<Link
className={vstack({
bgColor: 'neutral.surface.200',
h: 'full',
justify: 'center',
w: 'full',
rounded: 'md',
transition: 'background-color, color 250ms ease-in-out',
_hover: {
bgColor: 'action.navigation.initial',
color: 'neutral.surface.100 !important',
},
})}
href={`/icons/${name}`}
>
<Icon size={24} />
</Link>
</li>
))}
</ul>
)
}
71 changes: 71 additions & 0 deletions docs/app/icons/all/components/searchable-icons.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
'use client'

import { Suspense, lazy, useState, type ChangeEvent } from 'react'
import { css } from '@/styled-system/css'
import { container } from '@/styled-system/patterns'

const IconsList = lazy(() => import('./icons-list'))

export default function SearchableIcons() {
const [filter, setFilter] = useState<string>('')

function handleChange(event: ChangeEvent<HTMLInputElement>) {
setFilter(event.currentTarget.value)
}

return (
<div
className={container({
position: 'relative',
mt: '4',
})}
>
<div
className={css({
bgColor: 'neutral.surface.100',
position: 'sticky',
py: '2',
top: '0',
zIndex: 'sticky',
_after: {
bottom: '-1.8rem',
bgGradient: 'to-b',
content: '""',
gradientFrom: 'neutral.surface.100',
gradientTo: 'transparent',
h: '8',
left: '0',
position: 'absolute',
right: '0',
},
})}
>
<label aria-label="search icons">
<input
className={css({
border: '1px solid',
borderColor: 'neutral.border.initial',
borderRadius: 'full',
p: '4',
w: 'full',
_focus: {
outlineOffset: '4px',
outline: '2px solid',
outlineColor: 'info.border.initial',
},
})}
name="search"
placeholder="Search icons"
onChange={handleChange}
type="text"
value={filter}
/>
</label>
</div>

<Suspense>
<IconsList filter={filter} />
</Suspense>
</div>
)
}
19 changes: 19 additions & 0 deletions docs/app/icons/all/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import OnThisPage from '../../components/OnThisPage'
import { PageMainContent, PageSections } from '../../components/PageLayout'
import SearchableIcons from './components/searchable-icons'

export default function AllIconsPage() {
return (
<>
<PageMainContent>
<main>
<SearchableIcons />
</main>
</PageMainContent>

<PageSections>
<OnThisPage />
</PageSections>
</>
)
}
Loading

0 comments on commit f913c39

Please sign in to comment.