coles-solid-library 0.3.10 → 0.4.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.
|
@@ -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
|
|
|
@@ -2884,18 +2946,29 @@ const Menu = props => {
|
|
|
2884
2946
|
}
|
|
2885
2947
|
return '';
|
|
2886
2948
|
};
|
|
2949
|
+
// Persist the window manager entry so we unregister the same reference we registered.
|
|
2950
|
+
let wmEntry;
|
|
2887
2951
|
createEffect(() => {
|
|
2888
|
-
const
|
|
2889
|
-
|
|
2890
|
-
|
|
2891
|
-
|
|
2952
|
+
const visible = props.show[0]();
|
|
2953
|
+
const el = menuRef();
|
|
2954
|
+
if (visible && el) {
|
|
2955
|
+
if (!wmEntry) {
|
|
2956
|
+
wmEntry = {
|
|
2957
|
+
element: el,
|
|
2958
|
+
onClickOutside: () => props.show[1](false)
|
|
2959
|
+
};
|
|
2960
|
+
} else {
|
|
2961
|
+
// update element reference if it changed (shouldn't usually)
|
|
2962
|
+
wmEntry.element = el;
|
|
2892
2963
|
}
|
|
2893
|
-
|
|
2894
|
-
|
|
2895
|
-
|
|
2896
|
-
|
|
2897
|
-
|
|
2898
|
-
|
|
2964
|
+
// Avoid duplicate registrations: only register if not already last added.
|
|
2965
|
+
// Heuristic: assign zIndex each time we show and register once.
|
|
2966
|
+
wmEntry.element.style.zIndex = `${999 + getEntryAmount()}`;
|
|
2967
|
+
// If not already in entries array, register (simple containment check by identity).
|
|
2968
|
+
// We can't read the internal array directly besides length heuristic, so best effort:
|
|
2969
|
+
registerWindowManager(wmEntry);
|
|
2970
|
+
} else if (!visible && wmEntry) {
|
|
2971
|
+
unregisterWindowManager(wmEntry);
|
|
2899
2972
|
}
|
|
2900
2973
|
});
|
|
2901
2974
|
const getAndSetPosition = (menu, anchor) => {
|
|
@@ -2946,6 +3019,10 @@ const Menu = props => {
|
|
|
2946
3019
|
if (!isNullish(menuContext)) {
|
|
2947
3020
|
menuContext.unregisterWithParent(menuRef());
|
|
2948
3021
|
}
|
|
3022
|
+
if (wmEntry) {
|
|
3023
|
+
unregisterWindowManager(wmEntry);
|
|
3024
|
+
wmEntry = undefined;
|
|
3025
|
+
}
|
|
2949
3026
|
});
|
|
2950
3027
|
return createComponent(Portal, {
|
|
2951
3028
|
get children() {
|
|
@@ -3310,6 +3387,9 @@ class FormArray {
|
|
|
3310
3387
|
* @private
|
|
3311
3388
|
*/
|
|
3312
3389
|
setValues;
|
|
3390
|
+
// Snapshots of initial validation defs & values for reset purposes
|
|
3391
|
+
_initialValidationDefs;
|
|
3392
|
+
_initialValuesSnapshot;
|
|
3313
3393
|
/**
|
|
3314
3394
|
* Creates an instance of FormArray.
|
|
3315
3395
|
*
|
|
@@ -3325,6 +3405,29 @@ class FormArray {
|
|
|
3325
3405
|
this.setValidation = setValid;
|
|
3326
3406
|
this.internalValues = values;
|
|
3327
3407
|
this.setValues = setValues;
|
|
3408
|
+
this._initialValidationDefs = [...arrayValidation[0]];
|
|
3409
|
+
this._initialValuesSnapshot = Array.isArray(initialValues) ? Clone(initialValues) : Clone([initialValues]);
|
|
3410
|
+
}
|
|
3411
|
+
/**
|
|
3412
|
+
* Returns true if any validator (array-level or control-level) exists matching the provided errKey.
|
|
3413
|
+
* If no errKey provided, returns true when any validator exists at all.
|
|
3414
|
+
*/
|
|
3415
|
+
hasAnyValidator(errKey) {
|
|
3416
|
+
if (!errKey) {
|
|
3417
|
+
return this.internalArrayValidation.length > 0 || this.internalValidation.some(v => v[1].length > 0);
|
|
3418
|
+
}
|
|
3419
|
+
const inArray = this.internalArrayValidation.some(v => v.errKey === errKey);
|
|
3420
|
+
if (inArray) return true;
|
|
3421
|
+
return this.internalValidation.some(v => v[1].some(val => val.errKey === errKey));
|
|
3422
|
+
}
|
|
3423
|
+
/**
|
|
3424
|
+
* Resets the array to its initial validation definitions and values.
|
|
3425
|
+
* Clears any accumulated errors and added dynamic controls.
|
|
3426
|
+
*/
|
|
3427
|
+
reset() {
|
|
3428
|
+
this.setValidation(() => [...this._initialValidationDefs]);
|
|
3429
|
+
this.setValues(() => Clone(this._initialValuesSnapshot));
|
|
3430
|
+
this.errors = [];
|
|
3328
3431
|
}
|
|
3329
3432
|
/**
|
|
3330
3433
|
* Gets the current values of the form controls in the array.
|
|
@@ -3332,7 +3435,9 @@ class FormArray {
|
|
|
3332
3435
|
* @returns A cloned copy of the values array.
|
|
3333
3436
|
*/
|
|
3334
3437
|
get() {
|
|
3335
|
-
|
|
3438
|
+
// Return a cloned snapshot so external mutation of returned array/value objects
|
|
3439
|
+
// can't silently mutate internal reactive state or bypass validation/meta tracking.
|
|
3440
|
+
return Clone(this.internalValues);
|
|
3336
3441
|
}
|
|
3337
3442
|
/**
|
|
3338
3443
|
* Gets the value of a specific control in the array.
|
|
@@ -3596,15 +3701,45 @@ const FormField2 = props => {
|
|
|
3596
3701
|
if (Array.isArray(val) && val.length === 0) return false;
|
|
3597
3702
|
return true;
|
|
3598
3703
|
};
|
|
3704
|
+
// Track dirty meta so legend can float once a field has been modified even if some components
|
|
3705
|
+
// didn't properly synchronize their internal value signals yet.
|
|
3706
|
+
const isDirty = () => {
|
|
3707
|
+
if (!local?.formName) return false;
|
|
3708
|
+
try {
|
|
3709
|
+
return !!formContext?.formGroup?.getMeta?.(local.formName)?.dirty;
|
|
3710
|
+
} catch {
|
|
3711
|
+
return false;
|
|
3712
|
+
}
|
|
3713
|
+
};
|
|
3599
3714
|
const shouldFloat = createMemo(() => {
|
|
3600
3715
|
let currentVal;
|
|
3716
|
+
// Prefer form context store value
|
|
3601
3717
|
if (hasValue(formContext?.data?.[local?.formName ?? ''])) {
|
|
3602
3718
|
currentVal = formContext?.data?.[local?.formName ?? ''];
|
|
3603
3719
|
}
|
|
3720
|
+
// Fallback to local provider value (some components set this first)
|
|
3604
3721
|
if (hasValue(context?.getValue?.())) {
|
|
3605
3722
|
currentVal = context?.getValue?.();
|
|
3606
3723
|
}
|
|
3607
|
-
|
|
3724
|
+
// Float when value present, focused, or control marked dirty (programmatic set)
|
|
3725
|
+
// Access meta directly for reactivity on dirty change
|
|
3726
|
+
const metaDirty = local?.formName ? !!formContext?.formGroup?.getMeta?.(local.formName)?.dirty : false;
|
|
3727
|
+
// Float when there is a value, or focused, or meta marked dirty (programmatic set),
|
|
3728
|
+
// or legacy input marked dirty-on-focus even if still empty (metaDirty && no value yet)
|
|
3729
|
+
if (metaDirty && !hasValue(currentVal)) return true;
|
|
3730
|
+
return hasValue(currentVal) || context?.getFocused?.() || isDirty();
|
|
3731
|
+
});
|
|
3732
|
+
// Effect: if underlying meta resets to pristine and value cleared, ensure focused state cleared
|
|
3733
|
+
createEffect(() => {
|
|
3734
|
+
if (!local?.formName) return;
|
|
3735
|
+
const meta = formContext?.formGroup?.getMeta?.(local.formName);
|
|
3736
|
+
if (!meta) return;
|
|
3737
|
+
// Read current value from form context
|
|
3738
|
+
const value = formContext?.data?.[local.formName];
|
|
3739
|
+
const hasAny = hasValue(value);
|
|
3740
|
+
if (!meta.dirty && !meta.touched && !hasAny && context?.getFocused?.()) {
|
|
3741
|
+
context?.setFocused?.(false);
|
|
3742
|
+
}
|
|
3608
3743
|
});
|
|
3609
3744
|
const theChildren = children(() => props.children);
|
|
3610
3745
|
const formErrors = () => {
|
|
@@ -3634,7 +3769,8 @@ const FormField2 = props => {
|
|
|
3634
3769
|
createEffect(() => {
|
|
3635
3770
|
context.setName(props.name);
|
|
3636
3771
|
});
|
|
3637
|
-
|
|
3772
|
+
// Initialize focus state only once (was previously resetting every render and preventing focus-based floating)
|
|
3773
|
+
onMount(() => context?.setFocused?.(false));
|
|
3638
3774
|
useClickOutside([fieldRef], () => {
|
|
3639
3775
|
if (!isNullish(formContext?.data)) {
|
|
3640
3776
|
const value = formContext?.data?.[local?.formName ?? ''];
|
|
@@ -3876,7 +4012,9 @@ class FormGroup {
|
|
|
3876
4012
|
if (value instanceof FormArray) {
|
|
3877
4013
|
newData[key] = value;
|
|
3878
4014
|
} else {
|
|
3879
|
-
|
|
4015
|
+
// Deep clone initial value so external object mutations after FormGroup construction
|
|
4016
|
+
// do not mutate internal reactive store (primitive clone is cheap, objects safeguarded)
|
|
4017
|
+
newData[key] = CloneStore(value[0]);
|
|
3880
4018
|
newValidators[key] = value[1];
|
|
3881
4019
|
newErrors[key] = value[1].map(validator => ({
|
|
3882
4020
|
key: validator.errKey,
|
|
@@ -3958,7 +4096,8 @@ class FormGroup {
|
|
|
3958
4096
|
*/
|
|
3959
4097
|
hasValidator(key, errKey) {
|
|
3960
4098
|
if (this.internalDataSignal[0][key] instanceof FormArray) {
|
|
3961
|
-
|
|
4099
|
+
// BUGFIX: previously returned current error state (hasError). Now correctly checks for validator definition presence.
|
|
4100
|
+
return this.internalDataSignal[0][key].hasAnyValidator(errKey);
|
|
3962
4101
|
}
|
|
3963
4102
|
return this.errors[0]()?.[key]?.some(error => error.key === errKey) ?? false;
|
|
3964
4103
|
}
|
|
@@ -4035,7 +4174,14 @@ class FormGroup {
|
|
|
4035
4174
|
reset() {
|
|
4036
4175
|
for (const k of this.keys) {
|
|
4037
4176
|
const m = this.meta[k];
|
|
4038
|
-
|
|
4177
|
+
// If control is a FormArray, invoke its own reset to clear dynamic items & errors
|
|
4178
|
+
const current = this.internalDataSignal[0][k];
|
|
4179
|
+
if (current instanceof FormArray) {
|
|
4180
|
+
current.reset();
|
|
4181
|
+
} else {
|
|
4182
|
+
// Direct store write to avoid triggering meta touched/dirty logic in set()
|
|
4183
|
+
this.internalDataSignal[1](k, CloneStore(m.initialValue));
|
|
4184
|
+
}
|
|
4039
4185
|
m.touched = false;
|
|
4040
4186
|
m.dirty = false;
|
|
4041
4187
|
const errs = this.errors[0]();
|
|
@@ -4211,16 +4357,26 @@ var _tmpl$$1 = /*#__PURE__*/template(`<span role=alert aria-label="Cell render e
|
|
|
4211
4357
|
_tmpl$6 = /*#__PURE__*/template(`<td role=region aria-label="Dropdown row content">`),
|
|
4212
4358
|
_tmpl$7 = /*#__PURE__*/template(`<td>`);
|
|
4213
4359
|
const Table = props => {
|
|
4360
|
+
// Trigger to force row subtree reconciliation after toggling dropdown state.
|
|
4214
4361
|
const [redrawRows, setRedrawRows] = createSignal(false);
|
|
4362
|
+
// Historical mount signal (kept to avoid accidental behavioral shifts).
|
|
4215
4363
|
const [mounted, setMounted] = createSignal(false);
|
|
4216
4364
|
const tableContext = {};
|
|
4365
|
+
const debugEnabled = () => props.debugLogs === true;
|
|
4366
|
+
const debug = (...args) => {
|
|
4367
|
+
if (debugEnabled()) console.log('[TableV2 debug]', ...args);
|
|
4368
|
+
};
|
|
4217
4369
|
const getProps = () => props;
|
|
4218
4370
|
const tableData = () => {
|
|
4219
4371
|
redrawRows();
|
|
4220
4372
|
return props.data() ?? [];
|
|
4221
4373
|
};
|
|
4222
4374
|
// Create a reactive memo for columns to handle dynamic changes
|
|
4375
|
+
// Reactive columns accessor; consumers dynamically mutate columns[] causing
|
|
4376
|
+
// re-registration + style reset.
|
|
4223
4377
|
const reactiveColumns = createMemo(() => getProps().columns);
|
|
4378
|
+
// Convenience helper for frequent first-column lookups.
|
|
4379
|
+
const getFirstColumn = () => reactiveColumns()?.[0];
|
|
4224
4380
|
// Add effect to reset column styles when columns change
|
|
4225
4381
|
createEffect(() => {
|
|
4226
4382
|
const columns = reactiveColumns();
|
|
@@ -4229,6 +4385,53 @@ const Table = props => {
|
|
|
4229
4385
|
setColumnStyle({});
|
|
4230
4386
|
}
|
|
4231
4387
|
});
|
|
4388
|
+
// Preserve dropdown metadata (dropRow / dropHeader) when the first column name changes.
|
|
4389
|
+
// These flags are stored only on the first column's cell for each row. When the first
|
|
4390
|
+
// column is renamed, cells re‑register under the new key and lose the flags. We migrate
|
|
4391
|
+
// the metadata from the old first column key to the new one (or, as a fallback, from any
|
|
4392
|
+
// column in that row that still has the flags) so the table continues to recognize
|
|
4393
|
+
// dropdown header/content rows and render the region cell correctly.
|
|
4394
|
+
let prevFirstColumn;
|
|
4395
|
+
createEffect(() => {
|
|
4396
|
+
const cols = reactiveColumns();
|
|
4397
|
+
if (!cols || cols.length === 0) return;
|
|
4398
|
+
const newFirst = cols[0];
|
|
4399
|
+
if (prevFirstColumn && prevFirstColumn !== newFirst) {
|
|
4400
|
+
// Column rename detected: migrate flags.
|
|
4401
|
+
setRows(old => {
|
|
4402
|
+
const newRows = {
|
|
4403
|
+
...old
|
|
4404
|
+
};
|
|
4405
|
+
for (const r in newRows) {
|
|
4406
|
+
const row = {
|
|
4407
|
+
...newRows[r]
|
|
4408
|
+
};
|
|
4409
|
+
const prevCell = prevFirstColumn ? row[prevFirstColumn] : undefined;
|
|
4410
|
+
const newCell = row[newFirst];
|
|
4411
|
+
if (prevCell && newCell) {
|
|
4412
|
+
row[newFirst] = {
|
|
4413
|
+
...newCell,
|
|
4414
|
+
dropRow: prevCell.dropRow || newCell.dropRow || false,
|
|
4415
|
+
dropHeader: prevCell.dropHeader || newCell.dropHeader || false
|
|
4416
|
+
};
|
|
4417
|
+
} else if (newCell && !newCell.dropRow && !newCell.dropHeader) {
|
|
4418
|
+
// Fallback: scan other columns for any flagged cell (in case implementation changes)
|
|
4419
|
+
const flagged = Object.values(row).find(c => c && (c.dropRow || c.dropHeader));
|
|
4420
|
+
if (flagged && typeof flagged === 'object') {
|
|
4421
|
+
row[newFirst] = {
|
|
4422
|
+
...newCell,
|
|
4423
|
+
dropRow: flagged.dropRow || newCell.dropRow || false,
|
|
4424
|
+
dropHeader: flagged.dropHeader || newCell.dropHeader || false
|
|
4425
|
+
};
|
|
4426
|
+
}
|
|
4427
|
+
}
|
|
4428
|
+
newRows[r] = row;
|
|
4429
|
+
}
|
|
4430
|
+
return newRows;
|
|
4431
|
+
});
|
|
4432
|
+
}
|
|
4433
|
+
prevFirstColumn = newFirst;
|
|
4434
|
+
});
|
|
4232
4435
|
tableContext.setDataSource = data => {
|
|
4233
4436
|
getProps().setData?.(() => [...data]);
|
|
4234
4437
|
};
|
|
@@ -4356,7 +4559,7 @@ const Table = props => {
|
|
|
4356
4559
|
const [rowStyle, setRowStyle] = createSignal({});
|
|
4357
4560
|
tableContext.addRowStyle = (index, style, isDropRow, isDropHeader) => {
|
|
4358
4561
|
// If this is a dropdown row or dropdown header, update the row data
|
|
4359
|
-
const firstColumn =
|
|
4562
|
+
const firstColumn = getFirstColumn();
|
|
4360
4563
|
if (!isNullish(firstColumn)) {
|
|
4361
4564
|
setRows(old => {
|
|
4362
4565
|
// Get existing row data
|
|
@@ -4388,7 +4591,8 @@ const Table = props => {
|
|
|
4388
4591
|
...style,
|
|
4389
4592
|
all: {
|
|
4390
4593
|
...style?.all,
|
|
4391
|
-
isDropHeader: isDropHeader === true ? true : undefined
|
|
4594
|
+
isDropHeader: isDropHeader === true ? true : undefined,
|
|
4595
|
+
isDropRow: isDropRow === true ? true : undefined
|
|
4392
4596
|
}
|
|
4393
4597
|
}
|
|
4394
4598
|
};
|
|
@@ -4431,9 +4635,7 @@ const Table = props => {
|
|
|
4431
4635
|
return newStyles;
|
|
4432
4636
|
});
|
|
4433
4637
|
};
|
|
4434
|
-
const headerFactory = (index, column) =>
|
|
4435
|
-
return headers()?.[index]?.[column];
|
|
4436
|
-
};
|
|
4638
|
+
const headerFactory = (index, column) => headers()?.[index]?.[column];
|
|
4437
4639
|
const rowStyleFactory = rowNum => {
|
|
4438
4640
|
return rowStyle()?.[rowNum] ?? {};
|
|
4439
4641
|
};
|
|
@@ -4450,6 +4652,7 @@ const Table = props => {
|
|
|
4450
4652
|
row: row ?? -1,
|
|
4451
4653
|
dataIndex: index
|
|
4452
4654
|
};
|
|
4655
|
+
// Intentionally still surfaced (tests assert console error presence); keep single console.error.
|
|
4453
4656
|
console.error('[TableV2] Error rendering cell', error, ctx);
|
|
4454
4657
|
const custom = props.onCellError?.(error, ctx);
|
|
4455
4658
|
if (custom) return custom;
|
|
@@ -4473,11 +4676,11 @@ const Table = props => {
|
|
|
4473
4676
|
return props.getRowKey?.(dataItem, dataIndex) ?? dataIndex;
|
|
4474
4677
|
};
|
|
4475
4678
|
const getIsDropOpen = (rowNum, dataIndex, dataItem) => {
|
|
4476
|
-
//
|
|
4477
|
-
const
|
|
4478
|
-
const
|
|
4479
|
-
|
|
4480
|
-
if (
|
|
4679
|
+
// Evaluate dropdown visibility once per cell render.
|
|
4680
|
+
const rowData = rows()?.[rowNum]?.[getFirstColumn() ?? ''];
|
|
4681
|
+
const rowLevel = rowStyle()[rowNum]?.all;
|
|
4682
|
+
const hasDropFlag = rowLevel?.isDropRow || rowData?.dropRow;
|
|
4683
|
+
if (hasDropFlag) {
|
|
4481
4684
|
const key = resolveRowKey(dataItem, dataIndex);
|
|
4482
4685
|
return dropOpenStore[key] ?? false;
|
|
4483
4686
|
}
|
|
@@ -4485,11 +4688,8 @@ const Table = props => {
|
|
|
4485
4688
|
};
|
|
4486
4689
|
const dropRowStyle = (rowNum, dataIndex, dataItem) => {
|
|
4487
4690
|
// 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
|
-
}
|
|
4691
|
+
const rowMeta = rows()?.[rowNum]?.[getFirstColumn() ?? ''];
|
|
4692
|
+
if (rowMeta?.dropRow) return getIsDropOpen(rowNum, dataIndex, dataItem) ? styles$1.openDropRow : styles$1.dropRow;
|
|
4493
4693
|
return '';
|
|
4494
4694
|
};
|
|
4495
4695
|
return createComponent(TableProvider, mergeProps(tableContext, {
|
|
@@ -4587,80 +4787,108 @@ const Table = props => {
|
|
|
4587
4787
|
},
|
|
4588
4788
|
children: (item, index) => createComponent(For, {
|
|
4589
4789
|
get each() {
|
|
4590
|
-
return Object.keys(rows()).map(
|
|
4790
|
+
return Object.keys(rows()).map(k => parseInt(k)).sort((a, b) => a - b);
|
|
4591
4791
|
},
|
|
4592
4792
|
children: rowNum => {
|
|
4593
4793
|
const dataIndex = index();
|
|
4594
4794
|
const resolvedRowKey = resolveRowKey(item, dataIndex);
|
|
4595
|
-
const firstColumn =
|
|
4795
|
+
const firstColumn = getFirstColumn();
|
|
4796
|
+
const rowStyleEntry = rowStyleFactory(rowNum);
|
|
4797
|
+
const rowLevelAll = rowStyleEntry?.all;
|
|
4596
4798
|
const headerCellMeta = rows()?.[rowNum]?.[firstColumn ?? ''];
|
|
4597
|
-
const
|
|
4598
|
-
|
|
4599
|
-
|
|
4600
|
-
setDropOpenStore(resolvedKey, false);
|
|
4799
|
+
const isHeaderToggle = headerCellMeta?.dropHeader || rowLevelAll?.isDropHeader;
|
|
4800
|
+
if (isHeaderToggle && dropOpenStore[resolvedRowKey] === undefined) {
|
|
4801
|
+
setDropOpenStore(resolvedRowKey, false);
|
|
4601
4802
|
}
|
|
4602
4803
|
const firstColumnMeta = rows()?.[rowNum]?.[firstColumn ?? ''];
|
|
4603
|
-
const isContentRow = firstColumnMeta?.dropRow === true;
|
|
4604
|
-
const isOpen = getIsDropOpen(rowNum, dataIndex, item);
|
|
4605
|
-
const computedClass = (
|
|
4606
|
-
|
|
4607
|
-
|
|
4608
|
-
|
|
4804
|
+
const isContentRow = rowLevelAll?.isDropRow === true || firstColumnMeta?.dropRow === true;
|
|
4805
|
+
const isOpen = () => getIsDropOpen(rowNum, dataIndex, item);
|
|
4806
|
+
const computedClass = dropRowStyle(rowNum, dataIndex, item);
|
|
4807
|
+
if (rowNum === 1) debug('rowNum=1 dataIndex', dataIndex, 'isContentRow', isContentRow, 'isOpen', isOpen(), 'rowLevelAll', rowLevelAll);
|
|
4808
|
+
const handleToggle = e => {
|
|
4809
|
+
e.preventDefault();
|
|
4810
|
+
const newVal = !dropOpenStore[resolvedRowKey];
|
|
4811
|
+
setDropOpenStore(resolvedRowKey, newVal);
|
|
4812
|
+
setRedrawRows(r => !r);
|
|
4813
|
+
// Immediate DOM attribute sync for region cell to satisfy synchronous test expectations.
|
|
4814
|
+
try {
|
|
4815
|
+
const selector = `tbody tr[data-row-key="${resolvedRowKey}"][data-drop-row="true"] td[role="region"]`;
|
|
4816
|
+
const region = document.querySelector(selector);
|
|
4817
|
+
if (region) {
|
|
4818
|
+
region.setAttribute('data-drop-state', newVal ? 'open' : 'closed');
|
|
4819
|
+
region.setAttribute('aria-hidden', newVal ? 'false' : 'true');
|
|
4820
|
+
debug('manual region sync', selector, 'nowOpen', newVal);
|
|
4821
|
+
} else {
|
|
4822
|
+
debug('region not found for selector', selector, 'immediate after click');
|
|
4823
|
+
}
|
|
4824
|
+
} catch {/* swallow */}
|
|
4825
|
+
};
|
|
4826
|
+
const invokeOriginalRowHandler = e => {
|
|
4827
|
+
const styleAll = rowStyleEntry?.all;
|
|
4828
|
+
const original = styleAll?.__origOnClick ?? styleAll?.onClick;
|
|
4829
|
+
if (typeof original === 'function') {
|
|
4830
|
+
try {
|
|
4831
|
+
if (original.length >= 2) original(e, item);else original(e);
|
|
4832
|
+
} catch (err) {
|
|
4833
|
+
console.error('[TableV2] Row onClick handler error', err);
|
|
4834
|
+
}
|
|
4835
|
+
}
|
|
4836
|
+
};
|
|
4609
4837
|
return (() => {
|
|
4610
4838
|
var _el$10 = _tmpl$4();
|
|
4611
|
-
spread(_el$10, mergeProps(() =>
|
|
4839
|
+
spread(_el$10, mergeProps(() => rowStyleEntry?.all, {
|
|
4612
4840
|
"data-drop-row": isContentRow ? 'true' : undefined,
|
|
4613
|
-
"data-drop-open"
|
|
4841
|
+
get ["data-drop-open"]() {
|
|
4842
|
+
return isContentRow ? isOpen() ? 'true' : 'false' : undefined;
|
|
4843
|
+
},
|
|
4614
4844
|
get style() {
|
|
4615
|
-
return
|
|
4845
|
+
return rowStyleEntry?.style;
|
|
4616
4846
|
},
|
|
4617
4847
|
"data-row-key": resolvedRowKey,
|
|
4618
4848
|
"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
|
-
}
|
|
4849
|
+
if (isHeaderToggle) handleToggle(e);
|
|
4850
|
+
invokeOriginalRowHandler(e);
|
|
4640
4851
|
},
|
|
4641
4852
|
get ["class"]() {
|
|
4642
|
-
return `${computedClass} ${
|
|
4643
|
-
},
|
|
4644
|
-
get role() {
|
|
4645
|
-
return headerCellMeta?.dropHeader ? 'button' : undefined;
|
|
4853
|
+
return `${computedClass} ${rowStyleEntry?.all?.class ?? ''}`;
|
|
4646
4854
|
},
|
|
4855
|
+
"role": isHeaderToggle ? 'button' : undefined,
|
|
4647
4856
|
get ["aria-expanded"]() {
|
|
4648
|
-
return
|
|
4649
|
-
},
|
|
4650
|
-
get tabIndex() {
|
|
4651
|
-
return headerCellMeta?.dropHeader ? 0 : undefined;
|
|
4857
|
+
return isHeaderToggle ? dropOpenStore[resolvedRowKey] ? 'true' : 'false' : undefined;
|
|
4652
4858
|
},
|
|
4859
|
+
"tabIndex": isHeaderToggle ? 0 : undefined,
|
|
4653
4860
|
"onKeyDown": e => {
|
|
4654
4861
|
const isActivator = e.key === 'Enter' || e.key === ' ';
|
|
4655
|
-
if (isActivator &&
|
|
4862
|
+
if (isActivator && isHeaderToggle) {
|
|
4656
4863
|
e.preventDefault();
|
|
4657
|
-
|
|
4658
|
-
setRedrawRows(r => !r);
|
|
4864
|
+
handleToggle(e);
|
|
4659
4865
|
}
|
|
4660
4866
|
}
|
|
4661
4867
|
}), false, true);
|
|
4662
4868
|
insert(_el$10, () => {
|
|
4663
|
-
|
|
4869
|
+
if (isContentRow) {
|
|
4870
|
+
// Locate first available cell renderer for this dropdown row.
|
|
4871
|
+
const firstAvailableColumn = reactiveColumns().find(c => !!rows()[rowNum]?.[c]);
|
|
4872
|
+
const cellRenderer = firstAvailableColumn ? rows()[rowNum]?.[firstAvailableColumn]?.element : undefined;
|
|
4873
|
+
return (() => {
|
|
4874
|
+
var _el$11 = _tmpl$6();
|
|
4875
|
+
insert(_el$11, () => renderCell(cellRenderer, item, index(), firstAvailableColumn, rowNum));
|
|
4876
|
+
createRenderEffect(_p$ => {
|
|
4877
|
+
var _v$4 = reactiveColumns().length,
|
|
4878
|
+
_v$5 = isOpen() ? 'false' : 'true',
|
|
4879
|
+
_v$6 = isOpen() ? 'open' : 'closed';
|
|
4880
|
+
_v$4 !== _p$.e && setAttribute(_el$11, "colspan", _p$.e = _v$4);
|
|
4881
|
+
_v$5 !== _p$.t && setAttribute(_el$11, "aria-hidden", _p$.t = _v$5);
|
|
4882
|
+
_v$6 !== _p$.a && setAttribute(_el$11, "data-drop-state", _p$.a = _v$6);
|
|
4883
|
+
return _p$;
|
|
4884
|
+
}, {
|
|
4885
|
+
e: undefined,
|
|
4886
|
+
t: undefined,
|
|
4887
|
+
a: undefined
|
|
4888
|
+
});
|
|
4889
|
+
return _el$11;
|
|
4890
|
+
})();
|
|
4891
|
+
}
|
|
4664
4892
|
return createComponent(For, {
|
|
4665
4893
|
get each() {
|
|
4666
4894
|
return reactiveColumns();
|
|
@@ -4668,31 +4896,6 @@ const Table = props => {
|
|
|
4668
4896
|
children: column => {
|
|
4669
4897
|
const tableSpot = rows()[rowNum]?.[column];
|
|
4670
4898
|
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
4899
|
return !isNullish(cell) ? (() => {
|
|
4697
4900
|
var _el$12 = _tmpl$7();
|
|
4698
4901
|
spread(_el$12, mergeProps(() => cellStyle?.()?.[rowNum]?.[column]?.all, {
|