Skip to content

Commit

Permalink
feat: support FormApp
Browse files Browse the repository at this point in the history
  • Loading branch information
0xzio committed Jun 30, 2024
1 parent d5faad0 commit 505fb67
Show file tree
Hide file tree
Showing 39 changed files with 957 additions and 87 deletions.
6 changes: 4 additions & 2 deletions .prettierrc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"plugins": ["@ianvs/prettier-plugin-sort-imports"],
"plugins": [
"@ianvs/prettier-plugin-sort-imports"
],
"importOrder": [
"^react",
"<BUILTIN_MODULES>",
Expand All @@ -14,4 +16,4 @@
"^~(.*)$",
"^[./]"
]
}
}
4 changes: 2 additions & 2 deletions apps/desktop/.env
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# VITE_API_URL=https://app.penx.io
VITE_API_URL=http://localhost:3000
VITE_API_URL=https://app.penx.io
# VITE_API_URL=http://localhost:3000

NEXT_PUBLIC_PLATFORM=DESKTOP

Expand Down
1 change: 1 addition & 0 deletions apps/desktop/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@
"add": "^2.0.6",
"clsx": "^2.1.0",
"cmdk": "^1.0.0",
"fomir": "^0.22.0",
"idb-keyval": "^6.2.1",
"jotai": "^2.6.0",
"ky": "^1.1.3",
Expand Down
3 changes: 3 additions & 0 deletions apps/desktop/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { appEmitter } from '@penx/event'
import { StoreProvider } from '@penx/store'
import { TrpcProvider } from '@penx/trpc-client'
import '@glideapps/glide-data-grid/dist/index.css'
import { Fomir } from 'fomir'
import { fixPathEnv } from 'tauri-plugin-shellx-api'
import { registerDefaultAppHotkey } from '@penx/app'
import { handleEscape } from './common/handleEscape'
Expand All @@ -18,9 +19,11 @@ import { useInitThemeMode } from './hooks/useInitThemeMode'
import { MainApp } from './MainApp'
import '~/styles/globals.css'
import '~/styles/command.scss'
import FomirUIkit from './fomir-uikit'
import { config } from './config'

initFower()
Fomir.use(FomirUIkit)

async function init() {
handleEscape()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { memo, useEffect } from 'react'
import isEqual from 'react-fast-compare'
import { Box } from '@fower/react'
import { IListItem, isListApp, isMarkdownJSON } from '@penxio/preset-ui'
import { isFormApp, isListApp, isMarkdownJSON } from '@penxio/preset-ui'
import { Spinner } from 'uikit'
import { Command } from '@penx/model'
import { store } from '@penx/store'
Expand All @@ -11,6 +11,7 @@ import { CommandAppUI } from '~/hooks/useCommandAppUI'
import { AboutApp } from './AboutApp'
import { ClipboardHistoryApp } from './ClipboardHistoryApp'
import { DatabaseApp } from './DatabaseApp/DatabaseApp'
import { FormApp } from './FormApp/FormApp'
import { GeneralSettings } from './GeneralSettings/GeneralSettings'
import { InstalledExtensionsApp } from './InstalledExtensionsApp/InstalledExtensionsApp'
import { ListApp } from './ListApp/ListApp'
Expand Down Expand Up @@ -68,13 +69,19 @@ export const CommandApp = memo(
if (ui.type === 'render') {
const component = ui.component as any

console.log('=======component:', component)

if (isMarkdownJSON(component)) {
return <Markdown content={component.content} />
}

if (isListApp(component)) {
return <ListApp component={component} />
}

if (isFormApp(component)) {
return <FormApp component={component} />
}
}

return null
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { memo } from 'react'
import { Box } from '@fower/react'
import { FormJSON } from '@penxio/preset-ui'
import { Form, useForm } from 'fomir'

interface FormAppProps {
component: FormJSON
}

export const FormApp = memo(function FormApp({ component }: FormAppProps) {
console.log('=========component:', component)

const form = useForm({
onSubmit(values) {
alert(JSON.stringify(values, null, 2))
console.log('values', values)
},
children: [
...component.fields,
// {
// label: 'First Name',
// name: 'firstName',
// component: 'Input',
// value: '',
// validators: {
// required: 'First Name is requiredFirst Name is required',
// },
// },
// {
// label: 'Last Name',
// name: 'lastName',
// component: 'Input',
// value: '',
// validators: {
// required: 'First Name is required',
// },
// },
],
})

return (
<Box py4>
<Form form={form} />
</Box>
)
})
17 changes: 17 additions & 0 deletions apps/desktop/src/custom-types.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { FieldNode } from 'fomir'
import { FomirUIkitNode } from './fomir-uikit/fomir-uikit-node'

type CustomNode = FomirUIkitNode

declare module 'fomir' {
interface CustomTypes {
Node: CustomNode & { updatable?: boolean }
// | {
// component: CustomNode['component'] | ({} & string)
// [key: string]: any
// }
}
interface Validators {
moreThan?: [string, string]
}
}
26 changes: 26 additions & 0 deletions apps/desktop/src/fomir-uikit/Form.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import React, { FC, forwardRef, PropsWithChildren } from 'react'
import { Box } from '@fower/react'
import { FormRegisterProps, useFormContext } from 'fomir'

export const Form: FC<PropsWithChildren<FormRegisterProps>> = forwardRef<
HTMLFormElement,
PropsWithChildren<FormRegisterProps>
>(function FormNode({ children, submitForm }, ref) {
const form = useFormContext()
const { layout = 'vertical' } = form.schema

return (
<Box
as="form"
className={`uikit-form-${layout}`}
onSubmit={submitForm}
ref={ref as any}
display={layout === 'inline' ? 'flex' : 'block'}
toCenterY={layout === 'inline'}
gapX5={layout === 'inline'}
flexWrap={layout === 'inline'}
>
{children}
</Box>
)
})
80 changes: 80 additions & 0 deletions apps/desktop/src/fomir-uikit/FormField.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React, { FC, forwardRef, ReactNode } from 'react'
import type { SVGProps } from 'react'
import { Box, FowerHTMLProps } from '@fower/react'
import { Node, useFormContext } from 'fomir'
import { Info } from 'lucide-react'
import { Tooltip, TooltipContent, TooltipTrigger } from 'uikit'

export interface FormFieldProps extends FowerHTMLProps<'div'> {
showLabel?: boolean
node: Node
renderLabel?: () => ReactNode
}

export const FormField: FC<FormFieldProps> = forwardRef(function FormFieldComp(
props: FormFieldProps,
ref,
) {
const { children, node, showLabel = true, renderLabel, ...rest } = props
const { schema } = useFormContext()
const { layout = 'horizontal' } = schema
const { error, label, description, touched, wrapper } = node || {}

if (!wrapper) return <>{children}</>
return (
<Box
className="uikit-form-field"
ref={ref as any}
relative
flex
mb6
hidden={!node.display}
// w-100p
toCenterY
column={layout === 'vertical'}
{...rest}
gap2
>
<Box toCenterY w-190 toRight text-13>
{renderLabel?.()}
{showLabel && label && (
<Box
toCenterY
gap1
mb2={layout === 'vertical'}
toRight={layout === 'horizontal'}
pr2={layout !== 'vertical'}
w-100={layout === 'horizontal'}
>
{label && (
<Box as="label" className="uikit-form-field-label" leading-1em toCenterY>
{label}
</Box>
)}

{description && (
<Tooltip>
<TooltipTrigger>
<Info size={20} />
</TooltipTrigger>
<TooltipContent>{description}</TooltipContent>
</Tooltip>
)}
</Box>
)}
</Box>

<Box toCenterY flex-1>
{children}
</Box>

<Box w-190 toCenterY>
{error && touched && (
<Box h-1em red400 left0 text="0.9em">
{error}
</Box>
)}
</Box>
</Box>
)
})
13 changes: 13 additions & 0 deletions apps/desktop/src/fomir-uikit/fields/Box.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { css, Box as FowerBox } from '@fower/react'
import { NodeProps } from 'fomir'
import type { BoxNode } from '../fomir-uikit-node'

export const Box = ({ node }: NodeProps<BoxNode>) => {
if (!node.visible) return null

return (
<FowerBox className={css(node.css || '')}>
{node.text ? node.text : node.renderChildren?.(node)}
</FowerBox>
)
}
19 changes: 19 additions & 0 deletions apps/desktop/src/fomir-uikit/fields/Checkbox.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import React, { FC } from 'react'
import { NodeProps } from 'fomir'
import { CheckboxNode } from '../fomir-uikit-node'
import { Checkbox as BoneCheckbox } from 'uikit'
import { FormField } from '../FormField'

export const Checkbox: FC<NodeProps<CheckboxNode>> = (props) => {
const { value } = props.node

function handleChange(e: any) {
props.handler.handleChange(e.target.checked)
}

return (
<FormField node={props.node}>
<BoneCheckbox checked={value} onChange={handleChange} />
</FormField>
)
}
21 changes: 21 additions & 0 deletions apps/desktop/src/fomir-uikit/fields/CheckboxGroup.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { FC } from 'react'
import { NodeProps } from 'fomir'
import { CheckboxGroupNode } from '../fomir-uikit-node'
import { CheckboxGroup as BoneCheckboxGroup, Checkbox } from 'uikit'
import { FormField } from '../FormField'

export const CheckboxGroup: FC<NodeProps<CheckboxGroupNode>> = (props) => {
const { value, options = [] } = props.node

return (
<FormField node={props.node}>
<BoneCheckboxGroup value={value} onChange={props.handler.handleChange}>
{options.map((item: any) => (
<Checkbox key={item.value} value={item.value}>
{item.label}
</Checkbox>
))}
</BoneCheckboxGroup>
</FormField>
)
}
40 changes: 40 additions & 0 deletions apps/desktop/src/fomir-uikit/fields/CounterInput.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import { FC } from 'react'
import { NodeProps } from 'fomir'
import { Minus, Plus } from 'lucide-react'
import { CounterInputNode } from '../fomir-uikit-node'
import { Input as BoneInput, InputElement, InputGroup } from 'uikit'
import { FormField } from '../FormField'

export const CounterInput: FC<NodeProps<CounterInputNode>> = (props) => {
const { value, disabled, componentProps } = props.node

return (
<FormField node={props.node}>
<InputGroup>
<InputElement
cursorPointer
cursorNotAllowed={componentProps?.min === Number(value)}
onClick={() => {
if (componentProps?.min === Number(value)) return
props.handler.handleChange(Number(value) - 1)
}}
>
<Minus />
</InputElement>

<BoneInput
textCenter
disabled={disabled}
type={'text'}
value={value || ''}
onChange={props.handler.handleChange}
{...componentProps}
/>

<InputElement cursorPointer onClick={() => props.handler.handleChange(Number(value) + 1)}>
<Plus />
</InputElement>
</InputGroup>
</FormField>
)
}
Loading

0 comments on commit 505fb67

Please sign in to comment.