react-f0rm 0.2.2 → 0.3.0

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 (45) hide show
  1. package/README.md +527 -33
  2. package/dist/devtools/index.cjs.js +737 -0
  3. package/dist/devtools/index.cjs.js.map +1 -0
  4. package/dist/devtools/index.d.ts +33 -0
  5. package/dist/devtools/index.mjs +717 -0
  6. package/dist/devtools/index.mjs.map +1 -0
  7. package/dist/form-61297bc0.d.ts +578 -0
  8. package/dist/form-94c70b4b.mjs +378 -0
  9. package/dist/form-94c70b4b.mjs.map +1 -0
  10. package/dist/form-b9441d8c.cjs.js +387 -0
  11. package/dist/form-b9441d8c.cjs.js.map +1 -0
  12. package/dist/index.cjs.js +1257 -157
  13. package/dist/index.cjs.js.map +1 -1
  14. package/dist/index.d.ts +357 -53
  15. package/dist/index.mjs +1676 -0
  16. package/dist/index.mjs.map +1 -0
  17. package/dist/index.umd.js +1257 -157
  18. package/dist/index.umd.js.map +1 -1
  19. package/dist/index.umd.min.js +2 -2
  20. package/dist/index.umd.min.js.map +1 -1
  21. package/dist/resolvers/standard-schema.cjs.js +90 -0
  22. package/dist/resolvers/standard-schema.cjs.js.map +1 -0
  23. package/dist/resolvers/standard-schema.d.ts +66 -0
  24. package/dist/resolvers/standard-schema.mjs +86 -0
  25. package/dist/resolvers/standard-schema.mjs.map +1 -0
  26. package/dist/resolvers/yup.cjs.js +12 -2
  27. package/dist/resolvers/yup.cjs.js.map +1 -1
  28. package/dist/resolvers/yup.d.ts +2 -2
  29. package/dist/resolvers/yup.mjs +23 -0
  30. package/dist/resolvers/yup.mjs.map +1 -0
  31. package/dist/resolvers/zod.cjs.js +14 -1
  32. package/dist/resolvers/zod.cjs.js.map +1 -1
  33. package/dist/resolvers/zod.d.ts +2 -2
  34. package/dist/resolvers/zod.mjs +23 -0
  35. package/dist/resolvers/zod.mjs.map +1 -0
  36. package/dist/validate-148fe167.d.ts +22 -0
  37. package/package.json +34 -8
  38. package/dist/form-d06e6444.d.ts +0 -201
  39. package/dist/index.esm.js +0 -593
  40. package/dist/index.esm.js.map +0 -1
  41. package/dist/resolvers/yup.esm.js +0 -13
  42. package/dist/resolvers/yup.esm.js.map +0 -1
  43. package/dist/resolvers/zod.esm.js +0 -10
  44. package/dist/resolvers/zod.esm.js.map +0 -1
  45. package/dist/validate-0f17f86a.d.ts +0 -8
@@ -0,0 +1,717 @@
1
+ import * as React from 'react';
2
+ import React__default, { useCallback, useRef, createContext, useState, useContext, useId } from 'react';
3
+ import { o as on, g as getDirtyFields, a as getTouchedFields, b as getValues, c as getErrors, r as reset, t as trigger } from '../form-94c70b4b.mjs';
4
+
5
+ var shim = {exports: {}};
6
+
7
+ var useSyncExternalStoreShim_production = {};
8
+
9
+ /**
10
+ * @license React
11
+ * use-sync-external-store-shim.production.js
12
+ *
13
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
14
+ *
15
+ * This source code is licensed under the MIT license found in the
16
+ * LICENSE file in the root directory of this source tree.
17
+ */
18
+
19
+ var hasRequiredUseSyncExternalStoreShim_production;
20
+
21
+ function requireUseSyncExternalStoreShim_production () {
22
+ if (hasRequiredUseSyncExternalStoreShim_production) return useSyncExternalStoreShim_production;
23
+ hasRequiredUseSyncExternalStoreShim_production = 1;
24
+ var React = React__default;
25
+ function is(x, y) {
26
+ return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);
27
+ }
28
+ var objectIs = "function" === typeof Object.is ? Object.is : is,
29
+ useState = React.useState,
30
+ useEffect = React.useEffect,
31
+ useLayoutEffect = React.useLayoutEffect,
32
+ useDebugValue = React.useDebugValue;
33
+ function useSyncExternalStore$2(subscribe, getSnapshot) {
34
+ var value = getSnapshot(),
35
+ _useState = useState({ inst: { value: value, getSnapshot: getSnapshot } }),
36
+ inst = _useState[0].inst,
37
+ forceUpdate = _useState[1];
38
+ useLayoutEffect(
39
+ function () {
40
+ inst.value = value;
41
+ inst.getSnapshot = getSnapshot;
42
+ checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });
43
+ },
44
+ [subscribe, value, getSnapshot]
45
+ );
46
+ useEffect(
47
+ function () {
48
+ checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });
49
+ return subscribe(function () {
50
+ checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });
51
+ });
52
+ },
53
+ [subscribe]
54
+ );
55
+ useDebugValue(value);
56
+ return value;
57
+ }
58
+ function checkIfSnapshotChanged(inst) {
59
+ var latestGetSnapshot = inst.getSnapshot;
60
+ inst = inst.value;
61
+ try {
62
+ var nextValue = latestGetSnapshot();
63
+ return !objectIs(inst, nextValue);
64
+ } catch (error) {
65
+ return !0;
66
+ }
67
+ }
68
+ function useSyncExternalStore$1(subscribe, getSnapshot) {
69
+ return getSnapshot();
70
+ }
71
+ var shim =
72
+ "undefined" === typeof window ||
73
+ "undefined" === typeof window.document ||
74
+ "undefined" === typeof window.document.createElement
75
+ ? useSyncExternalStore$1
76
+ : useSyncExternalStore$2;
77
+ useSyncExternalStoreShim_production.useSyncExternalStore =
78
+ void 0 !== React.useSyncExternalStore ? React.useSyncExternalStore : shim;
79
+ return useSyncExternalStoreShim_production;
80
+ }
81
+
82
+ var useSyncExternalStoreShim_development = {};
83
+
84
+ /**
85
+ * @license React
86
+ * use-sync-external-store-shim.development.js
87
+ *
88
+ * Copyright (c) Meta Platforms, Inc. and affiliates.
89
+ *
90
+ * This source code is licensed under the MIT license found in the
91
+ * LICENSE file in the root directory of this source tree.
92
+ */
93
+
94
+ var hasRequiredUseSyncExternalStoreShim_development;
95
+
96
+ function requireUseSyncExternalStoreShim_development () {
97
+ if (hasRequiredUseSyncExternalStoreShim_development) return useSyncExternalStoreShim_development;
98
+ hasRequiredUseSyncExternalStoreShim_development = 1;
99
+ "production" !== process.env.NODE_ENV &&
100
+ (function () {
101
+ function is(x, y) {
102
+ return (x === y && (0 !== x || 1 / x === 1 / y)) || (x !== x && y !== y);
103
+ }
104
+ function useSyncExternalStore$2(subscribe, getSnapshot) {
105
+ didWarnOld18Alpha ||
106
+ void 0 === React.startTransition ||
107
+ ((didWarnOld18Alpha = !0),
108
+ console.error(
109
+ "You are using an outdated, pre-release alpha of React 18 that does not support useSyncExternalStore. The use-sync-external-store shim will not work correctly. Upgrade to a newer pre-release."
110
+ ));
111
+ var value = getSnapshot();
112
+ if (!didWarnUncachedGetSnapshot) {
113
+ var cachedValue = getSnapshot();
114
+ objectIs(value, cachedValue) ||
115
+ (console.error(
116
+ "The result of getSnapshot should be cached to avoid an infinite loop"
117
+ ),
118
+ (didWarnUncachedGetSnapshot = !0));
119
+ }
120
+ cachedValue = useState({
121
+ inst: { value: value, getSnapshot: getSnapshot }
122
+ });
123
+ var inst = cachedValue[0].inst,
124
+ forceUpdate = cachedValue[1];
125
+ useLayoutEffect(
126
+ function () {
127
+ inst.value = value;
128
+ inst.getSnapshot = getSnapshot;
129
+ checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });
130
+ },
131
+ [subscribe, value, getSnapshot]
132
+ );
133
+ useEffect(
134
+ function () {
135
+ checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });
136
+ return subscribe(function () {
137
+ checkIfSnapshotChanged(inst) && forceUpdate({ inst: inst });
138
+ });
139
+ },
140
+ [subscribe]
141
+ );
142
+ useDebugValue(value);
143
+ return value;
144
+ }
145
+ function checkIfSnapshotChanged(inst) {
146
+ var latestGetSnapshot = inst.getSnapshot;
147
+ inst = inst.value;
148
+ try {
149
+ var nextValue = latestGetSnapshot();
150
+ return !objectIs(inst, nextValue);
151
+ } catch (error) {
152
+ return !0;
153
+ }
154
+ }
155
+ function useSyncExternalStore$1(subscribe, getSnapshot) {
156
+ return getSnapshot();
157
+ }
158
+ "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
159
+ "function" ===
160
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart &&
161
+ __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStart(Error());
162
+ var React = React__default,
163
+ objectIs = "function" === typeof Object.is ? Object.is : is,
164
+ useState = React.useState,
165
+ useEffect = React.useEffect,
166
+ useLayoutEffect = React.useLayoutEffect,
167
+ useDebugValue = React.useDebugValue,
168
+ didWarnOld18Alpha = !1,
169
+ didWarnUncachedGetSnapshot = !1,
170
+ shim =
171
+ "undefined" === typeof window ||
172
+ "undefined" === typeof window.document ||
173
+ "undefined" === typeof window.document.createElement
174
+ ? useSyncExternalStore$1
175
+ : useSyncExternalStore$2;
176
+ useSyncExternalStoreShim_development.useSyncExternalStore =
177
+ void 0 !== React.useSyncExternalStore ? React.useSyncExternalStore : shim;
178
+ "undefined" !== typeof __REACT_DEVTOOLS_GLOBAL_HOOK__ &&
179
+ "function" ===
180
+ typeof __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop &&
181
+ __REACT_DEVTOOLS_GLOBAL_HOOK__.registerInternalModuleStop(Error());
182
+ })();
183
+ return useSyncExternalStoreShim_development;
184
+ }
185
+
186
+ if (process.env.NODE_ENV === 'production') {
187
+ shim.exports = requireUseSyncExternalStoreShim_production();
188
+ } else {
189
+ shim.exports = requireUseSyncExternalStoreShim_development();
190
+ }
191
+
192
+ var shimExports = shim.exports;
193
+
194
+ function useWatchCore(subscribeFactory, getter) {
195
+ const cacheRef = useRef(null);
196
+ if (cacheRef.current === null) cacheRef.current = { hasValue: false };
197
+ const cache = cacheRef.current;
198
+ const getterRef = useRef(getter);
199
+ getterRef.current = getter;
200
+ const getSnapshot = useCallback(() => {
201
+ if (!cache.hasValue) {
202
+ cache.value = getterRef.current();
203
+ cache.hasValue = true;
204
+ }
205
+ return cache.value;
206
+ }, [cache]);
207
+ const subscribe = useCallback(
208
+ (notify) => {
209
+ cache.hasValue = false;
210
+ const invalidate = () => {
211
+ cache.hasValue = false;
212
+ notify();
213
+ };
214
+ return subscribeFactory(invalidate);
215
+ },
216
+ [subscribeFactory, cache]
217
+ );
218
+ return shimExports.useSyncExternalStore(subscribe, getSnapshot, getSnapshot);
219
+ }
220
+ function useWatch(emitter, event, getter) {
221
+ const subscribeFactory = useCallback(
222
+ (invalidate) => on(emitter, event, invalidate),
223
+ [emitter, event]
224
+ );
225
+ return useWatchCore(subscribeFactory, getter);
226
+ }
227
+ function useDirtyFields(form) {
228
+ return useWatch(form.emitter, "change", getDirtyFields.bind(null, form));
229
+ }
230
+ function useTouchedFields(form) {
231
+ return useWatch(form.emitter, "touched", getTouchedFields.bind(null, form));
232
+ }
233
+ function useIsSubmitting(form) {
234
+ return useWatch(form.emitter, "submitting", () => form.isSubmitting);
235
+ }
236
+ function useSubmitCount(form) {
237
+ return useWatch(form.emitter, "submitCount", () => form.submitCount);
238
+ }
239
+
240
+ const FormContext = createContext(null);
241
+ FormContext.Provider;
242
+ const CheckboxGroupContext = createContext(null);
243
+ CheckboxGroupContext.Provider;
244
+
245
+ const DEFAULT_OPEN_DEPTH = 1;
246
+ function JsonNode({ name, value, depth = 0 }) {
247
+ const [open, setOpen] = useState(depth <= DEFAULT_OPEN_DEPTH);
248
+ const label = name === void 0 ? null : /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-key" }, String(name)), /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-punct" }, ": "));
249
+ if (value !== null && typeof value === "object") {
250
+ const isArray = Array.isArray(value);
251
+ const entries = isArray ? value.map((v, i) => [i, v]) : Object.entries(value);
252
+ const openBracket = isArray ? "[" : "{";
253
+ const closeBracket = isArray ? "]" : "}";
254
+ const summary = open ? "" : `${openBracket}\u2026${closeBracket} ${entries.length}`;
255
+ return /* @__PURE__ */ React.createElement("div", { className: "rf0-dt-row", style: { paddingLeft: depth * 12 } }, /* @__PURE__ */ React.createElement(
256
+ "button",
257
+ {
258
+ type: "button",
259
+ className: "rf0-dt-node-toggle",
260
+ "aria-expanded": open,
261
+ onClick: () => setOpen(!open)
262
+ },
263
+ /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-caret" }, open ? "\u25BE" : "\u25B8"),
264
+ label,
265
+ /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-punct" }, open ? openBracket : summary)
266
+ ), open && /* @__PURE__ */ React.createElement(React.Fragment, null, entries.map(([k, v]) => /* @__PURE__ */ React.createElement(JsonNode, { key: String(k), name: k, value: v, depth: depth + 1 })), /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-punct", style: { paddingLeft: depth * 12 } }, closeBracket)));
267
+ }
268
+ return /* @__PURE__ */ React.createElement(
269
+ "span",
270
+ {
271
+ className: "rf0-dt-row",
272
+ style: { paddingLeft: depth * 12, display: "block" }
273
+ },
274
+ label,
275
+ /* @__PURE__ */ React.createElement(Primitive, { value })
276
+ );
277
+ }
278
+ function Primitive({ value }) {
279
+ if (value === void 0)
280
+ return /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-null" }, "undefined");
281
+ if (value === null) return /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-null" }, "null");
282
+ if (typeof value === "string")
283
+ return /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-string" }, '"', value, '"');
284
+ if (typeof value === "boolean")
285
+ return /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-boolean" }, String(value));
286
+ return /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-number" }, String(value));
287
+ }
288
+
289
+ const CSS = `
290
+ .rf0-dt {
291
+ position: fixed;
292
+ z-index: 2147483000;
293
+ box-sizing: border-box;
294
+ width: 308px;
295
+ max-width: calc(100vw - 16px);
296
+ max-height: min(70vh, 560px);
297
+ display: flex;
298
+ flex-direction: column;
299
+ font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',
300
+ Menlo, Consolas, 'Liberation Mono', monospace;
301
+ font-size: 11px;
302
+ line-height: 1.45;
303
+ color: #c7d0dc;
304
+ background: #0c1017;
305
+ border: 1px solid #1f2735;
306
+ border-radius: 4px;
307
+ box-shadow: 0 12px 32px rgba(0, 0, 0, 0.55), 0 0 0 1px rgba(0, 0, 0, 0.4);
308
+ }
309
+ .rf0-dt *,
310
+ .rf0-dt-badge * {
311
+ box-sizing: border-box;
312
+ }
313
+ .rf0-dt--top-right { top: 8px; right: 8px; }
314
+ .rf0-dt--bottom-right { bottom: 8px; right: 8px; }
315
+ .rf0-dt--top-left { top: 8px; left: 8px; }
316
+ .rf0-dt--bottom-left { bottom: 8px; left: 8px; }
317
+
318
+ /* ---- header -------------------------------------------------------- */
319
+ .rf0-dt-header {
320
+ display: flex;
321
+ align-items: center;
322
+ gap: 6px;
323
+ padding: 5px 6px 5px 9px;
324
+ border-bottom: 1px solid #1f2735;
325
+ background: #10151e;
326
+ }
327
+ .rf0-dt-title {
328
+ flex: 1;
329
+ min-width: 0;
330
+ color: #8b96a5;
331
+ font-size: 10px;
332
+ font-weight: 700;
333
+ letter-spacing: 0.14em;
334
+ text-transform: uppercase;
335
+ white-space: nowrap;
336
+ overflow: hidden;
337
+ text-overflow: ellipsis;
338
+ }
339
+ .rf0-dt-title::before {
340
+ content: '';
341
+ display: inline-block;
342
+ width: 6px;
343
+ height: 6px;
344
+ margin-right: 6px;
345
+ border-radius: 50%;
346
+ background: #e2b93b;
347
+ vertical-align: 1px;
348
+ }
349
+ .rf0-dt-headerbtn {
350
+ border: 1px solid transparent;
351
+ border-radius: 3px;
352
+ padding: 1px 5px;
353
+ color: #8b96a5;
354
+ background: transparent;
355
+ font: inherit;
356
+ font-size: 10px;
357
+ cursor: pointer;
358
+ }
359
+ .rf0-dt-headerbtn:hover { color: #dce3ec; border-color: #2a3547; }
360
+ .rf0-dt-headerbtn:focus-visible,
361
+ .rf0-dt-tab:focus-visible,
362
+ .rf0-dt-action:focus-visible,
363
+ .rf0-dt-badge:focus-visible,
364
+ .rf0-dt-node-toggle:focus-visible {
365
+ outline: 1px solid #e2b93b;
366
+ outline-offset: 1px;
367
+ }
368
+
369
+ /* ---- tabs ---------------------------------------------------------- */
370
+ .rf0-dt-tablist {
371
+ display: flex;
372
+ border-bottom: 1px solid #1f2735;
373
+ background: #0e131b;
374
+ }
375
+ .rf0-dt-tab {
376
+ flex: 1;
377
+ padding: 4px 2px 5px;
378
+ border: 0;
379
+ border-bottom: 2px solid transparent;
380
+ background: transparent;
381
+ color: #6b7686;
382
+ font: inherit;
383
+ font-size: 10px;
384
+ letter-spacing: 0.1em;
385
+ text-transform: uppercase;
386
+ cursor: pointer;
387
+ white-space: nowrap;
388
+ }
389
+ .rf0-dt-tab:hover { color: #aab5c4; }
390
+ .rf0-dt-tab[aria-selected='true'] {
391
+ color: #e7edf4;
392
+ border-bottom-color: #e2b93b;
393
+ }
394
+ .rf0-dt-tab-count {
395
+ margin-left: 3px;
396
+ color: inherit;
397
+ opacity: 0.75;
398
+ }
399
+ .rf0-dt-tab--danger[aria-selected='true'] {
400
+ border-bottom-color: #f0647c;
401
+ }
402
+
403
+ /* ---- panels -------------------------------------------------------- */
404
+ .rf0-dt-panel {
405
+ flex: 1;
406
+ min-height: 84px;
407
+ overflow: auto;
408
+ padding: 6px 8px;
409
+ scrollbar-width: thin;
410
+ scrollbar-color: #2a3547 transparent;
411
+ }
412
+ .rf0-dt-empty {
413
+ padding: 10px 2px;
414
+ color: #4d5766;
415
+ font-style: italic;
416
+ }
417
+
418
+ /* json tree */
419
+ .rf0-dt-row {
420
+ display: block;
421
+ white-space: pre;
422
+ tab-size: 2;
423
+ }
424
+ .rf0-dt-node-toggle {
425
+ border: 0;
426
+ padding: 0;
427
+ background: transparent;
428
+ color: inherit;
429
+ font: inherit;
430
+ text-align: left;
431
+ cursor: pointer;
432
+ white-space: pre;
433
+ }
434
+ .rf0-dt-node-toggle:hover .rf0-dt-key { color: #dce3ec; }
435
+ .rf0-dt-caret {
436
+ display: inline-block;
437
+ width: 1.2em;
438
+ color: #4d5766;
439
+ }
440
+ .rf0-dt-key { color: #8b96a5; }
441
+ .rf0-dt-punct { color: #4d5766; }
442
+ .rf0-dt-string { color: #8fd68a; }
443
+ .rf0-dt-number { color: #e2b93b; }
444
+ .rf0-dt-boolean { color: #6fb3d9; }
445
+ .rf0-dt-null { color: #55607080; font-style: italic; }
446
+
447
+ /* errors / touched / dirty lists */
448
+ .rf0-dt-item {
449
+ padding: 3px 2px;
450
+ border-bottom: 1px dotted #1a2230;
451
+ display: flex;
452
+ gap: 8px;
453
+ align-items: baseline;
454
+ }
455
+ .rf0-dt-item:last-child { border-bottom: 0; }
456
+ .rf0-dt-item-path {
457
+ color: #aab5c4;
458
+ word-break: break-all;
459
+ }
460
+ .rf0-dt-item-msg {
461
+ color: #f0647c;
462
+ word-break: break-word;
463
+ }
464
+ .rf0-dt-item-msg--ok { color: #8fd68a; }
465
+ .rf0-dt-item-tag {
466
+ flex: none;
467
+ color: #4d5766;
468
+ font-size: 10px;
469
+ }
470
+ .rf0-dt-item--touched .rf0-dt-item-path { color: #6fb3d9; }
471
+ .rf0-dt-item--dirty .rf0-dt-item-path { color: #e2b93b; }
472
+
473
+ /* ---- submit status + actions --------------------------------------- */
474
+ .rf0-dt-status {
475
+ display: flex;
476
+ gap: 10px;
477
+ padding: 4px 9px;
478
+ border-top: 1px solid #1f2735;
479
+ background: #10151e;
480
+ color: #6b7686;
481
+ font-size: 10px;
482
+ letter-spacing: 0.04em;
483
+ white-space: nowrap;
484
+ overflow: hidden;
485
+ }
486
+ .rf0-dt-status b { color: #aab5c4; font-weight: 400; }
487
+ .rf0-dt-status .rf0-dt-on { color: #e2b93b; }
488
+ .rf0-dt-status .rf0-dt-ok { color: #8fd68a; }
489
+ .rf0-dt-status .rf0-dt-err { color: #f0647c; }
490
+ .rf0-dt-actions {
491
+ display: flex;
492
+ gap: 6px;
493
+ padding: 6px 8px;
494
+ border-top: 1px solid #1f2735;
495
+ background: #10151e;
496
+ }
497
+ .rf0-dt-action {
498
+ flex: 1;
499
+ padding: 3px 0;
500
+ border: 1px solid #2a3547;
501
+ border-radius: 3px;
502
+ background: #151b26;
503
+ color: #c7d0dc;
504
+ font: inherit;
505
+ font-size: 10px;
506
+ letter-spacing: 0.1em;
507
+ text-transform: uppercase;
508
+ cursor: pointer;
509
+ }
510
+ .rf0-dt-action:hover { border-color: #3b4a61; background: #1a2230; color: #e7edf4; }
511
+ .rf0-dt-action:active { transform: translateY(1px); }
512
+
513
+ /* ---- collapsed badge ----------------------------------------------- */
514
+ .rf0-dt-badge {
515
+ position: fixed;
516
+ z-index: 2147483000;
517
+ width: 26px;
518
+ height: 26px;
519
+ border: 1px solid #2a3547;
520
+ border-radius: 50%;
521
+ background: #0c1017;
522
+ color: #e2b93b;
523
+ font: inherit;
524
+ font-family: ui-monospace, 'SF Mono', 'Cascadia Code', 'JetBrains Mono',
525
+ Menlo, Consolas, 'Liberation Mono', monospace;
526
+ font-size: 10px;
527
+ font-weight: 700;
528
+ letter-spacing: 0.02em;
529
+ cursor: pointer;
530
+ box-shadow: 0 4px 14px rgba(0, 0, 0, 0.5);
531
+ }
532
+ .rf0-dt-badge:hover { border-color: #e2b93b; }
533
+ .rf0-dt-badge--top-right { top: 10px; right: 10px; }
534
+ .rf0-dt-badge--bottom-right { bottom: 10px; right: 10px; }
535
+ .rf0-dt-badge--top-left { top: 10px; left: 10px; }
536
+ .rf0-dt-badge--bottom-left { bottom: 10px; left: 10px; }
537
+ .rf0-dt-badge .rf0-dt-dot {
538
+ position: absolute;
539
+ top: 3px;
540
+ right: 3px;
541
+ width: 5px;
542
+ height: 5px;
543
+ border-radius: 50%;
544
+ background: #f0647c;
545
+ display: none;
546
+ }
547
+ .rf0-dt-badge--has-errors .rf0-dt-dot { display: block; }
548
+ `;
549
+ const STYLE_ID = "react-f0rm-devtools-style";
550
+ function injectDevtoolsStyles() {
551
+ if (typeof document === "undefined") return;
552
+ if (document.getElementById(STYLE_ID)) return;
553
+ const style = document.createElement("style");
554
+ style.id = STYLE_ID;
555
+ style.textContent = CSS;
556
+ document.head.appendChild(style);
557
+ }
558
+ injectDevtoolsStyles();
559
+
560
+ const TABS = ["values", "errors", "touched", "dirty"];
561
+ function submitStatusClass(isSubmitSuccessful) {
562
+ if (isSubmitSuccessful === void 0) return void 0;
563
+ return isSubmitSuccessful ? "rf0-dt-ok" : "rf0-dt-err";
564
+ }
565
+ function countLeaves(value) {
566
+ if (value === null || typeof value !== "object") return 1;
567
+ let count = 0;
568
+ for (const v of Object.values(value)) {
569
+ count += countLeaves(v);
570
+ }
571
+ return count;
572
+ }
573
+ function Devtools({
574
+ form,
575
+ position = "top-right"
576
+ }) {
577
+ const contextForm = useContext(FormContext);
578
+ const f = form ?? contextForm;
579
+ if (!f) {
580
+ throw new Error(
581
+ "<Devtools> needs a form: pass the `form` prop or render it inside a <Form> / FormProvider."
582
+ );
583
+ }
584
+ const [open, setOpen] = useState(true);
585
+ const [tab, setTab] = useState("values");
586
+ const idPrefix = useId().replace(/[^a-zA-Z0-9-]/g, "");
587
+ const values = useWatch(f.emitter, "change", getValues.bind(null, f));
588
+ const errors = useWatch(
589
+ f.emitter,
590
+ "errors",
591
+ getErrors.bind(null, f)
592
+ );
593
+ const touched = useTouchedFields(f);
594
+ const dirty = useDirtyFields(f);
595
+ const isSubmitting = useIsSubmitting(f);
596
+ const submitCount = useSubmitCount(f);
597
+ const isSubmitSuccessful = useWatch(
598
+ f.emitter,
599
+ "submitSuccessful",
600
+ () => f.isSubmitSuccessful
601
+ );
602
+ if (!open) {
603
+ return /* @__PURE__ */ React.createElement(
604
+ "button",
605
+ {
606
+ type: "button",
607
+ className: `rf0-dt-badge rf0-dt-badge--${position}${errors.length > 0 ? " rf0-dt-badge--has-errors" : ""}`,
608
+ "aria-expanded": false,
609
+ "aria-label": `Open react-f0rm devtools (${errors.length} errors)`,
610
+ onClick: () => setOpen(true)
611
+ },
612
+ "f0",
613
+ /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-dot" })
614
+ );
615
+ }
616
+ const counts = {
617
+ values: countLeaves(values),
618
+ errors: errors.length,
619
+ touched: touched.length,
620
+ dirty: Object.keys(dirty).length
621
+ };
622
+ const onTabKeyDown = (e) => {
623
+ const deltas = {
624
+ ArrowRight: 1,
625
+ ArrowLeft: -1
626
+ };
627
+ const delta = deltas[e.key];
628
+ if (!delta) return;
629
+ e.preventDefault();
630
+ const next = TABS[(TABS.indexOf(tab) + delta + TABS.length) % TABS.length];
631
+ setTab(next);
632
+ document.getElementById(`${idPrefix}-tab-${next}`)?.focus();
633
+ };
634
+ return /* @__PURE__ */ React.createElement(
635
+ "section",
636
+ {
637
+ className: `rf0-dt rf0-dt--${position}`,
638
+ "aria-label": "react-f0rm devtools"
639
+ },
640
+ /* @__PURE__ */ React.createElement("header", { className: "rf0-dt-header" }, /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-title" }, "react-f0rm"), /* @__PURE__ */ React.createElement(
641
+ "button",
642
+ {
643
+ type: "button",
644
+ className: "rf0-dt-headerbtn",
645
+ "aria-label": "Collapse devtools",
646
+ onClick: () => setOpen(false)
647
+ },
648
+ "\u2013"
649
+ )),
650
+ /* @__PURE__ */ React.createElement(
651
+ "div",
652
+ {
653
+ className: "rf0-dt-tablist",
654
+ role: "tablist",
655
+ "aria-label": "Form state",
656
+ tabIndex: -1,
657
+ onKeyDown: onTabKeyDown
658
+ },
659
+ TABS.map((id) => /* @__PURE__ */ React.createElement(
660
+ "button",
661
+ {
662
+ key: id,
663
+ id: `${idPrefix}-tab-${id}`,
664
+ type: "button",
665
+ role: "tab",
666
+ className: `rf0-dt-tab${id === "errors" ? " rf0-dt-tab--danger" : ""}`,
667
+ "aria-selected": tab === id,
668
+ "aria-controls": `${idPrefix}-panel-${id}`,
669
+ tabIndex: tab === id ? 0 : -1,
670
+ onClick: () => setTab(id)
671
+ },
672
+ id,
673
+ /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-tab-count" }, counts[id])
674
+ ))
675
+ ),
676
+ /* @__PURE__ */ React.createElement(
677
+ "div",
678
+ {
679
+ id: `${idPrefix}-panel-${tab}`,
680
+ role: "tabpanel",
681
+ "aria-labelledby": `${idPrefix}-tab-${tab}`,
682
+ className: "rf0-dt-panel"
683
+ },
684
+ tab === "values" && /* @__PURE__ */ React.createElement(JsonNode, { value: values }),
685
+ tab === "errors" && (errors.length === 0 ? /* @__PURE__ */ React.createElement("p", { className: "rf0-dt-empty" }, "no errors") : errors.map(({ path, type, message }, index) => (
686
+ // Same path can hold several errors now; index keeps keys
687
+ // unique without changing what is rendered (messages may
688
+ // legitimately repeat for one path).
689
+ // eslint-disable-next-line react/no-array-index-key
690
+ /* @__PURE__ */ React.createElement("div", { key: `${path}:${index}`, className: "rf0-dt-item" }, /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-item-path" }, path), /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-item-msg" }, message), /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-item-tag" }, type))
691
+ ))),
692
+ tab === "touched" && (touched.length === 0 ? /* @__PURE__ */ React.createElement("p", { className: "rf0-dt-empty" }, "no touched fields") : touched.map((path) => /* @__PURE__ */ React.createElement("div", { key: path, className: "rf0-dt-item rf0-dt-item--touched" }, /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-item-path" }, path)))),
693
+ tab === "dirty" && (Object.keys(dirty).length === 0 ? /* @__PURE__ */ React.createElement("p", { className: "rf0-dt-empty" }, "no dirty fields") : Object.keys(dirty).map((path) => /* @__PURE__ */ React.createElement("div", { key: path, className: "rf0-dt-item rf0-dt-item--dirty" }, /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-item-path" }, path), /* @__PURE__ */ React.createElement("span", { className: "rf0-dt-item-msg rf0-dt-item-msg--ok" }, "changed"))))
694
+ ),
695
+ /* @__PURE__ */ React.createElement("p", { className: "rf0-dt-status", "aria-live": "polite" }, /* @__PURE__ */ React.createElement("span", { className: isSubmitting ? "rf0-dt-on" : void 0 }, "submitting ", /* @__PURE__ */ React.createElement("b", null, String(isSubmitting))), /* @__PURE__ */ React.createElement("span", null, "submits ", /* @__PURE__ */ React.createElement("b", null, submitCount)), /* @__PURE__ */ React.createElement("span", { className: submitStatusClass(isSubmitSuccessful) }, "ok", " ", /* @__PURE__ */ React.createElement("b", null, isSubmitSuccessful === void 0 ? "\u2013" : String(isSubmitSuccessful)))),
696
+ /* @__PURE__ */ React.createElement("div", { className: "rf0-dt-actions" }, /* @__PURE__ */ React.createElement(
697
+ "button",
698
+ {
699
+ type: "button",
700
+ className: "rf0-dt-action",
701
+ onClick: () => reset(f, f.initialValues)
702
+ },
703
+ "Reset"
704
+ ), /* @__PURE__ */ React.createElement(
705
+ "button",
706
+ {
707
+ type: "button",
708
+ className: "rf0-dt-action",
709
+ onClick: () => trigger(f)
710
+ },
711
+ "Validate"
712
+ ))
713
+ );
714
+ }
715
+
716
+ export { Devtools };
717
+ //# sourceMappingURL=index.mjs.map