@privaty/ui-forms 0.3.3 → 0.4.1

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/README.md CHANGED
@@ -102,12 +102,25 @@ everywhere else.
102
102
  // Checkboxes: unchecked submits NOTHING — the schema supplies the false.
103
103
  inStock: v.optional(v.boolean(), false),
104
104
 
105
- // Placeholder selects: the disabled placeholder is skipped by submission —
106
- // default "" so YOUR message fires instead of a raw missing-key error.
105
+ // REQUIRED placeholder selects: the disabled prompt is skipped by
106
+ // submission — default "" so YOUR message fires instead of a raw
107
+ // missing-key error. (Optional selects are clearable instead: the empty
108
+ // pick SUBMITS "", so a plain v.optional(v.string()) receives it.)
107
109
  category: v.pipe(v.optional(v.string(), ""), v.picklist(categories, "required")),
108
110
 
109
- // Month inputs submit "YYYY-MM"; empty submits "".
110
- availableFrom: v.pipe(v.string(), v.nonEmpty("required"), v.regex(/^\d{4}-\d{2}$/, "invalid-month")),
111
+ // Optional selects, when the handler prefers undefined over "": FormData
112
+ // cannot carry an absent value from an enabled option, so normalize at
113
+ // the schema boundary — the designed seam for exactly this.
114
+ categoryId: v.optional(v.pipe(v.string(), v.transform((value) => value || undefined))),
115
+
116
+ // Month inputs submit "YYYY-MM"; empty submits "". Two v.check actions,
117
+ // not nonEmpty+regex: valibot pipes run every action even after a failure,
118
+ // so an empty value would show BOTH messages stacked.
119
+ availableFrom: v.pipe(
120
+ v.string(),
121
+ v.check((value) => value.length > 0, "required"),
122
+ v.check((value) => value === "" || /^\d{4}-\d{2}$/.test(value), "invalid-month"),
123
+ ),
111
124
  ```
112
125
 
113
126
  ## Rules learned the hard way
@@ -27,8 +27,11 @@ instead of disabling — a disabled select is excluded from FormData.
27
27
  * `{ value: s, label: s }`; values must be unique — they key the
28
28
  * rendered list. */
29
29
  options: readonly (string | SelectOption)[];
30
- /** Rendered as a disabled empty option, shown until a value is chosen.
31
- * Its presence also makes "" the default seed for an unseeded field. */
30
+ /** The empty option's label — a disabled prompt on required fields, a
31
+ * selectable "none" row on optional ones (see Select's `clearable`;
32
+ * this input derives it from `required`, which never becomes the
33
+ * native attribute). Its presence also makes "" the default seed for
34
+ * an unseeded field. */
32
35
  placeholder?: string;
33
36
 
34
37
  /** Label placement: "top" (default), "left", or "hidden" — floating is
@@ -139,6 +142,7 @@ instead of disabling — a disabled select is excluded from FormData.
139
142
  {labelStyle}
140
143
  {options}
141
144
  {placeholder}
145
+ clearable={!required}
142
146
  errors={wired.errors}
143
147
  marker={wired.marker}
144
148
  aria-invalid={wired.errors.length > 0 ? true : undefined}
@@ -11,8 +11,11 @@ interface Props {
11
11
  * `{ value: s, label: s }`; values must be unique — they key the
12
12
  * rendered list. */
13
13
  options: readonly (string | SelectOption)[];
14
- /** Rendered as a disabled empty option, shown until a value is chosen.
15
- * Its presence also makes "" the default seed for an unseeded field. */
14
+ /** The empty option's label — a disabled prompt on required fields, a
15
+ * selectable "none" row on optional ones (see Select's `clearable`;
16
+ * this input derives it from `required`, which never becomes the
17
+ * native attribute). Its presence also makes "" the default seed for
18
+ * an unseeded field. */
16
19
  placeholder?: string;
17
20
  /** Label placement: "top" (default), "left", or "hidden" — floating is
18
21
  * not offered for selects. */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@privaty/ui-forms",
3
- "version": "0.3.3",
3
+ "version": "0.4.1",
4
4
  "license": "Apache-2.0",
5
5
  "repository": {
6
6
  "type": "git",
@@ -39,25 +39,25 @@
39
39
  "access": "public"
40
40
  },
41
41
  "peerDependencies": {
42
+ "@privaty/ui": "^0.4.1",
42
43
  "@sveltejs/kit": "^3.0.0-next.0",
43
- "svelte": "^5.29.0",
44
- "@privaty/ui": "^0.3.3"
44
+ "svelte": "^5.29.0"
45
45
  },
46
46
  "dependencies": {
47
- "@lucide/svelte": "^1.34.0",
47
+ "@lucide/svelte": "^1.39.0",
48
48
  "@standard-schema/spec": "^1.1.0"
49
49
  },
50
50
  "devDependencies": {
51
+ "@config/eslint": "0.0.0",
52
+ "@config/typescript": "0.0.0",
53
+ "@privaty/ui": "0.4.1",
51
54
  "@sveltejs/kit": "3.0.0-next.25",
52
55
  "@sveltejs/package": "^2.5.8",
53
56
  "eslint": "^10.9.1",
54
- "publint": "^0.3.21",
55
- "svelte": "^5.56.10",
57
+ "publint": "^0.3.24",
58
+ "svelte": "^5.57.0",
56
59
  "svelte-check": "^4.7.6",
57
- "typescript": "^6.0.3",
58
- "@config/eslint": "0.0.0",
59
- "@privaty/ui": "0.3.3",
60
- "@config/typescript": "0.0.0"
60
+ "typescript": "^6.0.3"
61
61
  },
62
62
  "scripts": {
63
63
  "check": "svelte-check --tsconfig ./tsconfig.json",