@samuel-charpentier/sform 0.0.2 → 0.0.4

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.
@@ -132,8 +163,14 @@ export interface SformContext {
132
163
  resetFieldStates: () => void;
133
164
  /** Register a field name (called by Sfield on mount) */
134
165
  registerField: (name: string) => void;
166
+ /** Register a field that displays its own issues (all except hidden) */
167
+ registerFieldWithIssueDisplay: (name: string) => void;
135
168
  /** Programmatically submit the form */
136
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[];
137
174
  }
138
175
  /**
139
176
  * Enhance callback options - typed based on form Input type.
@@ -365,8 +402,10 @@ export type SfieldToggleProps = SfieldPropsFrom<ToggleInputProps, boolean, 'togg
365
402
  export type SfieldToggleOptionsProps = SfieldPropsFrom<ToggleOptionsInputProps, string, 'toggle-options'>;
366
403
  /** Props for masked input */
367
404
  export type SfieldMaskedProps = SfieldPropsFrom<MaskedInputProps, string, 'masked'>;
405
+ /** Props for hidden input */
406
+ export type SfieldHiddenProps = SfieldPropsFrom<HiddenInputProps, string, 'hidden'>;
368
407
  /** All possible Sfield props as a discriminated union */
369
- 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;
370
409
  /**
371
410
  * Extract props that match a specific field value type.
372
411
  * This ensures type="number" is only valid for number fields, etc.
@@ -515,7 +554,18 @@ export interface RadioInputProps extends BaseInputComponentProps {
515
554
  options: SelectOption[] | string[];
516
555
  }
517
556
  /**
518
- * 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
519
569
  */
520
570
  export interface ButtonFormState {
521
571
  /** Whether form is submitting */
@@ -527,11 +577,26 @@ export interface ButtonFormState {
527
577
  /** The form result if available */
528
578
  result: unknown;
529
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
+ }
530
592
  /**
531
593
  * Props for button input component
594
+ * @template T - The type of the form result (inferred from form prop)
532
595
  */
533
- export interface ButtonInputProps {
534
- /** 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) */
535
600
  label?: string;
536
601
  /** Button type */
537
602
  buttonType?: 'submit' | 'reset' | 'button';
@@ -539,16 +604,21 @@ export interface ButtonInputProps {
539
604
  class?: string;
540
605
  /** Whether button is disabled */
541
606
  disabled?: boolean;
542
- /** Snippet for default state */
543
- defaultState?: Snippet;
544
- /** Snippet for pending state */
545
- pendingState?: Snippet;
546
- /** Snippet for success state */
547
- successState?: Snippet;
548
- /** Snippet for error/issues state */
549
- errorState?: Snippet;
550
- /** Access to form state for custom rendering */
551
- 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;
552
622
  }
553
623
  /**
554
624
  * Props for range input component
@@ -612,3 +682,13 @@ export interface MaskedInputProps extends BaseInputComponentProps, InputAffixPro
612
682
  /** Whether to store the unmasked (raw) value. If true, stores '1234567890'. If false, stores '(123) 456-7890'. Default: true */
613
683
  unmaskValue?: boolean;
614
684
  }
685
+ /**
686
+ * Props for Fieldset utils component
687
+ */
688
+ export type FieldsetProps = {
689
+ label?: string;
690
+ labelClass?: string;
691
+ className?: string;
692
+ disabled?: boolean;
693
+ children: Snippet;
694
+ };
@@ -0,0 +1,24 @@
1
+ <script lang="ts">
2
+ import type { Snippet } from 'svelte';
3
+ import type { FieldsetProps } from '../types';
4
+
5
+ let { label, labelClass, className, disabled, children }: FieldsetProps = $props();
6
+ </script>
7
+
8
+ <fieldset class="sform-checkbox-group {className ?? ''}" {disabled}>
9
+ {#if label}
10
+ <legend class={labelClass}>{label}</legend>
11
+ {/if}
12
+ {@render children()}
13
+ </fieldset>
14
+
15
+ <style>
16
+ .sform-checkbox-group legend {
17
+ padding: 0.25rem 0.5rem;
18
+ margin: 0;
19
+ border: var(--sform-border-width) solid var(--sform-border);
20
+ background: var(--sform-bg);
21
+ border-radius: 0.25rem;
22
+ line-height: 1;
23
+ }
24
+ </style>
@@ -0,0 +1,4 @@
1
+ import type { FieldsetProps } from '../types';
2
+ declare const Fieldset: import("svelte").Component<FieldsetProps, {}, "">;
3
+ type Fieldset = ReturnType<typeof Fieldset>;
4
+ export default Fieldset;
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.2",
3
+ "version": "0.0.4",
4
4
  "description": "A Svelte 5 form library for SvelteKit remote forms",
5
5
  "keywords": [
6
6
  "svelte",