UNPKG

@kbgarcia8/react-dynamic-form

Version:

A form that can be nested with editable, expandable and flexible input-forms

663 lines (657 loc) 19.6 kB
import { jsx as a, jsxs as g, Fragment as R } from "react/jsx-runtime"; import q, { forwardRef as J, createContext as K, useState as Q, useContext as V } from "react"; import r, { ThemeProvider as X } from "styled-components"; const e = { fonts: { secondary: "Raleway", tertiary: "Lato", fallback: "system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif" }, fontWeight: { light: 300, medium: 500, bold: 700, bolder: 900 }, fontSize: { xsmall: "0.75rem" }, spacing: { xxxsmall: "0.25rem", xxsmall: "0.375rem", xsmall: "0.5rem", small: "0.75rem", medium: "1.25rem" }, borderRadius: { xsmall: "0.125rem", small: "0.25rem", xlarge: "2rem" }, borderThickness: { thin: "0.0625rem", light: "0.125rem" } }, Y = r.button` display: flex; align-items: center; justify-content: center; background-color: ${({ theme: t }) => t.colors.blue || "blue"}; color: ${({ theme: t }) => t.colors.bg || "white"}; border: ${e.borderThickness.light} solid ${({ theme: t }) => t.colors.text || "black"}; border-radius: ${e.borderRadius.xlarge}; padding: ${e.spacing.xxxsmall} ${e.spacing.small}; margin: 0.125rem; width: auto; cursor: pointer; transition: background-color 0.2s ease, border-color 0.2s ease; &:hover { background-color: lightblue; border: ${e.borderThickness.light} solid gray; } & .button-icon-text-space { max-width: 100%; } & .button-icon-text-space svg { max-width: 100%; } `, Z = r.div` width: 100%; display: flex; flex-direction: row; align-items: center; justify-content: center; `, _ = r.img` maxwidth: 100%; `, ee = r.span` font-size: ${e.fontSize.xsmall}; font-weight: ${e.fontWeight.medium}; font-family: ${e.fonts.tertiary}, ${e.fonts.fallback}; display: flex; align-items: center; justify-content: center; `, x = ({ onClick: t, id: o, buttonType: i, source: l, svg: b, alt: d = "alt-button-icon", text: m = "", className: c = "", dataAttributes: f = {} }) => /* @__PURE__ */ a( Y, { onClick: t, id: o, type: i, className: c, ...f, children: /* @__PURE__ */ g(Z, { className: "button-icon-text-space", children: [ l ? /* @__PURE__ */ a(_, { src: l, alt: d }) : b || "", m && /* @__PURE__ */ a(ee, { id: o, children: m }) ] }) } ), te = r.div` display: flex; flex-direction: column; align-items: left; width: 100%; margin-bottom: ${e.spacing.small}; `, ae = r.div` display: flex; gap: ${e.spacing.xsmall}; width: auto; height: auto; `, ne = r.label` display: flex; align-items: center; justify-content:center; height: auto; flex-direction: ${(t) => t.$labelFlexDirection || "column"}; font-family: ${e.fonts.secondary}, ${e.fonts.fallback}; font-size: ${e.fontSize.xsmall}; font-weight: ${e.fontWeight.bold}; gap: ${e.spacing.xxsmall}; & .label-icon-container img, & .label-icon-container svg { max-width: 100%; height: auto; object-fit: contain; } `, oe = r.div` display: flex; flex-direction: column; align-items: center; justify-content: center; gap: ${e.spacing.xxxsmall}; `, le = r.span` font-weight: ${e.fontWeight.bolder}; `, ie = r.div` max-width: 100%; display: flex; align-items: center; `, se = r.span` font-weight: ${e.fontWeight.light}; `, B = ({ htmlFor: t, textLabel: o, additionalInfo: i, $labelFlexDirection: l, source: b, svg: d, className: m, children: c }) => /* @__PURE__ */ g(ne, { htmlFor: t, className: m, $labelFlexDirection: l, children: [ (b || d) && /* @__PURE__ */ a(ie, { className: "label-icon-container", children: b ? /* @__PURE__ */ a("img", { src: b, alt: `${t}-label-icon` }) : d || "" }), /* @__PURE__ */ g(oe, { className: "label-text-container", children: [ /* @__PURE__ */ a(le, { className: "main-label", children: o }), i && /* @__PURE__ */ a(se, { className: "additional-info", children: i }) ] }), c ] }), W = r.input` display: flex; place-content: center; font-family: ${e.fonts.secondary}, ${e.fonts.fallback}; font-size: ${e.fontSize.xsmall}; line-height: 1.75; padding: ${e.spacing.xxxsmall} ${e.spacing.xsmall}; max-width: 100%; height: auto; background-color: #FFFFFF; outline: none; border: ${e.borderThickness.thin} solid #000000; border-radius: ${e.borderRadius.xsmall}; &:focus{ border: ${e.borderThickness.thin} solid ${({ theme: t }) => t.colors.teal}; } `, ce = r.textarea` display: flex; place-content: center; border: ${e.borderThickness.light} solid ${({ theme: t }) => t.colors.text}; border-radius: ${e.borderRadius.small}; outline: none; line-height: ${e.spacing.small}; padding: ${e.spacing.xxxsmall}; font-family: ${e.fonts.secondary}, ${e.fonts.fallback}; font-size: ${e.fontSize.xsmall}; max-width: 100%; resize: none; overflow-y: auto; `, D = J((t, o) => { const { type: i, id: l, onChange: b, isRequired: d, dataAttributes: m, disabled: c, className: f } = t; if (i === "textarea") { const { rows: y = 5, cols: F = 30, value: v, ...A } = t; return /* @__PURE__ */ a( ce, { id: l, onChange: b, value: v, rows: y, cols: F, ...m, className: f, ref: o, disabled: c, required: d } ); } if (i === "radio" || i === "checkbox") { const { checked: y, ...F } = t; return /* @__PURE__ */ a( W, { ref: o, type: "checkbox", id: l, checked: y, onChange: b, disabled: c, className: f, ...m } ); } const $ = t, { value: N, pattern: p, placeholderText: C, ...w } = $; return /* @__PURE__ */ a( W, { id: l, name: l, placeholder: C, onChange: b, value: N, type: i, required: d, ...m, className: f, ref: o, disabled: c, pattern: p } ); }), E = (t) => { const { className: o, type: i, id: l, textLabel: b, additionalInfo: d, $labelFlexDirection: m, svg: c, labelClass: f, onChange: $, isRequired: N, dataAttributes: p, inputClass: C, ref: w, disabled: y, isEditable: F, editIcon: v, onClickEdit: A, deleteIcon: S, onClickDelete: z, idx: T, children: j } = t; return /* @__PURE__ */ g(te, { className: `${o} ${l.replace("#", "")}-label-input-container`, children: [ i !== "radio" && i !== "checkbox" && /* @__PURE__ */ a(B, { htmlFor: l, textLabel: b, additionalInfo: d, $labelFlexDirection: m, svg: c, className: f }), i !== "radio" && i !== "checkbox" && i === "textarea" && (() => { const { rows: n = 5, cols: u = 30, value: s, ...k } = t; return /* @__PURE__ */ a( D, { id: l, name: l, type: "textarea", isRequired: N, onChange: $, value: s, rows: n, cols: u, dataAttributes: p, className: C, ref: w, disabled: y } ); })(), i !== "radio" && i !== "checkbox" && i !== "textarea" && (() => { const n = t, { value: u, pattern: s, placeholderText: k, ...L } = n; return /* @__PURE__ */ a( D, { id: l, name: l, placeholderText: k, onChange: $, value: u, type: i, isRequired: N, dataAttributes: p, className: C, ref: w, disabled: y, pattern: s } ); })(), (i === "radio" || i === "checkbox") && (() => { const { checked: n, ...u } = t; return /* @__PURE__ */ g(R, { children: [ /* @__PURE__ */ a( D, { ref: w, type: "checkbox", name: l, id: l, isRequired: N, checked: n, onChange: $, disabled: y, className: C, dataAttributes: p } ), /* @__PURE__ */ a(B, { htmlFor: l, textLabel: b, additionalInfo: d, $labelFlexDirection: m, svg: c, className: f }) ] }); })(), F && /* @__PURE__ */ g(ae, { className: "input-edit-buttons", children: [ /* @__PURE__ */ a(x, { id: `editable-${l}-edit-btn`, svg: v, buttonType: "button", onClick: A, className: `edit-radio-${T}`, dataAttributes: p }), /* @__PURE__ */ a(x, { id: `editable-${l}-delete-btn`, svg: S, buttonType: "button", onClick: z, className: `delete-radio-${T}`, dataAttributes: p }) ] }), j ] }); }, re = r.fieldset` padding: 0; height: auto; width: 100%; `, de = r.legend` font-size: ${e.spacing.medium}; font-weight: 500; margin: 0 auto ${e.spacing.small} auto; text-align: center; font-family: ${e.fonts.secondary}, ${e.fonts.fallback}; `, me = r.div` display: flex; flex-direction: column; align-items: flex-start; width: 100%; margin-bottom: ${e.spacing.small}; `, be = r.div` display: flex; justify-content: space-between; width: 100%; `, P = ({ legend: t, idx: o, editableInformation: i, onChangeOfEditableOption: l, onClickSaveEdit: b, onClickCancelEdit: d, onClickDeleteEntry: m }) => /* @__PURE__ */ g(re, { className: "editable-option-fieldset", children: [ /* @__PURE__ */ a(de, { children: `${t} ${o + 1}` }), i?.map((c, f) => /* @__PURE__ */ a(me, { className: "editable-option-container", children: /* @__PURE__ */ a( D, { id: `editable-option-${f}`, name: `editable-option-${f}`, placeholderText: c.name.charAt(0).toUpperCase() + c.name.slice(1), onChange: l, value: c.info, type: c.type, isRequired: !0, className: "editable-option", dataAttributes: { "data-index": f, "data-key": c.info } } ) }, `${c.name}-${f}`)), /* @__PURE__ */ g(be, { className: "editable-option-button-space", children: [ /* @__PURE__ */ a(x, { id: `editable-option-${o}-submit`, buttonType: "button", text: "Save", onClick: b, className: "editable-option-btn", dataAttributes: { "data-index": o } }), /* @__PURE__ */ a(x, { id: `editable-option-${o}-cancel`, buttonType: "button", text: "Cancel", onClick: d, className: "editable-option-btn", dataAttributes: { "data-index": o } }), /* @__PURE__ */ a(x, { id: `editable-option-${o}-delete`, buttonType: "button", text: "Delete", onClick: m, className: "editable-option-btn", dataAttributes: { "data-index": o } }) ] }) ] }), he = r.div` display: flex; justify-content: space-between; max-width: 100%; `, fe = ({ id: t, hasSubmit: o, submitText: i, handleSubmit: l, hasReset: b, resetText: d, handleReset: m, hasCancel: c, cancelText: f, handleCancel: $ }) => /* @__PURE__ */ g(he, { className: "form-main-button-container", children: [ o && /* @__PURE__ */ a(x, { id: `form-${t}-submit`, buttonType: "submit", text: i ?? "Submit", onClick: l, className: "submit-form-btn" }), b && /* @__PURE__ */ a(x, { id: `form-${t}-edit`, buttonType: "button", text: d ?? "Reset", onClick: m, className: "reset-form-btn" }), c && /* @__PURE__ */ a(x, { id: `form-${t}-cancel`, buttonType: "button", text: f ?? "Cancel", onClick: $, className: "cancel-form-btn" }) ] }), M = r.legend` font-size: ${e.spacing.medium}; font-weight: 500; margin-bottom: ${e.spacing.small}; width: auto; text-align: center; font-family: ${e.fonts.secondary}, ${e.fonts.fallback}; `, U = r.fieldset` padding: 0; height: auto; width: 100%; `, H = r.div` padding: ${e.spacing.small}; height: auto; width: 100%; `, I = r.div` display: flex; justify-content: center; align-items: center; width: 100%; height: auto; font-family: ${e.fonts.tertiary}, ${e.fonts.fallback}; font-size: ${e.fontSize.xsmall}; font-weight: ${e.fontWeight.bold}; `, ge = r.form` display: flex; flex-direction: column; align-items: center; width: 100%; `, O = r.div` display: flex; justify-content: space-between; width: 100%; `, ue = r.div` width: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; `, Ce = ({ fieldsets: t = null, legendText: o, isExpandable: i, id: l, formInputs: b, labelAndInputContainerClass: d, labelClass: m, inputClass: c, onChangeOfEditableOption: f, handleAddingInputEntry: $, hasSubmit: N = !1, submitText: p, handleSubmit: C, hasReset: w = !1, resetText: y, handleReset: F, hasCancel: v = !1, cancelText: A, handleCancel: S, handleSubmitForm: z, className: T, children: j }) => /* @__PURE__ */ g(ge, { id: `${l}-form`, className: T, onSubmit: z, children: [ t ? t.map((n, u) => /* @__PURE__ */ g(H, { className: `${l}-fieldset-wrapper`, children: [ /* @__PURE__ */ g(U, { id: `${l}-form-fieldset-${u}`, className: `${n.legend}-fieldset`, children: [ n.legend && /* @__PURE__ */ a(M, { className: `${n.legend}-legend`, children: n.legend }), n.inputs.length !== 0 ? n.inputs.map((s, k) => /* @__PURE__ */ g(q.Fragment, { children: [ s.type === "textarea" && /* @__PURE__ */ a( E, { ...s, id: s.id ?? `${n.legend}-input`, labelClass: m, inputClass: c, idx: k, className: `${d} ${s?.uniqueClass}` } ), s.type !== "textarea" && s.type !== "radio" && s.type !== "checkbox" && /* @__PURE__ */ a( E, { ...s, id: s.id ?? `${n.legend}-input`, labelClass: m, inputClass: c, idx: k, className: `${d} ${s?.uniqueClass}` } ), (s.type === "radio" || s.type === "checkbox") && /* @__PURE__ */ g(R, { children: [ /* @__PURE__ */ a( E, { ...s, id: s.id ?? `${n.legend}-input`, labelClass: m, inputClass: c, idx: k, className: `${d} ${s?.uniqueClass}` } ), s.editing && s.isEditable && /* @__PURE__ */ a( P, { legend: `${n.legend}`, idx: k, editableInformation: s?.editableInformation || [], onChangeOfEditableOption: f, onClickSaveEdit: s?.onClickSave || ((L) => { }), onClickCancelEdit: s?.onClickCancel || ((L) => { }), onClickDeleteEntry: s?.onClickDelete || ((L) => { }) } ) ] }) ] }, `form-${l}-${k}`)) : n.isExpandable ? /* @__PURE__ */ a(I, { children: `No entry yet on ${n.legend}. Click "+" button to add entry.` }) : "" ] }), n.isExpandable && /* @__PURE__ */ a(O, { className: "add-input-button-space", children: /* @__PURE__ */ a(x, { id: `expand-${n.legend}-inputs`, buttonType: "button", text: "+", onClick: $, className: "add-input-entry" }) }) ] }, `${n.legend}-${u}`)) : /* @__PURE__ */ g(H, { className: `${l}-fieldset-wrapper`, children: [ /* @__PURE__ */ g(U, { id: `${l}-form-fieldset`, className: `${o}-fieldset`, children: [ o && /* @__PURE__ */ a(M, { className: `${o}-legend`, children: o }), !t && b && b.length !== 0 ? b.map((n, u) => /* @__PURE__ */ g(q.Fragment, { children: [ n.type === "textarea" && /* @__PURE__ */ a( E, { ...n, id: n.id ?? `${o}-input`, labelClass: m, inputClass: c, idx: u, className: `${d} ${n?.uniqueClass}` } ), n.type !== "textarea" && n.type !== "radio" && n.type !== "checkbox" && /* @__PURE__ */ a( E, { ...n, id: n.id ?? `${o}-input`, labelClass: m, inputClass: c, idx: u, className: `${d} ${n?.uniqueClass}` } ), (n.type === "radio" || n.type === "checkbox") && /* @__PURE__ */ g(R, { children: [ /* @__PURE__ */ a( E, { ...n, id: n.id ?? `${o}-input`, labelClass: m, inputClass: c, idx: u, className: `${d} ${n?.uniqueClass}` } ), n.editing && n.isEditable && /* @__PURE__ */ a( P, { legend: `${o}`, idx: u, editableInformation: n?.editableInformation, onChangeOfEditableOption: f, onClickSaveEdit: n?.onClickSave || ((s) => { }), onClickCancelEdit: n?.onClickCancel || ((s) => { }), onClickDeleteEntry: n?.onClickDelete || ((s) => { }) } ) ] }) ] }, `form-${l}-${u}`)) : i ? /* @__PURE__ */ a(I, { children: `No entry yet on ${o}. Please click "+" button to add` }) : "" ] }), i && /* @__PURE__ */ a(O, { className: "add-input-button-space", children: /* @__PURE__ */ a(x, { id: `expand-${o}-inputs`, buttonType: "button", text: "+", onClick: $, className: "add-input-entry" }) }) ] }), /* @__PURE__ */ a( fe, { id: l, hasSubmit: N, submitText: p, handleSubmit: C, hasReset: w, resetText: y, handleReset: F, hasCancel: v, cancelText: A, handleCancel: S } ), /* @__PURE__ */ a(ue, { className: "children-container", children: j }) ] }), $e = (t) => { const o = /^#([0-9A-Fa-f]{3}){1,2}$/, i = /^rgb(a)?\(\s*\d{1,3}\s*,\s*\d{1,3}\s*,\s*\d{1,3}(?:\s*,\s*(0|1|0?\.\d+))?\s*\)$/; return o.test(t) || i.test(t) || CSS.supports("color", t); }, h = (t) => { if (!$e(t)) throw new Error(`Invalid color: ${t}`); return t; }, we = { mobile: "320px", //Small iPhone SE & Medium: iPhone 12-15 tablet: "768px", //iPad desktop: "992px", //Macbook 13" (1280x800) largeDesktop: "1200px", largerDesktop: "1400px" }, xe = { name: "light", colors: { text: h("#333446"), bg: h("#EEEEEE"), blue: h("#7F8CAA"), blue2: h("#80A6FF"), teal: h("#5b8280ff"), teal2: h("#AEEAE7"), gray: h("#AEAEAE"), information: h("#202234"), success: h("#123524"), warning: h("#F2C265"), error: h("#990000") } }, pe = { name: "dark", colors: { bg: h("#333446"), text: h("#EEEEEE"), blue: h("#80A6FF"), blue2: h("#7F8CAA"), teal: h("#AEEAE7"), teal2: h("#5b8280ff"), gray: h("#D0D0DD"), information: h("#C9E6F0"), success: h("#9EDF9C"), warning: h("#FCFFC1"), error: h("#FAD4D4") } }, G = K({}), Fe = ({ children: t, initialTheme: o = xe, secondTheme: i = pe }) => { const [l, b] = Q(o), d = () => { b((m) => m === o ? i : o); }; return /* @__PURE__ */ a(G.Provider, { value: { currentTheme: l, toggleTheme: d }, children: /* @__PURE__ */ a(X, { theme: l, children: t }) }); }, Ee = () => V(G); export { Ce as DynamicForm, G as ThemeContext, Fe as ThemeContextProvider, we as breakpoints, pe as darkTheme, xe as lightTheme, Ee as useTheme };