@samuel-charpentier/sform 0.0.1 → 0.0.3

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.
@@ -82,7 +82,7 @@ export type AllowedSfieldType<T> = {
82
82
  */
83
83
  export type InputType = 'text' | 'email' | 'password' | 'number' | 'tel' | 'url' | 'search' | 'date' | 'datetime-local' | 'time' | 'month' | 'week' | 'color' | 'textarea' | 'select' | 'checkbox' | 'checkbox-group' | 'radio' | 'file' | 'hidden' | 'range' | 'toggle' | 'toggle-options' | 'masked';
84
84
  /** Text-like input types */
85
- export type TextInputType = 'text' | 'email' | 'tel' | 'url' | 'search' | 'date' | 'datetime-local' | 'time' | 'month' | 'week' | 'color' | 'file' | 'hidden';
85
+ export type TextInputType = 'text' | 'email' | 'tel' | 'url' | 'search' | 'date' | 'datetime-local' | 'time' | 'month' | 'week' | 'color' | 'file';
86
86
  /** Numeric input types */
87
87
  export type NumericInputType = 'number';
88
88
  /**
@@ -105,6 +105,37 @@ export type RemoteForm<Input extends RemoteFormInput | void = RemoteFormInput, O
105
105
  * The .for(id) method returns Omit<RemoteForm, 'for'>, so this type accepts both.
106
106
  */
107
107
  export type RemoteFormInstance<Input extends RemoteFormInput | void = RemoteFormInput, Output = unknown> = SvelteKitRemoteForm<Input, Output> | Omit<SvelteKitRemoteForm<Input, Output>, 'for'>;
108
+ /**
109
+ * Form state for buttons and state-aware components.
110
+ * State is derived from context - no result means default or pending,
111
+ * result with no issues means success, issues (via invalid()) means hasIssues.
112
+ * @template T - The type of the form result (defaults to unknown)
113
+ */
114
+ export type ButtonState<T = unknown> = {
115
+ state: 'default';
116
+ pending: false;
117
+ success: false;
118
+ hasIssues: false;
119
+ result: undefined;
120
+ } | {
121
+ state: 'pending';
122
+ pending: true;
123
+ success: false;
124
+ hasIssues: false;
125
+ result: undefined;
126
+ } | {
127
+ state: 'success';
128
+ pending: false;
129
+ success: true;
130
+ hasIssues: false;
131
+ result: T;
132
+ } | {
133
+ state: 'hasIssues';
134
+ pending: false;
135
+ success: false;
136
+ hasIssues: true;
137
+ result: undefined;
138
+ };
108
139
  /**
109
140
  * Form context provided by Sform to children.
110
141
  * Manages field state (touched, dirty, submitted) for validation display.
@@ -130,6 +161,16 @@ export interface SformContext {
130
161
  markAllFieldsDirty: () => void;
131
162
  /** Reset all field states (touched, dirty, submitted) */
132
163
  resetFieldStates: () => void;
164
+ /** Register a field name (called by Sfield on mount) */
165
+ registerField: (name: string) => void;
166
+ /** Register a field that displays its own issues (all except hidden) */
167
+ registerFieldWithIssueDisplay: (name: string) => void;
168
+ /** Programmatically submit the form */
169
+ submitForm: () => void;
170
+ /** Get the current form state (for buttons and state-aware components) */
171
+ getFormState: <T = unknown>() => ButtonState<T>;
172
+ /** Get issues that are not displayed by any Sfield component */
173
+ getUnhandledIssues: () => RemoteFormIssue[];
133
174
  }
134
175
  /**
135
176
  * Enhance callback options - typed based on form Input type.
@@ -361,8 +402,10 @@ export type SfieldToggleProps = SfieldPropsFrom<ToggleInputProps, boolean, 'togg
361
402
  export type SfieldToggleOptionsProps = SfieldPropsFrom<ToggleOptionsInputProps, string, 'toggle-options'>;
362
403
  /** Props for masked input */
363
404
  export type SfieldMaskedProps = SfieldPropsFrom<MaskedInputProps, string, 'masked'>;
405
+ /** Props for hidden input */
406
+ export type SfieldHiddenProps = SfieldPropsFrom<HiddenInputProps, string, 'hidden'>;
364
407
  /** All possible Sfield props as a discriminated union */
365
- type AllSfieldProps = SfieldTextProps | SfieldPasswordProps | SfieldNumberProps | SfieldTextareaProps | SfieldSelectProps | SfieldCheckboxProps | SfieldCheckboxGroupProps | SfieldRadioProps | SfieldRangeProps | SfieldToggleProps | SfieldToggleOptionsProps | SfieldMaskedProps;
408
+ type AllSfieldProps = SfieldTextProps | SfieldPasswordProps | SfieldNumberProps | SfieldTextareaProps | SfieldSelectProps | SfieldCheckboxProps | SfieldCheckboxGroupProps | SfieldRadioProps | SfieldRangeProps | SfieldToggleProps | SfieldToggleOptionsProps | SfieldMaskedProps | SfieldHiddenProps;
366
409
  /**
367
410
  * Extract props that match a specific field value type.
368
411
  * This ensures type="number" is only valid for number fields, etc.
@@ -511,7 +554,18 @@ export interface RadioInputProps extends BaseInputComponentProps {
511
554
  options: SelectOption[] | string[];
512
555
  }
513
556
  /**
514
- * Form state for button snippets
557
+ * Props for hidden input component
558
+ */
559
+ export interface HiddenInputProps {
560
+ /** Remote field from form */
561
+ field: RemoteFormField<RemoteFormFieldValue>;
562
+ /** Field name */
563
+ name: string;
564
+ /** The value for the hidden field - required for hidden inputs */
565
+ value?: string;
566
+ }
567
+ /**
568
+ * @deprecated Use ButtonState instead
515
569
  */
516
570
  export interface ButtonFormState {
517
571
  /** Whether form is submitting */
@@ -523,11 +577,26 @@ export interface ButtonFormState {
523
577
  /** The form result if available */
524
578
  result: unknown;
525
579
  }
580
+ /**
581
+ * Minimal form shape for Sbutton type inference.
582
+ * Allows the button to infer the result type T from the form.
583
+ */
584
+ export interface ButtonFormLike<T = unknown> {
585
+ result?: T;
586
+ pending?: number;
587
+ fields: {
588
+ allIssues?: () => RemoteFormIssue[] | undefined;
589
+ [key: string]: unknown;
590
+ };
591
+ }
526
592
  /**
527
593
  * Props for button input component
594
+ * @template T - The type of the form result (inferred from form prop)
528
595
  */
529
- export interface ButtonInputProps {
530
- /** Button text (used if no snippets provided) */
596
+ export interface ButtonInputProps<T = unknown> {
597
+ /** The remote form - used to infer the result type T */
598
+ form: ButtonFormLike<T>;
599
+ /** Button text (used if no children snippet provided) */
531
600
  label?: string;
532
601
  /** Button type */
533
602
  buttonType?: 'submit' | 'reset' | 'button';
@@ -535,16 +604,21 @@ export interface ButtonInputProps {
535
604
  class?: string;
536
605
  /** Whether button is disabled */
537
606
  disabled?: boolean;
538
- /** Snippet for default state */
539
- defaultState?: Snippet;
540
- /** Snippet for pending state */
541
- pendingState?: Snippet;
542
- /** Snippet for success state */
543
- successState?: Snippet;
544
- /** Snippet for error/issues state */
545
- errorState?: Snippet;
546
- /** Access to form state for custom rendering */
547
- formState?: ButtonFormState;
607
+ /** Children snippet receives ButtonState<T> for custom rendering with typed result */
608
+ children?: Snippet<[ButtonState<T>]>;
609
+ /** Callback that runs before validation/submission (can be async) */
610
+ onsubmit?: () => void | Promise<void>;
611
+ }
612
+ /**
613
+ * Props for SIssues component
614
+ */
615
+ export interface SIssuesProps {
616
+ /** General message shown when there are any issues */
617
+ message?: string | Snippet;
618
+ /** CSS class for the wrapper */
619
+ class?: string;
620
+ /** CSS class for the issues list */
621
+ listClass?: string;
548
622
  }
549
623
  /**
550
624
  * Props for range input component
package/dist/index.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- export { Sform, Sfield, Sbutton } from './Sform/index.js';
1
+ export { Sform, Sfield, Sbutton, SIssues, SResult } from './Sform/index.js';
2
2
  export type { ValidateOn, FieldState, SfieldClasses, InputType, SelectOption, SformContext, SformProps, ButtonFormState, ButtonInputProps, InputAffixProps, SfieldTypeMap, AllowedSfieldType, TypedSfieldProps, SfieldBaseProps, TypedBaseSfieldProps, SfieldTextProps, SfieldPasswordProps, SfieldNumberProps, SfieldTextareaProps, SfieldSelectProps, SfieldCheckboxProps, SfieldCheckboxGroupProps, SfieldRadioProps, SfieldRangeProps, SfieldToggleProps, SfieldToggleOptionsProps, SfieldMaskedProps, RemoteForm, RemoteFormField, RemoteFormFields, RemoteFormFieldValue, RemoteFormInput, RemoteFormIssue } from './Sform/index.js';
package/dist/index.js CHANGED
@@ -1 +1 @@
1
- export { Sform, Sfield, Sbutton } from './Sform/index.js';
1
+ export { Sform, Sfield, Sbutton, SIssues, SResult } from './Sform/index.js';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@samuel-charpentier/sform",
3
- "version": "0.0.1",
3
+ "version": "0.0.3",
4
4
  "description": "A Svelte 5 form library for SvelteKit remote forms",
5
5
  "keywords": [
6
6
  "svelte",
@@ -56,6 +56,7 @@
56
56
  "@sveltejs/vite-plugin-svelte": "^6.2.1",
57
57
  "@types/node": "^22",
58
58
  "@vitest/browser-playwright": "^4.0.15",
59
+ "@vitest/coverage-v8": "4.0.16",
59
60
  "eslint": "^9.39.1",
60
61
  "eslint-config-prettier": "^10.1.8",
61
62
  "eslint-plugin-svelte": "^3.13.1",