@bigbinary/neeto-atoms 1.0.139-beta1 → 1.0.139-beta3

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.
@@ -1,10 +1,10 @@
1
1
  import { type BlockNavigationAlertProps } from "./BlockNavigationAlert";
2
- export interface FormikBlockNavigationProps extends Omit<BlockNavigationAlertProps, "isOpen" | "isSaving" | "onClose" | "onSubmit" | "onDiscardChanges"> {
2
+ export interface FormikBlockNavigationProps extends Omit<BlockNavigationAlertProps, "isOpen" | "onClose" | "onSubmit" | "onDiscardChanges"> {
3
3
  /** Force the navigation block regardless of Formik dirty state. */
4
4
  isDirty?: boolean;
5
5
  }
6
6
  declare const FormikBlockNavigation: {
7
- ({ isDirty, saveAndContinue, ...otherProps }: FormikBlockNavigationProps): import("react/jsx-runtime").JSX.Element;
7
+ ({ isDirty, ...otherProps }: FormikBlockNavigationProps): import("react/jsx-runtime").JSX.Element;
8
8
  displayName: string;
9
9
  };
10
10
  export { FormikBlockNavigation };
@@ -1,6 +1,6 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
- import { useState, useRef, useCallback, useEffect } from 'react';
3
2
  import { useFormikContext } from 'formik';
3
+ import { useState, useRef, useCallback, useEffect } from 'react';
4
4
  import { useHistory } from 'react-router-dom';
5
5
  import { D as Dialog } from '../Dialog-ZTcfRPwU.js';
6
6
  import { b as Button } from '../Button-ZssOVkG0.js';
@@ -82,10 +82,7 @@ const useNavPrompt = ({
82
82
  unblockRef.current = history.block(
83
83
  (transition, action) => {
84
84
  setIsBlocked(true);
85
- navRef.current = {
86
- transition: `${transition.pathname}${transition.search ?? ""}${transition.hash ?? ""}`,
87
- action
88
- };
85
+ navRef.current = { transition: transition.pathname, action };
89
86
  return false;
90
87
  }
91
88
  );
@@ -100,27 +97,24 @@ const useNavPrompt = ({
100
97
 
101
98
  const BlockNavigationAlert = ({
102
99
  isOpen = false,
103
- isSaving = false,
104
- saveAndContinue = true,
105
100
  onClose,
106
101
  onSubmit,
107
102
  onDiscardChanges,
108
103
  title = "You have unsaved changes",
109
104
  message = "Are you sure you want to leave? All unsaved changes will be lost.",
110
105
  cancelButtonLabel = "Discard and leave",
111
- submitButtonLabel
106
+ submitButtonLabel = "Stay on this page"
112
107
  }) => {
113
108
  const submitButtonRef = useRef(null);
114
- const primaryButtonLabel = submitButtonLabel ?? (saveAndContinue ? "Save and continue" : "Stay on this page");
115
109
  return /* @__PURE__ */ jsxs(
116
110
  Dialog,
117
111
  {
118
112
  isOpen,
119
113
  onClose: onClose ?? (() => {
120
114
  }),
121
- closeButton: !isSaving,
122
- closeOnEsc: !isSaving,
123
- closeOnOutsideClick: !isSaving,
115
+ closeButton: true,
116
+ closeOnEsc: true,
117
+ closeOnOutsideClick: true,
124
118
  initialFocusRef: submitButtonRef,
125
119
  size: "medium",
126
120
  "data-testid": "alert-box",
@@ -139,7 +133,6 @@ const BlockNavigationAlert = ({
139
133
  Button,
140
134
  {
141
135
  "data-testid": "alert-cancel-button",
142
- disabled: isSaving,
143
136
  label: cancelButtonLabel,
144
137
  variant: "destructive",
145
138
  onClick: onDiscardChanges
@@ -149,8 +142,7 @@ const BlockNavigationAlert = ({
149
142
  Button,
150
143
  {
151
144
  "data-testid": "alert-submit-button",
152
- label: primaryButtonLabel,
153
- loading: isSaving,
145
+ label: submitButtonLabel,
154
146
  ref: submitButtonRef,
155
147
  onClick: onSubmit
156
148
  }
@@ -164,10 +156,8 @@ BlockNavigationAlert.displayName = "BlockNavigationAlert";
164
156
 
165
157
  const FormikBlockNavigation = ({
166
158
  isDirty = false,
167
- saveAndContinue = true,
168
159
  ...otherProps
169
160
  }) => {
170
- const [isSaving, setIsSaving] = useState(false);
171
161
  const formikContext = useFormikContext();
172
162
  const shouldBlock = isDirty || Boolean(formikContext) && Boolean(formikContext.dirty);
173
163
  const { isBlocked, continueNavigation, hidePrompt } = useNavPrompt({
@@ -178,35 +168,13 @@ const FormikBlockNavigation = ({
178
168
  hidePrompt();
179
169
  continueNavigation();
180
170
  };
181
- const handleSaveAndContinue = async () => {
182
- if (!formikContext) {
183
- hidePrompt();
184
- return;
185
- }
186
- setIsSaving(true);
187
- try {
188
- const errors = await formikContext.validateForm();
189
- if (Object.keys(errors).length > 0) {
190
- await formikContext.submitForm();
191
- hidePrompt();
192
- return;
193
- }
194
- await formikContext.submitForm();
195
- continueNavigation();
196
- } catch {
197
- hidePrompt();
198
- } finally {
199
- setIsSaving(false);
200
- }
201
- };
202
171
  return /* @__PURE__ */ jsx(
203
172
  BlockNavigationAlert,
204
173
  {
205
- ...{ isSaving, saveAndContinue },
206
174
  isOpen: isBlocked,
207
175
  onClose: hidePrompt,
208
176
  onDiscardChanges: handleDiscardChanges,
209
- onSubmit: saveAndContinue ? handleSaveAndContinue : hidePrompt,
177
+ onSubmit: hidePrompt,
210
178
  ...otherProps
211
179
  }
212
180
  );
@@ -1 +1 @@
1
- {"version":3,"file":"BlockNavigation.js","sources":["../../src/formik/BlockNavigation/hooks/useNavPrompt.ts","../../src/formik/BlockNavigation/BlockNavigationAlert.tsx","../../src/formik/BlockNavigation/index.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useHistory } from \"react-router-dom\";\n\ninterface UseNavPromptOptions {\n /** Whether navigation should be blocked. */\n shouldBlock?: boolean;\n}\n\ninterface UseNavPromptReturn {\n /** Whether a navigation attempt is currently blocked. */\n isBlocked: boolean;\n /** Unblock and continue the pending navigation. */\n continueNavigation: () => void;\n /** Dismiss the prompt without navigating. */\n hidePrompt: () => void;\n}\n\nconst useNavPrompt = ({\n shouldBlock = true,\n}: UseNavPromptOptions): UseNavPromptReturn => {\n const [isBlocked, setIsBlocked] = useState(false);\n\n const history = useHistory();\n\n const unblockRef = useRef<(() => void) | undefined>(undefined);\n const navRef = useRef<{ transition: string; action: string } | null>(null);\n\n const handleNavigation = useCallback(\n (action: string, transition: string) => {\n switch (action) {\n case \"PUSH\":\n history.push(transition);\n break;\n case \"REPLACE\":\n history.replace(transition);\n break;\n case \"POP\":\n history.goBack();\n break;\n }\n },\n [history]\n );\n\n const continueNavigation = useCallback(() => {\n if (isBlocked && unblockRef.current) {\n unblockRef.current();\n setIsBlocked(false);\n if (navRef.current) {\n const { action, transition } = navRef.current;\n handleNavigation(action, transition);\n }\n }\n }, [isBlocked, handleNavigation]);\n\n const hidePrompt = useCallback(() => setIsBlocked(false), []);\n\n useEffect(() => {\n if (shouldBlock) {\n unblockRef.current = history.block(\n (\n transition: { pathname: string; search?: string; hash?: string },\n action: string\n ) => {\n setIsBlocked(true);\n navRef.current = {\n transition: `${transition.pathname}${transition.search ?? \"\"}${\n transition.hash ?? \"\"\n }`,\n action,\n };\n\n return false;\n }\n ) as unknown as () => void;\n\n return () => {\n unblockRef.current?.();\n };\n }\n\n return undefined;\n }, [shouldBlock, history]);\n\n return { isBlocked, continueNavigation, hidePrompt };\n};\n\nexport { useNavPrompt };\nexport type { UseNavPromptOptions, UseNavPromptReturn };\n","import { useRef } from \"react\";\n\nimport { Button } from \"src/components/Button\";\nimport { Dialog } from \"src/components/Dialog\";\n\nexport interface BlockNavigationAlertProps {\n /** Whether the alert dialog is open. */\n isOpen?: boolean;\n /** Whether the form is being saved. Shows a loader on the primary button and\n * prevents the dialog from being dismissed. */\n isSaving?: boolean;\n /** Whether the primary button saves the form before resuming navigation.\n * Defaults to true; pass false for the older \"Stay on this page\" behaviour. */\n saveAndContinue?: boolean;\n /** Called when the dialog should close. */\n onClose?: () => void;\n /** Called when the user chooses to save and continue, or to stay. */\n onSubmit?: () => void;\n /** Called when the user chooses to discard and leave. */\n onDiscardChanges?: () => void;\n /** Custom title for the alert. */\n title?: string;\n /** Custom message for the alert body. */\n message?: string;\n /** Custom label for the discard/cancel button. */\n cancelButtonLabel?: string;\n /** Custom label for the primary button. Defaults to \"Save and continue\" when\n * saveAndContinue is enabled and \"Stay on this page\" otherwise. */\n submitButtonLabel?: string;\n}\n\nconst BlockNavigationAlert = ({\n isOpen = false,\n isSaving = false,\n saveAndContinue = true,\n onClose,\n onSubmit,\n onDiscardChanges,\n title = \"You have unsaved changes\",\n message = \"Are you sure you want to leave? All unsaved changes will be lost.\",\n cancelButtonLabel = \"Discard and leave\",\n submitButtonLabel,\n}: BlockNavigationAlertProps) => {\n const submitButtonRef = useRef<HTMLButtonElement>(null);\n\n const primaryButtonLabel =\n submitButtonLabel ??\n (saveAndContinue ? \"Save and continue\" : \"Stay on this page\");\n\n return (\n <Dialog\n isOpen={isOpen}\n onClose={onClose ?? (() => {})}\n closeButton={!isSaving}\n closeOnEsc={!isSaving}\n closeOnOutsideClick={!isSaving}\n initialFocusRef={submitButtonRef}\n size=\"medium\"\n data-testid=\"alert-box\"\n >\n <Dialog.Header>\n <Dialog.Title data-testid=\"alert-title\">{title}</Dialog.Title>\n </Dialog.Header>\n <Dialog.Body>\n <p\n data-testid=\"alert-message\"\n className=\"text-sm text-muted-foreground\"\n >\n {message}\n </p>\n </Dialog.Body>\n <Dialog.Footer className=\"flex items-center justify-end gap-2\">\n <Button\n data-testid=\"alert-cancel-button\"\n disabled={isSaving}\n label={cancelButtonLabel}\n variant=\"destructive\"\n onClick={onDiscardChanges}\n />\n <Button\n data-testid=\"alert-submit-button\"\n label={primaryButtonLabel}\n loading={isSaving}\n ref={submitButtonRef}\n onClick={onSubmit}\n />\n </Dialog.Footer>\n </Dialog>\n );\n};\n\nBlockNavigationAlert.displayName = \"BlockNavigationAlert\";\n\nexport { BlockNavigationAlert };\n","import { useState } from \"react\";\n\nimport { useFormikContext } from \"formik\";\n\nimport { useNavPrompt } from \"./hooks/useNavPrompt\";\n\nimport {\n BlockNavigationAlert,\n type BlockNavigationAlertProps,\n} from \"./BlockNavigationAlert\";\n\nexport interface FormikBlockNavigationProps extends Omit<\n BlockNavigationAlertProps,\n \"isOpen\" | \"isSaving\" | \"onClose\" | \"onSubmit\" | \"onDiscardChanges\"\n> {\n /** Force the navigation block regardless of Formik dirty state. */\n isDirty?: boolean;\n}\n\nconst FormikBlockNavigation = ({\n isDirty = false,\n saveAndContinue = true,\n ...otherProps\n}: FormikBlockNavigationProps) => {\n const [isSaving, setIsSaving] = useState(false);\n\n const formikContext = useFormikContext();\n const shouldBlock =\n isDirty || (Boolean(formikContext) && Boolean(formikContext.dirty));\n\n const { isBlocked, continueNavigation, hidePrompt } = useNavPrompt({\n shouldBlock,\n });\n\n const handleDiscardChanges = () => {\n if (formikContext) formikContext.resetForm();\n hidePrompt();\n continueNavigation();\n };\n\n const handleSaveAndContinue = async () => {\n if (!formikContext) {\n hidePrompt();\n\n return;\n }\n\n setIsSaving(true);\n try {\n const errors = await formikContext.validateForm();\n if (Object.keys(errors).length > 0) {\n await formikContext.submitForm();\n hidePrompt();\n\n return;\n }\n\n await formikContext.submitForm();\n continueNavigation();\n } catch {\n hidePrompt();\n } finally {\n setIsSaving(false);\n }\n };\n\n return (\n <BlockNavigationAlert\n {...{ isSaving, saveAndContinue }}\n isOpen={isBlocked}\n onClose={hidePrompt}\n onDiscardChanges={handleDiscardChanges}\n onSubmit={saveAndContinue ? handleSaveAndContinue : hidePrompt}\n {...otherProps}\n />\n );\n};\n\nFormikBlockNavigation.displayName = \"FormikBlockNavigation\";\n\nexport { FormikBlockNavigation };\nexport type { BlockNavigationAlertProps };\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"BlockNavigation.js","sources":["../../src/formik/BlockNavigation/hooks/useNavPrompt.ts","../../src/formik/BlockNavigation/BlockNavigationAlert.tsx","../../src/formik/BlockNavigation/index.tsx"],"sourcesContent":["import { useCallback, useEffect, useRef, useState } from \"react\";\n\nimport { useHistory } from \"react-router-dom\";\n\ninterface UseNavPromptOptions {\n /** Whether navigation should be blocked. */\n shouldBlock?: boolean;\n}\n\ninterface UseNavPromptReturn {\n /** Whether a navigation attempt is currently blocked. */\n isBlocked: boolean;\n /** Unblock and continue the pending navigation. */\n continueNavigation: () => void;\n /** Dismiss the prompt without navigating. */\n hidePrompt: () => void;\n}\n\nconst useNavPrompt = ({\n shouldBlock = true,\n}: UseNavPromptOptions): UseNavPromptReturn => {\n const [isBlocked, setIsBlocked] = useState(false);\n\n const history = useHistory();\n\n const unblockRef = useRef<(() => void) | undefined>(undefined);\n const navRef = useRef<{ transition: string; action: string } | null>(null);\n\n const handleNavigation = useCallback(\n (action: string, transition: string) => {\n switch (action) {\n case \"PUSH\":\n history.push(transition);\n break;\n case \"REPLACE\":\n history.replace(transition);\n break;\n case \"POP\":\n history.goBack();\n break;\n }\n },\n [history]\n );\n\n const continueNavigation = useCallback(() => {\n if (isBlocked && unblockRef.current) {\n unblockRef.current();\n setIsBlocked(false);\n if (navRef.current) {\n const { action, transition } = navRef.current;\n handleNavigation(action, transition);\n }\n }\n }, [isBlocked, handleNavigation]);\n\n const hidePrompt = useCallback(() => setIsBlocked(false), []);\n\n useEffect(() => {\n if (shouldBlock) {\n unblockRef.current = history.block(\n (transition: { pathname: string }, action: string) => {\n setIsBlocked(true);\n navRef.current = { transition: transition.pathname, action };\n\n return false;\n }\n ) as unknown as () => void;\n\n return () => {\n unblockRef.current?.();\n };\n }\n\n return undefined;\n }, [shouldBlock, history]);\n\n return { isBlocked, continueNavigation, hidePrompt };\n};\n\nexport { useNavPrompt };\nexport type { UseNavPromptOptions, UseNavPromptReturn };\n","import { useRef } from \"react\";\n\nimport { Button } from \"src/components/Button\";\nimport { Dialog } from \"src/components/Dialog\";\n\nexport interface BlockNavigationAlertProps {\n /** Whether the alert dialog is open. */\n isOpen?: boolean;\n /** Called when the dialog should close. */\n onClose?: () => void;\n /** Called when the user chooses to stay. */\n onSubmit?: () => void;\n /** Called when the user chooses to discard and leave. */\n onDiscardChanges?: () => void;\n /** Custom title for the alert. */\n title?: string;\n /** Custom message for the alert body. */\n message?: string;\n /** Custom label for the discard/cancel button. */\n cancelButtonLabel?: string;\n /** Custom label for the stay/submit button. */\n submitButtonLabel?: string;\n}\n\nconst BlockNavigationAlert = ({\n isOpen = false,\n onClose,\n onSubmit,\n onDiscardChanges,\n title = \"You have unsaved changes\",\n message = \"Are you sure you want to leave? All unsaved changes will be lost.\",\n cancelButtonLabel = \"Discard and leave\",\n submitButtonLabel = \"Stay on this page\",\n}: BlockNavigationAlertProps) => {\n const submitButtonRef = useRef<HTMLButtonElement>(null);\n\n return (\n <Dialog\n isOpen={isOpen}\n onClose={onClose ?? (() => {})}\n closeButton\n closeOnEsc\n closeOnOutsideClick\n initialFocusRef={submitButtonRef}\n size=\"medium\"\n data-testid=\"alert-box\"\n >\n <Dialog.Header>\n <Dialog.Title data-testid=\"alert-title\">{title}</Dialog.Title>\n </Dialog.Header>\n <Dialog.Body>\n <p\n data-testid=\"alert-message\"\n className=\"text-sm text-muted-foreground\"\n >\n {message}\n </p>\n </Dialog.Body>\n <Dialog.Footer className=\"flex items-center justify-end gap-2\">\n <Button\n data-testid=\"alert-cancel-button\"\n label={cancelButtonLabel}\n variant=\"destructive\"\n onClick={onDiscardChanges}\n />\n <Button\n data-testid=\"alert-submit-button\"\n label={submitButtonLabel}\n ref={submitButtonRef}\n onClick={onSubmit}\n />\n </Dialog.Footer>\n </Dialog>\n );\n};\n\nBlockNavigationAlert.displayName = \"BlockNavigationAlert\";\n\nexport { BlockNavigationAlert };\n","import { useFormikContext } from \"formik\";\n\nimport { useNavPrompt } from \"./hooks/useNavPrompt\";\n\nimport {\n BlockNavigationAlert,\n type BlockNavigationAlertProps,\n} from \"./BlockNavigationAlert\";\n\nexport interface FormikBlockNavigationProps extends Omit<\n BlockNavigationAlertProps,\n \"isOpen\" | \"onClose\" | \"onSubmit\" | \"onDiscardChanges\"\n> {\n /** Force the navigation block regardless of Formik dirty state. */\n isDirty?: boolean;\n}\n\nconst FormikBlockNavigation = ({\n isDirty = false,\n ...otherProps\n}: FormikBlockNavigationProps) => {\n const formikContext = useFormikContext();\n const shouldBlock =\n isDirty || (Boolean(formikContext) && Boolean(formikContext.dirty));\n\n const { isBlocked, continueNavigation, hidePrompt } = useNavPrompt({\n shouldBlock,\n });\n\n const handleDiscardChanges = () => {\n if (formikContext) formikContext.resetForm();\n hidePrompt();\n continueNavigation();\n };\n\n return (\n <BlockNavigationAlert\n isOpen={isBlocked}\n onClose={hidePrompt}\n onDiscardChanges={handleDiscardChanges}\n onSubmit={hidePrompt}\n {...otherProps}\n />\n );\n};\n\nFormikBlockNavigation.displayName = \"FormikBlockNavigation\";\n\nexport { FormikBlockNavigation };\nexport type { BlockNavigationAlertProps };\n"],"names":[],"mappings":"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"}
@@ -76,11 +76,11 @@ const FormWrapper = forwardRef(
76
76
  scrollToError(formRef, errors, status);
77
77
  }
78
78
  } else {
79
- await submitForm();
79
+ submitForm();
80
80
  }
81
81
  } catch (error) {
82
82
  console.error(
83
- "An unhandled error was caught while validating or submitting the form",
83
+ "An unhandled error was caught from validateForm()",
84
84
  error
85
85
  );
86
86
  }
@@ -1 +1 @@
1
- {"version":3,"file":"Form.js","sources":["../../src/formik/Form/ScrollToErrorField/utils.ts","../../src/formik/Form/ScrollToErrorField/index.tsx","../../src/formik/Form/FormWrapper.tsx","../../src/formik/Form/index.tsx"],"sourcesContent":["import { is } from \"ramda\";\n\nconst transformObjectToDotNotation = (\n object: Record<string, unknown>,\n prefix = \"\"\n): string[] => {\n const result: string[] = [];\n\n Object.entries(object).forEach(([key, value]) => {\n if (!value) return;\n const nextKey = prefix ? `${prefix}.${key}` : key;\n if (is(Object, value)) {\n result.push(\n ...transformObjectToDotNotation(\n value as Record<string, unknown>,\n nextKey\n )\n );\n } else {\n result.push(nextKey);\n }\n });\n\n return result;\n};\n\nconst getErrorFieldName = (\n formikErrors: Record<string, unknown>\n): string | undefined => transformObjectToDotNotation(formikErrors)?.[0];\n\nexport const getFieldsWithServerError = (\n status: Record<string, unknown> = {}\n): string[] =>\n Object.keys(status).filter(\n fieldName => status[fieldName] != null && status[fieldName] !== \"\"\n );\n\nexport const scrollToError = (\n formRef: React.RefObject<HTMLFormElement | null>,\n errors: Record<string, unknown>,\n status: Record<string, unknown>\n): void => {\n const fieldErrorName = getErrorFieldName(errors);\n const [fieldWithServerError] = getFieldsWithServerError(status);\n\n if (!fieldErrorName && !fieldWithServerError) return;\n\n const errorFieldName = fieldErrorName || fieldWithServerError;\n const errorFormElement = formRef.current?.querySelector(\n `[name=\"${errorFieldName}\"]`\n );\n\n errorFormElement?.scrollIntoView({\n behavior: \"smooth\",\n block: \"center\",\n });\n};\n","import { useEffect } from \"react\";\n\nimport { useFormikContext } from \"formik\";\n\nimport { getFieldsWithServerError, scrollToError } from \"./utils\";\n\ninterface ScrollToErrorFieldProps {\n formRef: React.RefObject<HTMLFormElement | null>;\n}\n\nconst ScrollToErrorField = ({ formRef }: ScrollToErrorFieldProps) => {\n const { submitCount, isValid, errors, status } = useFormikContext();\n\n useEffect(() => {\n const fieldsWithServerError = getFieldsWithServerError(status);\n if (!formRef.current || (isValid && fieldsWithServerError.length === 0)) {\n return;\n }\n\n scrollToError(formRef, errors as Record<string, unknown>, status);\n }, [submitCount]);\n\n return null;\n};\n\nexport default ScrollToErrorField;\n","import React, { useCallback, forwardRef, useRef } from \"react\";\n\nimport { Form as FormikForm, useFormikContext } from \"formik\";\n\nimport ScrollToErrorField from \"./ScrollToErrorField\";\nimport {\n getFieldsWithServerError,\n scrollToError,\n} from \"./ScrollToErrorField/utils\";\n\ninterface FormWrapperProps {\n className?: string;\n formProps?: React.ComponentProps<\"form\">;\n children: React.ReactNode;\n scrollToErrorField: boolean;\n}\n\nconst FormWrapper = forwardRef<HTMLFormElement, FormWrapperProps>(\n ({ className, formProps, children, scrollToErrorField }, ref) => {\n const { validateForm, setErrors, setTouched, submitForm, ...formikBag } =\n useFormikContext();\n\n const { dirty: isFormDirty, isSubmitting, status } = formikBag;\n\n const formRefForScrollToErrorField = useRef<HTMLFormElement>(null);\n const formRef =\n (ref as React.RefObject<HTMLFormElement | null>) ||\n formRefForScrollToErrorField;\n\n const handleKeyDown = useCallback(\n async (event: React.KeyboardEvent<HTMLFormElement>) => {\n const target = event.target as HTMLElement & { editor?: unknown };\n const isEventFromEditorOrTextarea =\n target.tagName === \"TEXTAREA\" || target.editor;\n\n if (event.key !== \"Enter\") return;\n if (isEventFromEditorOrTextarea && !event.metaKey) return;\n\n event.preventDefault();\n\n if (event.shiftKey) return;\n if (!isFormDirty || isSubmitting) return;\n\n try {\n const errors = await validateForm();\n const fieldStatuses = getFieldsWithServerError(status);\n\n if (\n Object.keys(errors).length > 0 ||\n Object.keys(fieldStatuses).length > 0\n ) {\n setErrors(errors);\n setTouched({ ...errors, ...status });\n if (scrollToErrorField) {\n scrollToError(formRef, errors as Record<string, unknown>, status);\n }\n } else {\n await submitForm();\n }\n } catch (error) {\n console.error(\n \"An unhandled error was caught while validating or submitting the form\",\n error\n );\n }\n },\n [\n validateForm,\n setErrors,\n setTouched,\n isFormDirty,\n isSubmitting,\n submitForm,\n status,\n scrollToErrorField,\n formRef,\n ]\n );\n\n return (\n <FormikForm\n {...{ className }}\n noValidate\n ref={formRef}\n onKeyDown={handleKeyDown}\n {...formProps}\n >\n {scrollToErrorField && <ScrollToErrorField {...{ formRef }} />}\n {children}\n </FormikForm>\n );\n }\n);\n\nFormWrapper.displayName = \"FormWrapper\";\n\nexport default FormWrapper;\n","import React, { forwardRef, useRef } from \"react\";\n\nimport { Formik, FormikConfig, FormikProps, FormikValues } from \"formik\";\n\nimport FormWrapper from \"./FormWrapper\";\nimport { getFieldsWithServerError } from \"./ScrollToErrorField/utils\";\n\nexport interface FormProps<T extends FormikValues = FormikValues> {\n /** Formik configuration: initialValues, validationSchema, onSubmit, etc. */\n formikProps: FormikConfig<T>;\n /** Props forwarded to the HTML form element. */\n formProps?: React.ComponentProps<\"form\">;\n /** Auto-scroll to the first error field on submit. */\n scrollToErrorField?: boolean;\n /** Additional class name for the form wrapper. */\n className?: string;\n /** Render children directly or as a function receiving FormikProps. */\n children: React.ReactNode | ((props: FormikProps<T>) => React.ReactNode);\n}\n\nconst Form = forwardRef<HTMLFormElement, FormProps>(\n (\n { className, children, formikProps, formProps, scrollToErrorField = false },\n ref\n ) => {\n const formikRef = useRef<FormikProps<FormikValues>>(null);\n\n const handleSubmit: FormikConfig<FormikValues>[\"onSubmit\"] = (\n values,\n actions\n ) => {\n const fieldsWithServerError = getFieldsWithServerError(\n formikRef.current?.status\n );\n\n if (fieldsWithServerError.length > 0) {\n actions.setSubmitting(false);\n\n return undefined;\n }\n\n return formikProps.onSubmit(values, actions);\n };\n\n return (\n <Formik\n innerRef={formikRef as React.Ref<FormikProps<FormikValues>>}\n {...formikProps}\n onSubmit={handleSubmit}\n >\n {props => (\n <FormWrapper {...{ className, formProps, ref, scrollToErrorField }}>\n {typeof children === \"function\" ? children(props) : children}\n </FormWrapper>\n )}\n </Formik>\n );\n }\n);\n\nForm.displayName = \"Form\";\n\nexport { Form };\n"],"names":["FormikForm"],"mappings":"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"}
1
+ {"version":3,"file":"Form.js","sources":["../../src/formik/Form/ScrollToErrorField/utils.ts","../../src/formik/Form/ScrollToErrorField/index.tsx","../../src/formik/Form/FormWrapper.tsx","../../src/formik/Form/index.tsx"],"sourcesContent":["import { is } from \"ramda\";\n\nconst transformObjectToDotNotation = (\n object: Record<string, unknown>,\n prefix = \"\"\n): string[] => {\n const result: string[] = [];\n\n Object.entries(object).forEach(([key, value]) => {\n if (!value) return;\n const nextKey = prefix ? `${prefix}.${key}` : key;\n if (is(Object, value)) {\n result.push(\n ...transformObjectToDotNotation(\n value as Record<string, unknown>,\n nextKey\n )\n );\n } else {\n result.push(nextKey);\n }\n });\n\n return result;\n};\n\nconst getErrorFieldName = (\n formikErrors: Record<string, unknown>\n): string | undefined => transformObjectToDotNotation(formikErrors)?.[0];\n\nexport const getFieldsWithServerError = (\n status: Record<string, unknown> = {}\n): string[] =>\n Object.keys(status).filter(\n fieldName => status[fieldName] != null && status[fieldName] !== \"\"\n );\n\nexport const scrollToError = (\n formRef: React.RefObject<HTMLFormElement | null>,\n errors: Record<string, unknown>,\n status: Record<string, unknown>\n): void => {\n const fieldErrorName = getErrorFieldName(errors);\n const [fieldWithServerError] = getFieldsWithServerError(status);\n\n if (!fieldErrorName && !fieldWithServerError) return;\n\n const errorFieldName = fieldErrorName || fieldWithServerError;\n const errorFormElement = formRef.current?.querySelector(\n `[name=\"${errorFieldName}\"]`\n );\n\n errorFormElement?.scrollIntoView({\n behavior: \"smooth\",\n block: \"center\",\n });\n};\n","import { useEffect } from \"react\";\n\nimport { useFormikContext } from \"formik\";\n\nimport { getFieldsWithServerError, scrollToError } from \"./utils\";\n\ninterface ScrollToErrorFieldProps {\n formRef: React.RefObject<HTMLFormElement | null>;\n}\n\nconst ScrollToErrorField = ({ formRef }: ScrollToErrorFieldProps) => {\n const { submitCount, isValid, errors, status } = useFormikContext();\n\n useEffect(() => {\n const fieldsWithServerError = getFieldsWithServerError(status);\n if (!formRef.current || (isValid && fieldsWithServerError.length === 0)) {\n return;\n }\n\n scrollToError(formRef, errors as Record<string, unknown>, status);\n }, [submitCount]);\n\n return null;\n};\n\nexport default ScrollToErrorField;\n","import React, { useCallback, forwardRef, useRef } from \"react\";\n\nimport { Form as FormikForm, useFormikContext } from \"formik\";\n\nimport ScrollToErrorField from \"./ScrollToErrorField\";\nimport {\n getFieldsWithServerError,\n scrollToError,\n} from \"./ScrollToErrorField/utils\";\n\ninterface FormWrapperProps {\n className?: string;\n formProps?: React.ComponentProps<\"form\">;\n children: React.ReactNode;\n scrollToErrorField: boolean;\n}\n\nconst FormWrapper = forwardRef<HTMLFormElement, FormWrapperProps>(\n ({ className, formProps, children, scrollToErrorField }, ref) => {\n const { validateForm, setErrors, setTouched, submitForm, ...formikBag } =\n useFormikContext();\n\n const { dirty: isFormDirty, isSubmitting, status } = formikBag;\n\n const formRefForScrollToErrorField = useRef<HTMLFormElement>(null);\n const formRef =\n (ref as React.RefObject<HTMLFormElement | null>) ||\n formRefForScrollToErrorField;\n\n const handleKeyDown = useCallback(\n async (event: React.KeyboardEvent<HTMLFormElement>) => {\n const target = event.target as HTMLElement & { editor?: unknown };\n const isEventFromEditorOrTextarea =\n target.tagName === \"TEXTAREA\" || target.editor;\n\n if (event.key !== \"Enter\") return;\n if (isEventFromEditorOrTextarea && !event.metaKey) return;\n\n event.preventDefault();\n\n if (event.shiftKey) return;\n if (!isFormDirty || isSubmitting) return;\n\n try {\n const errors = await validateForm();\n const fieldStatuses = getFieldsWithServerError(status);\n\n if (\n Object.keys(errors).length > 0 ||\n Object.keys(fieldStatuses).length > 0\n ) {\n setErrors(errors);\n setTouched({ ...errors, ...status });\n if (scrollToErrorField) {\n scrollToError(formRef, errors as Record<string, unknown>, status);\n }\n } else {\n submitForm();\n }\n } catch (error) {\n console.error(\n \"An unhandled error was caught from validateForm()\",\n error\n );\n }\n },\n [\n validateForm,\n setErrors,\n setTouched,\n isFormDirty,\n isSubmitting,\n submitForm,\n status,\n scrollToErrorField,\n formRef,\n ]\n );\n\n return (\n <FormikForm\n {...{ className }}\n noValidate\n ref={formRef}\n onKeyDown={handleKeyDown}\n {...formProps}\n >\n {scrollToErrorField && <ScrollToErrorField {...{ formRef }} />}\n {children}\n </FormikForm>\n );\n }\n);\n\nFormWrapper.displayName = \"FormWrapper\";\n\nexport default FormWrapper;\n","import React, { forwardRef, useRef } from \"react\";\n\nimport { Formik, FormikConfig, FormikProps, FormikValues } from \"formik\";\n\nimport FormWrapper from \"./FormWrapper\";\nimport { getFieldsWithServerError } from \"./ScrollToErrorField/utils\";\n\nexport interface FormProps<T extends FormikValues = FormikValues> {\n /** Formik configuration: initialValues, validationSchema, onSubmit, etc. */\n formikProps: FormikConfig<T>;\n /** Props forwarded to the HTML form element. */\n formProps?: React.ComponentProps<\"form\">;\n /** Auto-scroll to the first error field on submit. */\n scrollToErrorField?: boolean;\n /** Additional class name for the form wrapper. */\n className?: string;\n /** Render children directly or as a function receiving FormikProps. */\n children: React.ReactNode | ((props: FormikProps<T>) => React.ReactNode);\n}\n\nconst Form = forwardRef<HTMLFormElement, FormProps>(\n (\n { className, children, formikProps, formProps, scrollToErrorField = false },\n ref\n ) => {\n const formikRef = useRef<FormikProps<FormikValues>>(null);\n\n const handleSubmit: FormikConfig<FormikValues>[\"onSubmit\"] = (\n values,\n actions\n ) => {\n const fieldsWithServerError = getFieldsWithServerError(\n formikRef.current?.status\n );\n\n if (fieldsWithServerError.length > 0) {\n actions.setSubmitting(false);\n\n return undefined;\n }\n\n return formikProps.onSubmit(values, actions);\n };\n\n return (\n <Formik\n innerRef={formikRef as React.Ref<FormikProps<FormikValues>>}\n {...formikProps}\n onSubmit={handleSubmit}\n >\n {props => (\n <FormWrapper {...{ className, formProps, ref, scrollToErrorField }}>\n {typeof children === \"function\" ? children(props) : children}\n </FormWrapper>\n )}\n </Formik>\n );\n }\n);\n\nForm.displayName = \"Form\";\n\nexport { Form };\n"],"names":["FormikForm"],"mappings":"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"}
package/dist/index.css CHANGED
@@ -24,6 +24,7 @@
24
24
  --color-primary-foreground: var(--primary-foreground);
25
25
  --color-brand: var(--brand);
26
26
  --color-brand-foreground: var(--brand-foreground);
27
+ --color-brand-text: var(--brand-text);
27
28
  --color-secondary: var(--secondary);
28
29
  --color-secondary-foreground: var(--secondary-foreground);
29
30
  --color-muted: var(--muted);
@@ -99,6 +100,7 @@
99
100
  --primary-foreground: oklch(0.985 0 0);
100
101
  --brand: oklch(0.5349 0.1023 173.46);
101
102
  --brand-foreground: oklch(1 0 0);
103
+ --brand-text: var(--brand);
102
104
  --secondary: oklch(0.97 0 0);
103
105
  --secondary-foreground: oklch(0.205 0 0);
104
106
  --muted: oklch(0.97 0 0);
@@ -138,13 +140,13 @@
138
140
  --chart-3: oklch(0.546 0.245 262.881);
139
141
  --chart-4: oklch(0.488 0.243 264.376);
140
142
  --chart-5: oklch(0.424 0.199 265.638);
141
- --sidebar: oklch(0.985 0 0);
143
+ --sidebar: oklch(0.975 0 0);
142
144
  --sidebar-foreground: oklch(0.145 0 0);
143
- --sidebar-primary: oklch(0.205 0 0);
144
- --sidebar-primary-foreground: oklch(0.985 0 0);
145
+ --sidebar-primary: var(--background); /* v2: selected item sits on a card-white box */
146
+ --sidebar-primary-foreground: var(--brand-text); /* v2: selected item text and icons */
145
147
  --sidebar-accent: oklch(0.94 0 0);
146
148
  --sidebar-accent-foreground: oklch(0.205 0 0);
147
- --sidebar-border: oklch(0.922 0 0);
149
+ --sidebar-border: var(--border);
148
150
  --sidebar-ring: oklch(0.708 0 0);
149
151
  --avatar-l: 60%;
150
152
  --avatar-c: 0.15;
@@ -163,6 +165,7 @@
163
165
  --primary-foreground: oklch(0.205 0 0);
164
166
  --brand: oklch(0.55 0.102 173.46);
165
167
  --brand-foreground: oklch(1 0 0);
168
+ --brand-text: oklch(0.7 0.102 173.46);
166
169
  --secondary: oklch(0.269 0 0);
167
170
  --secondary-foreground: oklch(0.985 0 0);
168
171
  --muted: oklch(0.269 0 0);
@@ -196,11 +199,11 @@
196
199
  --chart-5: oklch(0.424 0.199 265.638);
197
200
  --sidebar: oklch(0.205 0 0);
198
201
  --sidebar-foreground: oklch(0.985 0 0);
199
- --sidebar-primary: oklch(0.488 0.243 264.376);
200
- --sidebar-primary-foreground: oklch(0.985 0 0);
202
+ --sidebar-primary: var(--sidebar-accent); /* v2: raised surface, mirrors the light card */
203
+ --sidebar-primary-foreground: var(--brand-text); /* v2: selected item text and icons */
201
204
  --sidebar-accent: oklch(0.269 0 0);
202
205
  --sidebar-accent-foreground: oklch(0.985 0 0);
203
- --sidebar-border: oklch(1 0 0 / 10%);
206
+ --sidebar-border: var(--border);
204
207
  --sidebar-ring: oklch(0.556 0 0);
205
208
  --avatar-l: 55%;
206
209
  --avatar-c: 0.13;
@@ -392,7 +392,7 @@ function SidebarGroupAction$1({
392
392
  "data-slot": "sidebar-group-action",
393
393
  "data-sidebar": "group-action",
394
394
  className: cn(
395
- "absolute top-3.5 end-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
395
+ "absolute top-3.5 end-3 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
396
396
  className
397
397
  ),
398
398
  ...props
@@ -436,12 +436,12 @@ function SidebarMenuItem$1({ className, ...props }) {
436
436
  );
437
437
  }
438
438
  const sidebarMenuButtonVariants = cva(
439
- "peer/menu-button group/menu-button flex w-full items-center gap-2 overflow-hidden rounded-md p-2 text-start text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pe-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-accent-foreground data-active:bg-sidebar-accent data-active:font-medium data-active:text-sidebar-accent-foreground [&_svg]:size-4 [&_svg]:shrink-0 [&>span:last-child]:truncate",
439
+ "peer/menu-button group/menu-button flex w-full items-center gap-3 overflow-hidden rounded-md p-2 text-start text-sm ring-sidebar-ring outline-hidden transition-[width,height,padding] group-has-data-[sidebar=menu-action]/menu-item:pe-8 group-data-[collapsible=icon]:size-8! group-data-[collapsible=icon]:p-2! hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-open:hover:bg-sidebar-accent data-open:hover:text-sidebar-foreground data-active:bg-sidebar-primary data-active:shadow-sm data-active:shadow-sidebar-foreground/10 dark:data-active:shadow-none data-active:font-medium data-active:text-sidebar-primary-foreground data-active:hover:bg-sidebar-primary data-active:hover:text-sidebar-primary-foreground data-active:[&_svg]:text-sidebar-primary-foreground [&_svg]:size-4 [&_svg]:[stroke-width:1.8] [&_svg]:shrink-0 [&>span:last-child]:truncate",
440
440
  {
441
441
  variants: {
442
442
  variant: {
443
- default: "hover:bg-sidebar-accent hover:text-sidebar-accent-foreground",
444
- outline: "bg-background shadow-[0_0_0_1px_hsl(var(--sidebar-border))] hover:bg-sidebar-accent hover:text-sidebar-accent-foreground hover:shadow-[0_0_0_1px_hsl(var(--sidebar-accent))]"
443
+ default: "hover:bg-sidebar-accent hover:text-sidebar-foreground",
444
+ outline: "bg-background shadow-[0_0_0_1px_var(--sidebar-border)] hover:bg-sidebar-accent hover:text-sidebar-foreground hover:shadow-[0_0_0_1px_var(--sidebar-accent)]"
445
445
  },
446
446
  size: {
447
447
  default: "h-8 text-sm",
@@ -511,8 +511,8 @@ function SidebarMenuAction$1({
511
511
  "data-slot": "sidebar-menu-action",
512
512
  "data-sidebar": "menu-action",
513
513
  className: cn(
514
- "absolute top-1.5 end-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
515
- showOnHover && "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-accent-foreground aria-expanded:opacity-100 md:opacity-0",
514
+ "absolute top-1.5 end-1 flex aspect-square w-5 items-center justify-center rounded-md p-0 text-sidebar-foreground ring-sidebar-ring outline-hidden transition-transform group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 after:absolute after:-inset-2 hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:ring-2 md:after:hidden [&>svg]:size-4 [&>svg]:shrink-0",
515
+ showOnHover && "group-focus-within/menu-item:opacity-100 group-hover/menu-item:opacity-100 peer-data-active/menu-button:text-sidebar-primary-foreground aria-expanded:opacity-100 md:opacity-0",
516
516
  className
517
517
  ),
518
518
  ...props
@@ -529,7 +529,7 @@ function SidebarMenuBadge$1({
529
529
  "data-slot": "sidebar-menu-badge",
530
530
  "data-sidebar": "menu-badge",
531
531
  className: cn(
532
- "pointer-events-none absolute end-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-accent-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-active/menu-button:text-sidebar-accent-foreground",
532
+ "pointer-events-none absolute end-1 flex h-5 min-w-5 items-center justify-center rounded-md px-1 text-xs font-medium text-sidebar-foreground tabular-nums select-none group-data-[collapsible=icon]:hidden peer-hover/menu-button:text-sidebar-foreground peer-data-[size=default]/menu-button:top-1.5 peer-data-[size=lg]/menu-button:top-2.5 peer-data-[size=sm]/menu-button:top-1 peer-data-active/menu-button:text-sidebar-primary-foreground",
533
533
  className
534
534
  ),
535
535
  ...props
@@ -580,7 +580,7 @@ function SidebarMenuSub$1({ className, ...props }) {
580
580
  "data-slot": "sidebar-menu-sub",
581
581
  "data-sidebar": "menu-sub",
582
582
  className: cn(
583
- "mx-3.5 flex min-w-0 translate-x-px rtl:-translate-x-px flex-col gap-1 border-s border-sidebar-border px-2.5 py-0.5 group-data-[collapsible=icon]:hidden",
583
+ "mx-3.5 flex min-w-0 translate-x-px rtl:-translate-x-px flex-col gap-1 border-s border-sidebar-border ps-[13px] py-0.5 group-data-[collapsible=icon]:hidden",
584
584
  className
585
585
  ),
586
586
  ...props
@@ -617,7 +617,7 @@ function SidebarMenuSubButton$1({
617
617
  "data-size": size,
618
618
  "data-active": isActive,
619
619
  className: cn(
620
- "flex h-7 min-w-0 -translate-x-px rtl:translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-accent-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-accent-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:bg-sidebar-accent data-active:text-sidebar-accent-foreground [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:shrink-0 [&>svg]:text-sidebar-accent-foreground",
620
+ "flex h-7 min-w-0 -translate-x-px rtl:translate-x-px items-center gap-2 overflow-hidden rounded-md px-2 text-sidebar-foreground ring-sidebar-ring outline-hidden group-data-[collapsible=icon]:hidden hover:bg-sidebar-accent hover:text-sidebar-foreground focus-visible:ring-2 active:bg-sidebar-accent active:text-sidebar-foreground disabled:pointer-events-none disabled:opacity-50 aria-disabled:pointer-events-none aria-disabled:opacity-50 data-[size=md]:text-sm data-[size=sm]:text-xs data-active:font-medium data-active:text-brand-text data-active:hover:text-brand-text data-active:[&>svg]:text-brand-text [&>span:last-child]:truncate [&>svg]:size-4 [&>svg]:[stroke-width:1.8] [&>svg]:shrink-0 [&>svg]:text-sidebar-foreground",
621
621
  className
622
622
  ),
623
623
  ...props
@@ -628,7 +628,7 @@ function SidebarMenuSubButton$1({
628
628
  const Sidebar = ({
629
629
  className,
630
630
  ...props
631
- }) => /* @__PURE__ */ jsx(Sidebar$1, { className: cn("border-border", className), ...props });
631
+ }) => /* @__PURE__ */ jsx(Sidebar$1, { className: cn("border-sidebar-border", className), ...props });
632
632
  const SidebarContent = (props) => /* @__PURE__ */ jsx(SidebarContent$1, { ...props });
633
633
  const SidebarFooter = (props) => /* @__PURE__ */ jsx(SidebarFooter$1, { ...props });
634
634
  const SidebarGroup = (props) => /* @__PURE__ */ jsx(SidebarGroup$1, { ...props });