Skip to content

Commit

Permalink
feat: prepare component
Browse files Browse the repository at this point in the history
  • Loading branch information
nonzzz committed Apr 2, 2024
1 parent f6048ab commit 6068b54
Show file tree
Hide file tree
Showing 12 changed files with 409 additions and 40 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
"@carrotsearch/foamtree": "^3.5.1",
"@geist-ui/core": "^2.3.8",
"@geist-ui/icons": "^1.0.2",
"@radix-ui/react-slot": "^1.0.2",
"@stylexjs/stylex": "^0.5.1",
"@types/node": "^20.7.0",
"@types/react": "^18.2.31",
Expand Down
55 changes: 26 additions & 29 deletions src/client/application.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { RefObject, useCallback, useRef, useState } from 'react'
import { Spacer, Text } from '@geist-ui/core'
import stylex from '@stylexjs/stylex'
import { GeistProvider } from '@geist-ui/core'
import { FoamDataObject } from '@carrotsearch/foamtree'
import { ModuleSize, TreeMap, TreeMapComponent } from './components/tree-map'
import { ApplicationContext, SIZE_RECORD } from './context'
Expand Down Expand Up @@ -45,33 +44,31 @@ export function App() {
}, [])

return (
<GeistProvider>
<ApplicationContext.Provider
value={initialValue}
>
<div {...stylex.props(styles.app)}>
<SidebarProvider>
<Sidebar foamModule={foamModule} mode={sizes} onModeChange={handleModeChange} onVisibleChange={(s) => setTooltipVisible(!s)} />
</SidebarProvider>
<TreeMap ref={(instance: any) => treeMapRef.current = instance} onGroupHover={handleGroupHover} />
<Tooltip visible={tooltipVisible}>
{tooltipContent && (
<>
<Text p b font="14px">{tooltipContent.label}</Text>
<Spacer h={0.5} />
<ModuleSize module={tooltipContent} sizes="statSize" checkedSizes={sizes} />
<ModuleSize module={tooltipContent} sizes="parsedSize" checkedSizes={sizes} />
<ModuleSize module={tooltipContent} sizes="gzipSize" checkedSizes={sizes} />
<Text p font="12px">
path:
{' '}
{tooltipContent.id}
</Text>
</>
)}
</Tooltip>
</div>
</ApplicationContext.Provider>
</GeistProvider>
<ApplicationContext.Provider
value={initialValue}
>
<div {...stylex.props(styles.app)}>
<SidebarProvider>
<Sidebar foamModule={foamModule} mode={sizes} onModeChange={handleModeChange} onVisibleChange={(s) => setTooltipVisible(!s)} />
</SidebarProvider>
<TreeMap ref={(instance: any) => treeMapRef.current = instance} onGroupHover={handleGroupHover} />
<Tooltip visible={tooltipVisible}>
{tooltipContent && (
<>
<Text p b font="14px">{tooltipContent.label}</Text>
<Spacer h={0.5} />
<ModuleSize module={tooltipContent} sizes="statSize" checkedSizes={sizes} />
<ModuleSize module={tooltipContent} sizes="parsedSize" checkedSizes={sizes} />
<ModuleSize module={tooltipContent} sizes="gzipSize" checkedSizes={sizes} />
<Text p font="12px">
path:
{' '}
{tooltipContent.id}
</Text>
</>
)}
</Tooltip>
</div>
</ApplicationContext.Provider>
)
}
88 changes: 88 additions & 0 deletions src/client/components/button/button-drip.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, { useEffect, useRef } from 'react'
import * as stylex from '@stylexjs/stylex'

interface Props {
x: number
y: number
onCompleted: () => void
color: string
}

const defaultProps = {
x: 0,
y: 0
}

export type ButtonDrip = Props

const expand = stylex.keyframes({
'0%': {
opacity: 0,
transform: 'scale(1)'
},
'30%': {
opacity: 1
},
'80%': {
opacity: 0.5
},
'100%': {
transform: 'scale(28)',
opacity: 0
}
})

const styles = stylex.create({
drip: {
position: 'absolute',
left: 0,
right: 0,
top: 0,
bottom: 0
},
svg: {
position: 'absolute',
animationName: expand,
animationDuration: '350ms',
animationTimingFunction: 'ease-in',
animationFillMode: 'forwards',
width: '1rem',
height: '1rem'
}
})

const ButtonDrip: React.FC<ButtonDrip> = ({
x,
y,
color,
onCompleted
}: ButtonDrip & typeof defaultProps) => {
const dripRef = useRef<HTMLDivElement>(null)
const top = Number.isNaN(+y) ? 0 : y - 10
const left = Number.isNaN(+x) ? 0 : x - 10

useEffect(() => {
if (!dripRef.current) return
dripRef.current.addEventListener('animationend', onCompleted)
return () => {
if (!dripRef!.current) return
dripRef!.current.removeEventListener('animationend', onCompleted)
}
})

return (
<div ref={dripRef} {...stylex.props(styles.drip)}>
<svg width="20" height="20" viewBox="0 0 20 20" {...stylex.props(styles.svg)} style={{ top, left }}>
<g stroke="none" strokeWidth="1" fill="none" fillRule="evenodd">
<g fill={color}>
<rect width="100%" height="100%" rx="10" />
</g>
</g>
</svg>
</div>
)
}

ButtonDrip.defaultProps = defaultProps
ButtonDrip.displayName = 'GeistButtonDrip'
export default ButtonDrip
144 changes: 144 additions & 0 deletions src/client/components/button/button.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,144 @@
import React from 'react'
import * as stylex from '@stylexjs/stylex'
import { useClasses, useScale, withScale } from '@geist-ui/core'
import type { ScaleConfig } from '@geist-ui/core'

export type SCALES = ScaleConfig['SCALES']

interface Props {
icon?: React.ReactNode
auto?: boolean
type?: 'default' | 'secondary'
}

type ButtonProps = Props & Omit<React.ButtonHTMLAttributes<HTMLButtonElement>, keyof Props>

const defaultProps: Props = {
auto: false,
type: 'default'
}

const styles = stylex.create({
button: {
boxSizing: 'border-box',
display: 'inline-block',
borderRadius: '6px',
fontWeight: 400,
userSelect: 'none',
outline: 'none',
textTransform: 'capitalize',
justifyContent: 'center',
textAlign: 'center',
whiteSpace: 'nowrap',
transition: 'background-color 200ms ease 0s, box-shadow 200ms ease 0ms, border 200ms ease 0ms, color 200ms ease 0ms',
position: 'relative',
overflow: 'hidden',
color: {
default: '#666',
':hover': '#000'
},
backgroundColor: '#fff',
border: '1px solid #eaeaea',
cursor: 'pointer',
width: 'initial',
':hover': {
borderColor: '#000'
}
},
auto: {
width: 'auto'
},
layout: (scale: SCALES, auto: boolean) => ({
minWidth: auto ? 'min-content' : scale.width(10.5),
lineHeight: scale.height(2.5),
fontSize: scale.font(0.875),
height: scale.height(2.5),
padding: `${scale.pt(0)} ${auto ? scale.pr(1.15) : scale.pr(1.375)} ${scale.pt(0)} ${auto ? scale.pl(1.15) : scale.pl(1.375)}`,
margin: `${scale.mt(0)} ${scale.mr(0)} ${scale.mb(0)} ${scale.ml(0)}`,
'--button-height': scale.height(2.5),
'--button-icon-padding': scale.pl(0.727)
}),
text: {
position: 'relative',
zIndex: 1,
display: 'inline-flex',
justifyContent: 'center',
textAlign: 'center',
lineHeight: 'inherit',
top: '-1px'
},
icon: {
position: 'absolute',
right: 'auto',
top: '50%',
transform: 'translateY(-50%)',
display: 'flex',
justifyContent: 'center',
color: '#666',
alignItems: 'center',
zIndex: 1,
':not(#__unused__) svg': {
background: 'transparent',
height: 'calc(var(--button-height) / 2.35)',
width: 'calc(var(--button-height) / 2.35)'
}
},
single: {
position: 'static',
transform: 'none'
},
autoPaddingLeft: {
paddingLeft: 'calc(var(--button-height) / 2 + var(--button-icon-padding) * .5)'
},
autoPaddingRight: {
paddingRight: 'calc(var(--button-height) / 2 + var(--button-icon-padding) * .5)'
},
secondary: {
backgroundColor: '#000',
borderColor: '#000',
color: '#fff'
}
})

function getButtonChildrenWithIcon(auto: boolean, icon: React.ReactNode, children: React.ReactNode) {
if (!icon) return <div {...stylex.props(styles.text)}>{children}</div>
if (icon && !children) return <span {...stylex.props(styles.icon, styles.single)}>{icon}</span>
return (
<>
<span {...stylex.props(styles.icon)}>{icon}</span>
<div {...stylex.props(styles.text)}>{children}</div>
</>
)
}

const ButtonComponent = React.forwardRef<HTMLButtonElement, ButtonProps>((props, ref) => {
const {
type = 'default',
className: userClassName, style: userStyle, auto = false,
icon, children, ...rest
} = props
const { SCALES } = useScale()

const { className, style } = stylex.props(styles.button,
styles.layout(SCALES, auto), auto && styles.auto, type === 'secondary' &&
styles.secondary)

const classes = useClasses('button', className, userClassName)

return (
<button
ref={ref}
className={classes}
style={{ ...style, ...userStyle }}
{...rest}
type="button"
>
{getButtonChildrenWithIcon(auto, icon, children)}
</button>
)
})

ButtonComponent.displayName = 'Button'
ButtonComponent.defaultProps = defaultProps

export const Button = withScale(ButtonComponent)
1 change: 1 addition & 0 deletions src/client/components/button/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './button'
1 change: 1 addition & 0 deletions src/client/components/input/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from './input'
74 changes: 74 additions & 0 deletions src/client/components/input/input.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
import React from 'react'
import * as stylex from '@stylexjs/stylex'
import { useClasses, useScale, withScale } from '@geist-ui/core'
import type { SCALES } from '../button'

interface Props {
clearable?: boolean
}

type InputProps = Props & Omit<React.InputHTMLAttributes<HTMLInputElement>, keyof Props>

const styles = stylex.create({
label: (scale: SCALES) => ({
fontSize: scale.font(0.875),
width: scale.width(1, 'initial'),
padding: `${scale.pt(0)} ${scale.pr(0)} ${scale.pb(0)} ${scale.pl(0)}`,
margin: `${scale.mt(0)} ${scale.mr(0)} ${scale.mb(0)} ${scale.ml(0)}`,
'--input-height': scale.height(2.25)
}),
container: (scale: SCALES) => ({
display: 'inline-flex',
alignItems: 'center',
width: scale.width(1, 'initial'),
height: 'var(--input-height)'
}),
wrapper: {
display: 'inline-flex',
verticalAlign: 'middle',
alignItems: 'center',
height: '100%',
flex: 1,
userSelect: 'none',
borderRadius: '6px',
border: '1px solid #666',
transition: 'border 0.2s ease 0s, color 0.2s ease 0s'
},
input: (scale: SCALES) => ({
padding: 0,
boxShadow: 'none',
margin: '0 0.25em 0.625em',
fontSize: scale.font(0.875),
backgroundColor: 'transparent',
border: 'none',
color: '#000',
outline: 'none',
borderRadius: 0,
width: '100%',
minWidth: 0,
WebkitAppearance: 'none'
})
})

const InputComponent = React.forwardRef<HTMLInputElement, InputProps>((props, ref) => {
const { className: userClassName, style: userStyle, type, ...rest } = props

const { SCALES } = useScale()
const { className, style } = stylex.props(styles.input(SCALES))

const classes = useClasses('input', className, userClassName)

return (
<div {...stylex.props(styles.label(SCALES))}>
<div {...stylex.props(styles.container(SCALES))}>
<div {...stylex.props(styles.wrapper)}>
<input type={type} ref={ref} className={classes} style={{ ...style, ...userStyle }} {...rest} />
</div>
</div>
</div>
)
})

InputComponent.displayName = 'Input'

export const Input = withScale(InputComponent)
Loading

0 comments on commit 6068b54

Please sign in to comment.