coles-solid-library 0.3.7 → 0.3.8
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.
|
@@ -14,6 +14,11 @@ export declare class FormGroup<T extends object> {
|
|
|
14
14
|
private meta;
|
|
15
15
|
private keys;
|
|
16
16
|
constructor(data: FormGroupData<T>);
|
|
17
|
+
/**
|
|
18
|
+
* INTERNAL: returns the reactive internal store reference (DO NOT MUTATE outside FormGroup).
|
|
19
|
+
* Used for bridging into FormContext without triggering cloning loops.
|
|
20
|
+
*/
|
|
21
|
+
_unsafeRaw(): T;
|
|
17
22
|
/**
|
|
18
23
|
* Gets the current form data or the value of a specific control.
|
|
19
24
|
*
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { Accessor, Component, JSX, Setter } from "solid-js";
|
|
2
2
|
interface Props extends JSX.TextareaHTMLAttributes<HTMLTextAreaElement> {
|
|
3
|
-
|
|
4
|
-
|
|
3
|
+
/** Optional external accessor (legacy). If omitted and inside a FormField+Form, form data is used. */
|
|
4
|
+
text?: Accessor<string>;
|
|
5
|
+
/** Optional external setter (legacy). */
|
|
6
|
+
setText?: Setter<string>;
|
|
5
7
|
class?: string;
|
|
6
8
|
tooltip?: string;
|
|
7
9
|
transparent?: boolean;
|
package/dist/index.esm.js
CHANGED
|
@@ -1441,6 +1441,17 @@ const FormInner = props => {
|
|
|
1441
1441
|
const Provider = props => {
|
|
1442
1442
|
const defaultData = props.value ?? {};
|
|
1443
1443
|
const [data, setData] = createStore(defaultData);
|
|
1444
|
+
// Bridge: keep FormContext store in sync with underlying FormGroup reactive store
|
|
1445
|
+
// This allows programmatic calls to formGroup.set(...) to propagate into bound fields.
|
|
1446
|
+
createEffect(() => {
|
|
1447
|
+
const raw = props.formGroup._unsafeRaw(); // reactive read of underlying store
|
|
1448
|
+
for (const k in raw) {
|
|
1449
|
+
const nextVal = raw[k];
|
|
1450
|
+
if (data[k] !== nextVal) {
|
|
1451
|
+
setData(k, nextVal);
|
|
1452
|
+
}
|
|
1453
|
+
}
|
|
1454
|
+
});
|
|
1444
1455
|
return createComponent(FormContext.Provider, {
|
|
1445
1456
|
get value() {
|
|
1446
1457
|
return {
|
|
@@ -1588,23 +1599,52 @@ styleInject(css_248z$d);
|
|
|
1588
1599
|
var _tmpl$$h = /*#__PURE__*/template(`<label><input type=checkbox><span>`),
|
|
1589
1600
|
_tmpl$2$9 = /*#__PURE__*/template(`<span>`);
|
|
1590
1601
|
function Checkbox(props) {
|
|
1591
|
-
|
|
1602
|
+
const field = useFormProvider();
|
|
1603
|
+
const formCtx = useFormContext();
|
|
1604
|
+
const formName = field?.formName;
|
|
1605
|
+
// Internal state for uncontrolled usage outside form context
|
|
1592
1606
|
const [internalChecked, setInternalChecked] = createSignal(props.defaultChecked ?? false);
|
|
1593
|
-
|
|
1607
|
+
// Derive current checked state with priority: controlled prop -> form context -> field local value -> internal state
|
|
1608
|
+
const checkedState = createMemo(() => {
|
|
1609
|
+
if (props.checked !== undefined) return !!props.checked;
|
|
1610
|
+
if (formName && formCtx?.data) return !!formCtx.data[formName];
|
|
1611
|
+
if (field?.getValue) return !!field.getValue();
|
|
1612
|
+
return internalChecked();
|
|
1613
|
+
});
|
|
1614
|
+
// Keep field/form floating states in sync if programmatic changes occur
|
|
1615
|
+
createEffect(() => {
|
|
1616
|
+
const c = checkedState();
|
|
1617
|
+
if (formName && formCtx?.data) {
|
|
1618
|
+
field?.setTextInside?.(c);
|
|
1619
|
+
field?.setValue?.(c);
|
|
1620
|
+
}
|
|
1621
|
+
});
|
|
1622
|
+
const commitValue = next => {
|
|
1623
|
+
if (props.checked === undefined) {
|
|
1624
|
+
if (formName && formCtx?.formGroup) {
|
|
1625
|
+
formCtx.formGroup.set(formName, next);
|
|
1626
|
+
formCtx.setData?.(formName, next);
|
|
1627
|
+
} else if (field?.setValue) {
|
|
1628
|
+
field.setValue(next);
|
|
1629
|
+
} else {
|
|
1630
|
+
setInternalChecked(next);
|
|
1631
|
+
}
|
|
1632
|
+
}
|
|
1633
|
+
field?.setTextInside?.(next);
|
|
1634
|
+
props.onChange?.(next);
|
|
1635
|
+
};
|
|
1594
1636
|
const handleChange = e => {
|
|
1595
1637
|
const target = e.currentTarget;
|
|
1596
1638
|
const newChecked = target.checked;
|
|
1597
|
-
if (props.checked
|
|
1598
|
-
|
|
1599
|
-
props.onChange?.(newChecked);
|
|
1600
|
-
} else {
|
|
1601
|
-
// Controlled: prevent native state flip from persisting
|
|
1639
|
+
if (props.checked !== undefined) {
|
|
1640
|
+
// controlled: revert DOM change and just emit
|
|
1602
1641
|
e.preventDefault();
|
|
1603
|
-
// Re-sync DOM to prop (in case browser already toggled visually before preventDefault took effect)
|
|
1604
1642
|
queueMicrotask(() => {
|
|
1605
1643
|
target.checked = !!props.checked;
|
|
1606
1644
|
});
|
|
1607
1645
|
props.onChange?.(newChecked);
|
|
1646
|
+
} else {
|
|
1647
|
+
commitValue(newChecked);
|
|
1608
1648
|
}
|
|
1609
1649
|
};
|
|
1610
1650
|
const handleClick = e => {
|
|
@@ -2006,6 +2046,9 @@ function Select(props) {
|
|
|
2006
2046
|
}
|
|
2007
2047
|
// store orientation for class assignment
|
|
2008
2048
|
setDropTop(placeAbove);
|
|
2049
|
+
// Compute available vertical space for dropdown and clamp to a sensible minimum
|
|
2050
|
+
const availableSpace = (placeAbove ? spaceAbove : spaceBelow) - VIEWPORT_MARGIN;
|
|
2051
|
+
const maxHeight = Math.max(160, availableSpace); // ensure at least 160px so a few options are visible
|
|
2009
2052
|
let newY;
|
|
2010
2053
|
if (!placeAbove) {
|
|
2011
2054
|
newY = baseRect.bottom + window.scrollY; // default below
|
|
@@ -2027,6 +2070,7 @@ function Select(props) {
|
|
|
2027
2070
|
dropdown.style.left = `${newX}px`;
|
|
2028
2071
|
dropdown.style.top = `${newY}px`;
|
|
2029
2072
|
dropdown.style.width = `${baseRect.width}px`;
|
|
2073
|
+
dropdown.style.maxHeight = `${maxHeight}px`;
|
|
2030
2074
|
};
|
|
2031
2075
|
// Update width of select to match option text width
|
|
2032
2076
|
createEffect(() => {
|
|
@@ -2178,7 +2222,6 @@ function Select(props) {
|
|
|
2178
2222
|
get children() {
|
|
2179
2223
|
var _el$12 = _tmpl$4$1();
|
|
2180
2224
|
use(setDropdownRef, _el$12);
|
|
2181
|
-
_el$12.style.setProperty("max-height", "calc(100vh - 8px)");
|
|
2182
2225
|
insert(_el$12, () => props.children);
|
|
2183
2226
|
createRenderEffect(_p$ => {
|
|
2184
2227
|
var _v$ = `${styles$7['solid_select__dropdown']} ${dropTop() ? styles$7.dropTop : styles$7.dropBottom} ${open() ? styles$7.open : ''} ${props.dropdownClass || ""}`,
|
|
@@ -2345,76 +2388,101 @@ var _tmpl$$e = /*#__PURE__*/template(`<textarea>`);
|
|
|
2345
2388
|
const TextArea = props => {
|
|
2346
2389
|
let myElement;
|
|
2347
2390
|
const [customProps, normalProps] = splitProps(props, ["minSize", "text", "setText", "class", "tooltip", "transparent"]);
|
|
2348
|
-
const
|
|
2349
|
-
|
|
2350
|
-
|
|
2351
|
-
|
|
2352
|
-
|
|
2353
|
-
|
|
2354
|
-
|
|
2355
|
-
|
|
2356
|
-
|
|
2357
|
-
|
|
2358
|
-
|
|
2359
|
-
|
|
2391
|
+
const fieldCtx = useFormProvider();
|
|
2392
|
+
const formCtx = useFormContext();
|
|
2393
|
+
const formName = fieldCtx?.formName;
|
|
2394
|
+
// Internal state only used when not provided externally and not in form context.
|
|
2395
|
+
const [internal, setInternal] = createSignal(customProps.text ? customProps.text() : "");
|
|
2396
|
+
// Determine current value priority: FormGroup -> external accessor -> internal state
|
|
2397
|
+
const areaValue = createMemo(() => {
|
|
2398
|
+
if (formName && formCtx?.data && !isNullish(formCtx.data[formName])) {
|
|
2399
|
+
return formCtx.data[formName];
|
|
2400
|
+
}
|
|
2401
|
+
if (customProps.text) return customProps.text();
|
|
2402
|
+
return internal();
|
|
2403
|
+
});
|
|
2404
|
+
function resizeToContent() {
|
|
2405
|
+
if (!myElement) return;
|
|
2406
|
+
myElement.style.height = 'auto';
|
|
2407
|
+
const minHeight = customProps.minSize?.height ?? 100;
|
|
2408
|
+
const currentHeight = Math.max(minHeight, myElement.scrollHeight);
|
|
2409
|
+
myElement.style.height = `${currentHeight}px`;
|
|
2410
|
+
myElement.style.overflowY = 'hidden';
|
|
2360
2411
|
}
|
|
2361
|
-
//
|
|
2412
|
+
// Set field type & initial floating state
|
|
2362
2413
|
onMount(() => {
|
|
2363
|
-
|
|
2364
|
-
|
|
2414
|
+
resizeToContent();
|
|
2415
|
+
fieldCtx?.setFieldType?.('textarea');
|
|
2416
|
+
if (formName && formCtx?.data) {
|
|
2417
|
+
const v = formCtx.data[formName];
|
|
2418
|
+
if (!isNullish(v) && String(v).trim() !== '') {
|
|
2419
|
+
fieldCtx?.setValue?.(v);
|
|
2420
|
+
fieldCtx?.setTextInside?.(true);
|
|
2421
|
+
} else {
|
|
2422
|
+
fieldCtx?.setTextInside?.(false);
|
|
2423
|
+
}
|
|
2424
|
+
} else if (customProps.text) {
|
|
2425
|
+
const v = customProps.text();
|
|
2426
|
+
fieldCtx?.setTextInside?.(!(v === undefined || v === null || v.trim() === ''));
|
|
2427
|
+
}
|
|
2365
2428
|
});
|
|
2366
|
-
//
|
|
2367
|
-
|
|
2368
|
-
|
|
2369
|
-
|
|
2429
|
+
// React to programmatic FormGroup.set changes
|
|
2430
|
+
createEffect(() => {
|
|
2431
|
+
if (formName && formCtx?.data) {
|
|
2432
|
+
const v = formCtx.data[formName];
|
|
2433
|
+
// keep internal state aligned if unmanaged
|
|
2434
|
+
if (!customProps.text && internal() !== v) setInternal(String(v ?? ''));
|
|
2435
|
+
if (fieldCtx) {
|
|
2436
|
+
const has = !(v === undefined || v === null || typeof v === 'string' && v.trim() === '');
|
|
2437
|
+
fieldCtx.setTextInside(has);
|
|
2438
|
+
fieldCtx.setValue?.(v);
|
|
2439
|
+
}
|
|
2440
|
+
}
|
|
2370
2441
|
});
|
|
2442
|
+
// Resize whenever value changes (user or programmatic)
|
|
2371
2443
|
createEffect(() => {
|
|
2372
|
-
|
|
2373
|
-
|
|
2444
|
+
areaValue();
|
|
2445
|
+
queueMicrotask(resizeToContent);
|
|
2446
|
+
});
|
|
2447
|
+
const handleInput = e => {
|
|
2448
|
+
const newVal = e.currentTarget.value;
|
|
2449
|
+
// Update whichever source is active
|
|
2450
|
+
if (formName && formCtx?.formGroup) {
|
|
2451
|
+
formCtx.formGroup.set(formName, newVal);
|
|
2452
|
+
formCtx.setData?.(formName, newVal);
|
|
2453
|
+
} else if (customProps.setText) {
|
|
2454
|
+
customProps.setText(newVal);
|
|
2374
2455
|
} else {
|
|
2375
|
-
|
|
2456
|
+
setInternal(newVal);
|
|
2376
2457
|
}
|
|
2377
|
-
|
|
2458
|
+
if (fieldCtx) {
|
|
2459
|
+
fieldCtx.setValue?.(newVal);
|
|
2460
|
+
fieldCtx.setTextInside?.(newVal.trim().length > 0);
|
|
2461
|
+
}
|
|
2462
|
+
resizeToContent();
|
|
2463
|
+
};
|
|
2378
2464
|
return (() => {
|
|
2379
2465
|
var _el$ = _tmpl$$e();
|
|
2380
2466
|
use(el => {
|
|
2381
2467
|
myElement = el;
|
|
2382
|
-
|
|
2468
|
+
resizeToContent();
|
|
2383
2469
|
}, _el$);
|
|
2384
2470
|
spread(_el$, mergeProps(normalProps, {
|
|
2385
|
-
|
|
2386
|
-
|
|
2387
|
-
context?.setFocused(true);
|
|
2388
|
-
}
|
|
2389
|
-
},
|
|
2390
|
-
"onBlur": e => {
|
|
2391
|
-
if (context?.setFocused) {
|
|
2392
|
-
context?.setFocused(false);
|
|
2393
|
-
}
|
|
2394
|
-
},
|
|
2395
|
-
get placeholder() {
|
|
2396
|
-
return !!context?.getName && context?.getTextInside() && !context?.getFocused() ? "" : props.placeholder;
|
|
2471
|
+
get value() {
|
|
2472
|
+
return areaValue();
|
|
2397
2473
|
},
|
|
2398
2474
|
get ["class"]() {
|
|
2399
|
-
return `${style$4.areaStyle} ${customProps.class ??
|
|
2475
|
+
return `${style$4.areaStyle} ${customProps.class ?? ''} ${customProps.transparent ? customProps.transparent : ''}`;
|
|
2400
2476
|
},
|
|
2401
|
-
get
|
|
2402
|
-
return
|
|
2403
|
-
},
|
|
2404
|
-
"onInput": e => {
|
|
2405
|
-
customProps.setText(e.currentTarget.value);
|
|
2406
|
-
OnInput();
|
|
2407
|
-
if (!!context?.getName && !!e.currentTarget.value.trim()) {
|
|
2408
|
-
context?.setValue(e.currentTarget.value);
|
|
2409
|
-
context?.setTextInside(true);
|
|
2410
|
-
} else if (!!context?.getName && !e.currentTarget.value.trim()) {
|
|
2411
|
-
context?.setValue("");
|
|
2412
|
-
context?.setTextInside(false);
|
|
2413
|
-
}
|
|
2477
|
+
get placeholder() {
|
|
2478
|
+
return fieldCtx?.getTextInside?.() && !fieldCtx?.getFocused?.() ? '' : props.placeholder;
|
|
2414
2479
|
},
|
|
2415
2480
|
get title() {
|
|
2416
2481
|
return customProps.tooltip;
|
|
2417
|
-
}
|
|
2482
|
+
},
|
|
2483
|
+
"onInput": handleInput,
|
|
2484
|
+
"onFocus": () => fieldCtx?.setFocused?.(true),
|
|
2485
|
+
"onBlur": () => fieldCtx?.setFocused?.(false)
|
|
2418
2486
|
}), false, false);
|
|
2419
2487
|
return _el$;
|
|
2420
2488
|
})();
|
|
@@ -3834,6 +3902,13 @@ class FormGroup {
|
|
|
3834
3902
|
this.validators = newValidators;
|
|
3835
3903
|
this.errors = createSignal(newErrors);
|
|
3836
3904
|
}
|
|
3905
|
+
/**
|
|
3906
|
+
* INTERNAL: returns the reactive internal store reference (DO NOT MUTATE outside FormGroup).
|
|
3907
|
+
* Used for bridging into FormContext without triggering cloning loops.
|
|
3908
|
+
*/
|
|
3909
|
+
_unsafeRaw() {
|
|
3910
|
+
return this.internalDataSignal[0];
|
|
3911
|
+
}
|
|
3837
3912
|
get(key) {
|
|
3838
3913
|
if (!key) {
|
|
3839
3914
|
// Custom clone that preserves FormArray instances (structuredClone fails on functions inside)
|