najm-kit 0.0.27 → 0.0.29

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.d.ts CHANGED
@@ -2228,9 +2228,16 @@ interface UseFormSubmissionOptions {
2228
2228
  }
2229
2229
  interface FormSubmissionState {
2230
2230
  formDataRef: React.MutableRefObject<Record<string, any>>;
2231
- handleStepSubmit: (stepData: any) => Promise<void>;
2231
+ handleStepSubmit: (stepData: any) => Promise<StepSubmitResult>;
2232
2232
  getStepDefaultValues: (stepId: string) => Record<string, any>;
2233
2233
  }
2234
+ type StepSubmitResult = {
2235
+ ok: true;
2236
+ } | {
2237
+ ok: false;
2238
+ error: any;
2239
+ data: Record<string, any>;
2240
+ };
2234
2241
  declare function useFormSubmission({ steps, schema, defaultValues, onSubmit, currentStep, isLastStep, handleNext, markStepCompleted, reset, }: UseFormSubmissionOptions): FormSubmissionState;
2235
2242
 
2236
2243
  interface NTableClassNames {
package/dist/index.mjs CHANGED
@@ -7709,25 +7709,41 @@ function useFormSubmission({
7709
7709
  if (stepId) {
7710
7710
  formDataRef.current = { ...formDataRef.current, ...stepData };
7711
7711
  }
7712
- markStepCompleted(currentStep - 1);
7713
7712
  if (!isLastStep) {
7713
+ markStepCompleted(currentStep - 1);
7714
7714
  handleNext();
7715
+ return { ok: true };
7715
7716
  } else {
7716
7717
  const fullData = { ...formDataRef.current };
7718
+ let submitData = fullData;
7717
7719
  if (schema?.parse) {
7718
- const validated = schema.parse(fullData);
7719
- await onSubmit(validated);
7720
- } else {
7721
- await onSubmit(fullData);
7720
+ const result = schema.safeParse?.(fullData);
7721
+ if (result) {
7722
+ if (!result.success) {
7723
+ return { ok: false, error: result.error, data: fullData };
7724
+ }
7725
+ submitData = result.data;
7726
+ } else {
7727
+ submitData = schema.parse(fullData);
7728
+ }
7722
7729
  }
7730
+ await onSubmit(submitData);
7731
+ markStepCompleted(currentStep - 1);
7723
7732
  reset();
7724
- formDataRef.current = {};
7733
+ formDataRef.current = defaultValues ?? {};
7734
+ return { ok: true };
7725
7735
  }
7726
7736
  },
7727
- [steps, currentStep, isLastStep, schema, onSubmit, handleNext, markStepCompleted, reset]
7737
+ [steps, currentStep, isLastStep, schema, onSubmit, handleNext, markStepCompleted, reset, defaultValues]
7728
7738
  );
7729
7739
  return { formDataRef, handleStepSubmit, getStepDefaultValues };
7730
7740
  }
7741
+ function issuePath(issue) {
7742
+ return issue.path?.map(String).join(".") ?? "";
7743
+ }
7744
+ function matchesFieldPath(path, field) {
7745
+ return path === field || path.startsWith(`${field}.`) || field.startsWith(`${path}.`);
7746
+ }
7731
7747
  function WizardForm({
7732
7748
  steps,
7733
7749
  schema,
@@ -7765,6 +7781,15 @@ function WizardForm({
7765
7781
  });
7766
7782
  const currentStepConfig = nav.currentStepConfig;
7767
7783
  const stepDefaults = getStepDefaultValues(currentStepConfig.id);
7784
+ const pendingIssuesRef = useRef(null);
7785
+ const getIssueStepIndex = (issue) => {
7786
+ const path = issuePath(issue);
7787
+ if (!path) return nav.currentStep - 1;
7788
+ const stepIndex = steps.findIndex(
7789
+ (step) => step.fields?.some((field) => matchesFieldPath(path, field))
7790
+ );
7791
+ return stepIndex >= 0 ? stepIndex : nav.currentStep - 1;
7792
+ };
7768
7793
  const stepSchema = useMemo(() => {
7769
7794
  if (currentStepConfig.schema) return currentStepConfig.schema;
7770
7795
  if (currentStepConfig.fields && schema?.pick) {
@@ -7781,16 +7806,52 @@ function WizardForm({
7781
7806
  const newDefaults = getStepDefaultValues(currentStepConfig.id);
7782
7807
  form.reset(newDefaults);
7783
7808
  }, [nav.currentStep, currentStepConfig.id]);
7809
+ useEffect(() => {
7810
+ const pending = pendingIssuesRef.current;
7811
+ if (!pending || pending.stepIndex !== nav.currentStep - 1) return;
7812
+ pendingIssuesRef.current = null;
7813
+ pending.issues.forEach((issue, index) => {
7814
+ const name = issuePath(issue) || "root";
7815
+ form.setError(
7816
+ name,
7817
+ { type: "validate", message: issue.message ?? "Invalid value" },
7818
+ { shouldFocus: index === 0 }
7819
+ );
7820
+ });
7821
+ }, [nav.currentStep, currentStepConfig.id, form]);
7784
7822
  const handleSubmit = async (data) => {
7785
7823
  onStepComplete?.(nav.currentStep - 1, data);
7786
- await handleStepSubmit(data);
7824
+ const result = await handleStepSubmit(data);
7825
+ if (result.ok === true) return;
7826
+ const issues = Array.isArray(result.error?.issues) ? result.error.issues : [];
7827
+ if (issues.length === 0) {
7828
+ throw result.error;
7829
+ }
7830
+ const targetStepIndex = getIssueStepIndex(issues[0]);
7831
+ const targetIssues = issues.filter((issue) => getIssueStepIndex(issue) === targetStepIndex);
7832
+ if (targetStepIndex === nav.currentStep - 1) {
7833
+ pendingIssuesRef.current = { stepIndex: targetStepIndex, issues: targetIssues };
7834
+ const pending = pendingIssuesRef.current;
7835
+ pendingIssuesRef.current = null;
7836
+ pending.issues.forEach((issue, index) => {
7837
+ const name = issuePath(issue) || "root";
7838
+ form.setError(
7839
+ name,
7840
+ { type: "validate", message: issue.message ?? "Invalid value" },
7841
+ { shouldFocus: index === 0 }
7842
+ );
7843
+ });
7844
+ return;
7845
+ }
7846
+ pendingIssuesRef.current = { stepIndex: targetStepIndex, issues: targetIssues };
7847
+ nav.goToStep(targetStepIndex + 1);
7787
7848
  };
7788
7849
  return /* @__PURE__ */ jsxs(
7789
7850
  "div",
7790
7851
  {
7791
7852
  "data-najm-wizard-form": "true",
7792
7853
  "data-najm-dialog-actions": "content",
7793
- className: cn("flex w-full flex-col gap-4", classNames?.root, className),
7854
+ className: cn("flex min-h-full w-full flex-col gap-4", classNames?.root, className),
7794
7855
  children: [
7795
7856
  showHeader && /* @__PURE__ */ jsx(
7796
7857
  StepsHeader,
@@ -7807,7 +7868,7 @@ function WizardForm({
7807
7868
  {
7808
7869
  id: `step-${currentStepConfig.id}`,
7809
7870
  onSubmit: form.handleSubmit(handleSubmit),
7810
- className: cn("flex flex-col gap-4", classNames?.step),
7871
+ className: cn("min-h-0 flex-1 overflow-y-auto pb-4 flex flex-col gap-4", classNames?.step),
7811
7872
  autoComplete: "off",
7812
7873
  children: [
7813
7874
  currentStepConfig.description && /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: currentStepConfig.description }),
@@ -7815,7 +7876,7 @@ function WizardForm({
7815
7876
  ]
7816
7877
  }
7817
7878
  ) }) }),
7818
- showFooter && /* @__PURE__ */ jsxs("div", { className: cn("flex items-center justify-between", classNames?.footer), children: [
7879
+ showFooter && /* @__PURE__ */ jsxs("div", { className: cn("sticky bottom-0 z-10 mt-auto flex shrink-0 items-center justify-between border-t border-border bg-background/95 pt-3 backdrop-blur", classNames?.footer), children: [
7819
7880
  /* @__PURE__ */ jsx("div", { children: !nav.isFirstStep && /* @__PURE__ */ jsx(Button, { type: "button", variant: "outline", onClick: nav.handlePrevious, children: previousLabel }) }),
7820
7881
  /* @__PURE__ */ jsxs("div", { className: "flex items-center gap-2", children: [
7821
7882
  footerSlot,
@@ -7837,8 +7898,8 @@ var handler = {
7837
7898
  };
7838
7899
  useTableStore.use = new Proxy({}, handler);
7839
7900
  var actionButtonClass = (bordered, danger) => cn(
7840
- "flex h-7 w-7 items-center justify-center rounded-md text-muted-foreground transition-colors",
7841
- danger ? "hover:bg-red-500/10 hover:text-red-400" : "hover:bg-muted hover:text-foreground",
7901
+ "flex h-7 w-7 cursor-pointer items-center justify-center rounded-md border border-transparent text-muted-foreground transition-colors",
7902
+ danger ? "hover:border-red-200 hover:bg-red-50 hover:text-red-500" : "hover:border-border hover:bg-muted hover:text-foreground",
7842
7903
  bordered && "border border-muted-foreground"
7843
7904
  );
7844
7905
  function TableActionCell({ row, onView, onEdit, onDelete, openRowMenu, menuButton, bordered }) {
@@ -9568,7 +9629,8 @@ function NTable(props) {
9568
9629
  [onRowContextMenu]
9569
9630
  );
9570
9631
  const autoOpenRowMenu = effectiveRowMenu ? handleOpenRowMenu : onRowContextMenu ? handleManualRowMenu : null;
9571
- const effectiveMenuButton = Boolean(autoOpenRowMenu) && (menuButtonProp ?? true);
9632
+ const shouldCollapseActions = props.columns.length > 5;
9633
+ const effectiveMenuButton = Boolean(autoOpenRowMenu) && (menuButtonProp ?? shouldCollapseActions);
9572
9634
  const store = useStoreSync({
9573
9635
  data: props.data ?? [],
9574
9636
  columns: props.columns ?? [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "najm-kit",
3
- "version": "0.0.27",
3
+ "version": "0.0.29",
4
4
  "type": "module",
5
5
  "description": "Reusable React UI component package for Najm framework",
6
6
  "main": "./dist/index.mjs",