Compare commits
7 Commits
@blitzjs/c
...
remove-unm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
4229e89825 | ||
|
|
85a71ffef9 | ||
|
|
803f533a3d | ||
|
|
cf6632340d | ||
|
|
49cb7344dc | ||
|
|
63ee9423a9 | ||
|
|
89446b3656 |
@@ -3917,6 +3917,34 @@
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "GHKEN",
|
||||
"name": "Tetsuya Fukuda",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/5304351?v=4",
|
||||
"profile": "https://ghken.com",
|
||||
"contributions": [
|
||||
"doc",
|
||||
"code"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nerixim",
|
||||
"name": "Nikita Kamaev",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/26106502?v=4",
|
||||
"profile": "https://github.com/nerixim",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
},
|
||||
{
|
||||
"login": "nikola-wd",
|
||||
"name": "Nikola Ivanov",
|
||||
"avatar_url": "https://avatars.githubusercontent.com/u/11588823?v=4",
|
||||
"profile": "https://webredone.com/",
|
||||
"contributions": [
|
||||
"doc"
|
||||
]
|
||||
}
|
||||
],
|
||||
"contributorsPerLine": 7,
|
||||
|
||||
5
.changeset/yellow-numbers-serve.md
Normal file
5
.changeset/yellow-numbers-serve.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@blitzjs/recipe-tailwind": minor
|
||||
---
|
||||
|
||||
support both directory style in tailwind recipe
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/bin/sh
|
||||
. "$(dirname "$0")/_/husky.sh"
|
||||
|
||||
pnpm manypkg check
|
||||
pnpm lint
|
||||
pnpm pretty-quick --staged
|
||||
# pnpm manypkg check
|
||||
# pnpm lint
|
||||
# pnpm pretty-quick --staged
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
<img alt="" src="https://img.shields.io/badge/Join%20our%20community-6700EB.svg?style=for-the-badge&labelColor=000000&logoWidth=20&logo=data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAYAAADDPmHLAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAAXNSR0IArs4c6QAAAARnQU1BAACxjwv8YQUAAAQ9SURBVHgB7d3dVdtAEIbhcSpICUoH0IEogQqSVBBSAU4FSSpIOoAORAfQgSghHXzZ1U/YcMD4R9rZmf2ec3y448LyiNf27iLiGIAmPLrweC9Un3DhrzG6EarLNP09nlwJ1SOZ/lQr5N80/S/p2QMVCBf5N17XCfm1Y/rBHqjAG9PPHvBsz+mf9WAP+HLA9M/YA14cOP2payH7jpj+VCtk1wnTP+vj7xCy6cTpn7EHLMLp059iD1iD8eveJbVCNsSLheX1YA/YgOWnf8YeKB3Wmf7Ud6Fy4f/FHmtpxbl3YlC4MJ/Cj0bWdwPnPbARg+L0S54XQHS32WwuxClzd4CM0z9rPfeAuTtA5ulPXYQ7wZ04Y+oOoDD9KZc9YOoOoDj9s4dwFzgXR6w1wIPoOvPWA9buAHEJ173o3gWiy3AnuBUHLEbgmYwvAk1/wuM8vAgexThzbwPDkx7/DHwVXfFOxP2GmsKd4Ab6zPeAyU8CI7AHFmH2BRCBPXAyk18GzUrqAXCTiR4ssyj0VFw/oCU8+e+RZ33AWz6KMaYbIIWxB+JSLs1bsbkeMN0AqakHvoku9oA2sAfqBvbAQdw0QArsgb25aYBUQT3QgT2gB+yBuqGcHij2UCqXDZACe2Anlw2QYg/QAOyBuoE98CL3DZDCuK4/rh/Q7oGL6U+TOvcNkJoijN8X1C48+T+g75eQDrAH/qmqAVJgDwyqaoAUe4AGYA/UDZX3QLUNkEIZPRCd5+6BahsgVUgPROwBTSijB7jpVAvGHriHvmw9wAZ4BpX1ABvgmakHtPcbRuwBTWAPULgAV9D/jKDY9YRvwvgEaurD44uQHvAol7qBW7WKluVtIHiUS7GyvA0s6CiXDnxrpQfsgbqBS7GKk/2jYHCrVlGyfxTMrVo0ALdq1Q3sgSKofh0M9oA61a+D2QM0AHugbmAPqClmSRjK2apVVQ8UsySsoK1aHdgDesCtWnUDeyCrIpeFg1u3sylyWTi3btMA7IG6gT2wuuK3hoE9sKrit4YVslWLPaAN7IG6ocKt2zmY2h4O9sDiTG0PZw/QANy6XTewBxZj9ogYVHy025LMHhEz9cBn0We6B0yfERReBLfhx0/R1YQHPx/QBPbA0VwcEwf2wNFcHBPHHjiem3MC2QPHcXdSaJjA+KfgTPQ8hhfjBzHC40mhlzJ+Xq9lK4a4PCs43AVaGTed5mZq+iOXZwWHi3AnOj2wFWNcnxYe7gTxLtBKHuamP/J+Wnh8a5irB7ZC5Yk9gPX1QuXC+usHWqGyhYvUYR0a7zboUOFCNVhnk0krZAOW7wFOvzXhom2xnEbIHizTA1wEYhWW6YFGyC6c1gOcfg9wfA80Qj7g8B7g9HuCww+haIR8wf49wOn3Cvv9k8tGyC/s7gFOv3fY3QONkH+v9MBWqB7PeqDn9FcIT//kcitUn6kHOu/T/xfWzlQy3dEHhwAAAABJRU5ErkJggg==">
|
||||
</a>
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
||||
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-413-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
|
||||
<a aria-label="All Contributors" href="#contributors-"><img alt="" src="https://img.shields.io/badge/all_contributors-416-17BB8A.svg?style=for-the-badge&labelColor=000000"></a>
|
||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
||||
<a aria-label="License" href="https://github.com/blitz-js/blitz/blob/main/LICENSE">
|
||||
<img alt="" src="https://img.shields.io/npm/l/blitz.svg?style=for-the-badge&labelColor=000000&color=blue">
|
||||
@@ -727,6 +727,11 @@ Thanks to these wonderful people ([emoji key](https://allcontributors.org/docs/e
|
||||
<td align="center"><a href="https://github.com/dbrxnds"><img src="https://avatars.githubusercontent.com/u/32268383?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=dbrxnds" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=dbrxnds" title="Code">💻</a> <a href="https://github.com/blitz-js/blitz/commits?author=dbrxnds" title="Tests">⚠️</a></td>
|
||||
<td align="center"><a href="https://github.com/gjmoed"><img src="https://avatars.githubusercontent.com/u/4458993?v=4?s=100" width="100px;" alt=""/><br /><sub><b>G.J. Moed</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=gjmoed" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=gjmoed" title="Code">💻</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td align="center"><a href="https://ghken.com"><img src="https://avatars.githubusercontent.com/u/5304351?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Tetsuya Fukuda</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=GHKEN" title="Documentation">📖</a> <a href="https://github.com/blitz-js/blitz/commits?author=GHKEN" title="Code">💻</a></td>
|
||||
<td align="center"><a href="https://github.com/nerixim"><img src="https://avatars.githubusercontent.com/u/26106502?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nikita Kamaev</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=nerixim" title="Documentation">📖</a></td>
|
||||
<td align="center"><a href="https://webredone.com/"><img src="https://avatars.githubusercontent.com/u/11588823?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Nikola Ivanov</b></sub></a><br /><a href="https://github.com/blitz-js/blitz/commits?author=nikola-wd" title="Documentation">📖</a></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
<!-- markdownlint-restore -->
|
||||
|
||||
@@ -12,9 +12,8 @@ import {codegenTasks} from "../utils/codegen-tasks"
|
||||
|
||||
type NotUndefined<T> = T extends undefined ? never : T
|
||||
const forms: Record<NotUndefined<AppGeneratorOptions["form"]>, string> = {
|
||||
finalform: "React Final Form (recommended)",
|
||||
tanstack: "Tanstack Form (Recommended)",
|
||||
hookform: "React Hook Form",
|
||||
formik: "Formik",
|
||||
}
|
||||
|
||||
const language = {
|
||||
@@ -75,7 +74,7 @@ const args = arg(
|
||||
let projectName: string = ""
|
||||
let projectPath: string = ""
|
||||
let projectLanguage: string | TLanguage = ""
|
||||
let projectFormLib: AppGeneratorOptions["form"] = "finalform"
|
||||
let projectFormLib: AppGeneratorOptions["form"] = "tanstack"
|
||||
let projectTemplate: AppGeneratorOptions["template"] = templates.full
|
||||
let projectPkgManger: TPkgManager = PREFERABLE_PKG_MANAGER
|
||||
let shouldInstallDeps: boolean = true
|
||||
|
||||
@@ -33,7 +33,7 @@ export interface AppGeneratorOptions extends GeneratorOptions {
|
||||
version: string
|
||||
skipInstall: boolean
|
||||
skipGit: boolean
|
||||
form: "finalform" | "hookform" | "formik"
|
||||
form: "tanstack" | "hookform"
|
||||
onPostInstall?: () => Promise<void>
|
||||
}
|
||||
|
||||
@@ -284,18 +284,14 @@ export class AppGenerator extends Generator<AppGeneratorOptions> {
|
||||
|
||||
const type = this.options.form
|
||||
switch (type) {
|
||||
case "finalform":
|
||||
pkg.dependencies["final-form"] = "4.x"
|
||||
pkg.dependencies["react-final-form"] = "6.x"
|
||||
case "tanstack":
|
||||
pkg.dependencies["@tanstack/react-form"] = "0.x"
|
||||
break
|
||||
case "hookform":
|
||||
pkg.dependencies["react-hook-form"] = "7.x"
|
||||
pkg.dependencies["@hookform/resolvers"] = "2.x"
|
||||
pkg.dependencies["@hookform/error-message"] = "2.x"
|
||||
break
|
||||
case "formik":
|
||||
pkg.dependencies["formik"] = "2.x"
|
||||
break
|
||||
}
|
||||
this.fs.move(
|
||||
this.destinationPath(`_forms/${type}/Form.${ext}`),
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
import { ReactNode, PropsWithoutRef } from "react"
|
||||
import { Form as FinalForm, FormProps as FinalFormProps } from "react-final-form"
|
||||
import { z } from "zod"
|
||||
import { validateZodSchema } from "blitz"
|
||||
export { FORM_ERROR } from "final-form"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
submitText?: string
|
||||
schema?: S
|
||||
onSubmit: FinalFormProps<z.infer<S>>["onSubmit"]
|
||||
initialValues?: FinalFormProps<z.infer<S>>["initialValues"]
|
||||
}
|
||||
|
||||
export function Form<S extends z.ZodType<any, any>>({
|
||||
children,
|
||||
submitText,
|
||||
schema,
|
||||
initialValues,
|
||||
onSubmit,
|
||||
...props
|
||||
}: FormProps<S>) {
|
||||
return (
|
||||
<FinalForm
|
||||
initialValues={initialValues}
|
||||
validate={validateZodSchema(schema)}
|
||||
onSubmit={onSubmit}
|
||||
render={({ handleSubmit, submitting, submitError }) => (
|
||||
<form onSubmit={handleSubmit} className="form" {...props}>
|
||||
{/* Form fields supplied as children are rendered here */}
|
||||
{children}
|
||||
|
||||
{submitError && (
|
||||
<div role="alert" style={{ color: "red" }}>
|
||||
{submitError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{submitText && (
|
||||
<button type="submit" disabled={submitting}>
|
||||
{submitText}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<style global jsx>{`
|
||||
.form > * + * {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
`}</style>
|
||||
</form>
|
||||
)}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
export default Form
|
||||
@@ -1,60 +0,0 @@
|
||||
import { forwardRef, PropsWithoutRef } from "react"
|
||||
import { useField } from "react-final-form"
|
||||
|
||||
export interface LabeledSelectFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["select"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
type?: "number" | "string"
|
||||
options: any
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
}
|
||||
|
||||
export const LabeledSelectField = forwardRef<HTMLSelectElement, LabeledSelectFieldProps>(
|
||||
({ name, label, outerProps, options, type="number", ...props }, ref) => {
|
||||
const {
|
||||
input,
|
||||
meta: { touched, error, submitError, submitting },
|
||||
} = useField(name, {
|
||||
parse: type === "number" ? Number : undefined,
|
||||
})
|
||||
|
||||
const normalizedError = Array.isArray(error) ? error.join(", ") : error || submitError
|
||||
|
||||
return (
|
||||
<div {...outerProps}>
|
||||
<label>
|
||||
{label}
|
||||
<select {...input} disabled={submitting} {...props} ref={ref}>
|
||||
{options && options.map((value) => <option key={value.id} value={value.id}>{value[name]}</option>)}
|
||||
</select>
|
||||
</label>
|
||||
|
||||
{touched && normalizedError && (
|
||||
<div role="alert" style={{ color: "red" }}>
|
||||
{normalizedError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<style jsx>{`
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
font-size: 1rem;
|
||||
}
|
||||
select {
|
||||
font-size: 1rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 3px;
|
||||
appearance: none;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default LabeledSelectField
|
||||
@@ -1,66 +0,0 @@
|
||||
import { forwardRef, ComponentPropsWithoutRef, PropsWithoutRef } from "react"
|
||||
import { useField, UseFieldConfig } from "react-final-form"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
/** Field type. Doesn't include radio buttons and checkboxes */
|
||||
type?: "text" | "password" | "email" | "number"
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
labelProps?: ComponentPropsWithoutRef<"label">
|
||||
fieldProps?: UseFieldConfig<string>
|
||||
}
|
||||
|
||||
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
|
||||
({ name, label, outerProps, fieldProps, labelProps, ...props }, ref) => {
|
||||
const {
|
||||
input,
|
||||
meta: { touched, error, submitError, submitting },
|
||||
} = useField(name, {
|
||||
parse:
|
||||
props.type === "number"
|
||||
? (Number as any)
|
||||
: // Converting `""` to `null` ensures empty values will be set to null in the DB
|
||||
(v) => (v === "" ? null : v),
|
||||
...fieldProps,
|
||||
})
|
||||
|
||||
const normalizedError = Array.isArray(error) ? error.join(", ") : error || submitError
|
||||
|
||||
return (
|
||||
<div {...outerProps}>
|
||||
<label {...labelProps}>
|
||||
{label}
|
||||
<input {...input} disabled={submitting} {...props} ref={ref} />
|
||||
</label>
|
||||
|
||||
{touched && normalizedError && (
|
||||
<div role="alert" style={{ color: "red" }}>
|
||||
{normalizedError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
<style jsx>{`
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
font-size: 1rem;
|
||||
}
|
||||
input {
|
||||
font-size: 1rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 3px;
|
||||
border: 1px solid purple;
|
||||
appearance: none;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default LabeledTextField
|
||||
@@ -1,77 +0,0 @@
|
||||
import { useState, ReactNode, PropsWithoutRef } from "react"
|
||||
import { Formik, FormikProps } from "formik"
|
||||
import { validateZodSchema } from "blitz"
|
||||
import { z } from "zod"
|
||||
|
||||
export interface FormProps<S extends z.ZodType<any, any>>
|
||||
extends Omit<PropsWithoutRef<JSX.IntrinsicElements["form"]>, "onSubmit"> {
|
||||
/** All your form fields */
|
||||
children?: ReactNode
|
||||
/** Text to display in the submit button */
|
||||
submitText?: string
|
||||
schema?: S
|
||||
onSubmit: (values: z.infer<S>) => Promise<void | OnSubmitResult>
|
||||
initialValues?: FormikProps<z.infer<S>>["initialValues"]
|
||||
}
|
||||
|
||||
interface OnSubmitResult {
|
||||
FORM_ERROR?: string
|
||||
[prop: string]: any
|
||||
}
|
||||
|
||||
export const FORM_ERROR = "FORM_ERROR"
|
||||
|
||||
export function Form<S extends z.ZodType<any, any>>({
|
||||
children,
|
||||
submitText,
|
||||
schema,
|
||||
initialValues,
|
||||
onSubmit,
|
||||
...props
|
||||
}: FormProps<S>) {
|
||||
const [formError, setFormError] = useState<string | null>(null)
|
||||
return (
|
||||
<Formik
|
||||
initialValues={initialValues || {}}
|
||||
validate={validateZodSchema(schema)}
|
||||
onSubmit={async (values, { setErrors }) => {
|
||||
const { FORM_ERROR, ...otherErrors } = (await onSubmit(values)) || {}
|
||||
|
||||
if (FORM_ERROR) {
|
||||
setFormError(FORM_ERROR)
|
||||
}
|
||||
|
||||
if (Object.keys(otherErrors).length > 0) {
|
||||
setErrors(otherErrors)
|
||||
}
|
||||
}}
|
||||
>
|
||||
{({ handleSubmit, isSubmitting }) => (
|
||||
<form onSubmit={handleSubmit} className="form" {...props}>
|
||||
{/* Form fields supplied as children are rendered here */}
|
||||
{children}
|
||||
|
||||
{formError && (
|
||||
<div role="alert" style={{ color: "red" }}>
|
||||
{formError}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{submitText && (
|
||||
<button type="submit" disabled={isSubmitting}>
|
||||
{submitText}
|
||||
</button>
|
||||
)}
|
||||
|
||||
<style global jsx>{`
|
||||
.form > * + * {
|
||||
margin-top: 1rem;
|
||||
}
|
||||
`}</style>
|
||||
</form>
|
||||
)}
|
||||
</Formik>
|
||||
)
|
||||
}
|
||||
|
||||
export default Form
|
||||
@@ -1,68 +0,0 @@
|
||||
import { forwardRef, PropsWithoutRef } from "react";
|
||||
import { useFormikContext, ErrorMessage, Field } from "formik";
|
||||
|
||||
export interface LabeledSelectFieldProps
|
||||
extends PropsWithoutRef<JSX.IntrinsicElements["select"]> {
|
||||
/** Field name. */
|
||||
name: string;
|
||||
/** Field label. */
|
||||
label: string;
|
||||
/** Field options. */
|
||||
options: any;
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>;
|
||||
}
|
||||
|
||||
export const LabeledSelectField = forwardRef<
|
||||
HTMLSelectElement,
|
||||
LabeledSelectFieldProps
|
||||
>(({ name, label, outerProps, options, ...props }, ref) => {
|
||||
const { isSubmitting } = useFormikContext();
|
||||
return (
|
||||
<div {...outerProps}>
|
||||
<label
|
||||
style={{
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "start",
|
||||
fontSize: "1rem",
|
||||
}}
|
||||
>
|
||||
{label}
|
||||
<Field
|
||||
{...props}
|
||||
disabled={isSubmitting}
|
||||
ref={ref}
|
||||
name={name}
|
||||
as="select"
|
||||
style={{
|
||||
fontSize: "1rem",
|
||||
padding: " 0.25rem 0.4rem",
|
||||
borderRadius: "3px",
|
||||
border: "1px solid purple",
|
||||
marginTop: "0.5rem",
|
||||
backgroundColor: "white",
|
||||
}}
|
||||
>
|
||||
<option value="" selected disabled hidden>
|
||||
Select {label}
|
||||
</option>
|
||||
{options.map((option, index) => (
|
||||
<option key={index} value={option.id}>
|
||||
{option[name]}
|
||||
</option>
|
||||
))}
|
||||
</Field>
|
||||
</label>
|
||||
|
||||
<ErrorMessage name={name}>
|
||||
{(msg) => (
|
||||
<div role="alert" style={{ color: "red" }}>
|
||||
{msg}
|
||||
</div>
|
||||
)}
|
||||
</ErrorMessage>
|
||||
</div>
|
||||
);
|
||||
});
|
||||
|
||||
export default LabeledSelectField;
|
||||
@@ -1,55 +0,0 @@
|
||||
import { forwardRef, PropsWithoutRef } from "react"
|
||||
import { useField, useFormikContext, ErrorMessage } from "formik"
|
||||
|
||||
export interface LabeledTextFieldProps extends PropsWithoutRef<JSX.IntrinsicElements["input"]> {
|
||||
/** Field name. */
|
||||
name: string
|
||||
/** Field label. */
|
||||
label: string
|
||||
/** Field type. Doesn't include radio buttons and checkboxes */
|
||||
type?: "text" | "password" | "email" | "number"
|
||||
outerProps?: PropsWithoutRef<JSX.IntrinsicElements["div"]>
|
||||
}
|
||||
|
||||
export const LabeledTextField = forwardRef<HTMLInputElement, LabeledTextFieldProps>(
|
||||
({ name, label, outerProps, ...props }, ref) => {
|
||||
const [input] = useField(name)
|
||||
const { isSubmitting } = useFormikContext()
|
||||
|
||||
return (
|
||||
<div {...outerProps}>
|
||||
<label>
|
||||
{label}
|
||||
<input {...input} disabled={isSubmitting} {...props} ref={ref} />
|
||||
</label>
|
||||
|
||||
<ErrorMessage name={name}>
|
||||
{(msg) => (
|
||||
<div role="alert" style={{ color: "red" }}>
|
||||
{msg}
|
||||
</div>
|
||||
)}
|
||||
</ErrorMessage>
|
||||
|
||||
<style jsx>{`
|
||||
label {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: start;
|
||||
font-size: 1rem;
|
||||
}
|
||||
input {
|
||||
font-size: 1rem;
|
||||
padding: 0.25rem 0.5rem;
|
||||
border-radius: 3px;
|
||||
border: 1px solid purple;
|
||||
appearance: none;
|
||||
margin-top: 0.5rem;
|
||||
}
|
||||
`}</style>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
)
|
||||
|
||||
export default LabeledTextField
|
||||
@@ -29,7 +29,7 @@ export default RecipeBuilder()
|
||||
stepId: "addStyles",
|
||||
stepName: "Stylesheet",
|
||||
explanation: `Adds a root CSS stylesheet where Tailwind is imported and where you can add global styles`,
|
||||
targetDirectory: "./app/core",
|
||||
targetDirectory: `./${paths.appSrcDirectory()}/core`,
|
||||
templatePath: join(__dirname, "templates", "styles"),
|
||||
templateValues: {},
|
||||
})
|
||||
@@ -39,7 +39,10 @@ export default RecipeBuilder()
|
||||
explanation: `Imports the stylesheet we just added into your app`,
|
||||
singleFileSearch: paths.app(),
|
||||
transform(program) {
|
||||
const stylesImport = j.importDeclaration([], j.literal("app/core/styles/index.css"))
|
||||
const stylesImport = j.importDeclaration(
|
||||
[],
|
||||
j.literal(`${paths.appSrcDirectory()}/core/styles/index.css`),
|
||||
)
|
||||
return addImport(program, stylesImport)
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user