@dsn-starter-kit/components-react 3.5.0 → 4.1.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.
- package/dist/Card/Card.d.mts +86 -21
- package/dist/Card/Card.d.mts.map +1 -1
- package/dist/Card/Card2.mjs +100 -17
- package/dist/Card/Card2.mjs.map +1 -1
- package/dist/Card/index.d.mts +2 -2
- package/dist/DateInput/DateInput.d.mts +1 -1
- package/dist/DateInput/DateInput2.mjs +1 -1
- package/dist/DateInput/DateInput2.mjs.map +1 -1
- package/dist/DateInputGroup/DateInputGroup2.mjs +3 -3
- package/dist/DateInputGroup/DateInputGroup2.mjs.map +1 -1
- package/dist/Drawer/Drawer.d.mts +5 -2
- package/dist/Drawer/Drawer.d.mts.map +1 -1
- package/dist/Drawer/Drawer2.mjs +18 -3
- package/dist/Drawer/Drawer2.mjs.map +1 -1
- package/dist/EmailInput/EmailInput.d.mts +10 -5
- package/dist/EmailInput/EmailInput.d.mts.map +1 -1
- package/dist/EmailInput/EmailInput2.mjs +2 -2
- package/dist/EmailInput/EmailInput2.mjs.map +1 -1
- package/dist/ModalDialog/ModalDialog.d.mts +13 -3
- package/dist/ModalDialog/ModalDialog.d.mts.map +1 -1
- package/dist/ModalDialog/ModalDialog2.mjs +10 -3
- package/dist/ModalDialog/ModalDialog2.mjs.map +1 -1
- package/dist/NumberInput/NumberInput.d.mts +4 -4
- package/dist/NumberInput/NumberInput.d.mts.map +1 -1
- package/dist/NumberInput/NumberInput2.mjs +2 -2
- package/dist/NumberInput/NumberInput2.mjs.map +1 -1
- package/dist/PageHeader/PageHeader.d.mts.map +1 -1
- package/dist/PageHeader/PageHeader2.mjs +19 -2
- package/dist/PageHeader/PageHeader2.mjs.map +1 -1
- package/dist/PasswordInput/PasswordInput.d.mts +10 -5
- package/dist/PasswordInput/PasswordInput.d.mts.map +1 -1
- package/dist/PasswordInput/PasswordInput2.mjs +2 -2
- package/dist/PasswordInput/PasswordInput2.mjs.map +1 -1
- package/dist/Popover/Popover.d.mts +16 -23
- package/dist/Popover/Popover.d.mts.map +1 -1
- package/dist/Popover/Popover2.mjs +33 -119
- package/dist/Popover/Popover2.mjs.map +1 -1
- package/dist/PopoverMenu/PopoverMenu.d.mts +62 -0
- package/dist/PopoverMenu/PopoverMenu.d.mts.map +1 -0
- package/dist/PopoverMenu/PopoverMenu2.mjs +66 -0
- package/dist/PopoverMenu/PopoverMenu2.mjs.map +1 -0
- package/dist/PopoverMenu/index.d.mts +2 -0
- package/dist/SearchInput/SearchInput.d.mts +10 -5
- package/dist/SearchInput/SearchInput.d.mts.map +1 -1
- package/dist/SearchInput/SearchInput2.mjs +2 -2
- package/dist/SearchInput/SearchInput2.mjs.map +1 -1
- package/dist/Select/Select.d.mts +6 -6
- package/dist/Select/Select.d.mts.map +1 -1
- package/dist/Select/Select2.mjs +4 -4
- package/dist/Select/Select2.mjs.map +1 -1
- package/dist/TelephoneInput/TelephoneInput.d.mts +10 -5
- package/dist/TelephoneInput/TelephoneInput.d.mts.map +1 -1
- package/dist/TelephoneInput/TelephoneInput2.mjs +2 -2
- package/dist/TelephoneInput/TelephoneInput2.mjs.map +1 -1
- package/dist/TextArea/TextArea.d.mts +10 -5
- package/dist/TextArea/TextArea.d.mts.map +1 -1
- package/dist/TextArea/TextArea2.mjs +2 -2
- package/dist/TextArea/TextArea2.mjs.map +1 -1
- package/dist/TextInput/TextInput.d.mts +4 -4
- package/dist/TextInput/TextInput.d.mts.map +1 -1
- package/dist/TextInput/TextInput2.mjs +2 -2
- package/dist/TextInput/TextInput2.mjs.map +1 -1
- package/dist/TimeInput/TimeInput.d.mts +1 -1
- package/dist/TimeInput/TimeInput2.mjs +1 -1
- package/dist/TimeInput/TimeInput2.mjs.map +1 -1
- package/dist/index.cjs +315 -79
- package/dist/index.cjs.map +1 -1
- package/dist/index.css +5950 -5217
- package/dist/index.d.cts +243 -66
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +4 -2
- package/dist/index.mjs +3 -2
- package/dist/utils/popover.d.mts +27 -0
- package/dist/utils/popover.d.mts.map +1 -0
- package/dist/utils/popover.mjs +142 -0
- package/dist/utils/popover.mjs.map +1 -0
- package/package.json +3 -3
package/dist/index.cjs
CHANGED
|
@@ -1630,8 +1630,8 @@ LinkButton.displayName = "LinkButton";
|
|
|
1630
1630
|
* <TextInput readOnly value="Cannot edit this" />
|
|
1631
1631
|
* ```
|
|
1632
1632
|
*/
|
|
1633
|
-
const TextInput = react.default.forwardRef(({ className, invalid,
|
|
1634
|
-
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-text-input",
|
|
1633
|
+
const TextInput = react.default.forwardRef(({ className, invalid, inlineSize, type = "text", ...props }, ref) => {
|
|
1634
|
+
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-text-input", inlineSize && `dsn-text-input--inline-size-${inlineSize}`, className);
|
|
1635
1635
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1636
1636
|
ref,
|
|
1637
1637
|
type,
|
|
@@ -1694,8 +1694,8 @@ FileInput.displayName = "FileInput";
|
|
|
1694
1694
|
* <EmailInput invalid aria-invalid="true" aria-describedby="error" />
|
|
1695
1695
|
* ```
|
|
1696
1696
|
*/
|
|
1697
|
-
const EmailInput = react.default.forwardRef(({ className, invalid,
|
|
1698
|
-
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-text-input",
|
|
1697
|
+
const EmailInput = react.default.forwardRef(({ className, invalid, inlineSize, autoComplete, ...props }, ref) => {
|
|
1698
|
+
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-text-input", inlineSize && `dsn-text-input--inline-size-${inlineSize}`, className);
|
|
1699
1699
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1700
1700
|
ref,
|
|
1701
1701
|
type: "email",
|
|
@@ -1731,8 +1731,8 @@ EmailInput.displayName = "EmailInput";
|
|
|
1731
1731
|
* <PasswordInput invalid aria-invalid="true" aria-describedby="error" />
|
|
1732
1732
|
* ```
|
|
1733
1733
|
*/
|
|
1734
|
-
const PasswordInput = react.default.forwardRef(({ passwordAutocomplete = "current-password", className, invalid,
|
|
1735
|
-
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-text-input",
|
|
1734
|
+
const PasswordInput = react.default.forwardRef(({ passwordAutocomplete = "current-password", className, invalid, inlineSize, autoComplete, ...props }, ref) => {
|
|
1735
|
+
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-text-input", inlineSize && `dsn-text-input--inline-size-${inlineSize}`, className);
|
|
1736
1736
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1737
1737
|
ref,
|
|
1738
1738
|
type: "password",
|
|
@@ -1762,8 +1762,8 @@ PasswordInput.displayName = "PasswordInput";
|
|
|
1762
1762
|
* <NumberInput invalid aria-invalid="true" aria-describedby="error" />
|
|
1763
1763
|
* ```
|
|
1764
1764
|
*/
|
|
1765
|
-
const NumberInput = react.default.forwardRef(({ className, invalid,
|
|
1766
|
-
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-text-input",
|
|
1765
|
+
const NumberInput = react.default.forwardRef(({ className, invalid, inlineSize, allowDecimals = false, ...props }, ref) => {
|
|
1766
|
+
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-text-input", inlineSize && `dsn-text-input--inline-size-${inlineSize}`, className);
|
|
1767
1767
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1768
1768
|
ref,
|
|
1769
1769
|
type: "text",
|
|
@@ -1795,8 +1795,8 @@ NumberInput.displayName = "NumberInput";
|
|
|
1795
1795
|
* <TelephoneInput invalid aria-invalid="true" aria-describedby="error" />
|
|
1796
1796
|
* ```
|
|
1797
1797
|
*/
|
|
1798
|
-
const TelephoneInput = react.default.forwardRef(({ className, invalid,
|
|
1799
|
-
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-text-input",
|
|
1798
|
+
const TelephoneInput = react.default.forwardRef(({ className, invalid, inlineSize = "md", autoComplete, ...props }, ref) => {
|
|
1799
|
+
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-text-input", inlineSize && `dsn-text-input--inline-size-${inlineSize}`, className);
|
|
1800
1800
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
|
|
1801
1801
|
ref,
|
|
1802
1802
|
type: "tel",
|
|
@@ -1828,8 +1828,8 @@ TelephoneInput.displayName = "TelephoneInput";
|
|
|
1828
1828
|
* <SearchInput invalid aria-invalid="true" aria-describedby="error" />
|
|
1829
1829
|
* ```
|
|
1830
1830
|
*/
|
|
1831
|
-
const SearchInput = react.default.forwardRef(({ className, invalid,
|
|
1832
|
-
const wrapperClasses = (0, _dsn_starter_kit_core.classNames)("dsn-search-input-wrapper",
|
|
1831
|
+
const SearchInput = react.default.forwardRef(({ className, invalid, inlineSize, ...props }, ref) => {
|
|
1832
|
+
const wrapperClasses = (0, _dsn_starter_kit_core.classNames)("dsn-search-input-wrapper", inlineSize && `dsn-search-input-wrapper--inline-size-${inlineSize}`);
|
|
1833
1833
|
const inputClasses = (0, _dsn_starter_kit_core.classNames)("dsn-text-input", "dsn-search-input", className);
|
|
1834
1834
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1835
1835
|
className: wrapperClasses,
|
|
@@ -1852,7 +1852,7 @@ SearchInput.displayName = "SearchInput";
|
|
|
1852
1852
|
/**
|
|
1853
1853
|
* Time Input component
|
|
1854
1854
|
* Time input with an interactive clock button at inline-end that opens the native time picker.
|
|
1855
|
-
* Fixed
|
|
1855
|
+
* Fixed inline-size (sm): not configurable, as time inputs have a predictable content length.
|
|
1856
1856
|
*
|
|
1857
1857
|
* @example
|
|
1858
1858
|
* ```tsx
|
|
@@ -1911,7 +1911,7 @@ TimeInput.displayName = "TimeInput";
|
|
|
1911
1911
|
/**
|
|
1912
1912
|
* Date Input component
|
|
1913
1913
|
* Date input with an interactive calendar button at inline-end that opens the native date picker.
|
|
1914
|
-
* Fixed
|
|
1914
|
+
* Fixed inline-size (md): not configurable, as date inputs have a predictable content length.
|
|
1915
1915
|
*
|
|
1916
1916
|
* @example
|
|
1917
1917
|
* ```tsx
|
|
@@ -2024,7 +2024,7 @@ const DateInputGroup = react.default.forwardRef(({ className, value, onChange, i
|
|
|
2024
2024
|
children: "Dag"
|
|
2025
2025
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberInput, {
|
|
2026
2026
|
id: dayId,
|
|
2027
|
-
|
|
2027
|
+
inlineSize: "xs",
|
|
2028
2028
|
value: value?.day ?? "",
|
|
2029
2029
|
onChange: handleChange("day"),
|
|
2030
2030
|
invalid,
|
|
@@ -2043,7 +2043,7 @@ const DateInputGroup = react.default.forwardRef(({ className, value, onChange, i
|
|
|
2043
2043
|
children: "Maand"
|
|
2044
2044
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberInput, {
|
|
2045
2045
|
id: monthId,
|
|
2046
|
-
|
|
2046
|
+
inlineSize: "xs",
|
|
2047
2047
|
value: value?.month ?? "",
|
|
2048
2048
|
onChange: handleChange("month"),
|
|
2049
2049
|
invalid,
|
|
@@ -2062,7 +2062,7 @@ const DateInputGroup = react.default.forwardRef(({ className, value, onChange, i
|
|
|
2062
2062
|
children: "Jaar"
|
|
2063
2063
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(NumberInput, {
|
|
2064
2064
|
id: yearId,
|
|
2065
|
-
|
|
2065
|
+
inlineSize: "sm",
|
|
2066
2066
|
value: value?.year ?? "",
|
|
2067
2067
|
onChange: handleChange("year"),
|
|
2068
2068
|
invalid,
|
|
@@ -2102,15 +2102,15 @@ DateInputGroup.displayName = "DateInputGroup";
|
|
|
2102
2102
|
* </Select>
|
|
2103
2103
|
* </FormField>
|
|
2104
2104
|
*
|
|
2105
|
-
* //
|
|
2106
|
-
* <Select
|
|
2105
|
+
* // Inline-size variant
|
|
2106
|
+
* <Select inlineSize="lg">...</Select>
|
|
2107
2107
|
*
|
|
2108
2108
|
* // Invalid state
|
|
2109
2109
|
* <Select invalid aria-invalid="true" aria-describedby="error">...</Select>
|
|
2110
2110
|
* ```
|
|
2111
2111
|
*/
|
|
2112
|
-
const Select = react.default.forwardRef(({ className, invalid,
|
|
2113
|
-
const wrapperClasses = (0, _dsn_starter_kit_core.classNames)("dsn-select-wrapper",
|
|
2112
|
+
const Select = react.default.forwardRef(({ className, invalid, inlineSize, disabled, children, ...props }, ref) => {
|
|
2113
|
+
const wrapperClasses = (0, _dsn_starter_kit_core.classNames)("dsn-select-wrapper", inlineSize && `dsn-select-wrapper--inline-size-${inlineSize}`);
|
|
2114
2114
|
const selectClasses = (0, _dsn_starter_kit_core.classNames)("dsn-text-input", "dsn-select", className);
|
|
2115
2115
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
2116
2116
|
className: wrapperClasses,
|
|
@@ -2154,8 +2154,8 @@ Select.displayName = "Select";
|
|
|
2154
2154
|
* <TextArea readOnly value="View only" rows={3} />
|
|
2155
2155
|
* ```
|
|
2156
2156
|
*/
|
|
2157
|
-
const TextArea = react.default.forwardRef(({ className, invalid,
|
|
2158
|
-
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-text-area",
|
|
2157
|
+
const TextArea = react.default.forwardRef(({ className, invalid, inlineSize, rows = 4, ...props }, ref) => {
|
|
2158
|
+
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-text-area", inlineSize && `dsn-text-area--inline-size-${inlineSize}`, className);
|
|
2159
2159
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("textarea", {
|
|
2160
2160
|
ref,
|
|
2161
2161
|
className: classes,
|
|
@@ -3562,14 +3562,28 @@ const Drawer = react.default.forwardRef(({ className, isOpen, onClose, modal = t
|
|
|
3562
3562
|
const dialogRef = ref ?? internalRef;
|
|
3563
3563
|
const headingId = react.default.useId();
|
|
3564
3564
|
const headingRef = react.default.useRef(null);
|
|
3565
|
+
const popoverShownRef = react.default.useRef(false);
|
|
3566
|
+
const previousFocusRef = react.default.useRef(null);
|
|
3565
3567
|
react.default.useEffect(() => {
|
|
3566
3568
|
const dialog = dialogRef.current;
|
|
3567
3569
|
if (!dialog) return;
|
|
3568
|
-
|
|
3570
|
+
const isShown = dialog.open || popoverShownRef.current;
|
|
3571
|
+
if (isOpen && !isShown) {
|
|
3569
3572
|
if (modal) dialog.showModal();
|
|
3570
|
-
else
|
|
3573
|
+
else {
|
|
3574
|
+
previousFocusRef.current = document.activeElement instanceof HTMLElement ? document.activeElement : null;
|
|
3575
|
+
dialog.showPopover();
|
|
3576
|
+
popoverShownRef.current = true;
|
|
3577
|
+
}
|
|
3571
3578
|
(headingRef.current ?? dialog).focus();
|
|
3572
|
-
} else if (!isOpen &&
|
|
3579
|
+
} else if (!isOpen && isShown) {
|
|
3580
|
+
if (popoverShownRef.current) {
|
|
3581
|
+
const focusWasInside = dialog.contains(document.activeElement);
|
|
3582
|
+
dialog.hidePopover();
|
|
3583
|
+
popoverShownRef.current = false;
|
|
3584
|
+
if (focusWasInside) previousFocusRef.current?.focus();
|
|
3585
|
+
} else dialog.close();
|
|
3586
|
+
}
|
|
3573
3587
|
}, [
|
|
3574
3588
|
isOpen,
|
|
3575
3589
|
modal,
|
|
@@ -3616,6 +3630,7 @@ const Drawer = react.default.forwardRef(({ className, isOpen, onClose, modal = t
|
|
|
3616
3630
|
"aria-labelledby": headingId,
|
|
3617
3631
|
onCancel: handleCancel,
|
|
3618
3632
|
tabIndex: -1,
|
|
3633
|
+
popover: modal ? void 0 : "manual",
|
|
3619
3634
|
...props,
|
|
3620
3635
|
children
|
|
3621
3636
|
})
|
|
@@ -3697,6 +3712,10 @@ const DrawerFooter = react.default.forwardRef(({ className, children, ...props }
|
|
|
3697
3712
|
DrawerFooter.displayName = "DrawerFooter";
|
|
3698
3713
|
//#endregion
|
|
3699
3714
|
//#region src/PageHeader/PageHeader.tsx
|
|
3715
|
+
/** Auto-hide: boven deze scrollpositie (px) blijft de header altijd zichtbaar */
|
|
3716
|
+
const AUTO_HIDE_OFFSET = 100;
|
|
3717
|
+
/** Auto-hide: zoveel px omhoog scrollen voordat de header terugkomt */
|
|
3718
|
+
const AUTO_HIDE_REVEAL_DISTANCE = 64;
|
|
3700
3719
|
/**
|
|
3701
3720
|
* PageHeader component
|
|
3702
3721
|
* Primaire navigatieheader voor een pagina. Mobile-first implementatie.
|
|
@@ -3744,11 +3763,24 @@ const PageHeader = react.default.forwardRef(({ className, logoSlot, sticky = "no
|
|
|
3744
3763
|
react.default.useEffect(() => {
|
|
3745
3764
|
if (sticky !== "auto-hide") return;
|
|
3746
3765
|
let lastScrollY = window.scrollY;
|
|
3766
|
+
let scrolledUp = 0;
|
|
3747
3767
|
const handleScroll = () => {
|
|
3748
3768
|
const currentScrollY = window.scrollY;
|
|
3749
|
-
const
|
|
3750
|
-
combinedRef.current?.setAttribute("data-hidden", String(isScrollingDown));
|
|
3769
|
+
const delta = currentScrollY - lastScrollY;
|
|
3751
3770
|
lastScrollY = currentScrollY;
|
|
3771
|
+
let hidden;
|
|
3772
|
+
if (currentScrollY <= AUTO_HIDE_OFFSET) {
|
|
3773
|
+
hidden = false;
|
|
3774
|
+
scrolledUp = 0;
|
|
3775
|
+
} else if (delta > 0) {
|
|
3776
|
+
hidden = true;
|
|
3777
|
+
scrolledUp = 0;
|
|
3778
|
+
} else if (delta < 0) {
|
|
3779
|
+
scrolledUp -= delta;
|
|
3780
|
+
if (scrolledUp < AUTO_HIDE_REVEAL_DISTANCE) return;
|
|
3781
|
+
hidden = false;
|
|
3782
|
+
} else return;
|
|
3783
|
+
combinedRef.current?.setAttribute("data-hidden", String(hidden));
|
|
3752
3784
|
};
|
|
3753
3785
|
window.addEventListener("scroll", handleScroll, { passive: true });
|
|
3754
3786
|
return () => window.removeEventListener("scroll", handleScroll);
|
|
@@ -4494,22 +4526,29 @@ Image.displayName = "Image";
|
|
|
4494
4526
|
const CardContext = react.default.createContext({});
|
|
4495
4527
|
/**
|
|
4496
4528
|
* Card component
|
|
4497
|
-
* Configureerbare container
|
|
4498
|
-
*
|
|
4499
|
-
*
|
|
4529
|
+
* Configureerbare container met vier optionele secties: `CardPreHeader`,
|
|
4530
|
+
* `CardHeader`, `CardBody` en `CardFooter` (zie DR-2026-11).
|
|
4531
|
+
*
|
|
4532
|
+
* Zet `CardHeader` in de JSX altijd vóór `CardPreHeader`: zo kondigt een
|
|
4533
|
+
* screenreader de heading eerst aan. De CSS zet de pre-header visueel bovenaan.
|
|
4534
|
+
*
|
|
4535
|
+
* Met `href` wordt de heading een stretched link: de link dekt de volledige
|
|
4536
|
+
* card, en screenreaders lezen alleen de heading-tekst als linknaam.
|
|
4500
4537
|
*
|
|
4501
4538
|
* @example
|
|
4502
4539
|
* ```tsx
|
|
4503
4540
|
* <Card href="/artikel/slug">
|
|
4504
4541
|
* <CardHeader>
|
|
4505
|
-
* <
|
|
4542
|
+
* <CardHeading level={2}>Artikeltitel</CardHeading>
|
|
4506
4543
|
* </CardHeader>
|
|
4544
|
+
* <CardPreHeader>
|
|
4545
|
+
* <Image src="/foto.jpg" alt="" width={800} height={450} ratio="16:9" />
|
|
4546
|
+
* </CardPreHeader>
|
|
4507
4547
|
* <CardBody>
|
|
4508
|
-
* <CardHeading level={2}>Artikeltitel</CardHeading>
|
|
4509
4548
|
* <Paragraph>Korte beschrijving.</Paragraph>
|
|
4510
4549
|
* </CardBody>
|
|
4511
4550
|
* <CardFooter>
|
|
4512
|
-
* <
|
|
4551
|
+
* <CardAffordance>Lees meer</CardAffordance>
|
|
4513
4552
|
* </CardFooter>
|
|
4514
4553
|
* </Card>
|
|
4515
4554
|
* ```
|
|
@@ -4527,13 +4566,14 @@ const Card = react.default.forwardRef(({ className, href, children, ...props },
|
|
|
4527
4566
|
});
|
|
4528
4567
|
Card.displayName = "Card";
|
|
4529
4568
|
/**
|
|
4530
|
-
*
|
|
4569
|
+
* CardPreHeader sub-component
|
|
4570
|
+
* Staat in de DOM ná `CardHeader` en komt visueel bovenaan via `order: -1`.
|
|
4531
4571
|
* Zonder children → toont automatisch een afbeeldingsplaceholder.
|
|
4532
4572
|
*/
|
|
4533
|
-
const
|
|
4573
|
+
const CardPreHeader = react.default.forwardRef(({ className, children, ...props }, ref) => {
|
|
4534
4574
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4535
4575
|
ref,
|
|
4536
|
-
className: (0, _dsn_starter_kit_core.classNames)("dsn-
|
|
4576
|
+
className: (0, _dsn_starter_kit_core.classNames)("dsn-card__pre-header", className),
|
|
4537
4577
|
...props,
|
|
4538
4578
|
children: children ? children : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4539
4579
|
className: "dsn-card__image-placeholder",
|
|
@@ -4541,10 +4581,24 @@ const CardHeader = react.default.forwardRef(({ className, children, ...props },
|
|
|
4541
4581
|
})
|
|
4542
4582
|
});
|
|
4543
4583
|
});
|
|
4584
|
+
CardPreHeader.displayName = "CardPreHeader";
|
|
4585
|
+
/**
|
|
4586
|
+
* CardHeader sub-component
|
|
4587
|
+
* Bevat de heading of het label van de card. Zet hem in de JSX vóór
|
|
4588
|
+
* `CardPreHeader`.
|
|
4589
|
+
*/
|
|
4590
|
+
const CardHeader = react.default.forwardRef(({ className, children, ...props }, ref) => {
|
|
4591
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4592
|
+
ref,
|
|
4593
|
+
className: (0, _dsn_starter_kit_core.classNames)("dsn-card__header", className),
|
|
4594
|
+
...props,
|
|
4595
|
+
children
|
|
4596
|
+
});
|
|
4597
|
+
});
|
|
4544
4598
|
CardHeader.displayName = "CardHeader";
|
|
4545
4599
|
/**
|
|
4546
4600
|
* CardBody sub-component
|
|
4547
|
-
*
|
|
4601
|
+
* Inhoudssectie onder de header.
|
|
4548
4602
|
*/
|
|
4549
4603
|
const CardBody = react.default.forwardRef(({ className, children, ...props }, ref) => {
|
|
4550
4604
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -4558,7 +4612,7 @@ CardBody.displayName = "CardBody";
|
|
|
4558
4612
|
/**
|
|
4559
4613
|
* CardHeading sub-component
|
|
4560
4614
|
* Ontvangt `href` automatisch via React context van de parent `Card`.
|
|
4561
|
-
* Wanneer `href` beschikbaar is,
|
|
4615
|
+
* Wanneer `href` beschikbaar is, staat de heading-tekst in een `<a class="dsn-card__link">`.
|
|
4562
4616
|
* Het `::before` pseudo-element dekt de volledige card (stretched link).
|
|
4563
4617
|
*/
|
|
4564
4618
|
const CardHeading = react.default.forwardRef(({ className, level = 2, children, ...props }, ref) => {
|
|
@@ -4566,21 +4620,67 @@ const CardHeading = react.default.forwardRef(({ className, level = 2, children,
|
|
|
4566
4620
|
const Tag = `h${level}`;
|
|
4567
4621
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tag, {
|
|
4568
4622
|
ref,
|
|
4569
|
-
className: (0, _dsn_starter_kit_core.classNames)("dsn-
|
|
4623
|
+
className: (0, _dsn_starter_kit_core.classNames)("dsn-card__heading", className),
|
|
4570
4624
|
...props,
|
|
4571
4625
|
children: href ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
4572
4626
|
href,
|
|
4573
|
-
className: "dsn-
|
|
4627
|
+
className: "dsn-card__link",
|
|
4574
4628
|
children
|
|
4575
4629
|
}) : children
|
|
4576
4630
|
});
|
|
4577
4631
|
});
|
|
4578
4632
|
CardHeading.displayName = "CardHeading";
|
|
4579
4633
|
/**
|
|
4634
|
+
* CardLabel sub-component
|
|
4635
|
+
* Gebruik in plaats van `CardHeading` wanneer de card geen nieuw onderwerp
|
|
4636
|
+
* introduceert, zoals bij login-opties of instellingen. Ontvangt `href` via
|
|
4637
|
+
* context, net als `CardHeading`.
|
|
4638
|
+
*/
|
|
4639
|
+
const CardLabel = react.default.forwardRef(({ className, children, ...props }, ref) => {
|
|
4640
|
+
const { href } = react.default.useContext(CardContext);
|
|
4641
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4642
|
+
ref,
|
|
4643
|
+
className: (0, _dsn_starter_kit_core.classNames)("dsn-card__label", className),
|
|
4644
|
+
...props,
|
|
4645
|
+
children: href ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
4646
|
+
href,
|
|
4647
|
+
className: "dsn-card__link",
|
|
4648
|
+
children
|
|
4649
|
+
}) : children
|
|
4650
|
+
});
|
|
4651
|
+
});
|
|
4652
|
+
CardLabel.displayName = "CardLabel";
|
|
4653
|
+
/**
|
|
4654
|
+
* CardDescription sub-component
|
|
4655
|
+
* Korte omschrijving met eigen typografie-tokens.
|
|
4656
|
+
*/
|
|
4657
|
+
const CardDescription = react.default.forwardRef(({ className, children, ...props }, ref) => {
|
|
4658
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4659
|
+
ref,
|
|
4660
|
+
className: (0, _dsn_starter_kit_core.classNames)("dsn-card__description", className),
|
|
4661
|
+
...props,
|
|
4662
|
+
children
|
|
4663
|
+
});
|
|
4664
|
+
});
|
|
4665
|
+
CardDescription.displayName = "CardDescription";
|
|
4666
|
+
/**
|
|
4667
|
+
* CardMeta sub-component
|
|
4668
|
+
* Aanvullende gegevens bij de card, zoals een datum.
|
|
4669
|
+
*/
|
|
4670
|
+
const CardMeta = react.default.forwardRef(({ className, children, ...props }, ref) => {
|
|
4671
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
|
|
4672
|
+
ref,
|
|
4673
|
+
className: (0, _dsn_starter_kit_core.classNames)("dsn-card__meta", className),
|
|
4674
|
+
...props,
|
|
4675
|
+
children
|
|
4676
|
+
});
|
|
4677
|
+
});
|
|
4678
|
+
CardMeta.displayName = "CardMeta";
|
|
4679
|
+
/**
|
|
4580
4680
|
* CardFooter sub-component
|
|
4581
|
-
*
|
|
4582
|
-
*
|
|
4583
|
-
* bestemming
|
|
4681
|
+
* Staat altijd onderaan de card. Links en buttons staan boven de stretched
|
|
4682
|
+
* link en blijven zelfstandig klikbaar. Voor een visuele "Lees meer" naar
|
|
4683
|
+
* dezelfde bestemming als de card: gebruik `CardAffordance`.
|
|
4584
4684
|
*/
|
|
4585
4685
|
const CardFooter = react.default.forwardRef(({ className, children, ...props }, ref) => {
|
|
4586
4686
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -4592,6 +4692,21 @@ const CardFooter = react.default.forwardRef(({ className, children, ...props },
|
|
|
4592
4692
|
});
|
|
4593
4693
|
CardFooter.displayName = "CardFooter";
|
|
4594
4694
|
/**
|
|
4695
|
+
* CardAffordance sub-component
|
|
4696
|
+
* Een `<span aria-hidden="true">` die eruitziet als een link. De stretched
|
|
4697
|
+
* link vangt de klik al af, dus er is geen tweede tabstop nodig.
|
|
4698
|
+
*/
|
|
4699
|
+
const CardAffordance = react.default.forwardRef(({ className, children, ...props }, ref) => {
|
|
4700
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
4701
|
+
ref,
|
|
4702
|
+
className: (0, _dsn_starter_kit_core.classNames)("dsn-card__affordance", className),
|
|
4703
|
+
"aria-hidden": "true",
|
|
4704
|
+
...props,
|
|
4705
|
+
children
|
|
4706
|
+
});
|
|
4707
|
+
});
|
|
4708
|
+
CardAffordance.displayName = "CardAffordance";
|
|
4709
|
+
/**
|
|
4595
4710
|
* CardGroup sub-component
|
|
4596
4711
|
* Flexbox wrapper die gelijke hoogte garandeert en de footer in elke card onderaan uitlijnt.
|
|
4597
4712
|
* Rendert `role="list"` wanneer `as="ul"` — nodig omdat CSS-resets de lijstsemantiek verwijderen.
|
|
@@ -4611,6 +4726,7 @@ CardGroup.displayName = "CardGroup";
|
|
|
4611
4726
|
//#region src/ModalDialog/ModalDialog.tsx
|
|
4612
4727
|
const ModalDialogContext = react.default.createContext({
|
|
4613
4728
|
headingId: "",
|
|
4729
|
+
bodyId: "",
|
|
4614
4730
|
headingRef: { current: null }
|
|
4615
4731
|
});
|
|
4616
4732
|
/**
|
|
@@ -4624,7 +4740,7 @@ const ModalDialogContext = react.default.createContext({
|
|
|
4624
4740
|
*
|
|
4625
4741
|
* @example
|
|
4626
4742
|
* ```tsx
|
|
4627
|
-
* <ModalDialog isOpen={isOpen} onClose={() => setIsOpen(false)}>
|
|
4743
|
+
* <ModalDialog alert isOpen={isOpen} onClose={() => setIsOpen(false)}>
|
|
4628
4744
|
* <ModalDialogHeader>
|
|
4629
4745
|
* <ModalDialogHeading>Bevestig verwijderen</ModalDialogHeading>
|
|
4630
4746
|
* </ModalDialogHeader>
|
|
@@ -4633,17 +4749,18 @@ const ModalDialogContext = react.default.createContext({
|
|
|
4633
4749
|
* </ModalDialogBody>
|
|
4634
4750
|
* <ModalDialogFooter>
|
|
4635
4751
|
* <ActionGroup>
|
|
4636
|
-
* <Button variant="negative" onClick={() => setIsOpen(false)}>Verwijderen</Button>
|
|
4752
|
+
* <Button variant="strong-negative" onClick={() => setIsOpen(false)}>Verwijderen</Button>
|
|
4637
4753
|
* <Button variant="default" onClick={() => setIsOpen(false)}>Annuleren</Button>
|
|
4638
4754
|
* </ActionGroup>
|
|
4639
4755
|
* </ModalDialogFooter>
|
|
4640
4756
|
* </ModalDialog>
|
|
4641
4757
|
* ```
|
|
4642
4758
|
*/
|
|
4643
|
-
const ModalDialog = react.default.forwardRef(({ className, isOpen, onClose, triggerRef, children, ...props }, ref) => {
|
|
4759
|
+
const ModalDialog = react.default.forwardRef(({ className, isOpen, onClose, triggerRef, alert = false, children, ...props }, ref) => {
|
|
4644
4760
|
const internalRef = react.default.useRef(null);
|
|
4645
4761
|
const dialogRef = ref ?? internalRef;
|
|
4646
4762
|
const headingId = react.default.useId();
|
|
4763
|
+
const bodyId = react.default.useId();
|
|
4647
4764
|
const headingRef = react.default.useRef(null);
|
|
4648
4765
|
react.default.useEffect(() => {
|
|
4649
4766
|
const dialog = dialogRef.current;
|
|
@@ -4670,13 +4787,16 @@ const ModalDialog = react.default.forwardRef(({ className, isOpen, onClose, trig
|
|
|
4670
4787
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ModalDialogContext.Provider, {
|
|
4671
4788
|
value: {
|
|
4672
4789
|
headingId,
|
|
4790
|
+
bodyId,
|
|
4673
4791
|
headingRef,
|
|
4674
4792
|
onClose
|
|
4675
4793
|
},
|
|
4676
4794
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("dialog", {
|
|
4677
4795
|
ref: dialogRef,
|
|
4678
4796
|
className: classes,
|
|
4797
|
+
role: alert ? "alertdialog" : void 0,
|
|
4679
4798
|
"aria-labelledby": headingId,
|
|
4799
|
+
"aria-describedby": alert ? bodyId : void 0,
|
|
4680
4800
|
onCancel: handleCancel,
|
|
4681
4801
|
tabIndex: -1,
|
|
4682
4802
|
...props,
|
|
@@ -4737,8 +4857,10 @@ ModalDialogHeading.displayName = "ModalDialogHeading";
|
|
|
4737
4857
|
* De scrollbare inhoudssectie van het dialoogvenster met scroll-affordance schaduw.
|
|
4738
4858
|
*/
|
|
4739
4859
|
const ModalDialogBody = react.default.forwardRef(({ className, children, ...props }, ref) => {
|
|
4860
|
+
const { bodyId } = react.default.useContext(ModalDialogContext);
|
|
4740
4861
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
4741
4862
|
ref,
|
|
4863
|
+
id: bodyId,
|
|
4742
4864
|
className: (0, _dsn_starter_kit_core.classNames)("dsn-modal-dialog__body", className),
|
|
4743
4865
|
...props,
|
|
4744
4866
|
children
|
|
@@ -4759,7 +4881,7 @@ const ModalDialogFooter = react.default.forwardRef(({ className, children, ...pr
|
|
|
4759
4881
|
});
|
|
4760
4882
|
ModalDialogFooter.displayName = "ModalDialogFooter";
|
|
4761
4883
|
//#endregion
|
|
4762
|
-
//#region src/
|
|
4884
|
+
//#region src/utils/popover.ts
|
|
4763
4885
|
const GAP = 4;
|
|
4764
4886
|
function positionPopover(popover, trigger, placement) {
|
|
4765
4887
|
const triggerRect = trigger.getBoundingClientRect();
|
|
@@ -4812,39 +4934,32 @@ function focusFirstInteractive(container) {
|
|
|
4812
4934
|
if (first) first.focus();
|
|
4813
4935
|
else container.focus();
|
|
4814
4936
|
}
|
|
4815
|
-
const PopoverContext = react.default.createContext({ headingId: "" });
|
|
4816
4937
|
/**
|
|
4817
|
-
*
|
|
4818
|
-
*
|
|
4819
|
-
*
|
|
4820
|
-
*
|
|
4821
|
-
*
|
|
4822
|
-
*
|
|
4823
|
-
*
|
|
4824
|
-
*
|
|
4825
|
-
*
|
|
4826
|
-
* const [isOpen, setIsOpen] = useState(false);
|
|
4827
|
-
*
|
|
4828
|
-
* <Button ref={triggerRef} onClick={() => setIsOpen(true)}>Opties</Button>
|
|
4829
|
-
* <Popover isOpen={isOpen} onClose={() => setIsOpen(false)} triggerRef={triggerRef} label="Opties">
|
|
4830
|
-
* <PopoverBody>
|
|
4831
|
-
* <Menu>...</Menu>
|
|
4832
|
-
* </PopoverBody>
|
|
4833
|
-
* </Popover>
|
|
4834
|
-
* ```
|
|
4938
|
+
* Opent en sluit een element met `popover="auto"` synchroon met `isOpen`,
|
|
4939
|
+
* positioneert het bij de trigger en houdt `aria-expanded` op de trigger bij.
|
|
4940
|
+
*
|
|
4941
|
+
* Bij openen gaat de focus naar `initialFocusRef` (of de popover zelf als die
|
|
4942
|
+
* ref leeg is), en anders naar het eerste interactieve element. Bij sluiten
|
|
4943
|
+
* gaat de focus alleen terug naar de trigger wanneer dat zinvol is: als de
|
|
4944
|
+
* focus op dat moment nog in de popover stond (Escape, een sluitknop, een
|
|
4945
|
+
* actie in de popover). Klikt de gebruiker ergens anders, dan blijft de focus
|
|
4946
|
+
* daar; terugspringen naar de trigger zou die klik tenietdoen.
|
|
4835
4947
|
*/
|
|
4836
|
-
|
|
4837
|
-
const
|
|
4838
|
-
const
|
|
4839
|
-
const
|
|
4948
|
+
function usePopover({ popoverRef, triggerRef, isOpen, onClose, placement, initialFocusRef }) {
|
|
4949
|
+
const restoreFocusRef = react.default.useRef(false);
|
|
4950
|
+
const dismissedByPointerRef = react.default.useRef(false);
|
|
4951
|
+
const onCloseRef = react.default.useRef(onClose);
|
|
4952
|
+
onCloseRef.current = onClose;
|
|
4840
4953
|
react.default.useEffect(() => {
|
|
4841
4954
|
const el = popoverRef.current;
|
|
4842
4955
|
if (!el) return;
|
|
4843
4956
|
if (isOpen) {
|
|
4957
|
+
dismissedByPointerRef.current = false;
|
|
4844
4958
|
el.showPopover();
|
|
4845
4959
|
const trigger = triggerRef.current;
|
|
4846
4960
|
if (trigger) positionPopover(el, trigger, placement);
|
|
4847
|
-
|
|
4961
|
+
if (initialFocusRef) (initialFocusRef.current ?? el).focus();
|
|
4962
|
+
else focusFirstInteractive(el);
|
|
4848
4963
|
} else try {
|
|
4849
4964
|
el.hidePopover();
|
|
4850
4965
|
} catch {}
|
|
@@ -4852,7 +4967,8 @@ const Popover = react.default.forwardRef(({ className, isOpen, onClose, triggerR
|
|
|
4852
4967
|
isOpen,
|
|
4853
4968
|
placement,
|
|
4854
4969
|
triggerRef,
|
|
4855
|
-
popoverRef
|
|
4970
|
+
popoverRef,
|
|
4971
|
+
initialFocusRef
|
|
4856
4972
|
]);
|
|
4857
4973
|
react.default.useEffect(() => {
|
|
4858
4974
|
const trigger = triggerRef.current;
|
|
@@ -4862,19 +4978,23 @@ const Popover = react.default.forwardRef(({ className, isOpen, onClose, triggerR
|
|
|
4862
4978
|
trigger.setAttribute("aria-expanded", "false");
|
|
4863
4979
|
};
|
|
4864
4980
|
}, [isOpen, triggerRef]);
|
|
4865
|
-
const onCloseRef = react.default.useRef(onClose);
|
|
4866
|
-
onCloseRef.current = onClose;
|
|
4867
4981
|
react.default.useEffect(() => {
|
|
4868
4982
|
const el = popoverRef.current;
|
|
4869
4983
|
if (!el) return;
|
|
4984
|
+
const handleBeforeToggle = (event) => {
|
|
4985
|
+
if (event.newState === "closed") restoreFocusRef.current = !dismissedByPointerRef.current && el.contains(document.activeElement);
|
|
4986
|
+
};
|
|
4870
4987
|
const handleToggle = (event) => {
|
|
4871
4988
|
if (event.newState === "closed") {
|
|
4872
4989
|
onCloseRef.current?.();
|
|
4873
|
-
triggerRef.current?.focus();
|
|
4990
|
+
if (restoreFocusRef.current) triggerRef.current?.focus();
|
|
4991
|
+
restoreFocusRef.current = false;
|
|
4874
4992
|
}
|
|
4875
4993
|
};
|
|
4994
|
+
el.addEventListener("beforetoggle", handleBeforeToggle);
|
|
4876
4995
|
el.addEventListener("toggle", handleToggle);
|
|
4877
4996
|
return () => {
|
|
4997
|
+
el.removeEventListener("beforetoggle", handleBeforeToggle);
|
|
4878
4998
|
el.removeEventListener("toggle", handleToggle);
|
|
4879
4999
|
};
|
|
4880
5000
|
}, [triggerRef, popoverRef]);
|
|
@@ -4885,8 +5005,8 @@ const Popover = react.default.forwardRef(({ className, isOpen, onClose, triggerR
|
|
|
4885
5005
|
const handlePointerDown = (event) => {
|
|
4886
5006
|
const target = event.target;
|
|
4887
5007
|
if (el.contains(target) || triggerRef.current?.contains(target)) return;
|
|
5008
|
+
dismissedByPointerRef.current = true;
|
|
4888
5009
|
onCloseRef.current?.();
|
|
4889
|
-
triggerRef.current?.focus();
|
|
4890
5010
|
};
|
|
4891
5011
|
document.addEventListener("pointerdown", handlePointerDown);
|
|
4892
5012
|
return () => {
|
|
@@ -4897,11 +5017,57 @@ const Popover = react.default.forwardRef(({ className, isOpen, onClose, triggerR
|
|
|
4897
5017
|
triggerRef,
|
|
4898
5018
|
popoverRef
|
|
4899
5019
|
]);
|
|
5020
|
+
}
|
|
5021
|
+
//#endregion
|
|
5022
|
+
//#region src/Popover/Popover.tsx
|
|
5023
|
+
const PopoverContext = react.default.createContext({
|
|
5024
|
+
headingId: "",
|
|
5025
|
+
headingRef: { current: null }
|
|
5026
|
+
});
|
|
5027
|
+
/**
|
|
5028
|
+
* Popover component
|
|
5029
|
+
* Niet-modaal dialoogvenster verankerd aan een triggerelement
|
|
5030
|
+
* (`role="dialog"`, `aria-modal="false"`).
|
|
5031
|
+
*
|
|
5032
|
+
* Gebaseerd op de HTML Popover API (`popover="auto"`) voor ingebakken
|
|
5033
|
+
* light-dismiss en top-layer gedrag. Positionering via JavaScript.
|
|
5034
|
+
*
|
|
5035
|
+
* Voor een lijst met acties of links: gebruik `PopoverMenu`. Een lijst is
|
|
5036
|
+
* geen dialoogvenster.
|
|
5037
|
+
*
|
|
5038
|
+
* @example
|
|
5039
|
+
* ```tsx
|
|
5040
|
+
* const triggerRef = useRef<HTMLButtonElement>(null);
|
|
5041
|
+
* const [isOpen, setIsOpen] = useState(false);
|
|
5042
|
+
*
|
|
5043
|
+
* <Button ref={triggerRef} onClick={() => setIsOpen((v) => !v)}>Filters</Button>
|
|
5044
|
+
* <Popover isOpen={isOpen} onClose={() => setIsOpen(false)} triggerRef={triggerRef}>
|
|
5045
|
+
* <PopoverHeader>
|
|
5046
|
+
* <PopoverHeading>Filters</PopoverHeading>
|
|
5047
|
+
* </PopoverHeader>
|
|
5048
|
+
* <PopoverBody>...</PopoverBody>
|
|
5049
|
+
* </Popover>
|
|
5050
|
+
* ```
|
|
5051
|
+
*/
|
|
5052
|
+
const Popover = react.default.forwardRef(({ className, isOpen, onClose, triggerRef, placement = "bottom", label, children, ...props }, ref) => {
|
|
5053
|
+
const internalRef = react.default.useRef(null);
|
|
5054
|
+
const popoverRef = ref ?? internalRef;
|
|
5055
|
+
const headingId = react.default.useId();
|
|
5056
|
+
const headingRef = react.default.useRef(null);
|
|
5057
|
+
usePopover({
|
|
5058
|
+
popoverRef,
|
|
5059
|
+
triggerRef,
|
|
5060
|
+
isOpen,
|
|
5061
|
+
onClose,
|
|
5062
|
+
placement,
|
|
5063
|
+
initialFocusRef: headingRef
|
|
5064
|
+
});
|
|
4900
5065
|
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-popover", `dsn-popover--placement-${placement}`, className);
|
|
4901
5066
|
const ariaProps = label ? { "aria-label": label } : { "aria-labelledby": headingId };
|
|
4902
5067
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PopoverContext.Provider, {
|
|
4903
5068
|
value: {
|
|
4904
5069
|
headingId,
|
|
5070
|
+
headingRef,
|
|
4905
5071
|
onClose
|
|
4906
5072
|
},
|
|
4907
5073
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
@@ -4945,14 +5111,21 @@ PopoverHeader.displayName = "PopoverHeader";
|
|
|
4945
5111
|
/**
|
|
4946
5112
|
* PopoverHeading
|
|
4947
5113
|
* De heading van de popover. ID wordt automatisch gegenereerd voor aria-labelledby.
|
|
5114
|
+
* Krijgt `tabindex="-1"` zodat de Popover er bij openen de focus op kan zetten.
|
|
4948
5115
|
*/
|
|
4949
5116
|
const PopoverHeading = react.default.forwardRef(({ className, level = 2, children, ...props }, ref) => {
|
|
4950
|
-
const { headingId } = react.default.useContext(PopoverContext);
|
|
5117
|
+
const { headingId, headingRef } = react.default.useContext(PopoverContext);
|
|
4951
5118
|
const Tag = `h${level}`;
|
|
5119
|
+
const setRef = react.default.useCallback((node) => {
|
|
5120
|
+
headingRef.current = node;
|
|
5121
|
+
if (typeof ref === "function") ref(node);
|
|
5122
|
+
else if (ref) ref.current = node;
|
|
5123
|
+
}, [headingRef, ref]);
|
|
4952
5124
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Tag, {
|
|
4953
|
-
ref,
|
|
5125
|
+
ref: setRef,
|
|
4954
5126
|
id: headingId,
|
|
4955
5127
|
className: (0, _dsn_starter_kit_core.classNames)("dsn-popover-heading", className),
|
|
5128
|
+
tabIndex: -1,
|
|
4956
5129
|
...props,
|
|
4957
5130
|
children
|
|
4958
5131
|
});
|
|
@@ -4985,6 +5158,63 @@ const PopoverFooter = react.default.forwardRef(({ className, children, ...props
|
|
|
4985
5158
|
});
|
|
4986
5159
|
PopoverFooter.displayName = "PopoverFooter";
|
|
4987
5160
|
//#endregion
|
|
5161
|
+
//#region src/PopoverMenu/PopoverMenu.tsx
|
|
5162
|
+
const ITEM_SELECTOR = ".dsn-menu-button__button, .dsn-menu-link__link";
|
|
5163
|
+
/**
|
|
5164
|
+
* PopoverMenu component
|
|
5165
|
+
* Lijst met acties of links in een zwevend paneel, geopend vanuit een
|
|
5166
|
+
* triggerknop.
|
|
5167
|
+
*
|
|
5168
|
+
* Anders dan `Popover` is dit géén dialoogvenster: het paneel heeft geen rol,
|
|
5169
|
+
* de inhoud is een gewone lijst (`<ul role="list">`). Ook bewust geen
|
|
5170
|
+
* `role="menu"`: die rol belooft pijltjesnavigatie en zet screenreaders in
|
|
5171
|
+
* een applicatiemodus. De context komt uit het label van de trigger.
|
|
5172
|
+
*
|
|
5173
|
+
* Het menu sluit zichzelf zodra een item wordt geactiveerd, en zet de focus
|
|
5174
|
+
* dan terug op de trigger.
|
|
5175
|
+
*
|
|
5176
|
+
* @example
|
|
5177
|
+
* ```tsx
|
|
5178
|
+
* const triggerRef = useRef<HTMLButtonElement>(null);
|
|
5179
|
+
* const [isOpen, setIsOpen] = useState(false);
|
|
5180
|
+
*
|
|
5181
|
+
* <Button ref={triggerRef} onClick={() => setIsOpen((v) => !v)}>Acties</Button>
|
|
5182
|
+
* <PopoverMenu isOpen={isOpen} onClose={() => setIsOpen(false)} triggerRef={triggerRef}>
|
|
5183
|
+
* <MenuButton onClick={edit}>Bewerken</MenuButton>
|
|
5184
|
+
* <MenuButton onClick={duplicate}>Dupliceren</MenuButton>
|
|
5185
|
+
* </PopoverMenu>
|
|
5186
|
+
* ```
|
|
5187
|
+
*/
|
|
5188
|
+
const PopoverMenu = react.default.forwardRef(({ className, isOpen, onClose, triggerRef, placement = "bottom", iconStart = false, onClick, children, ...props }, ref) => {
|
|
5189
|
+
const internalRef = react.default.useRef(null);
|
|
5190
|
+
const popoverRef = ref ?? internalRef;
|
|
5191
|
+
usePopover({
|
|
5192
|
+
popoverRef,
|
|
5193
|
+
triggerRef,
|
|
5194
|
+
isOpen,
|
|
5195
|
+
onClose,
|
|
5196
|
+
placement
|
|
5197
|
+
});
|
|
5198
|
+
const handleClick = (event) => {
|
|
5199
|
+
onClick?.(event);
|
|
5200
|
+
if (event.target.closest(ITEM_SELECTOR)) onClose?.();
|
|
5201
|
+
};
|
|
5202
|
+
const classes = (0, _dsn_starter_kit_core.classNames)("dsn-popover-menu", `dsn-popover-menu--placement-${placement}`, className);
|
|
5203
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
5204
|
+
ref: popoverRef,
|
|
5205
|
+
popover: "auto",
|
|
5206
|
+
className: classes,
|
|
5207
|
+
onClick: handleClick,
|
|
5208
|
+
...props,
|
|
5209
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Menu, {
|
|
5210
|
+
role: "list",
|
|
5211
|
+
iconStart,
|
|
5212
|
+
children
|
|
5213
|
+
})
|
|
5214
|
+
});
|
|
5215
|
+
});
|
|
5216
|
+
PopoverMenu.displayName = "PopoverMenu";
|
|
5217
|
+
//#endregion
|
|
4988
5218
|
//#region src/File/File.tsx
|
|
4989
5219
|
/**
|
|
4990
5220
|
* File component
|
|
@@ -5181,11 +5411,16 @@ exports.BreakoutSection = BreakoutSection;
|
|
|
5181
5411
|
exports.Button = Button;
|
|
5182
5412
|
exports.ButtonLink = ButtonLink;
|
|
5183
5413
|
exports.Card = Card;
|
|
5414
|
+
exports.CardAffordance = CardAffordance;
|
|
5184
5415
|
exports.CardBody = CardBody;
|
|
5416
|
+
exports.CardDescription = CardDescription;
|
|
5185
5417
|
exports.CardFooter = CardFooter;
|
|
5186
5418
|
exports.CardGroup = CardGroup;
|
|
5187
5419
|
exports.CardHeader = CardHeader;
|
|
5188
5420
|
exports.CardHeading = CardHeading;
|
|
5421
|
+
exports.CardLabel = CardLabel;
|
|
5422
|
+
exports.CardMeta = CardMeta;
|
|
5423
|
+
exports.CardPreHeader = CardPreHeader;
|
|
5189
5424
|
exports.Checkbox = Checkbox;
|
|
5190
5425
|
exports.CheckboxGroup = CheckboxGroup;
|
|
5191
5426
|
exports.CheckboxOption = CheckboxOption;
|
|
@@ -5246,6 +5481,7 @@ exports.PopoverBody = PopoverBody;
|
|
|
5246
5481
|
exports.PopoverFooter = PopoverFooter;
|
|
5247
5482
|
exports.PopoverHeader = PopoverHeader;
|
|
5248
5483
|
exports.PopoverHeading = PopoverHeading;
|
|
5484
|
+
exports.PopoverMenu = PopoverMenu;
|
|
5249
5485
|
exports.PreHeading = PreHeading;
|
|
5250
5486
|
exports.ProgressBar = ProgressBar;
|
|
5251
5487
|
exports.Radio = Radio;
|