UNPKG

@upstart.gg/sdk

Version:

You can test the CLI without recompiling by running:

325 lines (315 loc) 10 kB
import { StringEnum } from "../../utils/string-enum.js"; import { defineBrickManifest } from "../../brick-manifest.js"; import { cssLength } from "../props/css-length.js"; import { defineProps, group } from "../props/helpers.js"; import { fontSize } from "../props/text.js"; import { colorPreset } from "../props/color-preset.js"; import { border, rounding } from "../props/border.js"; import { direction } from "../props/direction.js"; import { datarecord } from "../props/datarecord.js"; import { Type } from "@sinclair/typebox"; import { FaWpforms } from "react-icons/fa6"; //#region src/shared/bricks/manifests/form.manifest.ts const manifest = defineBrickManifest({ type: "form", name: "Form", description: "A form element.", aiInstructions: `PURPOSE Dynamic form generator. Fields are inferred from the referenced datarecord schema (datarecordId). You NEVER list fields manually and the form does not accept children. REQUIRED • datarecordId must reference an existing datarecord definition. COLOR & STYLE • Optional colorPreset sets background + text. If omitted the form inherits parent background. • Allowed tokens: primary-/secondary-/accent-/neutral-/base-*** (and gradient variants). Do NOT invent success, warning, danger, info, etc. Map them (success->secondary or primary; warning->accent; danger->accent/primary). • Use rounding + border + padding for emphasis. Keep padding modest (13rem) unless it's a feature form hero (max ~4rem). LAYOUT • direction controls stacking (flex-col vs flex-row). Wide multi-column layouts usually flex-row on desktop, stacked (flex-col) on mobile. • Use mobileProps to reduce padding or switch direction ONLY if necessary. (If switching direction, you must repeat required props.) BUTTON GROUP • Only supply button overrides you actually change (color, size, position, rounding). Unspecified values fallback to defaults. • If button.size = wide then hide position (handled by schema metadata automatically—don't circumvent). CONTENT STRINGS • title, intro, buttonLabel, successMessage, errorMessage should be concise. • Avoid marketing fluff inside intro beyond one short paragraph. DYNAMIC DATA • Do not interpolate inside datarecordId (it must be a static id). • You MAY interpolate page queries aliases in title/intro (e.g. "Apply for {{job.title}}") if those fields exist in surrounding dataset context. DON'TS ✗ Don't invent props. ✗ Don't add HTML tags except basic inline markup if absolutely needed (prefer plain text). ✗ Don't set impossible color tokens. DO ✓ Keep examples lean. ✓ Use semantic color mapping guidelines. ✓ Provide accessible, human-readable labels. `, isContainer: false, icon: FaWpforms, minWidth: { desktop: 300 }, props: defineProps({ datarecordId: datarecord("Datarecord ID", { description: "The ID of the datarecord to use to generate the form fields. The datarecord must exist.", "ui:responsive": "desktop" }), colorPreset: Type.Optional(colorPreset({ title: "Color", default: "base-200" })), direction: Type.Optional(direction({ title: "Direction", description: "The direction of the form fields", default: "flex-col", "ui:responsive": "desktop", "ui:desktop-only": true })), padding: Type.Optional(cssLength({ default: "2rem", description: "Padding inside the form.", title: "Padding", "ui:responsive": true, "ui:placeholder": "Not specified", "ui:styleId": "styles:padding" })), rounding: Type.Optional(rounding({ default: "rounded-md" })), border: Type.Optional(border({})), fontSize: Type.Optional(fontSize({ default: "inherit", "ui:no-extra-large-sizes": true })), button: group({ title: "Button", children: { color: Type.Optional(StringEnum([ "btn-neutral", "btn-primary", "btn-secondary", "btn-accent" ], { enumNames: [ "Neutral", "Primary", "Secondary", "Accent" ], title: "Color", default: "btn-primary" })), size: Type.Optional(StringEnum(["block", "wide"], { title: "Size", description: "Button sizes.", enumNames: ["Block", "Wide"], default: "block", "ui:responsive": "desktop" })), position: Type.Optional(StringEnum([ "justify-start", "justify-center", "justify-end" ], { title: "Button Position", description: "The position of the button in the form", enumNames: [ "Left", "Center", "Right" ], default: "justify-end", "ui:responsive": "desktop", metadata: { filter: (manifestProps, formData) => { return formData.button?.size !== "wide"; } } })), rounding: rounding({ default: "rounded-md" }) } }), title: Type.Optional(Type.String({ title: "Form title", default: "My form", metadata: { category: "content" } })), intro: Type.Optional(Type.String({ title: "Intro", description: "The intro text of the form", "ui:multiline": true, metadata: { category: "content" } })), buttonLabel: Type.Optional(Type.String({ title: "Button label", default: "Submit", metadata: { category: "content" } })), successMessage: Type.Optional(Type.String({ title: "Success Message", description: "The message to display when the form is successfully submitted", default: "Thank you for your submission!", metadata: { category: "content" } })), errorMessage: Type.Optional(Type.String({ title: "Error Message", description: "The message to display when the form submission fails", default: "There was an error submitting the form. Please try again later.", metadata: { category: "content" } })) }) }); const examples = [ { description: "Basic contact form", type: "form", props: { title: "Contact Us", intro: "We'd love to hear from you. Send us a message and we'll respond as soon as possible.", direction: "flex-row", datarecordId: "contacts", buttonLabel: "Send Message", button: { size: "block" } } }, { description: "User registration form with large button", type: "form", props: { title: "Create Account", intro: "Join our platform and start your journey today.", direction: "flex-row", datarecordId: "user-registration", buttonLabel: "Register", button: { size: "wide" } } }, { description: "Responsive newsletter subscription: desktop horizontal -> mobile vertical", type: "form", props: { title: "Stay Updated", intro: "Subscribe for product news and occasional tips.", direction: "flex-row", datarecordId: "newsletter-subscription", padding: "3rem", buttonLabel: "Subscribe", button: { size: "block" }, colorPreset: { color: "neutral-100" } }, mobileProps: { title: "Stay Updated", intro: "Subscribe for product news and occasional tips.", direction: "flex-col", datarecordId: "newsletter-subscription", padding: "2rem", buttonLabel: "Subscribe", button: { size: "block" }, colorPreset: { color: "neutral-100" } } }, { description: "Newsletter subscription form (horizontal) with large padding", type: "form", props: { title: "Stay Updated", intro: "Subscribe to our newsletter for the latest updates and exclusive content.", direction: "flex-col", datarecordId: "newsletter-subscription", padding: "3rem", buttonLabel: "Subscribe", button: { size: "block" } } }, { description: "Minimal inline signup form (no background colorPreset, inherits parent)", type: "form", props: { title: "Join Beta", intro: "Access early features before public launch.", direction: "flex-row", datarecordId: "beta-signup", buttonLabel: "Request Access", button: { size: "block" }, padding: "1.5rem" } }, { description: "Dark themed form using neutral-800 background and accent button", type: "form", props: { title: "Feedback", intro: "Tell us how we can improve the product.", direction: "flex-col", datarecordId: "product-feedback", buttonLabel: "Send Feedback", colorPreset: { color: "neutral-800" }, padding: "2.5rem", button: { color: "btn-accent" }, rounding: "rounded-lg", border: { width: "border", color: "border-neutral-700" } } }, { description: "Gradient emphasis form (primary gradient) for event signup", type: "form", props: { title: "Conference RSVP", intro: "Reserve your seat for {{event.title}}.", direction: "flex-col", datarecordId: "event-registration", buttonLabel: "Reserve Seat", colorPreset: { color: "primary-gradient-400", gradientDirection: "bg-gradient-to-br" }, padding: "3rem", button: { color: "btn-secondary" }, rounding: "rounded-xl", border: { width: "border-2", color: "border-primary-300" } } }, { description: "Event registration form", type: "form", props: { title: "Conference Registration", intro: "Register for the Annual Tech Conference 2025. Early bird pricing ends soon!", direction: "flex-col", datarecordId: "event-registration", buttonLabel: "Register Now", button: { size: "block" } } }, { description: "Job application form", type: "form", props: { title: "Apply for Position", intro: "We're excited to learn more about you! Please fill out this application form completely.", direction: "flex-col", datarecordId: "job-application", buttonLabel: "Submit Application", button: { size: "block" } } }, { description: "Dynamic job application (title interpolated) mapping dataset field", type: "form", props: { title: "Apply for {{job.title}}", intro: "Join our team in {{job.location}}.", direction: "flex-col", datarecordId: "job-application", buttonLabel: "Apply Now", colorPreset: { color: "secondary-100" }, button: { size: "block", color: "btn-primary" }, padding: "2rem" } } ]; //#endregion export { examples, manifest }; //# sourceMappingURL=form.manifest.js.map