@nextlyhq/plugin-form-builder 0.0.2-alpha.6 → 0.0.2-alpha.60

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.
Files changed (38) hide show
  1. package/README.md +12 -5
  2. package/dist/admin/index.d.ts +123 -331
  3. package/dist/admin/index.js +3780 -3392
  4. package/dist/admin/index.js.map +1 -1
  5. package/dist/chunk-XCICDELK.js +559 -0
  6. package/dist/chunk-XCICDELK.js.map +1 -0
  7. package/dist/index.d.ts +12 -11
  8. package/dist/index.js +603 -665
  9. package/dist/index.js.map +1 -1
  10. package/dist/{index-C0kBvYjm.d.ts → types-DuEFdLNU.d.ts} +147 -128
  11. package/package.json +33 -31
  12. package/dist/CheckboxFieldEditor-K4T6QX52.js +0 -121
  13. package/dist/CheckboxFieldEditor-K4T6QX52.js.map +0 -1
  14. package/dist/DateFieldEditor-JFJFAVOH.js +0 -187
  15. package/dist/DateFieldEditor-JFJFAVOH.js.map +0 -1
  16. package/dist/EmailFieldEditor-MMOXXL7P.js +0 -138
  17. package/dist/EmailFieldEditor-MMOXXL7P.js.map +0 -1
  18. package/dist/NumberFieldEditor-UISAYR6Z.js +0 -175
  19. package/dist/NumberFieldEditor-UISAYR6Z.js.map +0 -1
  20. package/dist/RadioFieldEditor-6UQFH4WT.js +0 -165
  21. package/dist/RadioFieldEditor-6UQFH4WT.js.map +0 -1
  22. package/dist/SelectFieldEditor-PSO4KHV4.js +0 -213
  23. package/dist/SelectFieldEditor-PSO4KHV4.js.map +0 -1
  24. package/dist/TextFieldEditor-5EU64EA5.js +0 -187
  25. package/dist/TextFieldEditor-5EU64EA5.js.map +0 -1
  26. package/dist/TextareaFieldEditor-773G3LNM.js +0 -190
  27. package/dist/TextareaFieldEditor-773G3LNM.js.map +0 -1
  28. package/dist/admin/index.css +0 -934
  29. package/dist/admin/index.css.map +0 -1
  30. package/dist/chunk-ABNZFTQK.js +0 -586
  31. package/dist/chunk-ABNZFTQK.js.map +0 -1
  32. package/dist/chunk-XTCEPJ7B.js +0 -256
  33. package/dist/chunk-XTCEPJ7B.js.map +0 -1
  34. package/dist/components/index.d.ts +0 -3
  35. package/dist/components/index.js +0 -9
  36. package/dist/components/index.js.map +0 -1
  37. package/dist/styles/form-builder.css +0 -1150
  38. package/dist/styles/submissions-filter.css +0 -75
@@ -1,5 +1,5 @@
1
- import React, { ComponentType } from 'react';
2
1
  import { RequestContext, CollectionConfig, FieldDefinition } from 'nextly';
2
+ import { ComponentType } from 'react';
3
3
 
4
4
  /**
5
5
  * Form Builder Plugin Types
@@ -34,19 +34,53 @@ interface EmailConfig {
34
34
  bcc?: string | string[];
35
35
  }
36
36
  /**
37
- * A single email notification integration stored on a form.
38
- * Matches the shape saved by the form builder admin UI.
37
+ * @experimental A resolved outgoing form notification (template-based) as built
38
+ * by the submission handler, before it is sent. This is the value transformed by
39
+ * the `form-builder.beforeEmail` D63 filter seam.
39
40
  */
40
- interface FormNotificationItem {
41
+ interface FormEmailNotification {
42
+ /** Resolved recipient address. */
43
+ to: string;
44
+ /** Email template slug to render. */
45
+ templateSlug: string;
46
+ /** Template variables (submitted data + form metadata). */
47
+ variables: Record<string, unknown>;
48
+ /** Email provider id; undefined = system default. */
49
+ providerId?: string;
50
+ /**
51
+ * Resolved sender address (rule's senderEmail, else the plugin's
52
+ * defaultFrom); undefined = template/provider default.
53
+ */
54
+ from?: string;
55
+ /** Resolved Reply-To address, if the rule configured one. */
56
+ replyTo?: string;
57
+ /** CC addresses, if any. */
58
+ cc?: string[];
59
+ /** BCC addresses, if any. */
60
+ bcc?: string[];
61
+ /** Source notification id; undefined for filter-injected emails. */
62
+ notificationId?: string;
63
+ }
64
+ /**
65
+ * A single email notification rule stored on a form.
66
+ * Matches the shape saved by the form builder admin UI. This is the ONE
67
+ * notification rule type — the admin context re-exports it rather than
68
+ * keeping a parallel copy.
69
+ */
70
+ interface FormNotification {
41
71
  /** Unique ID for this notification */
42
72
  id: string;
43
- /** Display name for this integration */
73
+ /** Display name for this notification rule */
44
74
  name: string;
45
75
  /** Whether this notification is active */
46
76
  enabled: boolean;
47
77
  /** ID of the email provider to use; undefined = system default */
48
78
  providerId?: string;
49
- /** Sender email override; undefined = use provider's configured address */
79
+ /**
80
+ * Sender email override. Resolution order at send time:
81
+ * this value, else the plugin's `notifications.defaultFrom` option, else
82
+ * the template/provider default.
83
+ */
50
84
  senderEmail?: string;
51
85
  /** How the recipient address is determined */
52
86
  recipientType: "static" | "field";
@@ -56,9 +90,26 @@ interface FormNotificationItem {
56
90
  cc: string[];
57
91
  /** BCC email addresses */
58
92
  bcc: string[];
93
+ /**
94
+ * Reply-To address: a `{{fieldName}}` reference to one of the form's
95
+ * email fields (resolved against the submission) or a literal address.
96
+ */
97
+ replyTo?: string;
98
+ /**
99
+ * Optional send condition evaluated against the submitted data. When set
100
+ * and unmet, the rule is skipped for that submission. A single condition
101
+ * covers the common case; the JSON storage leaves room to grow into a
102
+ * multi-condition tree without a migration.
103
+ */
104
+ condition?: ConditionalLogicCondition;
59
105
  /** Slug of the email template to use */
60
106
  templateSlug?: string;
61
107
  }
108
+ /**
109
+ * @deprecated Use {@link FormNotification}. Alias kept so existing imports
110
+ * keep compiling during the alpha; will be removed before a stable release.
111
+ */
112
+ type FormNotificationItem = FormNotification;
62
113
  /**
63
114
  * Form document structure (stored in database).
64
115
  */
@@ -66,9 +117,9 @@ interface FormDocument {
66
117
  id: string;
67
118
  slug: string;
68
119
  name: string;
69
- fields: FormField[];
120
+ fields: AnyFormField[];
70
121
  settings: FormSettings;
71
- notifications: FormNotificationItem[];
122
+ notifications: FormNotification[];
72
123
  webhooks?: WebhookConfig[];
73
124
  status: "draft" | "published" | "closed";
74
125
  createdAt: Date;
@@ -81,9 +132,19 @@ interface SubmissionDocument {
81
132
  id: string;
82
133
  form: string | FormDocument;
83
134
  data: Record<string, unknown>;
84
- status: "new" | "read" | "archived";
135
+ /**
136
+ * Where the submission sits in review.
137
+ *
138
+ * `spam` is one of them, not a separate flag: the stored field offers it, the
139
+ * admin has a Spam tab and filters the other views with `not_equals: "spam"`,
140
+ * the notification hook skips it, and marking something "Not spam" moves it
141
+ * back to `new`. `spamReason` records WHICH check flagged it.
142
+ */
143
+ status: "new" | "read" | "archived" | "spam";
85
144
  ipAddress?: string;
86
145
  userAgent?: string;
146
+ /** Which spam check flagged this, when one did. */
147
+ spamReason?: string | null;
87
148
  submittedAt: Date;
88
149
  createdAt: Date;
89
150
  updatedAt: Date;
@@ -200,26 +261,6 @@ interface FormBuilderPluginOptions {
200
261
  defaultFields: FieldDefinition[];
201
262
  }) => FieldDefinition[]);
202
263
  };
203
- /**
204
- * @deprecated Use `formOverrides` and `formSubmissionOverrides` instead.
205
- * This option is kept for backward compatibility.
206
- */
207
- collections?: {
208
- forms?: Partial<CollectionConfig> & {
209
- slug?: string;
210
- labels?: {
211
- singular?: string;
212
- plural?: string;
213
- };
214
- };
215
- submissions?: Partial<CollectionConfig> & {
216
- slug?: string;
217
- labels?: {
218
- singular?: string;
219
- plural?: string;
220
- };
221
- };
222
- };
223
264
  /**
224
265
  * Configure which field types are available in the form builder.
225
266
  *
@@ -272,25 +313,24 @@ interface FormBuilderPluginOptions {
272
313
  /**
273
314
  * Hook called before sending email notifications.
274
315
  *
275
- * Allows modifying, adding, or filtering emails before they are sent.
276
- * Return the modified emails array.
316
+ * Runs as the `form-builder.beforeEmail` D63 filter: the resolved outgoing
317
+ * notifications (template + recipient + variables) are threaded through this
318
+ * callback before they are sent. Allows modifying, adding, or filtering the
319
+ * outgoing notifications. Return the modified array.
277
320
  *
278
321
  * @example
279
322
  * ```typescript
280
323
  * beforeEmail: async ({ emails, form, submission }) => {
281
- * // Add BCC to all emails
282
- * return emails.map(email => ({
283
- * ...email,
284
- * bcc: ['archive@example.com'],
285
- * }));
324
+ * // Add BCC to all outgoing notifications
325
+ * return emails.map((e) => ({ ...e, bcc: ['archive@example.com'] }));
286
326
  * }
287
327
  * ```
288
328
  */
289
329
  beforeEmail?: (args: {
290
- emails: EmailConfig[];
330
+ emails: FormEmailNotification[];
291
331
  form: FormDocument;
292
332
  submission: SubmissionDocument;
293
- }) => Promise<EmailConfig[]> | EmailConfig[];
333
+ }) => Promise<FormEmailNotification[]> | FormEmailNotification[];
294
334
  /**
295
335
  * Email notification settings.
296
336
  */
@@ -413,7 +453,7 @@ interface FormConfig {
413
453
  /**
414
454
  * Form fields definition.
415
455
  */
416
- fields: FormField[];
456
+ fields: AnyFormField[];
417
457
  /**
418
458
  * Form settings and behavior.
419
459
  */
@@ -441,9 +481,32 @@ interface FormConfig {
441
481
  */
442
482
  type FormField = TextFormField | EmailFormField | NumberFormField | PhoneFormField | UrlFormField | TextareaFormField | SelectFormField | CheckboxFormField | RadioFormField | FileFormField | DateFormField | TimeFormField | HiddenFormField;
443
483
  /**
444
- * All available form field type identifiers.
484
+ * A form field of a built-in type OR a plugin-contributed type. Stored form
485
+ * data and the builder work with this; `isKnownFormField` narrows it back to
486
+ * the closed `FormField` union so `switch (field.type)` keeps discriminating
487
+ * the built-ins (a `CustomFormField` in the union itself would collapse that).
488
+ */
489
+ type AnyFormField = FormField | CustomFormField;
490
+ /**
491
+ * All available built-in form field type identifiers.
445
492
  */
446
493
  type FormFieldType = "text" | "email" | "number" | "phone" | "url" | "textarea" | "select" | "checkbox" | "radio" | "file" | "date" | "time" | "hidden";
494
+ /**
495
+ * A form field's type on the wire: a built-in or a plugin-contributed type id
496
+ * that opted into the forms surface. The `(string & {})` arm admits the plugin
497
+ * id while keeping built-in autocomplete. Built-in `case` narrowing still works
498
+ * because every field-type switch keeps a `default` for the custom arm.
499
+ */
500
+ type FormFieldTypeId = FormFieldType | (string & {});
501
+ /** Every built-in form field type, as a value list (the single source shared
502
+ * by the field-type guard and the config validator). */
503
+ declare const BUILT_IN_FORM_FIELD_TYPES: readonly FormFieldType[];
504
+ /**
505
+ * Narrow an `AnyFormField` to the closed built-in `FormField` union. Returns
506
+ * `false` for a plugin-contributed field, so callers handle it generically
507
+ * before switching on the built-in discriminant.
508
+ */
509
+ declare function isKnownFormField(field: AnyFormField): field is FormField;
447
510
  /**
448
511
  * Base form field properties.
449
512
  * All form field types extend this interface.
@@ -475,6 +538,16 @@ interface BaseFormField {
475
538
  width?: "25%" | "33%" | "50%" | "66%" | "75%" | "100%";
476
539
  };
477
540
  }
541
+ /**
542
+ * A plugin-contributed form field type (outside the built-in union). Carries
543
+ * only the base properties — the plugin owns its editor component and value
544
+ * semantics — and its `type` is the plugin's registered field-type id. Kept a
545
+ * distinct union member so built-in `case` narrowing is unaffected while a
546
+ * plugin field still type-checks as a `FormField`.
547
+ */
548
+ interface CustomFormField extends Omit<BaseFormField, "type"> {
549
+ type: string & {};
550
+ }
478
551
  /**
479
552
  * Text input field.
480
553
  */
@@ -650,11 +723,19 @@ interface FormSettings {
650
723
  /**
651
724
  * Confirmation type after successful submission.
652
725
  * - "message": Display a success message
653
- * - "redirect": Redirect to a URL or linked document
726
+ * - "redirect": Redirect to a URL
727
+ * - "relationship": Redirect to a linked document (requires
728
+ * `redirectRelationships` in the plugin options)
654
729
  *
655
730
  * @default "message"
656
731
  */
657
- confirmationType?: "message" | "redirect";
732
+ confirmationType?: "message" | "redirect" | "relationship";
733
+ /**
734
+ * Linked document for relationship-based redirects (the collection's
735
+ * `redirectPage` relationship value). Preserved verbatim — resolution
736
+ * happens where the relationship is rendered.
737
+ */
738
+ redirectPage?: unknown;
658
739
  /** Success message (supports HTML) - used when confirmationType is "message" */
659
740
  successMessage?: string;
660
741
  /** Redirect URL after successful submission - used when confirmationType is "redirect" */
@@ -667,13 +748,20 @@ interface FormSettings {
667
748
  relationTo: string;
668
749
  value: string;
669
750
  };
670
- /** Allow multiple submissions from same user/IP */
751
+ /** Allow multiple submissions from same IP (default true) */
671
752
  allowMultipleSubmissions?: boolean;
672
- /** reCAPTCHA configuration */
673
- captcha?: {
674
- enabled: boolean;
675
- siteKey?: string;
676
- };
753
+ /**
754
+ * Per-form honeypot override. Unset inherits the plugin's
755
+ * `spamProtection.honeypot`; the form wins where set.
756
+ */
757
+ honeypotEnabled?: boolean;
758
+ /**
759
+ * Per-form reCAPTCHA override. Unset inherits the plugin's
760
+ * `spamProtection.recaptcha.enabled`; the form wins where set.
761
+ */
762
+ captchaEnabled?: boolean;
763
+ /** reCAPTCHA site key (client-facing) when captcha is enabled per-form */
764
+ captchaSiteKey?: string;
677
765
  }
678
766
  /**
679
767
  * Email notification configuration for a form.
@@ -826,8 +914,16 @@ interface FormSubmission {
826
914
  formSlug: string;
827
915
  /** Submission data (JSON) */
828
916
  data: Record<string, unknown>;
829
- /** Submission status */
830
- status: "new" | "read" | "archived";
917
+ /**
918
+ * Submission status.
919
+ *
920
+ * `spam` is one of them, not a separate flag: the stored field offers it, the
921
+ * admin has a Spam tab and filters its other views with `not_equals: "spam"`,
922
+ * and marking something "Not spam" moves it back to `new`.
923
+ */
924
+ status: "new" | "read" | "archived" | "spam";
925
+ /** Which spam check flagged this, when one did. */
926
+ spamReason?: string | null;
831
927
  /** Internal notes (admin only) */
832
928
  notes?: string;
833
929
  /** Metadata */
@@ -932,81 +1028,4 @@ declare function isCheckboxFormField(field: FormField): field is CheckboxFormFie
932
1028
  */
933
1029
  declare function isFileFormField(field: FormField): field is FileFormField;
934
1030
 
935
- /**
936
- * Props for the SubmissionList component.
937
- */
938
- interface SubmissionListProps {
939
- /** Form document containing field definitions */
940
- form: FormDocument;
941
- /** Array of submission documents to display */
942
- submissions: SubmissionDocument[];
943
- /** Whether data is currently loading */
944
- isLoading?: boolean;
945
- /** Callback when submission status is changed */
946
- onStatusChange?: (id: string, status: "new" | "read" | "archived") => Promise<void>;
947
- /** Callback when submission is deleted */
948
- onDelete?: (id: string) => Promise<void>;
949
- /** Callback when bulk delete is triggered */
950
- onBulkDelete?: (ids: string[]) => Promise<void>;
951
- /** Callback when bulk status change is triggered */
952
- onBulkStatusChange?: (ids: string[], status: "new" | "read" | "archived") => Promise<void>;
953
- /** Callback when export is triggered */
954
- onExport?: (format: "csv" | "json") => void;
955
- /** Callback when a submission is clicked for detail view */
956
- onViewDetail?: (submission: SubmissionDocument) => void;
957
- }
958
- /**
959
- * Submission List Component
960
- *
961
- * Displays form submissions in a table with filtering, selection, and actions.
962
- *
963
- * @example
964
- * ```tsx
965
- * <SubmissionList
966
- * form={form}
967
- * submissions={submissions}
968
- * onStatusChange={handleStatusChange}
969
- * onDelete={handleDelete}
970
- * onExport={handleExport}
971
- * onViewDetail={handleViewDetail}
972
- * />
973
- * ```
974
- */
975
- declare function SubmissionList({ form, submissions, isLoading, onStatusChange, onDelete, onBulkDelete, onBulkStatusChange, onExport, onViewDetail, }: SubmissionListProps): React.ReactElement;
976
-
977
- /**
978
- * Props for the SubmissionDetail component.
979
- */
980
- interface SubmissionDetailProps {
981
- /** Form document containing field definitions */
982
- form: FormDocument;
983
- /** Submission document to display */
984
- submission: SubmissionDocument;
985
- /** Callback when status is changed */
986
- onStatusChange?: (status: "new" | "read" | "archived") => Promise<void>;
987
- /** Callback when notes are updated */
988
- onNotesUpdate?: (notes: string) => Promise<void>;
989
- /** Callback when back button is clicked */
990
- onBack?: () => void;
991
- /** Callback when delete is triggered */
992
- onDelete?: () => Promise<void>;
993
- }
994
- /**
995
- * Submission Detail Component
996
- *
997
- * Displays full submission data with status management and notes.
998
- *
999
- * @example
1000
- * ```tsx
1001
- * <SubmissionDetail
1002
- * form={form}
1003
- * submission={submission}
1004
- * onStatusChange={handleStatusChange}
1005
- * onNotesUpdate={handleNotesUpdate}
1006
- * onBack={handleBack}
1007
- * />
1008
- * ```
1009
- */
1010
- declare function SubmissionDetail({ form, submission, onStatusChange, onNotesUpdate, onBack, onDelete, }: SubmissionDetailProps): React.ReactElement;
1011
-
1012
- export { isFormFieldType as A, type BaseFormField as B, type CheckboxFormField as C, type DateFormField as D, type EmailFormField as E, type FormBuilderPluginOptions as F, isSelectFormField as G, type HiddenFormField as H, isTextFormField as I, SubmissionDetail as J, SubmissionList as K, type NumberFormField as N, type PhoneFormField as P, type ResolvedFormBuilderConfig as R, type SelectFormField as S, type TextFormField as T, type UrlFormField as U, type ValidationRules as V, type WebhookEvent as W, type FormConfig as a, type FormSettings as b, type FormNotifications as c, type FileFormField as d, type RadioFormField as e, type TextareaFormField as f, type TimeFormField as g, type ConditionalLogic as h, type FormField as i, type SubmissionDocument as j, type FormDocument as k, type ConditionalLogicCondition as l, type EmailConfig as m, type FieldBlockConfig as n, type FormAccess as o, type FormFieldType as p, type FormHookContext as q, type FormHooks as r, type FormSubmission as s, type SubmissionDetailProps as t, type SubmissionListProps as u, type WebhookConfig as v, isCheckboxFormField as w, isEmailFormField as x, isFileFormField as y, isFormField as z };
1031
+ export { type AnyFormField as A, BUILT_IN_FORM_FIELD_TYPES as B, type CheckboxFormField as C, type DateFormField as D, type EmailFormField as E, type FormBuilderPluginOptions as F, isEmailFormField as G, type HiddenFormField as H, isFileFormField as I, isFormField as J, isFormFieldType as K, isKnownFormField as L, isSelectFormField as M, type NumberFormField as N, isTextFormField as O, type PhoneFormField as P, type ResolvedFormBuilderConfig as R, type SelectFormField as S, type TextFormField as T, type UrlFormField as U, type ValidationRules as V, type WebhookEvent as W, type FormConfig as a, type FormSettings as b, type FormNotifications as c, type FileFormField as d, type RadioFormField as e, type TextareaFormField as f, type TimeFormField as g, type ConditionalLogic as h, type SubmissionDocument as i, type FormDocument as j, type BaseFormField as k, type ConditionalLogicCondition as l, type CustomFormField as m, type EmailConfig as n, type FieldBlockConfig as o, type FormAccess as p, type FormField as q, type FormFieldType as r, type FormFieldTypeId as s, type FormHookContext as t, type FormHooks as u, type FormNotification as v, type FormNotificationItem as w, type FormSubmission as x, type WebhookConfig as y, isCheckboxFormField as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nextlyhq/plugin-form-builder",
3
- "version": "0.0.2-alpha.6",
3
+ "version": "0.0.2-alpha.60",
4
4
  "description": "Visual form builder plugin for Nextly with Code-First and Schema Builder support",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -10,58 +10,60 @@
10
10
  "exports": {
11
11
  ".": {
12
12
  "types": "./dist/index.d.ts",
13
- "import": "./dist/index.js"
13
+ "import": "./dist/index.js",
14
+ "default": "./dist/index.js"
14
15
  },
15
16
  "./admin": {
16
17
  "types": "./dist/admin/index.d.ts",
17
- "import": "./dist/admin/index.js"
18
- },
19
- "./components": {
20
- "types": "./dist/components/index.d.ts",
21
- "import": "./dist/components/index.js"
22
- },
23
- "./styles/builder.css": "./dist/styles/form-builder.css",
24
- "./styles/submissions-filter.css": "./dist/styles/submissions-filter.css"
18
+ "import": "./dist/admin/index.js",
19
+ "default": "./dist/admin/index.js"
20
+ }
25
21
  },
26
22
  "files": [
27
23
  "dist",
28
24
  "README.md"
29
25
  ],
30
26
  "dependencies": {
31
- "@dnd-kit/core": "^6.1.0",
32
- "@dnd-kit/sortable": "^8.0.0",
27
+ "@dnd-kit/core": "^6.3.1",
28
+ "@dnd-kit/sortable": "^10.0.0",
33
29
  "@dnd-kit/utilities": "^3.2.2",
34
- "zod": "^3.22.4",
35
- "react-hook-form": "^7.50.0",
36
- "@hookform/resolvers": "^3.3.4"
30
+ "zod": "^4.1.12"
37
31
  },
38
32
  "peerDependencies": {
39
- "@nextlyhq/admin": "0.0.2-alpha.6",
40
- "nextly": "0.0.2-alpha.6",
41
- "@nextlyhq/ui": "0.0.2-alpha.6",
33
+ "@nextlyhq/admin": "0.0.2-alpha.60",
42
34
  "@tanstack/react-query": ">=5.0.0",
43
35
  "lucide-react": ">=0.400.0",
36
+ "next": "^14.0.0 || ^15.0.0 || ^16.0.0",
37
+ "nextly": "0.0.2-alpha.60",
44
38
  "react": "^18.0.0 || ^19.0.0",
45
- "next": "^14.0.0 || ^15.0.0 || ^16.0.0"
39
+ "@nextlyhq/plugin-sdk": "0.0.2-alpha.60",
40
+ "@nextlyhq/ui": "^0.0.2-alpha.60"
46
41
  },
47
42
  "devDependencies": {
48
- "@tanstack/react-query": "^5.0.0",
49
- "@types/node": "^20.0.0",
43
+ "@tanstack/react-query": "^5.62.0",
44
+ "@testing-library/jest-dom": "^6.9.1",
45
+ "@testing-library/react": "^16.3.0",
46
+ "@testing-library/user-event": "^14.6.1",
47
+ "@types/node": "^20.19.17",
50
48
  "@types/react": "19.2.0",
51
- "lucide-react": "^0.469.0",
52
- "tsup": "^8.0.0",
53
- "typescript": "^5.3.0",
54
- "vitest": "^4.0.8",
55
- "@nextlyhq/tsconfig": "0.0.2-alpha.5",
56
- "@nextlyhq/admin": "0.0.2-alpha.6",
57
- "nextly": "0.0.2-alpha.6",
58
- "@nextlyhq/ui": "0.0.2-alpha.6"
49
+ "jsdom": "^27.1.0",
50
+ "lucide-react": "^0.544.0",
51
+ "tsup": "^8.5.0",
52
+ "typescript": "^5.9.3",
53
+ "vitest": "^4.1.0",
54
+ "@nextlyhq/admin": "0.0.2-alpha.60",
55
+ "@nextlyhq/eslint-config": "0.0.2-alpha.60",
56
+ "@nextlyhq/plugin-sdk": "0.0.2-alpha.60",
57
+ "@nextlyhq/ui": "0.0.2-alpha.60",
58
+ "@nextlyhq/tsconfig": "0.0.2-alpha.60",
59
+ "nextly": "0.0.2-alpha.60"
59
60
  },
60
61
  "engines": {
61
- "node": ">=20.0.0"
62
+ "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
62
63
  },
63
64
  "keywords": [
64
65
  "nextly",
66
+ "nextly-plugin",
65
67
  "plugin",
66
68
  "forms",
67
69
  "form-builder",
@@ -86,7 +88,7 @@
86
88
  "author": "Nextly <contact@nextlyhq.com> (https://nextlyhq.com)",
87
89
  "scripts": {
88
90
  "build": "tsup",
89
- "dev": "tsup src/index.ts src/admin/index.ts src/components/index.ts --format esm --dts --watch",
91
+ "dev": "tsup src/index.ts src/admin/index.ts --format esm --dts --watch",
90
92
  "check-types": "tsc --noEmit",
91
93
  "lint": "eslint . --max-warnings 0",
92
94
  "lint:fix": "eslint . --fix",
@@ -1,121 +0,0 @@
1
- "use client";
2
-
3
- // src/admin/fields/CheckboxFieldEditor.tsx
4
- import { Input } from "@nextlyhq/ui";
5
- import { jsx, jsxs } from "react/jsx-runtime";
6
- function CheckboxFieldEditor({
7
- field,
8
- onUpdate
9
- }) {
10
- const updateValidation = (key, value) => {
11
- onUpdate({
12
- validation: {
13
- ...field.validation,
14
- [key]: value
15
- }
16
- });
17
- };
18
- return /* @__PURE__ */ jsxs("div", { className: "checkbox-field-editor", children: [
19
- /* @__PURE__ */ jsxs("div", { className: "checkbox-field-editor__section", children: [
20
- /* @__PURE__ */ jsx("h4", { className: "checkbox-field-editor__section-title", children: "General" }),
21
- /* @__PURE__ */ jsxs("div", { className: "checkbox-field-editor__field", children: [
22
- /* @__PURE__ */ jsx("label", { className: "checkbox-field-editor__label", htmlFor: "field-label", children: "Label" }),
23
- /* @__PURE__ */ jsx(
24
- Input,
25
- {
26
- id: "field-label",
27
- type: "text",
28
- value: field.label || "",
29
- onChange: (e) => onUpdate({ label: e.target.value }),
30
- placeholder: "I agree to the terms and conditions"
31
- }
32
- ),
33
- /* @__PURE__ */ jsx("p", { className: "checkbox-field-editor__hint", children: "Text displayed next to the checkbox" })
34
- ] }),
35
- /* @__PURE__ */ jsxs("div", { className: "checkbox-field-editor__field", children: [
36
- /* @__PURE__ */ jsx("label", { className: "checkbox-field-editor__label", htmlFor: "field-name", children: "Field Name" }),
37
- /* @__PURE__ */ jsx(
38
- Input,
39
- {
40
- id: "field-name",
41
- type: "text",
42
- value: field.name || "",
43
- onChange: (e) => onUpdate({
44
- name: e.target.value.replace(/\s+/g, "_").replace(/[^a-zA-Z0-9_]/g, "").toLowerCase()
45
- }),
46
- placeholder: "field_name",
47
- className: "checkbox-field-editor__input--mono"
48
- }
49
- ),
50
- /* @__PURE__ */ jsx("p", { className: "checkbox-field-editor__hint", children: "Used as the key in submission data. Only letters, numbers, and underscores." })
51
- ] }),
52
- /* @__PURE__ */ jsxs("div", { className: "checkbox-field-editor__field", children: [
53
- /* @__PURE__ */ jsx("label", { className: "checkbox-field-editor__label", htmlFor: "field-help", children: "Help Text" }),
54
- /* @__PURE__ */ jsx(
55
- Input,
56
- {
57
- id: "field-help",
58
- type: "text",
59
- value: field.helpText || "",
60
- onChange: (e) => onUpdate({ helpText: e.target.value }),
61
- placeholder: "Additional guidance for users"
62
- }
63
- ),
64
- /* @__PURE__ */ jsx("p", { className: "checkbox-field-editor__hint", children: "Displayed below the field" })
65
- ] }),
66
- /* @__PURE__ */ jsxs("div", { className: "checkbox-field-editor__field checkbox-field-editor__field--inline", children: [
67
- /* @__PURE__ */ jsxs("label", { className: "checkbox-field-editor__checkbox-label", children: [
68
- /* @__PURE__ */ jsx(
69
- "input",
70
- {
71
- type: "checkbox",
72
- checked: field.defaultValue || false,
73
- onChange: (e) => onUpdate({ defaultValue: e.target.checked }),
74
- className: "checkbox-field-editor__checkbox"
75
- }
76
- ),
77
- /* @__PURE__ */ jsx("span", { children: "Checked by default" })
78
- ] }),
79
- /* @__PURE__ */ jsx("p", { className: "checkbox-field-editor__hint", children: "Whether the checkbox is pre-checked when the form loads" })
80
- ] }),
81
- /* @__PURE__ */ jsxs("div", { className: "checkbox-field-editor__field checkbox-field-editor__field--inline", children: [
82
- /* @__PURE__ */ jsxs("label", { className: "checkbox-field-editor__checkbox-label", children: [
83
- /* @__PURE__ */ jsx(
84
- "input",
85
- {
86
- type: "checkbox",
87
- checked: field.required || false,
88
- onChange: (e) => onUpdate({ required: e.target.checked }),
89
- className: "checkbox-field-editor__checkbox"
90
- }
91
- ),
92
- /* @__PURE__ */ jsx("span", { children: "Required field" })
93
- ] }),
94
- /* @__PURE__ */ jsx("p", { className: "checkbox-field-editor__hint", children: "User must check this box to submit the form (useful for terms acceptance)" })
95
- ] })
96
- ] }),
97
- /* @__PURE__ */ jsxs("div", { className: "checkbox-field-editor__section", children: [
98
- /* @__PURE__ */ jsx("h4", { className: "checkbox-field-editor__section-title", children: "Validation" }),
99
- /* @__PURE__ */ jsxs("div", { className: "checkbox-field-editor__field", children: [
100
- /* @__PURE__ */ jsx("label", { className: "checkbox-field-editor__label", htmlFor: "field-error", children: "Error Message" }),
101
- /* @__PURE__ */ jsx(
102
- Input,
103
- {
104
- id: "field-error",
105
- type: "text",
106
- value: field.validation?.errorMessage || "",
107
- onChange: (e) => updateValidation("errorMessage", e.target.value || void 0),
108
- placeholder: "You must accept the terms to continue"
109
- }
110
- ),
111
- /* @__PURE__ */ jsx("p", { className: "checkbox-field-editor__hint", children: "Custom message shown when required checkbox is not checked" })
112
- ] })
113
- ] })
114
- ] });
115
- }
116
- var CheckboxFieldEditor_default = CheckboxFieldEditor;
117
- export {
118
- CheckboxFieldEditor,
119
- CheckboxFieldEditor_default as default
120
- };
121
- //# sourceMappingURL=CheckboxFieldEditor-K4T6QX52.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/admin/fields/CheckboxFieldEditor.tsx"],"sourcesContent":["/**\n * Checkbox Field Editor\n *\n * Editor component for configuring checkbox field properties.\n * Includes general settings (label, name, help text, required) and\n * default value configuration.\n *\n * Checkbox fields are typically used for:\n * - Terms and conditions acceptance\n * - Newsletter opt-in\n * - Boolean yes/no questions\n *\n * @module admin/fields/CheckboxFieldEditor\n * @since 0.1.0\n */\n\n\"use client\";\n\nimport { Input } from \"@nextlyhq/ui\";\n\nimport type { CheckboxFormField } from \"../../types\";\n\nimport type { FieldEditorProps } from \"./index\";\n\n// ============================================================================\n// Main Component\n// ============================================================================\n\n/**\n * CheckboxFieldEditor - Configure checkbox field properties\n *\n * Provides configuration options for:\n * - Label: Text shown next to the checkbox (e.g., \"I agree to the terms\")\n * - Field Name: Key used in submission data (auto-formatted)\n * - Help Text: Additional guidance shown below the field\n * - Required: Whether the checkbox must be checked (for consent forms)\n * - Default Value: Whether the checkbox is checked by default\n * - Validation: error message\n *\n * @example\n * ```tsx\n * <CheckboxFieldEditor\n * field={checkboxField}\n * allFields={formFields}\n * onUpdate={(updates) => handleUpdate(updates)}\n * />\n * ```\n */\nexport function CheckboxFieldEditor({\n field,\n onUpdate,\n}: FieldEditorProps<CheckboxFormField>) {\n // Helper to update validation properties\n const updateValidation = (key: string, value: unknown) => {\n onUpdate({\n validation: {\n ...field.validation,\n [key]: value,\n },\n });\n };\n\n return (\n <div className=\"checkbox-field-editor\">\n {/* General Settings Section */}\n <div className=\"checkbox-field-editor__section\">\n <h4 className=\"checkbox-field-editor__section-title\">General</h4>\n\n {/* Label */}\n <div className=\"checkbox-field-editor__field\">\n <label className=\"checkbox-field-editor__label\" htmlFor=\"field-label\">\n Label\n </label>\n <Input\n id=\"field-label\"\n type=\"text\"\n value={field.label || \"\"}\n onChange={e => onUpdate({ label: e.target.value })}\n placeholder=\"I agree to the terms and conditions\"\n />\n <p className=\"checkbox-field-editor__hint\">\n Text displayed next to the checkbox\n </p>\n </div>\n\n {/* Field Name */}\n <div className=\"checkbox-field-editor__field\">\n <label className=\"checkbox-field-editor__label\" htmlFor=\"field-name\">\n Field Name\n </label>\n <Input\n id=\"field-name\"\n type=\"text\"\n value={field.name || \"\"}\n onChange={e =>\n onUpdate({\n name: e.target.value\n .replace(/\\s+/g, \"_\")\n .replace(/[^a-zA-Z0-9_]/g, \"\")\n .toLowerCase(),\n })\n }\n placeholder=\"field_name\"\n className=\"checkbox-field-editor__input--mono\"\n />\n <p className=\"checkbox-field-editor__hint\">\n Used as the key in submission data. Only letters, numbers, and\n underscores.\n </p>\n </div>\n\n {/* Help Text */}\n <div className=\"checkbox-field-editor__field\">\n <label className=\"checkbox-field-editor__label\" htmlFor=\"field-help\">\n Help Text\n </label>\n <Input\n id=\"field-help\"\n type=\"text\"\n value={field.helpText || \"\"}\n onChange={e => onUpdate({ helpText: e.target.value })}\n placeholder=\"Additional guidance for users\"\n />\n <p className=\"checkbox-field-editor__hint\">\n Displayed below the field\n </p>\n </div>\n\n {/* Default Value */}\n <div className=\"checkbox-field-editor__field checkbox-field-editor__field--inline\">\n <label className=\"checkbox-field-editor__checkbox-label\">\n <input\n type=\"checkbox\"\n checked={field.defaultValue || false}\n onChange={e => onUpdate({ defaultValue: e.target.checked })}\n className=\"checkbox-field-editor__checkbox\"\n />\n <span>Checked by default</span>\n </label>\n <p className=\"checkbox-field-editor__hint\">\n Whether the checkbox is pre-checked when the form loads\n </p>\n </div>\n\n {/* Required */}\n <div className=\"checkbox-field-editor__field checkbox-field-editor__field--inline\">\n <label className=\"checkbox-field-editor__checkbox-label\">\n <input\n type=\"checkbox\"\n checked={field.required || false}\n onChange={e => onUpdate({ required: e.target.checked })}\n className=\"checkbox-field-editor__checkbox\"\n />\n <span>Required field</span>\n </label>\n <p className=\"checkbox-field-editor__hint\">\n User must check this box to submit the form (useful for terms\n acceptance)\n </p>\n </div>\n </div>\n\n {/* Validation Section */}\n <div className=\"checkbox-field-editor__section\">\n <h4 className=\"checkbox-field-editor__section-title\">Validation</h4>\n\n {/* Error Message */}\n <div className=\"checkbox-field-editor__field\">\n <label className=\"checkbox-field-editor__label\" htmlFor=\"field-error\">\n Error Message\n </label>\n <Input\n id=\"field-error\"\n type=\"text\"\n value={field.validation?.errorMessage || \"\"}\n onChange={e =>\n updateValidation(\"errorMessage\", e.target.value || undefined)\n }\n placeholder=\"You must accept the terms to continue\"\n />\n <p className=\"checkbox-field-editor__hint\">\n Custom message shown when required checkbox is not checked\n </p>\n </div>\n </div>\n </div>\n );\n}\n\nexport default CheckboxFieldEditor;\n"],"mappings":";;;AAkBA,SAAS,aAAa;AAgDd,cAGA,YAHA;AAlBD,SAAS,oBAAoB;AAAA,EAClC;AAAA,EACA;AACF,GAAwC;AAEtC,QAAM,mBAAmB,CAAC,KAAa,UAAmB;AACxD,aAAS;AAAA,MACP,YAAY;AAAA,QACV,GAAG,MAAM;AAAA,QACT,CAAC,GAAG,GAAG;AAAA,MACT;AAAA,IACF,CAAC;AAAA,EACH;AAEA,SACE,qBAAC,SAAI,WAAU,yBAEb;AAAA,yBAAC,SAAI,WAAU,kCACb;AAAA,0BAAC,QAAG,WAAU,wCAAuC,qBAAO;AAAA,MAG5D,qBAAC,SAAI,WAAU,gCACb;AAAA,4BAAC,WAAM,WAAU,gCAA+B,SAAQ,eAAc,mBAEtE;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACH,MAAK;AAAA,YACL,OAAO,MAAM,SAAS;AAAA,YACtB,UAAU,OAAK,SAAS,EAAE,OAAO,EAAE,OAAO,MAAM,CAAC;AAAA,YACjD,aAAY;AAAA;AAAA,QACd;AAAA,QACA,oBAAC,OAAE,WAAU,+BAA8B,iDAE3C;AAAA,SACF;AAAA,MAGA,qBAAC,SAAI,WAAU,gCACb;AAAA,4BAAC,WAAM,WAAU,gCAA+B,SAAQ,cAAa,wBAErE;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACH,MAAK;AAAA,YACL,OAAO,MAAM,QAAQ;AAAA,YACrB,UAAU,OACR,SAAS;AAAA,cACP,MAAM,EAAE,OAAO,MACZ,QAAQ,QAAQ,GAAG,EACnB,QAAQ,kBAAkB,EAAE,EAC5B,YAAY;AAAA,YACjB,CAAC;AAAA,YAEH,aAAY;AAAA,YACZ,WAAU;AAAA;AAAA,QACZ;AAAA,QACA,oBAAC,OAAE,WAAU,+BAA8B,yFAG3C;AAAA,SACF;AAAA,MAGA,qBAAC,SAAI,WAAU,gCACb;AAAA,4BAAC,WAAM,WAAU,gCAA+B,SAAQ,cAAa,uBAErE;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACH,MAAK;AAAA,YACL,OAAO,MAAM,YAAY;AAAA,YACzB,UAAU,OAAK,SAAS,EAAE,UAAU,EAAE,OAAO,MAAM,CAAC;AAAA,YACpD,aAAY;AAAA;AAAA,QACd;AAAA,QACA,oBAAC,OAAE,WAAU,+BAA8B,uCAE3C;AAAA,SACF;AAAA,MAGA,qBAAC,SAAI,WAAU,qEACb;AAAA,6BAAC,WAAM,WAAU,yCACf;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM,gBAAgB;AAAA,cAC/B,UAAU,OAAK,SAAS,EAAE,cAAc,EAAE,OAAO,QAAQ,CAAC;AAAA,cAC1D,WAAU;AAAA;AAAA,UACZ;AAAA,UACA,oBAAC,UAAK,gCAAkB;AAAA,WAC1B;AAAA,QACA,oBAAC,OAAE,WAAU,+BAA8B,qEAE3C;AAAA,SACF;AAAA,MAGA,qBAAC,SAAI,WAAU,qEACb;AAAA,6BAAC,WAAM,WAAU,yCACf;AAAA;AAAA,YAAC;AAAA;AAAA,cACC,MAAK;AAAA,cACL,SAAS,MAAM,YAAY;AAAA,cAC3B,UAAU,OAAK,SAAS,EAAE,UAAU,EAAE,OAAO,QAAQ,CAAC;AAAA,cACtD,WAAU;AAAA;AAAA,UACZ;AAAA,UACA,oBAAC,UAAK,4BAAc;AAAA,WACtB;AAAA,QACA,oBAAC,OAAE,WAAU,+BAA8B,uFAG3C;AAAA,SACF;AAAA,OACF;AAAA,IAGA,qBAAC,SAAI,WAAU,kCACb;AAAA,0BAAC,QAAG,WAAU,wCAAuC,wBAAU;AAAA,MAG/D,qBAAC,SAAI,WAAU,gCACb;AAAA,4BAAC,WAAM,WAAU,gCAA+B,SAAQ,eAAc,2BAEtE;AAAA,QACA;AAAA,UAAC;AAAA;AAAA,YACC,IAAG;AAAA,YACH,MAAK;AAAA,YACL,OAAO,MAAM,YAAY,gBAAgB;AAAA,YACzC,UAAU,OACR,iBAAiB,gBAAgB,EAAE,OAAO,SAAS,MAAS;AAAA,YAE9D,aAAY;AAAA;AAAA,QACd;AAAA,QACA,oBAAC,OAAE,WAAU,+BAA8B,wEAE3C;AAAA,SACF;AAAA,OACF;AAAA,KACF;AAEJ;AAEA,IAAO,8BAAQ;","names":[]}