coles-solid-library 0.3.10 → 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.
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import { Component, JSX } from "solid-js";
|
|
2
2
|
import { ThemeVariables } from "../..";
|
|
3
|
+
/**
|
|
4
|
+
* Library Button
|
|
5
|
+
* - Accepts theme + optional borderTheme variant ("none" disables border styling)
|
|
6
|
+
* - "transparent" acts as a boolean presence prop (transparent={false} disables when explicitly false)
|
|
7
|
+
* - Forwards all other native button props & ref
|
|
8
|
+
*/
|
|
3
9
|
interface Props extends JSX.ButtonHTMLAttributes<HTMLButtonElement> {
|
|
10
|
+
/** Apply transparent styling (presence = on; set false to force off) */
|
|
4
11
|
transparent?: boolean;
|
|
5
12
|
theme?: Exclude<ThemeVariables, "header" | "subheader">;
|
|
6
13
|
borderTheme?: Exclude<ThemeVariables, "header" | "subheader"> | "none";
|
|
@@ -43,6 +43,8 @@ export declare class FormArray<T extends object> {
|
|
|
43
43
|
* @private
|
|
44
44
|
*/
|
|
45
45
|
private setValues;
|
|
46
|
+
private readonly _initialValidationDefs;
|
|
47
|
+
private readonly _initialValuesSnapshot;
|
|
46
48
|
/**
|
|
47
49
|
* Creates an instance of FormArray.
|
|
48
50
|
*
|
|
@@ -51,6 +53,16 @@ export declare class FormArray<T extends object> {
|
|
|
51
53
|
* @param initialValues - Optional initial values for the form controls.
|
|
52
54
|
*/
|
|
53
55
|
constructor(arrayValidation: ArrayValidation<T>, initialValues?: T | T[]);
|
|
56
|
+
/**
|
|
57
|
+
* Returns true if any validator (array-level or control-level) exists matching the provided errKey.
|
|
58
|
+
* If no errKey provided, returns true when any validator exists at all.
|
|
59
|
+
*/
|
|
60
|
+
hasAnyValidator(errKey?: string): boolean;
|
|
61
|
+
/**
|
|
62
|
+
* Resets the array to its initial validation definitions and values.
|
|
63
|
+
* Clears any accumulated errors and added dynamic controls.
|
|
64
|
+
*/
|
|
65
|
+
reset(): void;
|
|
54
66
|
/**
|
|
55
67
|
* Gets the current values of the form controls in the array.
|
|
56
68
|
*
|
|
@@ -1,10 +1,18 @@
|
|
|
1
1
|
import { Component } from "solid-js";
|
|
2
|
+
/**
|
|
3
|
+
* Snackbar descriptor
|
|
4
|
+
* - message: text content
|
|
5
|
+
* - severity: maps to style variants
|
|
6
|
+
* - closeTimeout: ms before auto close (default 5000)
|
|
7
|
+
*/
|
|
2
8
|
export interface Snackbar {
|
|
3
9
|
message: string;
|
|
4
10
|
severity?: "error" | "warning" | "info" | "success";
|
|
5
11
|
closeTimeout?: number;
|
|
6
12
|
}
|
|
7
|
-
|
|
13
|
+
/** Queue a snackbar to display */
|
|
14
|
+
declare const addSnackbar: (snack: Snackbar) => Snackbar[];
|
|
15
|
+
/** Mount once near app root to render any queued snackbars (portal not required here but container simplifies styling) */
|
|
8
16
|
declare const SnackbarController: Component;
|
|
9
17
|
export { addSnackbar, SnackbarController };
|
|
10
18
|
export default addSnackbar;
|
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
import { Accessor, JSX, Setter } from "solid-js";
|
|
2
2
|
import { StyleContainer } from "./tableProvider";
|
|
3
|
+
/**
|
|
4
|
+
* Core Table component (TableV2): orchestrates a declarative registration system for
|
|
5
|
+
* columns, headers, rows, dropdown rows, and styling containers. Child components
|
|
6
|
+
* (Column, Header, Cell, Row, etc.) register themselves through context; this file
|
|
7
|
+
* renders the actual <table> DOM structure from that accumulated metadata.
|
|
8
|
+
*
|
|
9
|
+
* Design goals / constraints (tests rely on these):
|
|
10
|
+
* - Changing the first column name must retain dropdown (collapse/expand) metadata.
|
|
11
|
+
* - Row click handlers may receive (event, dataItem) when declared with >= 2 params.
|
|
12
|
+
* - Cell render errors surface a console.error and render a fallback element unless
|
|
13
|
+
* user provided onCellError returns a custom JSX fallback.
|
|
14
|
+
* - Dropdown header rows are focusable, toggle with Enter/Space, and manage an
|
|
15
|
+
* associated content region row with role="region" + aria-hidden / data attributes.
|
|
16
|
+
*/
|
|
3
17
|
interface TableProps<T = any> {
|
|
4
18
|
children: JSX.Element;
|
|
5
19
|
data: Accessor<T[]>;
|
|
@@ -22,6 +36,8 @@ interface TableProps<T = any> {
|
|
|
22
36
|
row: number;
|
|
23
37
|
dataIndex: number;
|
|
24
38
|
}) => JSX.Element | void;
|
|
39
|
+
/** Enable verbose debug logging (otherwise suppressed outside tests) */
|
|
40
|
+
debugLogs?: boolean;
|
|
25
41
|
}
|
|
26
42
|
export declare const Table: <T>(props: TableProps<T>) => JSX.Element;
|
|
27
43
|
export {};
|
package/dist/index.esm.js
CHANGED
|
@@ -521,48 +521,48 @@ var style$8 = {"customButtonStyle":"Button-module_customButtonStyle__RCNcn","pri
|
|
|
521
521
|
styleInject(css_248z$j);
|
|
522
522
|
|
|
523
523
|
var _tmpl$$o = /*#__PURE__*/template(`<button>`);
|
|
524
|
+
// Map theme -> border theme class key (kept as function to avoid changing test expectations around call order)
|
|
525
|
+
const getBorderThemeName = theme => {
|
|
526
|
+
switch (theme) {
|
|
527
|
+
case "primary":
|
|
528
|
+
return "borderPrimary";
|
|
529
|
+
case "primaryVariant":
|
|
530
|
+
return "borderPrimaryVariant";
|
|
531
|
+
case "secondary":
|
|
532
|
+
return "borderSecondary";
|
|
533
|
+
case "secondaryVariant":
|
|
534
|
+
return "borderSecondaryVariant";
|
|
535
|
+
case "surface":
|
|
536
|
+
return "borderSurface";
|
|
537
|
+
case "surfaceVariant":
|
|
538
|
+
return "borderSurfaceVariant";
|
|
539
|
+
case "background":
|
|
540
|
+
return "borderBackground";
|
|
541
|
+
case "container":
|
|
542
|
+
return "borderContainer";
|
|
543
|
+
case "error":
|
|
544
|
+
return "borderError";
|
|
545
|
+
default:
|
|
546
|
+
return "none";
|
|
547
|
+
}
|
|
548
|
+
};
|
|
524
549
|
const Button = props => {
|
|
525
550
|
const [local, others] = splitProps(props, ["theme", "transparent", "borderTheme"]);
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
return true;
|
|
529
|
-
}
|
|
530
|
-
return false;
|
|
531
|
-
};
|
|
532
|
-
const [buttonRef, setButtonRef] = createSignal();
|
|
551
|
+
/** Apply the transparent variant (presence of prop enables; set to false to force off) */
|
|
552
|
+
const isTransparent = () => "transparent" in props && props.transparent !== false;
|
|
533
553
|
const transparent = createMemo(() => isTransparent() ? style$8.transparent : "");
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
case "primary":
|
|
537
|
-
return "borderPrimary";
|
|
538
|
-
case "primaryVariant":
|
|
539
|
-
return "borderPrimaryVariant";
|
|
540
|
-
case "secondary":
|
|
541
|
-
return "borderSecondary";
|
|
542
|
-
case "secondaryVariant":
|
|
543
|
-
return "borderSecondaryVariant";
|
|
544
|
-
case "surface":
|
|
545
|
-
return "borderSurface";
|
|
546
|
-
case "surfaceVariant":
|
|
547
|
-
return "borderSurfaceVariant";
|
|
548
|
-
case "background":
|
|
549
|
-
return "borderBackground";
|
|
550
|
-
case "container":
|
|
551
|
-
return "borderContainer";
|
|
552
|
-
case "error":
|
|
553
|
-
return "borderError";
|
|
554
|
-
default:
|
|
555
|
-
return "none";
|
|
556
|
-
}
|
|
557
|
-
};
|
|
554
|
+
// Provide a default type to avoid implicit "submit" in forms when not specified
|
|
555
|
+
const explicitType = () => props.type ?? 'button';
|
|
558
556
|
return (() => {
|
|
559
557
|
var _el$ = _tmpl$$o();
|
|
560
558
|
use(ref => {
|
|
561
|
-
setButtonRef(ref);
|
|
562
559
|
// @ts-ignore
|
|
563
560
|
props?.ref?.(ref);
|
|
564
561
|
}, _el$);
|
|
565
562
|
spread(_el$, mergeProps(others, {
|
|
563
|
+
get type() {
|
|
564
|
+
return explicitType();
|
|
565
|
+
},
|
|
566
566
|
get ["class"]() {
|
|
567
567
|
return `${style$8?.[local?.theme ?? "container"] ?? ''} ${style$8?.[getBorderThemeName(local?.borderTheme ?? 'none')] ?? ''} ${style$8?.customButtonStyle} ${transparent()} ${props?.class ?? ""}`;
|
|
568
568
|
}
|
|
@@ -1230,12 +1230,18 @@ function createStore(...[store, options]) {
|
|
|
1230
1230
|
}
|
|
1231
1231
|
|
|
1232
1232
|
/**
|
|
1233
|
-
*
|
|
1234
|
-
*
|
|
1235
|
-
*
|
|
1233
|
+
* Set the data-theme attribute on <body>.
|
|
1234
|
+
* Accepts only 'dark' | 'light' (case-insensitive). Any invalid value falls back to 'dark'.
|
|
1235
|
+
* Idempotent: won't re-set attribute if already the desired value (avoids needless layout / mutation observers).
|
|
1236
|
+
* Returns the applied normalized theme string.
|
|
1236
1237
|
*/
|
|
1237
1238
|
function addTheme(theme = 'dark') {
|
|
1238
|
-
|
|
1239
|
+
const normalized = (theme || 'dark').toLowerCase();
|
|
1240
|
+
const applied = normalized === 'light' || normalized === 'dark' ? normalized : 'dark';
|
|
1241
|
+
if (document.body.getAttribute('data-theme') !== applied) {
|
|
1242
|
+
document.body.setAttribute('data-theme', applied);
|
|
1243
|
+
}
|
|
1244
|
+
return applied;
|
|
1239
1245
|
}
|
|
1240
1246
|
function isNullish(params) {
|
|
1241
1247
|
return params === null || params === undefined;
|
|
@@ -1529,9 +1535,27 @@ const Input = props => {
|
|
|
1529
1535
|
context.setValue(raw);
|
|
1530
1536
|
context.setTextInside(true);
|
|
1531
1537
|
}
|
|
1538
|
+
// If not in a form but controlled value provided
|
|
1539
|
+
} else if (!isNullish(props.value)) {
|
|
1540
|
+
const val = props.value;
|
|
1541
|
+
if (!(typeof val === 'string' && val.trim() === '')) {
|
|
1542
|
+
context.setValue(val);
|
|
1543
|
+
context.setTextInside(true);
|
|
1544
|
+
} else {
|
|
1545
|
+
context.setTextInside(false);
|
|
1546
|
+
}
|
|
1532
1547
|
}
|
|
1533
1548
|
}
|
|
1534
1549
|
});
|
|
1550
|
+
// React to controlled prop value changes outside of form context
|
|
1551
|
+
createEffect(() => {
|
|
1552
|
+
if (isNullish(formContext?.data) && !isNullish(props.value) && !isNullish(context.getName)) {
|
|
1553
|
+
const val = props.value;
|
|
1554
|
+
const empty = typeof val === 'string' ? val.trim() === '' : false;
|
|
1555
|
+
context.setValue(val);
|
|
1556
|
+
context.setTextInside(!empty);
|
|
1557
|
+
}
|
|
1558
|
+
});
|
|
1535
1559
|
const placeholder = () => {
|
|
1536
1560
|
const reqText = isRequired() ? " *" : "";
|
|
1537
1561
|
if (context?.getTextInside?.() && !context?.getFocused?.()) {
|
|
@@ -1561,6 +1585,12 @@ const Input = props => {
|
|
|
1561
1585
|
"onFocus": () => {
|
|
1562
1586
|
if (!isNullish(context.getName)) {
|
|
1563
1587
|
context.setFocused?.(true);
|
|
1588
|
+
// Mark dirty on initial focus if part of a form (requested behavior)
|
|
1589
|
+
const formName = context.formName;
|
|
1590
|
+
if (formName && formContext?.formGroup?.markDirty) {
|
|
1591
|
+
const meta = formContext.formGroup.getMeta(formName);
|
|
1592
|
+
if (meta && !meta.dirty) formContext.formGroup.markDirty(formName);
|
|
1593
|
+
}
|
|
1564
1594
|
}
|
|
1565
1595
|
},
|
|
1566
1596
|
"onBlur": e => {
|
|
@@ -2176,7 +2206,20 @@ function Select(props) {
|
|
|
2176
2206
|
setOpen(false);
|
|
2177
2207
|
}
|
|
2178
2208
|
};
|
|
2179
|
-
_el$7
|
|
2209
|
+
_el$7.$$click = e => {
|
|
2210
|
+
// existing click logic below will toggle dropdown; ensure dirty marking once
|
|
2211
|
+
if (form?.formName && formContext?.formGroup?.markDirty) {
|
|
2212
|
+
const meta = formContext.formGroup.getMeta(form?.formName);
|
|
2213
|
+
if (meta && !meta.dirty) formContext.formGroup.markDirty(form?.formName);
|
|
2214
|
+
}
|
|
2215
|
+
};
|
|
2216
|
+
_el$7.addEventListener("focus", () => {
|
|
2217
|
+
form?.setFocused?.(true);
|
|
2218
|
+
if (form?.formName && formContext?.formGroup?.markDirty) {
|
|
2219
|
+
const meta = formContext.formGroup.getMeta(form?.formName);
|
|
2220
|
+
if (meta && !meta.dirty) formContext.formGroup.markDirty(form?.formName);
|
|
2221
|
+
}
|
|
2222
|
+
});
|
|
2180
2223
|
_el$7._$owner = getOwner();
|
|
2181
2224
|
_el$8.$$click = e => {
|
|
2182
2225
|
const menuRect = menuLocRef()?.getBoundingClientRect();
|
|
@@ -2266,7 +2309,7 @@ function Select(props) {
|
|
|
2266
2309
|
return _el$7;
|
|
2267
2310
|
})();
|
|
2268
2311
|
}
|
|
2269
|
-
delegateEvents(["
|
|
2312
|
+
delegateEvents(["click", "keydown"]);
|
|
2270
2313
|
|
|
2271
2314
|
var _tmpl$$f = /*#__PURE__*/template(`<div role=option><span></span><span is=coles-select-label>`, true, false, false);
|
|
2272
2315
|
function Option(props) {
|
|
@@ -2473,7 +2516,19 @@ const TextArea = props => {
|
|
|
2473
2516
|
return customProps.tooltip;
|
|
2474
2517
|
},
|
|
2475
2518
|
"onInput": handleInput,
|
|
2476
|
-
"onFocus": () =>
|
|
2519
|
+
"onFocus": () => {
|
|
2520
|
+
fieldCtx?.setFocused?.(true);
|
|
2521
|
+
if (formName && formCtx?.formGroup?.markDirty) {
|
|
2522
|
+
const meta = formCtx.formGroup.getMeta(formName);
|
|
2523
|
+
if (meta && !meta.dirty) formCtx.formGroup.markDirty(formName);
|
|
2524
|
+
}
|
|
2525
|
+
},
|
|
2526
|
+
"onClick": () => {
|
|
2527
|
+
if (formName && formCtx?.formGroup?.markDirty) {
|
|
2528
|
+
const meta = formCtx.formGroup.getMeta(formName);
|
|
2529
|
+
if (meta && !meta.dirty) formCtx.formGroup.markDirty(formName);
|
|
2530
|
+
}
|
|
2531
|
+
},
|
|
2477
2532
|
"onBlur": () => fieldCtx?.setFocused?.(false)
|
|
2478
2533
|
}), false, false);
|
|
2479
2534
|
return _el$;
|
|
@@ -2492,19 +2547,35 @@ const Modal = props => {
|
|
|
2492
2547
|
const isShown = createMemo(() => props.show[0]());
|
|
2493
2548
|
const [innerPopup, setInnerPopup] = createSignal();
|
|
2494
2549
|
// const [ignoreClicks, setIgnoreClicks] = createSignal(true);
|
|
2550
|
+
// Maintain a stable entry reference so unregister removes the correct instance.
|
|
2551
|
+
let wmEntry;
|
|
2495
2552
|
createEffect(() => {
|
|
2496
|
-
const
|
|
2497
|
-
|
|
2498
|
-
|
|
2499
|
-
|
|
2553
|
+
const el = innerPopup();
|
|
2554
|
+
const visible = isShown();
|
|
2555
|
+
if (visible && el) {
|
|
2556
|
+
if (!wmEntry) {
|
|
2557
|
+
wmEntry = {
|
|
2558
|
+
element: el,
|
|
2559
|
+
onClickOutside: () => props.show[1](false)
|
|
2560
|
+
};
|
|
2561
|
+
// Slight delay retained from original logic (reduced) for potential mount animations
|
|
2562
|
+
setTimeout(() => {
|
|
2563
|
+
// Guard in case it was hidden before timeout fired
|
|
2564
|
+
if (wmEntry && isShown()) registerWindowManager(wmEntry);
|
|
2565
|
+
}, 50);
|
|
2566
|
+
} else {
|
|
2567
|
+
// Update element reference if it changed (shouldn't normally)
|
|
2568
|
+
wmEntry.element = el;
|
|
2500
2569
|
}
|
|
2501
|
-
}
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2570
|
+
} else if (!visible && wmEntry) {
|
|
2571
|
+
unregisterWindowManager(wmEntry);
|
|
2572
|
+
wmEntry = undefined;
|
|
2573
|
+
}
|
|
2574
|
+
});
|
|
2575
|
+
onCleanup(() => {
|
|
2576
|
+
if (wmEntry) {
|
|
2577
|
+
unregisterWindowManager(wmEntry);
|
|
2578
|
+
wmEntry = undefined;
|
|
2508
2579
|
}
|
|
2509
2580
|
});
|
|
2510
2581
|
const widthStyle = createMemo(() => {
|
|
@@ -2710,74 +2781,65 @@ var style$1 = {"snack":"snackbar-module_snack__Dkcmp","error":"snackbar-module_e
|
|
|
2710
2781
|
styleInject(css_248z$8);
|
|
2711
2782
|
|
|
2712
2783
|
var _tmpl$$a = /*#__PURE__*/template(`<div>`),
|
|
2713
|
-
_tmpl$2$5 = /*#__PURE__*/template(`<div><span>`);
|
|
2784
|
+
_tmpl$2$5 = /*#__PURE__*/template(`<div role=status aria-live=polite><span>`);
|
|
2714
2785
|
const [snackbars, setSnackbars] = createSignal([]);
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
const removeSnackbar = index =>
|
|
2719
|
-
|
|
2720
|
-
|
|
2721
|
-
|
|
2722
|
-
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2728
|
-
|
|
2729
|
-
|
|
2730
|
-
|
|
2786
|
+
/** Queue a snackbar to display */
|
|
2787
|
+
const addSnackbar = snack => setSnackbars(old => [...old, snack]);
|
|
2788
|
+
/** Remove snackbar at index */
|
|
2789
|
+
const removeSnackbar = index => setSnackbars(old => old.filter((_, i) => i !== index));
|
|
2790
|
+
/** Mount once near app root to render any queued snackbars (portal not required here but container simplifies styling) */
|
|
2791
|
+
const SnackbarController = () => createComponent(Show, {
|
|
2792
|
+
get when() {
|
|
2793
|
+
return snackbars().length > 0;
|
|
2794
|
+
},
|
|
2795
|
+
get children() {
|
|
2796
|
+
var _el$ = _tmpl$$a();
|
|
2797
|
+
insert(_el$, createComponent(For, {
|
|
2798
|
+
get each() {
|
|
2799
|
+
return snackbars();
|
|
2800
|
+
},
|
|
2801
|
+
children: (snack, index) => createComponent(Snackbar, mergeProps(snack, {
|
|
2802
|
+
get index() {
|
|
2803
|
+
return index();
|
|
2731
2804
|
},
|
|
2732
|
-
|
|
2733
|
-
|
|
2734
|
-
|
|
2735
|
-
|
|
2736
|
-
|
|
2737
|
-
|
|
2738
|
-
|
|
2739
|
-
createRenderEffect(() => className(_el$, style$1.snackContainer));
|
|
2740
|
-
return _el$;
|
|
2741
|
-
}
|
|
2742
|
-
});
|
|
2743
|
-
};
|
|
2805
|
+
onClose: () => removeSnackbar(index())
|
|
2806
|
+
}))
|
|
2807
|
+
}));
|
|
2808
|
+
createRenderEffect(() => className(_el$, style$1.snackContainer));
|
|
2809
|
+
return _el$;
|
|
2810
|
+
}
|
|
2811
|
+
});
|
|
2744
2812
|
const Snackbar = props => {
|
|
2745
|
-
|
|
2746
|
-
|
|
2747
|
-
|
|
2748
|
-
const
|
|
2813
|
+
// Timer management: ensure only one timeout per snackbar instance
|
|
2814
|
+
const timeout = setTimeout(props.onClose, props.closeTimeout ?? 5000);
|
|
2815
|
+
onCleanup(() => clearTimeout(timeout));
|
|
2816
|
+
const messageLinesHeuristic = Math.floor(props.message.length / 17); // maintain original vertical stacking calc
|
|
2749
2817
|
return createComponent(Portal, {
|
|
2750
2818
|
get mount() {
|
|
2751
2819
|
return document.body;
|
|
2752
2820
|
},
|
|
2753
2821
|
get children() {
|
|
2754
|
-
|
|
2755
|
-
|
|
2756
|
-
|
|
2822
|
+
var _el$2 = _tmpl$2$5(),
|
|
2823
|
+
_el$3 = _el$2.firstChild;
|
|
2824
|
+
insert(_el$3, () => props.message);
|
|
2825
|
+
insert(_el$2, createComponent(Button, {
|
|
2826
|
+
get onClick() {
|
|
2827
|
+
return props.onClose;
|
|
2757
2828
|
},
|
|
2758
|
-
|
|
2759
|
-
|
|
2760
|
-
|
|
2761
|
-
|
|
2762
|
-
|
|
2763
|
-
|
|
2764
|
-
|
|
2765
|
-
|
|
2766
|
-
|
|
2767
|
-
|
|
2768
|
-
|
|
2769
|
-
|
|
2770
|
-
_v$2 = `${style$1.primary} ${style$1.snack} ${style$1[props.severity ?? 'info']}`;
|
|
2771
|
-
_v$ !== _p$.e && ((_p$.e = _v$) != null ? _el$2.style.setProperty("bottom", _v$) : _el$2.style.removeProperty("bottom"));
|
|
2772
|
-
_v$2 !== _p$.t && className(_el$2, _p$.t = _v$2);
|
|
2773
|
-
return _p$;
|
|
2774
|
-
}, {
|
|
2775
|
-
e: undefined,
|
|
2776
|
-
t: undefined
|
|
2777
|
-
});
|
|
2778
|
-
return _el$2;
|
|
2779
|
-
}
|
|
2829
|
+
"aria-label": "Close notification",
|
|
2830
|
+
children: "X"
|
|
2831
|
+
}), null);
|
|
2832
|
+
createRenderEffect(_p$ => {
|
|
2833
|
+
var _v$ = `${10 + props.index * (50 + messageLinesHeuristic * 8)}px`,
|
|
2834
|
+
_v$2 = `${style$1.primary} ${style$1.snack} ${style$1[props.severity ?? 'info']}`;
|
|
2835
|
+
_v$ !== _p$.e && ((_p$.e = _v$) != null ? _el$2.style.setProperty("bottom", _v$) : _el$2.style.removeProperty("bottom"));
|
|
2836
|
+
_v$2 !== _p$.t && className(_el$2, _p$.t = _v$2);
|
|
2837
|
+
return _p$;
|
|
2838
|
+
}, {
|
|
2839
|
+
e: undefined,
|
|
2840
|
+
t: undefined
|
|
2780
2841
|
});
|
|
2842
|
+
return _el$2;
|
|
2781
2843
|
}
|
|
2782
2844
|
});
|
|
2783
2845
|
};
|
|
@@ -2801,7 +2863,7 @@ const Container = props => {
|
|
|
2801
2863
|
})();
|
|
2802
2864
|
};
|
|
2803
2865
|
|
|
2804
|
-
var css_248z$6 = ".icon-module_icon__K4BP- {\n outline: none !important;\n border: none !important;\n}\n.icon-module_icon__K4BP-:focus {\n outline: none !important;\n border: none !important;\n}\n.icon-module_icon__K4BP-:active {\n outline: none !important;\n border: none !important;\n}";
|
|
2866
|
+
var css_248z$6 = ".icon-module_icon__K4BP- {\n outline: none !important;\n border: none !important;\n}\n.icon-module_icon__K4BP- svg {\n fill: var(--header-on-background-color, #fff) !important;\n color: var(--header-on-background-color, #fff) !important;\n}\n.icon-module_icon__K4BP-:focus {\n outline: none !important;\n border: none !important;\n}\n.icon-module_icon__K4BP-:active {\n outline: none !important;\n border: none !important;\n}";
|
|
2805
2867
|
var styles$5 = {"icon":"icon-module_icon__K4BP-"};
|
|
2806
2868
|
styleInject(css_248z$6);
|
|
2807
2869
|
|
|
@@ -2844,6 +2906,7 @@ const Icon = props => {
|
|
|
2844
2906
|
}, _el$);
|
|
2845
2907
|
_el$.style.setProperty("display", "inline-block");
|
|
2846
2908
|
color != null ? _el$.style.setProperty("color", color) : _el$.style.removeProperty("color");
|
|
2909
|
+
color != null ? _el$.style.setProperty("fill", color) : _el$.style.removeProperty("fill");
|
|
2847
2910
|
createRenderEffect(_p$ => {
|
|
2848
2911
|
var _v$ = styles$5.icon,
|
|
2849
2912
|
_v$2 = `${size()}px`,
|
|
@@ -2884,18 +2947,29 @@ const Menu = props => {
|
|
|
2884
2947
|
}
|
|
2885
2948
|
return '';
|
|
2886
2949
|
};
|
|
2950
|
+
// Persist the window manager entry so we unregister the same reference we registered.
|
|
2951
|
+
let wmEntry;
|
|
2887
2952
|
createEffect(() => {
|
|
2888
|
-
const
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2953
|
+
const visible = props.show[0]();
|
|
2954
|
+
const el = menuRef();
|
|
2955
|
+
if (visible && el) {
|
|
2956
|
+
if (!wmEntry) {
|
|
2957
|
+
wmEntry = {
|
|
2958
|
+
element: el,
|
|
2959
|
+
onClickOutside: () => props.show[1](false)
|
|
2960
|
+
};
|
|
2961
|
+
} else {
|
|
2962
|
+
// update element reference if it changed (shouldn't usually)
|
|
2963
|
+
wmEntry.element = el;
|
|
2892
2964
|
}
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2965
|
+
// Avoid duplicate registrations: only register if not already last added.
|
|
2966
|
+
// Heuristic: assign zIndex each time we show and register once.
|
|
2967
|
+
wmEntry.element.style.zIndex = `${999 + getEntryAmount()}`;
|
|
2968
|
+
// If not already in entries array, register (simple containment check by identity).
|
|
2969
|
+
// We can't read the internal array directly besides length heuristic, so best effort:
|
|
2970
|
+
registerWindowManager(wmEntry);
|
|
2971
|
+
} else if (!visible && wmEntry) {
|
|
2972
|
+
unregisterWindowManager(wmEntry);
|
|
2899
2973
|
}
|
|
2900
2974
|
});
|
|
2901
2975
|
const getAndSetPosition = (menu, anchor) => {
|
|
@@ -2946,6 +3020,10 @@ const Menu = props => {
|
|
|
2946
3020
|
if (!isNullish(menuContext)) {
|
|
2947
3021
|
menuContext.unregisterWithParent(menuRef());
|
|
2948
3022
|
}
|
|
3023
|
+
if (wmEntry) {
|
|
3024
|
+
unregisterWindowManager(wmEntry);
|
|
3025
|
+
wmEntry = undefined;
|
|
3026
|
+
}
|
|
2949
3027
|
});
|
|
2950
3028
|
return createComponent(Portal, {
|
|
2951
3029
|
get children() {
|
|
@@ -3310,6 +3388,9 @@ class FormArray {
|
|
|
3310
3388
|
* @private
|
|
3311
3389
|
*/
|
|
3312
3390
|
setValues;
|
|
3391
|
+
// Snapshots of initial validation defs & values for reset purposes
|
|
3392
|
+
_initialValidationDefs;
|
|
3393
|
+
_initialValuesSnapshot;
|
|
3313
3394
|
/**
|
|
3314
3395
|
* Creates an instance of FormArray.
|
|
3315
3396
|
*
|
|
@@ -3325,6 +3406,29 @@ class FormArray {
|
|
|
3325
3406
|
this.setValidation = setValid;
|
|
3326
3407
|
this.internalValues = values;
|
|
3327
3408
|
this.setValues = setValues;
|
|
3409
|
+
this._initialValidationDefs = [...arrayValidation[0]];
|
|
3410
|
+
this._initialValuesSnapshot = Array.isArray(initialValues) ? Clone(initialValues) : Clone([initialValues]);
|
|
3411
|
+
}
|
|
3412
|
+
/**
|
|
3413
|
+
* Returns true if any validator (array-level or control-level) exists matching the provided errKey.
|
|
3414
|
+
* If no errKey provided, returns true when any validator exists at all.
|
|
3415
|
+
*/
|
|
3416
|
+
hasAnyValidator(errKey) {
|
|
3417
|
+
if (!errKey) {
|
|
3418
|
+
return this.internalArrayValidation.length > 0 || this.internalValidation.some(v => v[1].length > 0);
|
|
3419
|
+
}
|
|
3420
|
+
const inArray = this.internalArrayValidation.some(v => v.errKey === errKey);
|
|
3421
|
+
if (inArray) return true;
|
|
3422
|
+
return this.internalValidation.some(v => v[1].some(val => val.errKey === errKey));
|
|
3423
|
+
}
|
|
3424
|
+
/**
|
|
3425
|
+
* Resets the array to its initial validation definitions and values.
|
|
3426
|
+
* Clears any accumulated errors and added dynamic controls.
|
|
3427
|
+
*/
|
|
3428
|
+
reset() {
|
|
3429
|
+
this.setValidation(() => [...this._initialValidationDefs]);
|
|
3430
|
+
this.setValues(() => Clone(this._initialValuesSnapshot));
|
|
3431
|
+
this.errors = [];
|
|
3328
3432
|
}
|
|
3329
3433
|
/**
|
|
3330
3434
|
* Gets the current values of the form controls in the array.
|
|
@@ -3332,7 +3436,9 @@ class FormArray {
|
|
|
3332
3436
|
* @returns A cloned copy of the values array.
|
|
3333
3437
|
*/
|
|
3334
3438
|
get() {
|
|
3335
|
-
|
|
3439
|
+
// Return a cloned snapshot so external mutation of returned array/value objects
|
|
3440
|
+
// can't silently mutate internal reactive state or bypass validation/meta tracking.
|
|
3441
|
+
return Clone(this.internalValues);
|
|
3336
3442
|
}
|
|
3337
3443
|
/**
|
|
3338
3444
|
* Gets the value of a specific control in the array.
|
|
@@ -3596,15 +3702,45 @@ const FormField2 = props => {
|
|
|
3596
3702
|
if (Array.isArray(val) && val.length === 0) return false;
|
|
3597
3703
|
return true;
|
|
3598
3704
|
};
|
|
3705
|
+
// Track dirty meta so legend can float once a field has been modified even if some components
|
|
3706
|
+
// didn't properly synchronize their internal value signals yet.
|
|
3707
|
+
const isDirty = () => {
|
|
3708
|
+
if (!local?.formName) return false;
|
|
3709
|
+
try {
|
|
3710
|
+
return !!formContext?.formGroup?.getMeta?.(local.formName)?.dirty;
|
|
3711
|
+
} catch {
|
|
3712
|
+
return false;
|
|
3713
|
+
}
|
|
3714
|
+
};
|
|
3599
3715
|
const shouldFloat = createMemo(() => {
|
|
3600
3716
|
let currentVal;
|
|
3717
|
+
// Prefer form context store value
|
|
3601
3718
|
if (hasValue(formContext?.data?.[local?.formName ?? ''])) {
|
|
3602
3719
|
currentVal = formContext?.data?.[local?.formName ?? ''];
|
|
3603
3720
|
}
|
|
3721
|
+
// Fallback to local provider value (some components set this first)
|
|
3604
3722
|
if (hasValue(context?.getValue?.())) {
|
|
3605
3723
|
currentVal = context?.getValue?.();
|
|
3606
3724
|
}
|
|
3607
|
-
|
|
3725
|
+
// Float when value present, focused, or control marked dirty (programmatic set)
|
|
3726
|
+
// Access meta directly for reactivity on dirty change
|
|
3727
|
+
const metaDirty = local?.formName ? !!formContext?.formGroup?.getMeta?.(local.formName)?.dirty : false;
|
|
3728
|
+
// Float when there is a value, or focused, or meta marked dirty (programmatic set),
|
|
3729
|
+
// or legacy input marked dirty-on-focus even if still empty (metaDirty && no value yet)
|
|
3730
|
+
if (metaDirty && !hasValue(currentVal)) return true;
|
|
3731
|
+
return hasValue(currentVal) || context?.getFocused?.() || isDirty();
|
|
3732
|
+
});
|
|
3733
|
+
// Effect: if underlying meta resets to pristine and value cleared, ensure focused state cleared
|
|
3734
|
+
createEffect(() => {
|
|
3735
|
+
if (!local?.formName) return;
|
|
3736
|
+
const meta = formContext?.formGroup?.getMeta?.(local.formName);
|
|
3737
|
+
if (!meta) return;
|
|
3738
|
+
// Read current value from form context
|
|
3739
|
+
const value = formContext?.data?.[local.formName];
|
|
3740
|
+
const hasAny = hasValue(value);
|
|
3741
|
+
if (!meta.dirty && !meta.touched && !hasAny && context?.getFocused?.()) {
|
|
3742
|
+
context?.setFocused?.(false);
|
|
3743
|
+
}
|
|
3608
3744
|
});
|
|
3609
3745
|
const theChildren = children(() => props.children);
|
|
3610
3746
|
const formErrors = () => {
|
|
@@ -3634,7 +3770,8 @@ const FormField2 = props => {
|
|
|
3634
3770
|
createEffect(() => {
|
|
3635
3771
|
context.setName(props.name);
|
|
3636
3772
|
});
|
|
3637
|
-
|
|
3773
|
+
// Initialize focus state only once (was previously resetting every render and preventing focus-based floating)
|
|
3774
|
+
onMount(() => context?.setFocused?.(false));
|
|
3638
3775
|
useClickOutside([fieldRef], () => {
|
|
3639
3776
|
if (!isNullish(formContext?.data)) {
|
|
3640
3777
|
const value = formContext?.data?.[local?.formName ?? ''];
|
|
@@ -3876,7 +4013,9 @@ class FormGroup {
|
|
|
3876
4013
|
if (value instanceof FormArray) {
|
|
3877
4014
|
newData[key] = value;
|
|
3878
4015
|
} else {
|
|
3879
|
-
|
|
4016
|
+
// Deep clone initial value so external object mutations after FormGroup construction
|
|
4017
|
+
// do not mutate internal reactive store (primitive clone is cheap, objects safeguarded)
|
|
4018
|
+
newData[key] = CloneStore(value[0]);
|
|
3880
4019
|
newValidators[key] = value[1];
|
|
3881
4020
|
newErrors[key] = value[1].map(validator => ({
|
|
3882
4021
|
key: validator.errKey,
|
|
@@ -3958,7 +4097,8 @@ class FormGroup {
|
|
|
3958
4097
|
*/
|
|
3959
4098
|
hasValidator(key, errKey) {
|
|
3960
4099
|
if (this.internalDataSignal[0][key] instanceof FormArray) {
|
|
3961
|
-
|
|
4100
|
+
// BUGFIX: previously returned current error state (hasError). Now correctly checks for validator definition presence.
|
|
4101
|
+
return this.internalDataSignal[0][key].hasAnyValidator(errKey);
|
|
3962
4102
|
}
|
|
3963
4103
|
return this.errors[0]()?.[key]?.some(error => error.key === errKey) ?? false;
|
|
3964
4104
|
}
|
|
@@ -4035,7 +4175,14 @@ class FormGroup {
|
|
|
4035
4175
|
reset() {
|
|
4036
4176
|
for (const k of this.keys) {
|
|
4037
4177
|
const m = this.meta[k];
|
|
4038
|
-
|
|
4178
|
+
// If control is a FormArray, invoke its own reset to clear dynamic items & errors
|
|
4179
|
+
const current = this.internalDataSignal[0][k];
|
|
4180
|
+
if (current instanceof FormArray) {
|
|
4181
|
+
current.reset();
|
|
4182
|
+
} else {
|
|
4183
|
+
// Direct store write to avoid triggering meta touched/dirty logic in set()
|
|
4184
|
+
this.internalDataSignal[1](k, CloneStore(m.initialValue));
|
|
4185
|
+
}
|
|
4039
4186
|
m.touched = false;
|
|
4040
4187
|
m.dirty = false;
|
|
4041
4188
|
const errs = this.errors[0]();
|
|
@@ -4211,16 +4358,26 @@ var _tmpl$$1 = /*#__PURE__*/template(`<span role=alert aria-label="Cell render e
|
|
|
4211
4358
|
_tmpl$6 = /*#__PURE__*/template(`<td role=region aria-label="Dropdown row content">`),
|
|
4212
4359
|
_tmpl$7 = /*#__PURE__*/template(`<td>`);
|
|
4213
4360
|
const Table = props => {
|
|
4361
|
+
// Trigger to force row subtree reconciliation after toggling dropdown state.
|
|
4214
4362
|
const [redrawRows, setRedrawRows] = createSignal(false);
|
|
4363
|
+
// Historical mount signal (kept to avoid accidental behavioral shifts).
|
|
4215
4364
|
const [mounted, setMounted] = createSignal(false);
|
|
4216
4365
|
const tableContext = {};
|
|
4366
|
+
const debugEnabled = () => props.debugLogs === true;
|
|
4367
|
+
const debug = (...args) => {
|
|
4368
|
+
if (debugEnabled()) console.log('[TableV2 debug]', ...args);
|
|
4369
|
+
};
|
|
4217
4370
|
const getProps = () => props;
|
|
4218
4371
|
const tableData = () => {
|
|
4219
4372
|
redrawRows();
|
|
4220
4373
|
return props.data() ?? [];
|
|
4221
4374
|
};
|
|
4222
4375
|
// Create a reactive memo for columns to handle dynamic changes
|
|
4376
|
+
// Reactive columns accessor; consumers dynamically mutate columns[] causing
|
|
4377
|
+
// re-registration + style reset.
|
|
4223
4378
|
const reactiveColumns = createMemo(() => getProps().columns);
|
|
4379
|
+
// Convenience helper for frequent first-column lookups.
|
|
4380
|
+
const getFirstColumn = () => reactiveColumns()?.[0];
|
|
4224
4381
|
// Add effect to reset column styles when columns change
|
|
4225
4382
|
createEffect(() => {
|
|
4226
4383
|
const columns = reactiveColumns();
|
|
@@ -4229,6 +4386,53 @@ const Table = props => {
|
|
|
4229
4386
|
setColumnStyle({});
|
|
4230
4387
|
}
|
|
4231
4388
|
});
|
|
4389
|
+
// Preserve dropdown metadata (dropRow / dropHeader) when the first column name changes.
|
|
4390
|
+
// These flags are stored only on the first column's cell for each row. When the first
|
|
4391
|
+
// column is renamed, cells re‑register under the new key and lose the flags. We migrate
|
|
4392
|
+
// the metadata from the old first column key to the new one (or, as a fallback, from any
|
|
4393
|
+
// column in that row that still has the flags) so the table continues to recognize
|
|
4394
|
+
// dropdown header/content rows and render the region cell correctly.
|
|
4395
|
+
let prevFirstColumn;
|
|
4396
|
+
createEffect(() => {
|
|
4397
|
+
const cols = reactiveColumns();
|
|
4398
|
+
if (!cols || cols.length === 0) return;
|
|
4399
|
+
const newFirst = cols[0];
|
|
4400
|
+
if (prevFirstColumn && prevFirstColumn !== newFirst) {
|
|
4401
|
+
// Column rename detected: migrate flags.
|
|
4402
|
+
setRows(old => {
|
|
4403
|
+
const newRows = {
|
|
4404
|
+
...old
|
|
4405
|
+
};
|
|
4406
|
+
for (const r in newRows) {
|
|
4407
|
+
const row = {
|
|
4408
|
+
...newRows[r]
|
|
4409
|
+
};
|
|
4410
|
+
const prevCell = prevFirstColumn ? row[prevFirstColumn] : undefined;
|
|
4411
|
+
const newCell = row[newFirst];
|
|
4412
|
+
if (prevCell && newCell) {
|
|
4413
|
+
row[newFirst] = {
|
|
4414
|
+
...newCell,
|
|
4415
|
+
dropRow: prevCell.dropRow || newCell.dropRow || false,
|
|
4416
|
+
dropHeader: prevCell.dropHeader || newCell.dropHeader || false
|
|
4417
|
+
};
|
|
4418
|
+
} else if (newCell && !newCell.dropRow && !newCell.dropHeader) {
|
|
4419
|
+
// Fallback: scan other columns for any flagged cell (in case implementation changes)
|
|
4420
|
+
const flagged = Object.values(row).find(c => c && (c.dropRow || c.dropHeader));
|
|
4421
|
+
if (flagged && typeof flagged === 'object') {
|
|
4422
|
+
row[newFirst] = {
|
|
4423
|
+
...newCell,
|
|
4424
|
+
dropRow: flagged.dropRow || newCell.dropRow || false,
|
|
4425
|
+
dropHeader: flagged.dropHeader || newCell.dropHeader || false
|
|
4426
|
+
};
|
|
4427
|
+
}
|
|
4428
|
+
}
|
|
4429
|
+
newRows[r] = row;
|
|
4430
|
+
}
|
|
4431
|
+
return newRows;
|
|
4432
|
+
});
|
|
4433
|
+
}
|
|
4434
|
+
prevFirstColumn = newFirst;
|
|
4435
|
+
});
|
|
4232
4436
|
tableContext.setDataSource = data => {
|
|
4233
4437
|
getProps().setData?.(() => [...data]);
|
|
4234
4438
|
};
|
|
@@ -4356,7 +4560,7 @@ const Table = props => {
|
|
|
4356
4560
|
const [rowStyle, setRowStyle] = createSignal({});
|
|
4357
4561
|
tableContext.addRowStyle = (index, style, isDropRow, isDropHeader) => {
|
|
4358
4562
|
// If this is a dropdown row or dropdown header, update the row data
|
|
4359
|
-
const firstColumn =
|
|
4563
|
+
const firstColumn = getFirstColumn();
|
|
4360
4564
|
if (!isNullish(firstColumn)) {
|
|
4361
4565
|
setRows(old => {
|
|
4362
4566
|
// Get existing row data
|
|
@@ -4388,7 +4592,8 @@ const Table = props => {
|
|
|
4388
4592
|
...style,
|
|
4389
4593
|
all: {
|
|
4390
4594
|
...style?.all,
|
|
4391
|
-
isDropHeader: isDropHeader === true ? true : undefined
|
|
4595
|
+
isDropHeader: isDropHeader === true ? true : undefined,
|
|
4596
|
+
isDropRow: isDropRow === true ? true : undefined
|
|
4392
4597
|
}
|
|
4393
4598
|
}
|
|
4394
4599
|
};
|
|
@@ -4431,9 +4636,7 @@ const Table = props => {
|
|
|
4431
4636
|
return newStyles;
|
|
4432
4637
|
});
|
|
4433
4638
|
};
|
|
4434
|
-
const headerFactory = (index, column) =>
|
|
4435
|
-
return headers()?.[index]?.[column];
|
|
4436
|
-
};
|
|
4639
|
+
const headerFactory = (index, column) => headers()?.[index]?.[column];
|
|
4437
4640
|
const rowStyleFactory = rowNum => {
|
|
4438
4641
|
return rowStyle()?.[rowNum] ?? {};
|
|
4439
4642
|
};
|
|
@@ -4450,6 +4653,7 @@ const Table = props => {
|
|
|
4450
4653
|
row: row ?? -1,
|
|
4451
4654
|
dataIndex: index
|
|
4452
4655
|
};
|
|
4656
|
+
// Intentionally still surfaced (tests assert console error presence); keep single console.error.
|
|
4453
4657
|
console.error('[TableV2] Error rendering cell', error, ctx);
|
|
4454
4658
|
const custom = props.onCellError?.(error, ctx);
|
|
4455
4659
|
if (custom) return custom;
|
|
@@ -4473,11 +4677,11 @@ const Table = props => {
|
|
|
4473
4677
|
return props.getRowKey?.(dataItem, dataIndex) ?? dataIndex;
|
|
4474
4678
|
};
|
|
4475
4679
|
const getIsDropOpen = (rowNum, dataIndex, dataItem) => {
|
|
4476
|
-
//
|
|
4477
|
-
const
|
|
4478
|
-
const
|
|
4479
|
-
|
|
4480
|
-
if (
|
|
4680
|
+
// Evaluate dropdown visibility once per cell render.
|
|
4681
|
+
const rowData = rows()?.[rowNum]?.[getFirstColumn() ?? ''];
|
|
4682
|
+
const rowLevel = rowStyle()[rowNum]?.all;
|
|
4683
|
+
const hasDropFlag = rowLevel?.isDropRow || rowData?.dropRow;
|
|
4684
|
+
if (hasDropFlag) {
|
|
4481
4685
|
const key = resolveRowKey(dataItem, dataIndex);
|
|
4482
4686
|
return dropOpenStore[key] ?? false;
|
|
4483
4687
|
}
|
|
@@ -4485,11 +4689,8 @@ const Table = props => {
|
|
|
4485
4689
|
};
|
|
4486
4690
|
const dropRowStyle = (rowNum, dataIndex, dataItem) => {
|
|
4487
4691
|
// Only apply dropRow/openDropRow classes to rows that are dropdown content
|
|
4488
|
-
const
|
|
4489
|
-
|
|
4490
|
-
if (rowMeta?.dropRow) {
|
|
4491
|
-
return getIsDropOpen(rowNum, dataIndex, dataItem) ? styles$1.openDropRow : styles$1.dropRow;
|
|
4492
|
-
}
|
|
4692
|
+
const rowMeta = rows()?.[rowNum]?.[getFirstColumn() ?? ''];
|
|
4693
|
+
if (rowMeta?.dropRow) return getIsDropOpen(rowNum, dataIndex, dataItem) ? styles$1.openDropRow : styles$1.dropRow;
|
|
4493
4694
|
return '';
|
|
4494
4695
|
};
|
|
4495
4696
|
return createComponent(TableProvider, mergeProps(tableContext, {
|
|
@@ -4587,80 +4788,108 @@ const Table = props => {
|
|
|
4587
4788
|
},
|
|
4588
4789
|
children: (item, index) => createComponent(For, {
|
|
4589
4790
|
get each() {
|
|
4590
|
-
return Object.keys(rows()).map(
|
|
4791
|
+
return Object.keys(rows()).map(k => parseInt(k)).sort((a, b) => a - b);
|
|
4591
4792
|
},
|
|
4592
4793
|
children: rowNum => {
|
|
4593
4794
|
const dataIndex = index();
|
|
4594
4795
|
const resolvedRowKey = resolveRowKey(item, dataIndex);
|
|
4595
|
-
const firstColumn =
|
|
4796
|
+
const firstColumn = getFirstColumn();
|
|
4797
|
+
const rowStyleEntry = rowStyleFactory(rowNum);
|
|
4798
|
+
const rowLevelAll = rowStyleEntry?.all;
|
|
4596
4799
|
const headerCellMeta = rows()?.[rowNum]?.[firstColumn ?? ''];
|
|
4597
|
-
const
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
setDropOpenStore(resolvedKey, false);
|
|
4800
|
+
const isHeaderToggle = headerCellMeta?.dropHeader || rowLevelAll?.isDropHeader;
|
|
4801
|
+
if (isHeaderToggle && dropOpenStore[resolvedRowKey] === undefined) {
|
|
4802
|
+
setDropOpenStore(resolvedRowKey, false);
|
|
4601
4803
|
}
|
|
4602
4804
|
const firstColumnMeta = rows()?.[rowNum]?.[firstColumn ?? ''];
|
|
4603
|
-
const isContentRow = firstColumnMeta?.dropRow === true;
|
|
4604
|
-
const isOpen = getIsDropOpen(rowNum, dataIndex, item);
|
|
4605
|
-
const computedClass = (
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4805
|
+
const isContentRow = rowLevelAll?.isDropRow === true || firstColumnMeta?.dropRow === true;
|
|
4806
|
+
const isOpen = () => getIsDropOpen(rowNum, dataIndex, item);
|
|
4807
|
+
const computedClass = dropRowStyle(rowNum, dataIndex, item);
|
|
4808
|
+
if (rowNum === 1) debug('rowNum=1 dataIndex', dataIndex, 'isContentRow', isContentRow, 'isOpen', isOpen(), 'rowLevelAll', rowLevelAll);
|
|
4809
|
+
const handleToggle = e => {
|
|
4810
|
+
e.preventDefault();
|
|
4811
|
+
const newVal = !dropOpenStore[resolvedRowKey];
|
|
4812
|
+
setDropOpenStore(resolvedRowKey, newVal);
|
|
4813
|
+
setRedrawRows(r => !r);
|
|
4814
|
+
// Immediate DOM attribute sync for region cell to satisfy synchronous test expectations.
|
|
4815
|
+
try {
|
|
4816
|
+
const selector = `tbody tr[data-row-key="${resolvedRowKey}"][data-drop-row="true"] td[role="region"]`;
|
|
4817
|
+
const region = document.querySelector(selector);
|
|
4818
|
+
if (region) {
|
|
4819
|
+
region.setAttribute('data-drop-state', newVal ? 'open' : 'closed');
|
|
4820
|
+
region.setAttribute('aria-hidden', newVal ? 'false' : 'true');
|
|
4821
|
+
debug('manual region sync', selector, 'nowOpen', newVal);
|
|
4822
|
+
} else {
|
|
4823
|
+
debug('region not found for selector', selector, 'immediate after click');
|
|
4824
|
+
}
|
|
4825
|
+
} catch {/* swallow */}
|
|
4826
|
+
};
|
|
4827
|
+
const invokeOriginalRowHandler = e => {
|
|
4828
|
+
const styleAll = rowStyleEntry?.all;
|
|
4829
|
+
const original = styleAll?.__origOnClick ?? styleAll?.onClick;
|
|
4830
|
+
if (typeof original === 'function') {
|
|
4831
|
+
try {
|
|
4832
|
+
if (original.length >= 2) original(e, item);else original(e);
|
|
4833
|
+
} catch (err) {
|
|
4834
|
+
console.error('[TableV2] Row onClick handler error', err);
|
|
4835
|
+
}
|
|
4836
|
+
}
|
|
4837
|
+
};
|
|
4609
4838
|
return (() => {
|
|
4610
4839
|
var _el$10 = _tmpl$4();
|
|
4611
|
-
spread(_el$10, mergeProps(() =>
|
|
4840
|
+
spread(_el$10, mergeProps(() => rowStyleEntry?.all, {
|
|
4612
4841
|
"data-drop-row": isContentRow ? 'true' : undefined,
|
|
4613
|
-
"data-drop-open"
|
|
4842
|
+
get ["data-drop-open"]() {
|
|
4843
|
+
return isContentRow ? isOpen() ? 'true' : 'false' : undefined;
|
|
4844
|
+
},
|
|
4614
4845
|
get style() {
|
|
4615
|
-
return
|
|
4846
|
+
return rowStyleEntry?.style;
|
|
4616
4847
|
},
|
|
4617
4848
|
"data-row-key": resolvedRowKey,
|
|
4618
4849
|
"onClick": e => {
|
|
4619
|
-
if (
|
|
4620
|
-
|
|
4621
|
-
setDropOpenStore(resolvedRowKey, !dropOpenStore[resolvedRowKey]);
|
|
4622
|
-
setRedrawRows(r => !r);
|
|
4623
|
-
}
|
|
4624
|
-
// Invoke row-level handler stored by Row component. If original handler declared 2+ params pass data item.
|
|
4625
|
-
if (rowStyleFactory !== undefined) {
|
|
4626
|
-
const styleAll = rowStyleFactory(rowNum).all;
|
|
4627
|
-
const original = styleAll?.__origOnClick ?? styleAll?.onClick;
|
|
4628
|
-
if (typeof original === 'function') {
|
|
4629
|
-
try {
|
|
4630
|
-
if (original.length >= 2) {
|
|
4631
|
-
original(e, item);
|
|
4632
|
-
} else {
|
|
4633
|
-
original(e);
|
|
4634
|
-
}
|
|
4635
|
-
} catch (err) {
|
|
4636
|
-
console.error('[TableV2] Row onClick handler error', err);
|
|
4637
|
-
}
|
|
4638
|
-
}
|
|
4639
|
-
}
|
|
4850
|
+
if (isHeaderToggle) handleToggle(e);
|
|
4851
|
+
invokeOriginalRowHandler(e);
|
|
4640
4852
|
},
|
|
4641
4853
|
get ["class"]() {
|
|
4642
|
-
return `${computedClass} ${
|
|
4643
|
-
},
|
|
4644
|
-
get role() {
|
|
4645
|
-
return headerCellMeta?.dropHeader ? 'button' : undefined;
|
|
4854
|
+
return `${computedClass} ${rowStyleEntry?.all?.class ?? ''}`;
|
|
4646
4855
|
},
|
|
4856
|
+
"role": isHeaderToggle ? 'button' : undefined,
|
|
4647
4857
|
get ["aria-expanded"]() {
|
|
4648
|
-
return
|
|
4649
|
-
},
|
|
4650
|
-
get tabIndex() {
|
|
4651
|
-
return headerCellMeta?.dropHeader ? 0 : undefined;
|
|
4858
|
+
return isHeaderToggle ? dropOpenStore[resolvedRowKey] ? 'true' : 'false' : undefined;
|
|
4652
4859
|
},
|
|
4860
|
+
"tabIndex": isHeaderToggle ? 0 : undefined,
|
|
4653
4861
|
"onKeyDown": e => {
|
|
4654
4862
|
const isActivator = e.key === 'Enter' || e.key === ' ';
|
|
4655
|
-
if (isActivator &&
|
|
4863
|
+
if (isActivator && isHeaderToggle) {
|
|
4656
4864
|
e.preventDefault();
|
|
4657
|
-
|
|
4658
|
-
setRedrawRows(r => !r);
|
|
4865
|
+
handleToggle(e);
|
|
4659
4866
|
}
|
|
4660
4867
|
}
|
|
4661
4868
|
}), false, true);
|
|
4662
4869
|
insert(_el$10, () => {
|
|
4663
|
-
|
|
4870
|
+
if (isContentRow) {
|
|
4871
|
+
// Locate first available cell renderer for this dropdown row.
|
|
4872
|
+
const firstAvailableColumn = reactiveColumns().find(c => !!rows()[rowNum]?.[c]);
|
|
4873
|
+
const cellRenderer = firstAvailableColumn ? rows()[rowNum]?.[firstAvailableColumn]?.element : undefined;
|
|
4874
|
+
return (() => {
|
|
4875
|
+
var _el$11 = _tmpl$6();
|
|
4876
|
+
insert(_el$11, () => renderCell(cellRenderer, item, index(), firstAvailableColumn, rowNum));
|
|
4877
|
+
createRenderEffect(_p$ => {
|
|
4878
|
+
var _v$4 = reactiveColumns().length,
|
|
4879
|
+
_v$5 = isOpen() ? 'false' : 'true',
|
|
4880
|
+
_v$6 = isOpen() ? 'open' : 'closed';
|
|
4881
|
+
_v$4 !== _p$.e && setAttribute(_el$11, "colspan", _p$.e = _v$4);
|
|
4882
|
+
_v$5 !== _p$.t && setAttribute(_el$11, "aria-hidden", _p$.t = _v$5);
|
|
4883
|
+
_v$6 !== _p$.a && setAttribute(_el$11, "data-drop-state", _p$.a = _v$6);
|
|
4884
|
+
return _p$;
|
|
4885
|
+
}, {
|
|
4886
|
+
e: undefined,
|
|
4887
|
+
t: undefined,
|
|
4888
|
+
a: undefined
|
|
4889
|
+
});
|
|
4890
|
+
return _el$11;
|
|
4891
|
+
})();
|
|
4892
|
+
}
|
|
4664
4893
|
return createComponent(For, {
|
|
4665
4894
|
get each() {
|
|
4666
4895
|
return reactiveColumns();
|
|
@@ -4668,31 +4897,6 @@ const Table = props => {
|
|
|
4668
4897
|
children: column => {
|
|
4669
4898
|
const tableSpot = rows()[rowNum]?.[column];
|
|
4670
4899
|
const cell = tableSpot?.element;
|
|
4671
|
-
// Dropdown content row: render only once (first occurrence) spanning all columns
|
|
4672
|
-
if (tableSpot?.dropRow && !renderedDropContent) {
|
|
4673
|
-
renderedDropContent = true;
|
|
4674
|
-
return (() => {
|
|
4675
|
-
var _el$11 = _tmpl$6();
|
|
4676
|
-
insert(_el$11, () => renderCell(cell, item, index(), column, rowNum));
|
|
4677
|
-
createRenderEffect(_p$ => {
|
|
4678
|
-
var _v$4 = reactiveColumns().length,
|
|
4679
|
-
_v$5 = getIsDropOpen(rowNum, dataIndex, item) ? 'false' : 'true',
|
|
4680
|
-
_v$6 = getIsDropOpen(rowNum, dataIndex, item) ? 'open' : 'closed';
|
|
4681
|
-
_v$4 !== _p$.e && setAttribute(_el$11, "colspan", _p$.e = _v$4);
|
|
4682
|
-
_v$5 !== _p$.t && setAttribute(_el$11, "aria-hidden", _p$.t = _v$5);
|
|
4683
|
-
_v$6 !== _p$.a && setAttribute(_el$11, "data-drop-state", _p$.a = _v$6);
|
|
4684
|
-
return _p$;
|
|
4685
|
-
}, {
|
|
4686
|
-
e: undefined,
|
|
4687
|
-
t: undefined,
|
|
4688
|
-
a: undefined
|
|
4689
|
-
});
|
|
4690
|
-
return _el$11;
|
|
4691
|
-
})();
|
|
4692
|
-
}
|
|
4693
|
-
if (tableSpot?.dropRow) {
|
|
4694
|
-
return []; // Skip duplicate columns within same dropdown row
|
|
4695
|
-
}
|
|
4696
4900
|
return !isNullish(cell) ? (() => {
|
|
4697
4901
|
var _el$12 = _tmpl$7();
|
|
4698
4902
|
spread(_el$12, mergeProps(() => cellStyle?.()?.[rowNum]?.[column]?.all, {
|