Skip to content

Commit

Permalink
simplify className usage
Browse files Browse the repository at this point in the history
  • Loading branch information
samuelcolvin committed Nov 20, 2023
1 parent 95cf655 commit b0c93c4
Show file tree
Hide file tree
Showing 13 changed files with 131 additions and 61 deletions.
99 changes: 69 additions & 30 deletions packages/fastui/src/components/FormField.tsx
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
import { FC, useState } from 'react'

import { ClassName, useClassNameGenerator } from '../hooks/className'
import { ClassName, useClassName } from '../hooks/className'

interface BaseFormFieldProps {
name: string
title: string[]
required: boolean
locked: boolean
error?: string
description?: string
className?: ClassName
}

Expand All @@ -17,27 +18,29 @@ interface FormFieldInputProps extends BaseFormFieldProps {
type: 'FormFieldInput'
htmlType?: 'text' | 'date' | 'datetime-local' | 'time' | 'email' | 'url' | 'file' | 'number'
initial?: string | number
placeholder?: string
}

export const FormFieldInputComp: FC<FormFieldInputProps> = (props) => {
const { className, name, title, required, htmlType, locked } = props
const { name, placeholder, required, htmlType, locked } = props
const [value, setValue] = useState(props.initial ?? '')

// TODO placeholder
return (
<div className={useClassNameGenerator(className, props)}>
<label htmlFor={name}>
<Title title={title} />
</label>
<div className={useClassName(props)}>
<Label {...props} />
<input
type={htmlType}
className={useClassName(props, { el: 'input' })}
value={value}
onChange={(e) => setValue(e.target.value)}
id={inputId(props)}
name={name}
required={required}
disabled={locked}
placeholder={placeholder}
aria-describedby={descId(props)}
/>
{props.error ? <div>Error: {props.error}</div> : null}
<ErrorDescription {...props} />
</div>
)
}
Expand All @@ -48,14 +51,22 @@ interface FormFieldCheckboxProps extends BaseFormFieldProps {
}

export const FormFieldCheckboxComp: FC<FormFieldCheckboxProps> = (props) => {
const { className, name, title, required, locked } = props
const { name, required, locked } = props

return (
<div className={useClassNameGenerator(className, props)}>
<label htmlFor={name}>
<Title title={title} />
</label>
<input type="checkbox" defaultChecked={!!props.initial} name={name} required={required} disabled={locked} />
{props.error ? <div>Error: {props.error}</div> : null}
<div className={useClassName(props)}>
<Label {...props} />
<input
type="checkbox"
className={useClassName(props, { el: 'input' })}
defaultChecked={!!props.initial}
id={inputId(props)}
name={name}
required={required}
disabled={locked}
aria-describedby={descId(props)}
/>
<ErrorDescription {...props} />
</div>
)
}
Expand All @@ -67,20 +78,21 @@ interface FormFieldSelectProps extends BaseFormFieldProps {
}

export const FormFieldSelectComp: FC<FormFieldSelectProps> = (props) => {
const { className, name, title, required, locked, choices } = props
const { name, required, locked, choices } = props
const [value, setValue] = useState(props.initial ?? '')

return (
<div className={useClassNameGenerator(className, props)}>
<label htmlFor={name}>
<Title title={title} />
</label>
<div className={useClassName(props)}>
<Label {...props} />
<select
id={inputId(props)}
className={useClassName(props, { el: 'select' })}
value={value}
onChange={(e) => setValue(e.target.value)}
name={name}
required={required}
disabled={locked}
aria-describedby={descId(props)}
>
<option></option>
{choices.map(([value, label]) => (
Expand All @@ -89,7 +101,7 @@ export const FormFieldSelectComp: FC<FormFieldSelectProps> = (props) => {
</option>
))}
</select>
{props.error ? <div>Error: {props.error}</div> : null}
<ErrorDescription {...props} />
</div>
)
}
Expand All @@ -101,27 +113,54 @@ interface FormFieldFileProps extends BaseFormFieldProps {
}

export const FormFieldFileComp: FC<FormFieldFileProps> = (props) => {
const { className, name, title, required, locked, multiple, accept } = props
const { name, required, locked, multiple, accept } = props

return (
<div className={useClassNameGenerator(className, props)}>
<label htmlFor={name}>
<Title title={title} />
</label>
<input type="file" name={name} required={required} disabled={locked} multiple={multiple} accept={accept} />
{props.error ? <div>Error: {props.error}</div> : null}
<div className={useClassName(props)}>
<Label {...props} />
<input
type="file"
className={useClassName(props, { el: 'input' })}
id={inputId(props)}
name={name}
required={required}
disabled={locked}
multiple={multiple}
accept={accept}
/>
<ErrorDescription {...props} />
</div>
)
}

const Title: FC<{ title: string[] }> = ({ title }) => {
const Label: FC<FormFieldProps> = (props) => {
const { title } = props
return (
<>
<label htmlFor={inputId(props)} className={useClassName(props, { el: 'label' })}>
{title.map((t, i) => (
<span key={i}>
{i > 0 ? <> &rsaquo;</> : null} {t}
</span>
))}
</label>
)
}

const inputId = (props: FormFieldProps) => `form-field-${props.name}`
const descId = (props: FormFieldProps) => (props.description ? `${inputId(props)}-desc` : undefined)

const ErrorDescription: FC<FormFieldProps> = (props) => {
const { description, error } = props
const descClassName = useClassName(props, { el: 'description' })
const errorClassName = useClassName(props, { el: 'error' })
return (
<>
{description ? (
<div id={descId(props)} className={descClassName}>
{description}
</div>
) : null}
{error ? <div className={errorClassName}>Error: {error}</div> : null}
</>
)
}
3 changes: 3 additions & 0 deletions packages/fastui/src/components/Json.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,13 @@
import { FC } from 'react'

import { ClassName } from '../hooks/className'

export type JsonData = string | number | boolean | null | JsonData[] | { [key: string]: JsonData }

export interface JsonProps {
value: JsonData
type: 'JSON'
className?: ClassName
}

export const JsonComp: FC<JsonProps> = ({ value }) => {
Expand Down
6 changes: 3 additions & 3 deletions packages/fastui/src/components/button.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC } from 'react'

import { ClassName, useClassNameGenerator } from '../hooks/className'
import { ClassName, useClassName } from '../hooks/className'
import { useFireEvent, PageEvent, GoToEvent } from '../hooks/event'

export interface ButtonProps {
Expand All @@ -12,12 +12,12 @@ export interface ButtonProps {
}

export const ButtonComp: FC<ButtonProps> = (props) => {
const { className, text, onClick, htmlType } = props
const { text, onClick, htmlType } = props

const { fireEvent } = useFireEvent()

return (
<button className={useClassNameGenerator(className, props)} type={htmlType} onClick={() => fireEvent(onClick)}>
<button className={useClassName(props)} type={htmlType} onClick={() => fireEvent(onClick)}>
{text}
</button>
)
Expand Down
4 changes: 2 additions & 2 deletions packages/fastui/src/components/div.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC } from 'react'

import { ClassName, useClassNameGenerator } from '../hooks/className'
import { ClassName, useClassName } from '../hooks/className'

import { FastProps, RenderChildren } from './index'

Expand Down Expand Up @@ -38,7 +38,7 @@ interface Props {
}

export const DivComp: FC<Props> = (props) => (
<div className={useClassNameGenerator(props.className, props)}>
<div className={useClassName(props)}>
<RenderChildren children={props.children} />
</div>
)
6 changes: 3 additions & 3 deletions packages/fastui/src/components/form.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC, FormEvent, useState } from 'react'

import { ClassName, useClassNameGenerator } from '../hooks/className'
import { ClassName, useClassName } from '../hooks/className'
import { useFireEvent, PageEvent, GoToEvent } from '../hooks/event'
import { request } from '../tools'

Expand Down Expand Up @@ -30,7 +30,7 @@ interface FormResponse {
}

export const FormComp: FC<FormProps | ModelFormProps> = (props) => {
const { className, formFields, submitUrl, footer } = props
const { formFields, submitUrl, footer } = props

// mostly equivalent to `<input disabled`
const [locked, setLocked] = useState(false)
Expand Down Expand Up @@ -71,7 +71,7 @@ export const FormComp: FC<FormProps | ModelFormProps> = (props) => {
)

return (
<form className={useClassNameGenerator(className, props)} onSubmit={onSubmit}>
<form className={useClassName(props)} onSubmit={onSubmit}>
<RenderChildren children={fieldProps} />
{error ? <div>Error: {error}</div> : null}
<Footer footer={footer} />
Expand Down
8 changes: 4 additions & 4 deletions packages/fastui/src/components/heading.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC } from 'react'

import { ClassName, useClassNameGenerator } from '../hooks/className'
import { ClassName, useClassName } from '../hooks/className'

export interface HeadingProps {
type: 'Heading'
Expand All @@ -10,12 +10,12 @@ export interface HeadingProps {
}

export const HeadingComp: FC<HeadingProps> = (props) => {
const { level, text, className } = props
const { level, text } = props
const HeadingComponent = getComponent(level)
return <HeadingComponent text={text} className={useClassNameGenerator(className, props)} />
return <HeadingComponent text={text} className={useClassName(props)} />
}

function getComponent(level: 1 | 2 | 3 | 4 | 5 | 6): FC<{ text: string; className: string }> {
function getComponent(level: 1 | 2 | 3 | 4 | 5 | 6): FC<{ text: string; className?: string }> {
switch (level) {
case 1:
return ({ text, className }) => <h1 className={className}>{text}</h1>
Expand Down
2 changes: 2 additions & 0 deletions packages/fastui/src/components/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ export type FastProps =
| AllDisplayProps
| JsonProps

export type FastClassNameProps = Exclude<FastProps, TextProps | AllDisplayProps>

export const AnyComp: FC<FastProps> = (props) => {
const { DisplayError } = useContext(ErrorContext)

Expand Down
4 changes: 2 additions & 2 deletions packages/fastui/src/components/link.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC, MouseEventHandler, ReactNode } from 'react'

import { ClassName, useClassNameGenerator } from '../hooks/className'
import { ClassName, useClassName } from '../hooks/className'
import { useFireEvent, PageEvent, GoToEvent } from '../hooks/event'

import { FastProps, RenderChildren } from './index'
Expand All @@ -13,7 +13,7 @@ export interface LinkProps {
}

export const LinkComp: FC<LinkProps> = (props) => (
<LinkRender className={useClassNameGenerator(props.className, props)} onClick={props.onClick}>
<LinkRender className={useClassName(props)} onClick={props.onClick}>
<RenderChildren children={props.children} />
</LinkRender>
)
Expand Down
6 changes: 3 additions & 3 deletions packages/fastui/src/components/modal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC } from 'react'

import { ClassName, renderClassName, useClassNameGenerator } from '../hooks/className'
import { ClassName, renderClassName, useClassName } from '../hooks/className'
import { PageEvent, useEventListenerToggle } from '../hooks/event'

import { FastProps, RenderChildren } from './index'
Expand All @@ -17,13 +17,13 @@ export interface ModalProps {
}

export const ModalComp: FC<ModalProps> = (props) => {
const { title, body, footer, openTrigger, className } = props
const { title, body, footer, openTrigger } = props

const [open, toggle] = useEventListenerToggle(openTrigger, props.open)

return (
<div className={renderClassName({ 'fu-modal-overlay': true, open })}>
<div className={useClassNameGenerator(className, props, 'fu-modal-content')}>
<div className={useClassName(props, { dft: 'fu-modal-content' })}>
<div className="fu-model-header">
<h2>{title}</h2>
<div className="fu-close" onClick={toggle}>
Expand Down
6 changes: 3 additions & 3 deletions packages/fastui/src/components/table.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { FC } from 'react'
import type { JsonData } from './Json'

import { DisplayChoices, asTitle } from '../display'
import { ClassName, useClassNameGenerator } from '../hooks/className'
import { ClassName, useClassName } from '../hooks/className'
import { PageEvent, GoToEvent } from '../hooks/event'

import { DisplayComp } from './display'
Expand All @@ -27,10 +27,10 @@ export interface TableProps {
}

export const TableComp: FC<TableProps> = (props) => {
const { className, columns, data } = props
const { columns, data } = props

return (
<table className={useClassNameGenerator(className, props)}>
<table className={useClassName(props)}>
<thead>
<tr>
{columns.map((col, id) => (
Expand Down
Loading

0 comments on commit b0c93c4

Please sign in to comment.