@sproutsocial/seeds-react-form-field 1.1.20 → 1.1.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -36,7 +36,14 @@ __export(index_exports, {
36
36
  module.exports = __toCommonJS(index_exports);
37
37
 
38
38
  // src/FormField.tsx
39
- var import_react2 = require("react");
39
+ var import_react5 = __toESM(require("react"));
40
+
41
+ // src/FormFieldHybrid.tsx
42
+ var import_react4 = __toESM(require("react"));
43
+ var import_seeds_react_system_props = require("@sproutsocial/seeds-react-system-props");
44
+
45
+ // src/FormFieldStyled.tsx
46
+ var import_react2 = __toESM(require("react"));
40
47
 
41
48
  // ../seeds-react-hooks/dist/index.mjs
42
49
  var c = __toESM(require("react"), 1);
@@ -50,50 +57,158 @@ function J(t) {
50
57
  return o.current = r, o;
51
58
  }
52
59
 
53
- // src/FormField.tsx
60
+ // src/FormFieldStyled.tsx
54
61
  var import_seeds_react_box = __toESM(require("@sproutsocial/seeds-react-box"));
55
62
  var import_seeds_react_label = __toESM(require("@sproutsocial/seeds-react-label"));
56
63
  var import_seeds_react_text = __toESM(require("@sproutsocial/seeds-react-text"));
57
64
  var import_seeds_react_visually_hidden = require("@sproutsocial/seeds-react-visually-hidden");
58
- var import_jsx_runtime = require("react/jsx-runtime");
65
+
66
+ // src/formFieldId.ts
59
67
  var idCounter = 0;
60
- var FormField = ({
61
- children,
62
- error,
63
- helperText,
64
- id: identifier,
65
- isInvalid = false,
66
- label,
67
- mb = 400,
68
- qa,
69
- isLabelHidden = false,
70
- required,
71
- ...rest
72
- }) => {
73
- const [id] = (0, import_react2.useState)(identifier || `FormField-${idCounter++}`);
68
+ function nextFormFieldId() {
69
+ return `FormField-${idCounter++}`;
70
+ }
71
+
72
+ // src/FormFieldStyled.tsx
73
+ var import_jsx_runtime = require("react/jsx-runtime");
74
+ var FormFieldStyled = import_react2.default.forwardRef(
75
+ ({
76
+ children,
77
+ error,
78
+ helperText,
79
+ id: identifier,
80
+ isInvalid = false,
81
+ label,
82
+ mb = 400,
83
+ qa,
84
+ isLabelHidden = false,
85
+ required,
86
+ ...rest
87
+ }, ref) => {
88
+ const [id] = (0, import_react2.useState)(identifier || nextFormFieldId());
89
+ const errorId = `Error-${id}`;
90
+ const helperTextId = `HelperText-${id}`;
91
+ const containerText = J("");
92
+ const errorContainerText = J("");
93
+ const ariaDescribedby = (0, import_react2.useMemo)(() => {
94
+ const ids = [
95
+ helperText && helperTextId,
96
+ isInvalid && error && errorId
97
+ ].filter(Boolean);
98
+ return ids.length > 0 ? ids.join(" ") : void 0;
99
+ }, [helperText, helperTextId, isInvalid, error, errorId]);
100
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
101
+ import_seeds_react_box.default,
102
+ {
103
+ ref,
104
+ ...rest,
105
+ ...qa,
106
+ mb,
107
+ "data-qa-formfield": qa && qa["data-qa-formfield"] || id || containerText.current,
108
+ "data-qa-formfield-isinvalid": isInvalid === true,
109
+ children: [
110
+ isLabelHidden ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_visually_hidden.VisuallyHidden, { "data-testid": "visually-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_label.default, { htmlFor: id, required, children: label }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_label.default, { mb: helperText ? 100 : 300, htmlFor: id, required, children: label }),
111
+ helperText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
112
+ import_seeds_react_text.default,
113
+ {
114
+ as: "p",
115
+ fontSize: 200,
116
+ mb: 300,
117
+ color: "text.subtext",
118
+ id: helperTextId,
119
+ children: helperText
120
+ }
121
+ ),
122
+ children({
123
+ id,
124
+ isInvalid,
125
+ ariaDescribedby,
126
+ ...required !== void 0 && { required }
127
+ }),
128
+ isInvalid && error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
129
+ import_seeds_react_text.default,
130
+ {
131
+ as: "div",
132
+ fontSize: 200,
133
+ color: "text.error",
134
+ mt: 300,
135
+ id: errorId,
136
+ "data-qa-formfield-error": qa && qa["data-qa-formfield-error"] || errorContainerText.current,
137
+ children: error
138
+ }
139
+ )
140
+ ]
141
+ }
142
+ );
143
+ }
144
+ );
145
+ FormFieldStyled.displayName = "FormField";
146
+ var FormFieldStyled_default = FormFieldStyled;
147
+
148
+ // src/FormFieldTailwind.tsx
149
+ var import_react3 = __toESM(require("react"));
150
+ var import_seeds_react_label2 = __toESM(require("@sproutsocial/seeds-react-label"));
151
+ var import_seeds_react_text2 = __toESM(require("@sproutsocial/seeds-react-text"));
152
+ var import_seeds_react_visually_hidden2 = require("@sproutsocial/seeds-react-visually-hidden");
153
+ var import_jsx_runtime2 = require("react/jsx-runtime");
154
+ function cn(...inputs) {
155
+ const classes = [];
156
+ for (const input of inputs) {
157
+ if (!input) continue;
158
+ if (typeof input === "string") {
159
+ classes.push(input);
160
+ } else if (typeof input === "object") {
161
+ for (const [key, value] of Object.entries(input)) {
162
+ if (value) {
163
+ classes.push(key);
164
+ }
165
+ }
166
+ }
167
+ }
168
+ return classes.join(" ");
169
+ }
170
+ var FormFieldTailwind = import_react3.default.forwardRef((props, ref) => {
171
+ const {
172
+ children,
173
+ error,
174
+ helperText,
175
+ id: identifier,
176
+ isInvalid = false,
177
+ label,
178
+ mb = 400,
179
+ qa,
180
+ isLabelHidden = false,
181
+ required,
182
+ className,
183
+ style,
184
+ ...rest
185
+ } = props;
186
+ const [id] = (0, import_react3.useState)(identifier || nextFormFieldId());
74
187
  const errorId = `Error-${id}`;
75
188
  const helperTextId = `HelperText-${id}`;
76
189
  const containerText = J("");
77
190
  const errorContainerText = J("");
78
- const ariaDescribedby = (0, import_react2.useMemo)(() => {
191
+ const ariaDescribedby = (0, import_react3.useMemo)(() => {
79
192
  const ids = [
80
193
  helperText && helperTextId,
81
194
  isInvalid && error && errorId
82
195
  ].filter(Boolean);
83
196
  return ids.length > 0 ? ids.join(" ") : void 0;
84
197
  }, [helperText, helperTextId, isInvalid, error, errorId]);
85
- return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
86
- import_seeds_react_box.default,
198
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
199
+ "div",
87
200
  {
201
+ ref,
202
+ className: cn("seeds-form-field", className),
88
203
  ...rest,
89
204
  ...qa,
90
- mb,
205
+ style: { marginBottom: `var(--space-${String(mb)})`, ...style },
91
206
  "data-qa-formfield": qa && qa["data-qa-formfield"] || id || containerText.current,
92
207
  "data-qa-formfield-isinvalid": isInvalid === true,
93
208
  children: [
94
- isLabelHidden ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_visually_hidden.VisuallyHidden, { "data-testid": "visually-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_label.default, { htmlFor: id, required, children: label }) }) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_seeds_react_label.default, { mb: helperText ? 100 : 300, htmlFor: id, required, children: label }),
95
- helperText && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
96
- import_seeds_react_text.default,
209
+ isLabelHidden ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_seeds_react_visually_hidden2.VisuallyHidden, { "data-testid": "visually-hidden", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_seeds_react_label2.default, { htmlFor: id, required, children: label }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_seeds_react_label2.default, { mb: helperText ? 100 : 300, htmlFor: id, required, children: label }),
210
+ helperText && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
211
+ import_seeds_react_text2.default,
97
212
  {
98
213
  as: "p",
99
214
  fontSize: 200,
@@ -109,8 +224,8 @@ var FormField = ({
109
224
  ariaDescribedby,
110
225
  ...required !== void 0 && { required }
111
226
  }),
112
- isInvalid && error && /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
113
- import_seeds_react_text.default,
227
+ isInvalid && error && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
228
+ import_seeds_react_text2.default,
114
229
  {
115
230
  as: "div",
116
231
  fontSize: 200,
@@ -124,11 +239,48 @@ var FormField = ({
124
239
  ]
125
240
  }
126
241
  );
127
- };
242
+ });
243
+ FormFieldTailwind.displayName = "FormFieldTailwind";
244
+
245
+ // src/FormFieldHybrid.tsx
246
+ var import_jsx_runtime3 = require("react/jsx-runtime");
247
+ var SC_CLASS_PATTERN = /\bsc-[a-zA-Z0-9]+\b/;
248
+ var FormFieldHybrid = import_react4.default.forwardRef(
249
+ (props, ref) => {
250
+ const {
251
+ children,
252
+ error,
253
+ helperText,
254
+ id,
255
+ isInvalid,
256
+ label,
257
+ qa,
258
+ isLabelHidden,
259
+ required,
260
+ mb,
261
+ ...rest
262
+ } = props;
263
+ const mbIsResponsive = mb !== void 0 && typeof mb !== "number" && typeof mb !== "string";
264
+ const isScExtension = SC_CLASS_PATTERN.test(props.className ?? "");
265
+ if ((0, import_seeds_react_system_props.needsStyledComponents)(rest) || mbIsResponsive || isScExtension) {
266
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FormFieldStyled_default, { ...props, ref });
267
+ }
268
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(FormFieldTailwind, { ...props, ref });
269
+ }
270
+ );
271
+ FormFieldHybrid.displayName = "FormField";
272
+ var FormFieldHybrid_default = FormFieldHybrid;
273
+
274
+ // src/FormField.tsx
275
+ var import_jsx_runtime4 = require("react/jsx-runtime");
276
+ var FormField = import_react5.default.forwardRef(
277
+ (props, ref) => /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(FormFieldHybrid_default, { ...props, ref })
278
+ );
279
+ FormField.displayName = "FormField";
128
280
  var FormField_default = FormField;
129
281
 
130
282
  // src/FormFieldTypes.ts
131
- var React2 = require("react");
283
+ var React5 = require("react");
132
284
 
133
285
  // src/index.ts
134
286
  var index_default = FormField_default;
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../src/FormField.tsx","../../seeds-react-hooks/src/useMeasure/useMeasure.ts","../../seeds-react-hooks/src/useSelect/useSelect.ts","../../seeds-react-hooks/src/useMultiselect/useMultiselect.ts","../../seeds-react-hooks/src/useMutationObserver/useMutationObserver.ts","../../seeds-react-hooks/src/useTextContent/useTextContent.ts","../../seeds-react-hooks/src/useWhyDidYouUpdate/useWhyDidYouUpdate.ts","../../seeds-react-hooks/src/useInteractiveColor/useInteractiveColor.ts","../../seeds-react-hooks/src/useIsMobile/useIsMobile.ts","../src/FormFieldTypes.ts"],"sourcesContent":["import FormField from \"./FormField\";\n\nexport default FormField;\nexport { FormField };\nexport * from \"./FormFieldTypes\";\n","import React, { useMemo, useState } from \"react\";\nimport { useTextContent } from \"@sproutsocial/seeds-react-hooks\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport Label from \"@sproutsocial/seeds-react-label\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport { VisuallyHidden } from \"@sproutsocial/seeds-react-visually-hidden\";\nimport type { TypeFormFieldProps } from \"./FormFieldTypes\";\n\nlet idCounter = 0;\n\nconst FormField = ({\n children,\n error,\n helperText,\n id: identifier,\n isInvalid = false,\n label,\n mb = 400,\n qa,\n isLabelHidden = false,\n required,\n ...rest\n}: TypeFormFieldProps) => {\n const [id] = useState(identifier || `FormField-${idCounter++}`);\n const errorId = `Error-${id}`;\n const helperTextId = `HelperText-${id}`;\n const containerText = useTextContent(\"\");\n const errorContainerText = useTextContent(\"\");\n\n const ariaDescribedby = useMemo(() => {\n const ids = [\n helperText && helperTextId,\n isInvalid && error && errorId,\n ].filter(Boolean);\n return ids.length > 0 ? ids.join(\" \") : undefined;\n }, [helperText, helperTextId, isInvalid, error, errorId]);\n\n return (\n <Box\n {...rest}\n {...qa}\n mb={mb}\n data-qa-formfield={\n (qa && qa[\"data-qa-formfield\"]) || id || containerText.current\n }\n data-qa-formfield-isinvalid={isInvalid === true}\n >\n {isLabelHidden ? (\n <VisuallyHidden data-testid=\"visually-hidden\">\n <Label htmlFor={id} required={required}>\n {label}\n </Label>\n </VisuallyHidden>\n ) : (\n <Label mb={helperText ? 100 : 300} htmlFor={id} required={required}>\n {label}\n </Label>\n )}\n {helperText && (\n <Text\n as=\"p\"\n fontSize={200}\n mb={300}\n color=\"text.subtext\"\n id={helperTextId}\n >\n {helperText}\n </Text>\n )}\n {children({\n id,\n isInvalid,\n ariaDescribedby,\n ...(required !== undefined && { required }),\n })}\n {isInvalid && error && (\n <Text\n as=\"div\"\n fontSize={200}\n color=\"text.error\"\n mt={300}\n id={errorId}\n data-qa-formfield-error={\n (qa && qa[\"data-qa-formfield-error\"]) || errorContainerText.current\n }\n >\n {error}\n </Text>\n )}\n </Box>\n );\n};\n\nexport default FormField;\n","import { useState, useLayoutEffect, type RefObject } from \"react\";\n\ninterface DOMRectObject {\n x: number;\n y: number;\n width: number;\n height: number;\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\nconst initialBounds = Object.freeze({\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n});\n\nexport function useMeasure<TElement extends Element>(ref: RefObject<TElement>) {\n const [bounds, setContentRect] =\n useState<Readonly<DOMRectObject>>(initialBounds);\n\n useLayoutEffect(() => {\n const element = ref.current;\n\n if (\n !element ||\n // in non-browser environments (e.g. Jest tests) ResizeObserver is not defined\n !(\"ResizeObserver\" in window)\n ) {\n return;\n }\n\n const resizeObserver = new ResizeObserver(([entry]) => {\n if (!entry) return;\n const { x, y, width, height, top, right, bottom, left } =\n entry.contentRect;\n setContentRect({\n x,\n y,\n width,\n height,\n top,\n right,\n bottom,\n left,\n });\n });\n resizeObserver.observe(ref.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, [ref]);\n\n return bounds;\n}\n","import { useState, useCallback } from \"react\";\n\ntype TypeSingleSelectProps<T extends string> = {\n initialValue?: T | \"\";\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onChange?: (value: string | T) => any;\n};\n\nexport const useSelect = <T extends string>(\n {\n initialValue = \"\",\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: userOnChange = () => {},\n }: TypeSingleSelectProps<T> = {\n initialValue: \"\",\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: () => {},\n }\n) => {\n const [value, setValue] = useState<string | T>(initialValue);\n\n const onChange = useCallback(\n (newValue: string) => {\n if (newValue !== value) {\n setValue(newValue);\n userOnChange(newValue);\n }\n },\n [userOnChange, value]\n );\n\n return { value, onChange };\n};\n","import { useCallback, useEffect, useReducer, useRef } from \"react\";\n\ntype TypeMultiSelectProps<T extends string> = {\n initialValue?: T[];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onChange?: (value: Array<string | T>) => any;\n};\n\nconst valueReducer = (\n state: Set<string>,\n action: { type: string; value?: string }\n): Set<string> => {\n const newState = new Set(state);\n switch (action.type) {\n case \"reset\": {\n return new Set();\n }\n case \"toggle_item\":\n default: {\n if (action.value) {\n if (newState.has(action.value)) {\n newState.delete(action.value);\n } else {\n newState.add(action.value);\n }\n }\n return newState;\n }\n }\n};\n\nexport const useMultiselect = <T extends string>(\n {\n initialValue = [],\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: userOnChange = () => {},\n }: TypeMultiSelectProps<T> = {\n initialValue: [],\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: () => {},\n }\n) => {\n const [value, dispatch] = useReducer(valueReducer, new Set(initialValue));\n\n const getArrayValue = (value: Set<string | T>) =>\n Array.from<string | T>(value);\n\n const onChange = useCallback(\n (newValue: string) => {\n dispatch({ type: \"toggle_item\", value: newValue });\n },\n [dispatch]\n );\n\n const isFirstRun = useRef(true);\n\n useEffect(() => {\n if (isFirstRun.current) {\n isFirstRun.current = false;\n return;\n }\n userOnChange(getArrayValue(value));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [userOnChange, value]);\n\n const onClear = useCallback(() => {\n dispatch({ type: \"reset\" });\n }, [dispatch]);\n\n return { value: getArrayValue(value), onChange, onClear };\n};\n","import { canUseDOM } from \"@sproutsocial/seeds-react-utilities\";\nimport { useEffect, useMemo, useState } from \"react\";\n\ntype TypeMutationObserverInitRequired =\n | {\n childList: true;\n }\n | {\n attributes: true;\n }\n | {\n characterData: true;\n };\n\ntype TypeMutationObserverInit = {\n subtree?: boolean;\n attributeOldValue?: boolean;\n characterDataOldValue?: boolean;\n attributeFilter?: Array<string>;\n} & TypeMutationObserverInitRequired;\n\ntype TypeMutationObserverCallback = (\n mutationList?: MutationRecord[],\n observer?: MutationObserver\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n) => any;\n\nconst defaultCallback: TypeMutationObserverCallback = (mutationList) =>\n mutationList;\n\nexport function useMutationObserver(\n targetNode: Node | null,\n config: TypeMutationObserverInit,\n callback: TypeMutationObserverCallback = defaultCallback\n) {\n if (!canUseDOM()) {\n return;\n }\n /* eslint-disable-next-line */\n const [value, setValue] = useState(undefined);\n /* eslint-disable-next-line */\n const observer = useMemo(\n () =>\n new MutationObserver((mutationList, observer) => {\n const result = callback(mutationList, observer);\n setValue(result);\n }),\n [callback]\n );\n /* eslint-disable-next-line */\n useEffect(() => {\n if (targetNode) {\n observer.observe(targetNode, config);\n return () => {\n observer.disconnect();\n };\n }\n }, [targetNode, config, observer]);\n\n return value;\n}\n\nexport function useMutationObserverOnce(\n targetNode: Node | null,\n config: TypeMutationObserverInit,\n callback: TypeMutationObserverCallback\n) {\n const [isObserving, setObserving] = useState(true);\n const node = isObserving ? targetNode : null;\n const value = useMutationObserver(node, config, callback);\n if (value !== undefined && isObserving) {\n setObserving(false);\n }\n return value;\n}\n","import { useCallback, useState } from \"react\";\n\nexport type textContentRef = ((node: Node) => void) & { current?: string };\nexport function useTextContent(initial: string) {\n const [textContent, setTextContent] = useState(initial);\n\n const ref: textContentRef = useCallback((node: Node) => {\n if (node && node.textContent !== null) {\n setTextContent(node.textContent);\n }\n }, []);\n\n ref.current = textContent;\n return ref;\n}\n","import { useRef, useEffect } from \"react\";\n\nexport function useWhyDidYouUpdate(\n name: string,\n props: { [key: string]: any }\n) {\n // Get a mutable ref object where we can store props ...\n // ... for comparison next time this hook runs.\n const previousProps = useRef<typeof props>({});\n\n useEffect(() => {\n if (previousProps.current) {\n // Get all keys from previous and current props\n const allKeys = Object.keys({ ...previousProps.current, ...props });\n // Use this object to keep track of changed props\n const changesObj: typeof props = {};\n // Iterate through keys\n allKeys.forEach((key) => {\n // If previous is different from current\n\n if (previousProps.current[key] !== props[key]) {\n // Add to changesObj\n\n changesObj[key] = {\n from: previousProps.current[key],\n\n to: props[key],\n };\n }\n });\n\n // If changesObj not empty then output to console\n if (Object.keys(changesObj).length) {\n // eslint-disable-next-line no-console\n console.log(\"[why-did-you-update]\", name, changesObj);\n }\n }\n\n // Finally update previousProps with current props for next hook call\n previousProps.current = props;\n });\n}\n","import { useTheme } from \"styled-components\";\nimport type { TypeTheme } from \"@sproutsocial/seeds-react-theme\";\n\n/**\n * The useInteractiveColor hook has context of theme mode (light or dark)\n * and can be used to lighten or darken a color dynamically\n *\n * note: colors are limited to our theme colors\n */\nconst useInteractiveColor = (themeColor: string): string => {\n // Throw error if used outside of a ThemeProvider (styled-components)\n if (!useTheme()) {\n throw new Error(\n \"useInteractiveColor() must be used within a Styled Components ThemeProvider\"\n );\n }\n\n // Get the current theme mode ie. 'light' or 'dark'\n const theme: TypeTheme = useTheme() as TypeTheme;\n const themeMode = theme.mode;\n\n // If the theme mode is dark, return a lightened version of the themeValue\n if (themeMode === \"dark\") {\n return `color-mix(in srgb, ${themeColor}, white 20%)`;\n } else {\n // If the theme mode is light, return a darkened version of the themeValue\n return `color-mix(in srgb, ${themeColor}, black 20%)`;\n }\n};\n\nexport { useInteractiveColor };\n","import * as React from \"react\";\nimport { useTheme } from \"styled-components\";\nimport type { TypeTheme } from \"@sproutsocial/seeds-react-theme\";\n\n/**\n * Returns `true` when the viewport matches a mobile-width media query.\n *\n * @param breakpoint Optional override. Accepts a CSS string (`\"640px\"`,\n * `\"40em\"`) or a number (interpreted as px). Defaults to the styled-components\n * theme's `breakpoints.sm` (falling back to `\"640px\"` if the theme is\n * unavailable).\n */\nexport function useIsMobile(breakpoint?: number | string): boolean {\n const styledTheme = useTheme() as TypeTheme | undefined;\n const themeSm = styledTheme?.breakpoints?.sm ?? \"640px\";\n const resolved =\n typeof breakpoint === \"number\" ? `${breakpoint}px` : breakpoint ?? themeSm;\n\n const [isMobile, setIsMobile] = React.useState(() => {\n if (\n typeof window === \"undefined\" ||\n typeof window.matchMedia !== \"function\"\n ) {\n return false;\n }\n return window.matchMedia(`(max-width: ${resolved})`).matches;\n });\n\n React.useEffect(() => {\n if (\n typeof window === \"undefined\" ||\n typeof window.matchMedia !== \"function\"\n ) {\n return;\n }\n const mq = window.matchMedia(`(max-width: ${resolved})`);\n setIsMobile(mq.matches);\n const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches);\n mq.addEventListener?.(\"change\", handler);\n return () => mq.removeEventListener?.(\"change\", handler);\n }, [resolved]);\n\n return isMobile;\n}\n","import * as React from \"react\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\n\nexport interface TypeFormFieldProps extends Omit<TypeBoxProps, \"children\"> {\n /** A function that receives props that need to be spread onto the child element */\n children: (options: {\n id: string;\n isInvalid: boolean;\n ariaDescribedby?: string;\n required?: boolean;\n }) => React.ReactNode;\n\n /** Text describing any error with the field's content */\n error?: React.ReactNode;\n\n /** Text acting as a description blurb below the main label **/\n helperText?: React.ReactNode;\n\n /** ID of the form element (will be auto-generated if not provided) */\n id?: string;\n\n /** Whether the current contents of the field are invalid */\n isInvalid?: boolean;\n\n /** Label text to display above the form field */\n label: React.ReactNode;\n qa?: Record<string, any>;\n\n /** Whether the label text should be visually hidden */\n isLabelHidden?: boolean;\n\n /** Whether the form element is required */\n required?: boolean;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACAA,IAAAA,gBAAyC;A;;;;;ACYzC,IAAMC,IAAgB,OAAO,OAAO,EAClC,GAAG,GACH,GAAG,GACH,OAAO,GACP,QAAQ,GACR,KAAK,GACL,OAAO,GACP,QAAQ,GACR,MAAM,EACR,CAAC;AIlBM,SAASC,EAAeC,GAAiB;AAC9C,MAAM,CAACC,GAAaC,CAAc,QAAIC,uBAASH,CAAO,GAEhDI,QAAsBC,0BAAaC,OAAe;AAClDA,SAAQA,EAAK,gBAAgB,QAC/BJ,EAAeI,EAAK,WAAW;EAEnC,GAAG,CAAA,CAAE;AAEL,SAAAF,EAAI,UAAUH,GACPG;AACT;;;ALZA,6BAAgB;AAChB,+BAAkB;AAClB,8BAAiB;AACjB,yCAA+B;AAiC3B;AA9BJ,IAAI,YAAY;AAEhB,IAAM,YAAY,CAAC;AAAA,EACjB;AAAA,EACA;AAAA,EACA;AAAA,EACA,IAAI;AAAA,EACJ,YAAY;AAAA,EACZ;AAAA,EACA,KAAK;AAAA,EACL;AAAA,EACA,gBAAgB;AAAA,EAChB;AAAA,EACA,GAAG;AACL,MAA0B;AACxB,QAAM,CAAC,EAAE,QAAI,wBAAS,cAAc,aAAa,WAAW,EAAE;AAC9D,QAAM,UAAU,SAAS,EAAE;AAC3B,QAAM,eAAe,cAAc,EAAE;AACrC,QAAM,gBAAgB,EAAe,EAAE;AACvC,QAAM,qBAAqB,EAAe,EAAE;AAE5C,QAAM,sBAAkB,uBAAQ,MAAM;AACpC,UAAM,MAAM;AAAA,MACV,cAAc;AAAA,MACd,aAAa,SAAS;AAAA,IACxB,EAAE,OAAO,OAAO;AAChB,WAAO,IAAI,SAAS,IAAI,IAAI,KAAK,GAAG,IAAI;AAAA,EAC1C,GAAG,CAAC,YAAY,cAAc,WAAW,OAAO,OAAO,CAAC;AAExD,SACE;AAAA,IAAC,uBAAAG;AAAA,IAAA;AAAA,MACE,GAAG;AAAA,MACH,GAAG;AAAA,MACJ;AAAA,MACA,qBACG,MAAM,GAAG,mBAAmB,KAAM,MAAM,cAAc;AAAA,MAEzD,+BAA6B,cAAc;AAAA,MAE1C;AAAA,wBACC,4CAAC,qDAAe,eAAY,mBAC1B,sDAAC,yBAAAC,SAAA,EAAM,SAAS,IAAI,UACjB,iBACH,GACF,IAEA,4CAAC,yBAAAA,SAAA,EAAM,IAAI,aAAa,MAAM,KAAK,SAAS,IAAI,UAC7C,iBACH;AAAA,QAED,cACC;AAAA,UAAC,wBAAAC;AAAA,UAAA;AAAA,YACC,IAAG;AAAA,YACH,UAAU;AAAA,YACV,IAAI;AAAA,YACJ,OAAM;AAAA,YACN,IAAI;AAAA,YAEH;AAAA;AAAA,QACH;AAAA,QAED,SAAS;AAAA,UACR;AAAA,UACA;AAAA,UACA;AAAA,UACA,GAAI,aAAa,UAAa,EAAE,SAAS;AAAA,QAC3C,CAAC;AAAA,QACA,aAAa,SACZ;AAAA,UAAC,wBAAAA;AAAA,UAAA;AAAA,YACC,IAAG;AAAA,YACH,UAAU;AAAA,YACV,OAAM;AAAA,YACN,IAAI;AAAA,YACJ,IAAI;AAAA,YACJ,2BACG,MAAM,GAAG,yBAAyB,KAAM,mBAAmB;AAAA,YAG7D;AAAA;AAAA,QACH;AAAA;AAAA;AAAA,EAEJ;AAEJ;AAEA,IAAO,oBAAQ;;;AS7Ff,IAAAC,SAAuB;;;AVEvB,IAAO,gBAAQ;","names":["import_react","initialBounds","useTextContent","initial","textContent","setTextContent","useState","ref","useCallback","node","Box","Label","Text","React"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/FormField.tsx","../src/FormFieldHybrid.tsx","../src/FormFieldStyled.tsx","../../seeds-react-hooks/src/useMeasure/useMeasure.ts","../../seeds-react-hooks/src/useSelect/useSelect.ts","../../seeds-react-hooks/src/useMultiselect/useMultiselect.ts","../../seeds-react-hooks/src/useMutationObserver/useMutationObserver.ts","../../seeds-react-hooks/src/useTextContent/useTextContent.ts","../../seeds-react-hooks/src/useWhyDidYouUpdate/useWhyDidYouUpdate.ts","../../seeds-react-hooks/src/useInteractiveColor/useInteractiveColor.ts","../../seeds-react-hooks/src/useIsMobile/useIsMobile.ts","../src/formFieldId.ts","../src/FormFieldTailwind.tsx","../src/FormFieldTypes.ts"],"sourcesContent":["import FormField from \"./FormField\";\n\nexport default FormField;\nexport { FormField };\nexport * from \"./FormFieldTypes\";\n","import React from \"react\";\nimport type { TypeFormFieldProps } from \"./FormFieldTypes\";\nimport FormFieldHybrid from \"./FormFieldHybrid\";\n\n/**\n * FormField component. Automatically routes between the Tailwind implementation\n * (preferred) and the styled-components implementation (for consumers using\n * system props or a styled() extension).\n */\nconst FormField = React.forwardRef<HTMLDivElement, TypeFormFieldProps>(\n (props, ref) => <FormFieldHybrid {...props} ref={ref} />\n);\n\nFormField.displayName = \"FormField\";\nexport default FormField;\n","/**\n * Hybrid FormField Component\n * Automatically chooses between Tailwind and styled-components based on props.\n *\n * FormField's intrinsic props (notably `mb`, which always has a default and is\n * itself a styled-system prop) MUST be destructured out before the routing\n * check — otherwise hasStyledProps would always be true and the Tailwind path\n * would be dead code.\n */\n\nimport React from \"react\";\nimport { needsStyledComponents } from \"@sproutsocial/seeds-react-system-props\";\nimport type { TypeFormFieldProps } from \"./FormFieldTypes\";\nimport FormFieldStyled from \"./FormFieldStyled\";\nimport { FormFieldTailwind } from \"./FormFieldTailwind\";\n\n// styled-components injects a generated `sc-<hash>` className on extended\n// components. Detect it so `styled(FormField)` extensions render via the SC\n// (Box) path even when no ThemeProvider is in scope (no `theme`/\n// `forwardedComponent` prop is passed in that case).\nconst SC_CLASS_PATTERN = /\\bsc-[a-zA-Z0-9]+\\b/;\n\nconst FormFieldHybrid = React.forwardRef<HTMLDivElement, TypeFormFieldProps>(\n (props, ref) => {\n const {\n children,\n error,\n helperText,\n id,\n isInvalid,\n label,\n qa,\n isLabelHidden,\n required,\n mb,\n ...rest\n } = props;\n\n // The Tailwind path maps a scalar mb to an inline space token; a responsive\n // mb (array/object) must go through Box on the styled path.\n const mbIsResponsive =\n mb !== undefined && typeof mb !== \"number\" && typeof mb !== \"string\";\n\n const isScExtension = SC_CLASS_PATTERN.test(props.className ?? \"\");\n\n if (needsStyledComponents(rest) || mbIsResponsive || isScExtension) {\n return <FormFieldStyled {...props} ref={ref} />;\n }\n\n return <FormFieldTailwind {...props} ref={ref} />;\n }\n);\n\nFormFieldHybrid.displayName = \"FormField\";\nexport default FormFieldHybrid;\n","/**\n * Styled-components implementation of FormField.\n * Preserves the original styled-system behavior: the root Box handles all\n * styled-system props (px, width, color, bg, responsive mb, …) and supports\n * styled(FormField) extensions. This is the legacy path the Hybrid routes to\n * when styled-components rendering is required.\n */\n\nimport React, { useMemo, useState } from \"react\";\nimport { useTextContent } from \"@sproutsocial/seeds-react-hooks\";\nimport Box from \"@sproutsocial/seeds-react-box\";\nimport Label from \"@sproutsocial/seeds-react-label\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport { VisuallyHidden } from \"@sproutsocial/seeds-react-visually-hidden\";\nimport type { TypeFormFieldProps } from \"./FormFieldTypes\";\nimport { nextFormFieldId } from \"./formFieldId\";\n\nconst FormFieldStyled = React.forwardRef<HTMLDivElement, TypeFormFieldProps>(\n (\n {\n children,\n error,\n helperText,\n id: identifier,\n isInvalid = false,\n label,\n mb = 400,\n qa,\n isLabelHidden = false,\n required,\n ...rest\n },\n ref\n ) => {\n const [id] = useState(identifier || nextFormFieldId());\n const errorId = `Error-${id}`;\n const helperTextId = `HelperText-${id}`;\n const containerText = useTextContent(\"\");\n const errorContainerText = useTextContent(\"\");\n\n const ariaDescribedby = useMemo(() => {\n const ids = [\n helperText && helperTextId,\n isInvalid && error && errorId,\n ].filter(Boolean);\n return ids.length > 0 ? ids.join(\" \") : undefined;\n }, [helperText, helperTextId, isInvalid, error, errorId]);\n\n return (\n <Box\n ref={ref}\n {...rest}\n {...qa}\n mb={mb}\n data-qa-formfield={\n (qa && qa[\"data-qa-formfield\"]) || id || containerText.current\n }\n data-qa-formfield-isinvalid={isInvalid === true}\n >\n {isLabelHidden ? (\n <VisuallyHidden data-testid=\"visually-hidden\">\n <Label htmlFor={id} required={required}>\n {label}\n </Label>\n </VisuallyHidden>\n ) : (\n <Label mb={helperText ? 100 : 300} htmlFor={id} required={required}>\n {label}\n </Label>\n )}\n {helperText && (\n <Text\n as=\"p\"\n fontSize={200}\n mb={300}\n color=\"text.subtext\"\n id={helperTextId}\n >\n {helperText}\n </Text>\n )}\n {children({\n id,\n isInvalid,\n ariaDescribedby,\n ...(required !== undefined && { required }),\n })}\n {isInvalid && error && (\n <Text\n as=\"div\"\n fontSize={200}\n color=\"text.error\"\n mt={300}\n id={errorId}\n data-qa-formfield-error={\n (qa && qa[\"data-qa-formfield-error\"]) ||\n errorContainerText.current\n }\n >\n {error}\n </Text>\n )}\n </Box>\n );\n }\n);\n\nFormFieldStyled.displayName = \"FormField\";\nexport default FormFieldStyled;\n","import { useState, useLayoutEffect, type RefObject } from \"react\";\n\ninterface DOMRectObject {\n x: number;\n y: number;\n width: number;\n height: number;\n top: number;\n right: number;\n bottom: number;\n left: number;\n}\nconst initialBounds = Object.freeze({\n x: 0,\n y: 0,\n width: 0,\n height: 0,\n top: 0,\n right: 0,\n bottom: 0,\n left: 0,\n});\n\nexport function useMeasure<TElement extends Element>(ref: RefObject<TElement>) {\n const [bounds, setContentRect] =\n useState<Readonly<DOMRectObject>>(initialBounds);\n\n useLayoutEffect(() => {\n const element = ref.current;\n\n if (\n !element ||\n // in non-browser environments (e.g. Jest tests) ResizeObserver is not defined\n !(\"ResizeObserver\" in window)\n ) {\n return;\n }\n\n const resizeObserver = new ResizeObserver(([entry]) => {\n if (!entry) return;\n const { x, y, width, height, top, right, bottom, left } =\n entry.contentRect;\n setContentRect({\n x,\n y,\n width,\n height,\n top,\n right,\n bottom,\n left,\n });\n });\n resizeObserver.observe(ref.current);\n\n return () => {\n resizeObserver.disconnect();\n };\n }, [ref]);\n\n return bounds;\n}\n","import { useState, useCallback } from \"react\";\n\ntype TypeSingleSelectProps<T extends string> = {\n initialValue?: T | \"\";\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onChange?: (value: string | T) => any;\n};\n\nexport const useSelect = <T extends string>(\n {\n initialValue = \"\",\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: userOnChange = () => {},\n }: TypeSingleSelectProps<T> = {\n initialValue: \"\",\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: () => {},\n }\n) => {\n const [value, setValue] = useState<string | T>(initialValue);\n\n const onChange = useCallback(\n (newValue: string) => {\n if (newValue !== value) {\n setValue(newValue);\n userOnChange(newValue);\n }\n },\n [userOnChange, value]\n );\n\n return { value, onChange };\n};\n","import { useCallback, useEffect, useReducer, useRef } from \"react\";\n\ntype TypeMultiSelectProps<T extends string> = {\n initialValue?: T[];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n onChange?: (value: Array<string | T>) => any;\n};\n\nconst valueReducer = (\n state: Set<string>,\n action: { type: string; value?: string }\n): Set<string> => {\n const newState = new Set(state);\n switch (action.type) {\n case \"reset\": {\n return new Set();\n }\n case \"toggle_item\":\n default: {\n if (action.value) {\n if (newState.has(action.value)) {\n newState.delete(action.value);\n } else {\n newState.add(action.value);\n }\n }\n return newState;\n }\n }\n};\n\nexport const useMultiselect = <T extends string>(\n {\n initialValue = [],\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: userOnChange = () => {},\n }: TypeMultiSelectProps<T> = {\n initialValue: [],\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n onChange: () => {},\n }\n) => {\n const [value, dispatch] = useReducer(valueReducer, new Set(initialValue));\n\n const getArrayValue = (value: Set<string | T>) =>\n Array.from<string | T>(value);\n\n const onChange = useCallback(\n (newValue: string) => {\n dispatch({ type: \"toggle_item\", value: newValue });\n },\n [dispatch]\n );\n\n const isFirstRun = useRef(true);\n\n useEffect(() => {\n if (isFirstRun.current) {\n isFirstRun.current = false;\n return;\n }\n userOnChange(getArrayValue(value));\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, [userOnChange, value]);\n\n const onClear = useCallback(() => {\n dispatch({ type: \"reset\" });\n }, [dispatch]);\n\n return { value: getArrayValue(value), onChange, onClear };\n};\n","import { canUseDOM } from \"@sproutsocial/seeds-react-utilities\";\nimport { useEffect, useMemo, useState } from \"react\";\n\ntype TypeMutationObserverInitRequired =\n | {\n childList: true;\n }\n | {\n attributes: true;\n }\n | {\n characterData: true;\n };\n\ntype TypeMutationObserverInit = {\n subtree?: boolean;\n attributeOldValue?: boolean;\n characterDataOldValue?: boolean;\n attributeFilter?: Array<string>;\n} & TypeMutationObserverInitRequired;\n\ntype TypeMutationObserverCallback = (\n mutationList?: MutationRecord[],\n observer?: MutationObserver\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n) => any;\n\nconst defaultCallback: TypeMutationObserverCallback = (mutationList) =>\n mutationList;\n\nexport function useMutationObserver(\n targetNode: Node | null,\n config: TypeMutationObserverInit,\n callback: TypeMutationObserverCallback = defaultCallback\n) {\n if (!canUseDOM()) {\n return;\n }\n /* eslint-disable-next-line */\n const [value, setValue] = useState(undefined);\n /* eslint-disable-next-line */\n const observer = useMemo(\n () =>\n new MutationObserver((mutationList, observer) => {\n const result = callback(mutationList, observer);\n setValue(result);\n }),\n [callback]\n );\n /* eslint-disable-next-line */\n useEffect(() => {\n if (targetNode) {\n observer.observe(targetNode, config);\n return () => {\n observer.disconnect();\n };\n }\n }, [targetNode, config, observer]);\n\n return value;\n}\n\nexport function useMutationObserverOnce(\n targetNode: Node | null,\n config: TypeMutationObserverInit,\n callback: TypeMutationObserverCallback\n) {\n const [isObserving, setObserving] = useState(true);\n const node = isObserving ? targetNode : null;\n const value = useMutationObserver(node, config, callback);\n if (value !== undefined && isObserving) {\n setObserving(false);\n }\n return value;\n}\n","import { useCallback, useState } from \"react\";\n\nexport type textContentRef = ((node: Node) => void) & { current?: string };\nexport function useTextContent(initial: string) {\n const [textContent, setTextContent] = useState(initial);\n\n const ref: textContentRef = useCallback((node: Node) => {\n if (node && node.textContent !== null) {\n setTextContent(node.textContent);\n }\n }, []);\n\n ref.current = textContent;\n return ref;\n}\n","import { useRef, useEffect } from \"react\";\n\nexport function useWhyDidYouUpdate(\n name: string,\n props: { [key: string]: any }\n) {\n // Get a mutable ref object where we can store props ...\n // ... for comparison next time this hook runs.\n const previousProps = useRef<typeof props>({});\n\n useEffect(() => {\n if (previousProps.current) {\n // Get all keys from previous and current props\n const allKeys = Object.keys({ ...previousProps.current, ...props });\n // Use this object to keep track of changed props\n const changesObj: typeof props = {};\n // Iterate through keys\n allKeys.forEach((key) => {\n // If previous is different from current\n\n if (previousProps.current[key] !== props[key]) {\n // Add to changesObj\n\n changesObj[key] = {\n from: previousProps.current[key],\n\n to: props[key],\n };\n }\n });\n\n // If changesObj not empty then output to console\n if (Object.keys(changesObj).length) {\n // eslint-disable-next-line no-console\n console.log(\"[why-did-you-update]\", name, changesObj);\n }\n }\n\n // Finally update previousProps with current props for next hook call\n previousProps.current = props;\n });\n}\n","import { useTheme } from \"styled-components\";\nimport type { TypeTheme } from \"@sproutsocial/seeds-react-theme\";\n\n/**\n * The useInteractiveColor hook has context of theme mode (light or dark)\n * and can be used to lighten or darken a color dynamically\n *\n * note: colors are limited to our theme colors\n */\nconst useInteractiveColor = (themeColor: string): string => {\n // Throw error if used outside of a ThemeProvider (styled-components)\n if (!useTheme()) {\n throw new Error(\n \"useInteractiveColor() must be used within a Styled Components ThemeProvider\"\n );\n }\n\n // Get the current theme mode ie. 'light' or 'dark'\n const theme: TypeTheme = useTheme() as TypeTheme;\n const themeMode = theme.mode;\n\n // If the theme mode is dark, return a lightened version of the themeValue\n if (themeMode === \"dark\") {\n return `color-mix(in srgb, ${themeColor}, white 20%)`;\n } else {\n // If the theme mode is light, return a darkened version of the themeValue\n return `color-mix(in srgb, ${themeColor}, black 20%)`;\n }\n};\n\nexport { useInteractiveColor };\n","import * as React from \"react\";\nimport { useTheme } from \"styled-components\";\nimport type { TypeTheme } from \"@sproutsocial/seeds-react-theme\";\n\n/**\n * Returns `true` when the viewport matches a mobile-width media query.\n *\n * @param breakpoint Optional override. Accepts a CSS string (`\"640px\"`,\n * `\"40em\"`) or a number (interpreted as px). Defaults to the styled-components\n * theme's `breakpoints.sm` (falling back to `\"640px\"` if the theme is\n * unavailable).\n */\nexport function useIsMobile(breakpoint?: number | string): boolean {\n const styledTheme = useTheme() as TypeTheme | undefined;\n const themeSm = styledTheme?.breakpoints?.sm ?? \"640px\";\n const resolved =\n typeof breakpoint === \"number\" ? `${breakpoint}px` : breakpoint ?? themeSm;\n\n const [isMobile, setIsMobile] = React.useState(() => {\n if (\n typeof window === \"undefined\" ||\n typeof window.matchMedia !== \"function\"\n ) {\n return false;\n }\n return window.matchMedia(`(max-width: ${resolved})`).matches;\n });\n\n React.useEffect(() => {\n if (\n typeof window === \"undefined\" ||\n typeof window.matchMedia !== \"function\"\n ) {\n return;\n }\n const mq = window.matchMedia(`(max-width: ${resolved})`);\n setIsMobile(mq.matches);\n const handler = (e: MediaQueryListEvent) => setIsMobile(e.matches);\n mq.addEventListener?.(\"change\", handler);\n return () => mq.removeEventListener?.(\"change\", handler);\n }, [resolved]);\n\n return isMobile;\n}\n","/**\n * Single module-level counter shared by both FormField render paths\n * (FormFieldTailwind and FormFieldStyled). A page with mixed routing draws\n * sequential ids from this one counter, so auto-generated ids never collide\n * across the two paths. Preserves the pre-split `FormField-N` id format.\n */\n\nlet idCounter = 0;\n\nexport function nextFormFieldId(): string {\n return `FormField-${idCounter++}`;\n}\n","/**\n * Tailwind CSS implementation of FormField.\n * Uses the Seeds form-field CSS class from form-field.css. Only FormField's own\n * wrapper becomes a plain <div className=\"seeds-form-field\"> — the Label, helper\n * Text, error Text, VisuallyHidden, and children render-prop are composed exactly\n * as in the styled path so the accessibility/qa contract is preserved.\n */\n\nimport React, { useMemo, useState } from \"react\";\nimport { useTextContent } from \"@sproutsocial/seeds-react-hooks\";\nimport Label from \"@sproutsocial/seeds-react-label\";\nimport Text from \"@sproutsocial/seeds-react-text\";\nimport { VisuallyHidden } from \"@sproutsocial/seeds-react-visually-hidden\";\nimport type { TypeFormFieldProps } from \"./FormFieldTypes\";\nimport { nextFormFieldId } from \"./formFieldId\";\n\n// Utility for merging class names properly\nfunction cn(\n ...inputs: (string | undefined | null | false | Record<string, boolean>)[]\n): string {\n const classes: string[] = [];\n\n for (const input of inputs) {\n if (!input) continue;\n\n if (typeof input === \"string\") {\n classes.push(input);\n } else if (typeof input === \"object\") {\n for (const [key, value] of Object.entries(input)) {\n if (value) {\n classes.push(key);\n }\n }\n }\n }\n\n return classes.join(\" \");\n}\n\nexport const FormFieldTailwind = React.forwardRef<\n HTMLDivElement,\n TypeFormFieldProps\n>((props, ref) => {\n const {\n children,\n error,\n helperText,\n id: identifier,\n isInvalid = false,\n label,\n mb = 400,\n qa,\n isLabelHidden = false,\n required,\n className,\n style,\n ...rest\n } = props as TypeFormFieldProps & {\n className?: string;\n style?: React.CSSProperties;\n };\n\n const [id] = useState(identifier || nextFormFieldId());\n const errorId = `Error-${id}`;\n const helperTextId = `HelperText-${id}`;\n const containerText = useTextContent(\"\");\n const errorContainerText = useTextContent(\"\");\n\n const ariaDescribedby = useMemo(() => {\n const ids = [\n helperText && helperTextId,\n isInvalid && error && errorId,\n ].filter(Boolean);\n return ids.length > 0 ? ids.join(\" \") : undefined;\n }, [helperText, helperTextId, isInvalid, error, errorId]);\n\n return (\n <div\n ref={ref}\n className={cn(\"seeds-form-field\", className)}\n // `rest` is typed with the full styled-system Box prop surface (via\n // TypeFormFieldProps extends Omit<TypeBoxProps, \"children\">), but the\n // Hybrid routes any instance carrying styled-system props to the styled\n // path, so on the Tailwind path `rest` only ever holds native div\n // attributes at runtime. Widen the spread to the div's attribute type.\n {...(rest as unknown as React.HTMLAttributes<HTMLDivElement>)}\n {...qa}\n // FormField's only own style is the bottom margin; map the space token\n // inline so scalar mb matches the styled path (responsive mb is routed to\n // the styled path by FormFieldHybrid).\n style={{ marginBottom: `var(--space-${String(mb)})`, ...style }}\n data-qa-formfield={\n (qa && qa[\"data-qa-formfield\"]) || id || containerText.current\n }\n data-qa-formfield-isinvalid={isInvalid === true}\n >\n {isLabelHidden ? (\n <VisuallyHidden data-testid=\"visually-hidden\">\n <Label htmlFor={id} required={required}>\n {label}\n </Label>\n </VisuallyHidden>\n ) : (\n <Label mb={helperText ? 100 : 300} htmlFor={id} required={required}>\n {label}\n </Label>\n )}\n {helperText && (\n <Text\n as=\"p\"\n fontSize={200}\n mb={300}\n color=\"text.subtext\"\n id={helperTextId}\n >\n {helperText}\n </Text>\n )}\n {children({\n id,\n isInvalid,\n ariaDescribedby,\n ...(required !== undefined && { required }),\n })}\n {isInvalid && error && (\n <Text\n as=\"div\"\n fontSize={200}\n color=\"text.error\"\n mt={300}\n id={errorId}\n data-qa-formfield-error={\n (qa && qa[\"data-qa-formfield-error\"]) || errorContainerText.current\n }\n >\n {error}\n </Text>\n )}\n </div>\n );\n});\n\nFormFieldTailwind.displayName = \"FormFieldTailwind\";\n","import * as React from \"react\";\nimport type { TypeBoxProps } from \"@sproutsocial/seeds-react-box\";\n\nexport interface TypeFormFieldProps extends Omit<TypeBoxProps, \"children\"> {\n /** A function that receives props that need to be spread onto the child element */\n children: (options: {\n id: string;\n isInvalid: boolean;\n ariaDescribedby?: string;\n required?: boolean;\n }) => React.ReactNode;\n\n /** Text describing any error with the field's content */\n error?: React.ReactNode;\n\n /** Text acting as a description blurb below the main label **/\n helperText?: React.ReactNode;\n\n /** ID of the form element (will be auto-generated if not provided) */\n id?: string;\n\n /** Whether the current contents of the field are invalid */\n isInvalid?: boolean;\n\n /** Label text to display above the form field */\n label: React.ReactNode;\n qa?: Record<string, any>;\n\n /** Whether the label text should be visually hidden */\n isLabelHidden?: boolean;\n\n /** Whether the form element is required */\n required?: boolean;\n}\n"],"mappings":"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","names":["import_react","import_react","import_react","initialBounds","useTextContent","initial","textContent","setTextContent","useState","ref","useCallback","node","React","Box","Label","Text","import_react","import_seeds_react_label","import_seeds_react_text","import_seeds_react_visually_hidden","import_jsx_runtime","React","Label","Text","import_jsx_runtime","React","import_jsx_runtime","React","React"]}
package/package.json CHANGED
@@ -1,15 +1,26 @@
1
1
  {
2
2
  "name": "@sproutsocial/seeds-react-form-field",
3
- "version": "1.1.20",
3
+ "version": "1.1.21",
4
4
  "description": "Seeds React FormField",
5
5
  "author": "Sprout Social, Inc.",
6
6
  "license": "MIT",
7
7
  "main": "dist/index.js",
8
8
  "module": "dist/esm/index.js",
9
9
  "types": "dist/index.d.ts",
10
+ "files": [
11
+ "dist"
12
+ ],
13
+ "exports": {
14
+ ".": {
15
+ "import": "./dist/esm/index.js",
16
+ "require": "./dist/index.js",
17
+ "types": "./dist/index.d.ts"
18
+ },
19
+ "./dist/form-field.css": "./dist/form-field.css"
20
+ },
10
21
  "scripts": {
11
- "build": "tsup --dts",
12
- "build:debug": "tsup --dts --metafile",
22
+ "build": "tsup --dts && cp src/form-field.css dist/form-field.css",
23
+ "build:debug": "tsup --dts --metafile && cp src/form-field.css dist/form-field.css",
13
24
  "dev": "tsup --watch --dts",
14
25
  "clean": "rm -rf .turbo dist",
15
26
  "clean:modules": "rm -rf node_modules",
@@ -36,7 +47,7 @@
36
47
  "@sproutsocial/seeds-tsconfig": "*",
37
48
  "@sproutsocial/seeds-testing": "*",
38
49
  "@sproutsocial/seeds-react-testing-library": "*",
39
- "@sproutsocial/seeds-react-input": "^1.5.30"
50
+ "@sproutsocial/seeds-react-input": "^1.5.31"
40
51
  },
41
52
  "peerDependencies": {
42
53
  "styled-components": "^5.2.3"
package/.eslintignore DELETED
@@ -1,6 +0,0 @@
1
- # Node modules
2
- node_modules/
3
-
4
- # Build output
5
- dist/
6
- coverage/
package/.eslintrc.js DELETED
@@ -1,4 +0,0 @@
1
- module.exports = {
2
- root: true,
3
- extends: ["eslint-config-seeds/racine"],
4
- };
@@ -1,21 +0,0 @@
1
- yarn run v1.22.22
2
- $ tsup --dts
3
- CLI Building entry: src/index.ts
4
- CLI Using tsconfig: tsconfig.json
5
- CLI tsup v8.5.0
6
- CLI Using tsup config: /home/runner/_work/seeds/seeds/seeds-react/seeds-react-form-field/tsup.config.ts
7
- CLI Target: es2022
8
- CLI Cleaning output folder
9
- CJS Build start
10
- ESM Build start
11
- CJS dist/index.js 5.04 KB
12
- CJS dist/index.js.map 16.54 KB
13
- CJS ⚡️ Build success in 27ms
14
- ESM dist/esm/index.js 3.05 KB
15
- ESM dist/esm/index.js.map 16.48 KB
16
- ESM ⚡️ Build success in 27ms
17
- DTS Build start
18
- DTS ⚡️ Build success in 4557ms
19
- DTS dist/index.d.ts 1.35 KB
20
- DTS dist/index.d.mts 1.35 KB
21
- Done in 6.12s.