@postenbring/hedwig-react 0.0.79 → 0.0.81
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/accordion/accordion-content.js +1 -1
- package/dist/accordion/accordion-content.js.map +1 -1
- package/dist/accordion/accordion-content.mjs +1 -1
- package/dist/accordion/index.js +1 -1
- package/dist/accordion/index.js.map +1 -1
- package/dist/accordion/index.mjs +6 -6
- package/dist/{chunk-LTTJWLS7.mjs → chunk-3NDUE23B.mjs} +2 -2
- package/dist/{chunk-ZMLHJZQK.mjs → chunk-3ZFEUABU.mjs} +7 -7
- package/dist/{chunk-K3CBNVXC.mjs → chunk-67TP3E2D.mjs} +3 -2
- package/dist/{chunk-K3CBNVXC.mjs.map → chunk-67TP3E2D.mjs.map} +1 -1
- package/dist/{chunk-MPHMD4LO.mjs → chunk-6R7AXKBR.mjs} +63 -86
- package/dist/chunk-6R7AXKBR.mjs.map +1 -0
- package/dist/{chunk-HFXVRPBA.mjs → chunk-7JRS4WEB.mjs} +2 -2
- package/dist/{chunk-2UWPZNXC.mjs → chunk-GLAEI3SD.mjs} +2 -2
- package/dist/{chunk-2UWPZNXC.mjs.map → chunk-GLAEI3SD.mjs.map} +1 -1
- package/dist/chunk-MOU6WBT2.mjs +26 -0
- package/dist/chunk-MOU6WBT2.mjs.map +1 -0
- package/dist/{chunk-XDYXNQDV.mjs → chunk-V6UKKHKD.mjs} +2 -2
- package/dist/footer/footer.js +1 -1
- package/dist/footer/footer.js.map +1 -1
- package/dist/footer/footer.mjs +4 -4
- package/dist/footer/index.js +1 -1
- package/dist/footer/index.js.map +1 -1
- package/dist/footer/index.mjs +5 -5
- package/dist/index-no-css.js +69 -91
- package/dist/index-no-css.js.map +1 -1
- package/dist/index-no-css.mjs +19 -19
- package/dist/index.js +69 -91
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +19 -19
- package/dist/navbar/icons.js +6 -6
- package/dist/navbar/icons.js.map +1 -1
- package/dist/navbar/icons.mjs +1 -1
- package/dist/navbar/index.js +66 -89
- package/dist/navbar/index.js.map +1 -1
- package/dist/navbar/index.mjs +3 -3
- package/dist/navbar/navbar-expandable-menu.js +66 -89
- package/dist/navbar/navbar-expandable-menu.js.map +1 -1
- package/dist/navbar/navbar-expandable-menu.mjs +2 -2
- package/dist/show-more/index.js +2 -1
- package/dist/show-more/index.js.map +1 -1
- package/dist/show-more/index.mjs +1 -1
- package/dist/utilities/auto-animate-height.js +2 -1
- package/dist/utilities/auto-animate-height.js.map +1 -1
- package/dist/utilities/auto-animate-height.mjs +1 -1
- package/dist/utilities/index.js +2 -1
- package/dist/utilities/index.js.map +1 -1
- package/dist/utilities/index.mjs +1 -1
- package/package.json +4 -4
- package/dist/chunk-MPHMD4LO.mjs.map +0 -1
- package/dist/chunk-W2MRIP5P.mjs +0 -26
- package/dist/chunk-W2MRIP5P.mjs.map +0 -1
- /package/dist/{chunk-LTTJWLS7.mjs.map → chunk-3NDUE23B.mjs.map} +0 -0
- /package/dist/{chunk-ZMLHJZQK.mjs.map → chunk-3ZFEUABU.mjs.map} +0 -0
- /package/dist/{chunk-HFXVRPBA.mjs.map → chunk-7JRS4WEB.mjs.map} +0 -0
- /package/dist/{chunk-XDYXNQDV.mjs.map → chunk-V6UKKHKD.mjs.map} +0 -0
|
@@ -66,7 +66,6 @@ __export(navbar_expandable_menu_exports, {
|
|
|
66
66
|
});
|
|
67
67
|
module.exports = __toCommonJS(navbar_expandable_menu_exports);
|
|
68
68
|
var import_react2 = require("react");
|
|
69
|
-
var import_client = require("react-dom/client");
|
|
70
69
|
var import_typed_classname = require("@postenbring/hedwig-css/typed-classname");
|
|
71
70
|
|
|
72
71
|
// src/utils.ts
|
|
@@ -106,20 +105,20 @@ function releaseFocusTrap(inertElements) {
|
|
|
106
105
|
// src/navbar/icons.tsx
|
|
107
106
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
108
107
|
function CloseIcon() {
|
|
109
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { "aria-hidden": true,
|
|
108
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { "aria-hidden": true, xmlns: "http://www.w3.org/2000/svg", width: 24, height: 24, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
110
109
|
"path",
|
|
111
110
|
{
|
|
112
|
-
|
|
113
|
-
|
|
111
|
+
fill: "currentColor",
|
|
112
|
+
d: "m13.16 12.25 3.621-3.586.739-.738c.105-.106.105-.281 0-.422l-.774-.774c-.14-.105-.316-.105-.422 0L12 11.09 7.64 6.73c-.105-.105-.28-.105-.421 0l-.774.774c-.105.14-.105.316 0 .422l4.36 4.324-4.36 4.36c-.105.105-.105.28 0 .421l.774.774c.14.105.316.105.422 0L12 13.445l3.586 3.621.738.739c.106.105.281.105.422 0l.774-.774c.105-.14.105-.316 0-.422l-4.36-4.359Z"
|
|
114
113
|
}
|
|
115
114
|
) });
|
|
116
115
|
}
|
|
117
116
|
function MenuIcon() {
|
|
118
|
-
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", {
|
|
117
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)("svg", { "aria-hidden": true, xmlns: "http://www.w3.org/2000/svg", width: 32, height: 32, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
119
118
|
"path",
|
|
120
119
|
{
|
|
121
|
-
|
|
122
|
-
|
|
120
|
+
fill: "currentColor",
|
|
121
|
+
d: "M25.938 10.146a.57.57 0 0 0 .562-.563v-1.5c0-.281-.281-.562-.563-.562H6.063a.57.57 0 0 0-.562.562v1.5c0 .328.234.563.563.563h19.875Zm0 7.5a.57.57 0 0 0 .562-.563v-1.5c0-.281-.281-.563-.563-.563H6.063a.57.57 0 0 0-.562.563v1.5c0 .328.234.563.563.563h19.875Zm0 7.5a.57.57 0 0 0 .562-.563v-1.5c0-.281-.281-.563-.563-.563H6.063a.57.57 0 0 0-.562.563v1.5c0 .328.234.563.563.563h19.875Z"
|
|
123
122
|
}
|
|
124
123
|
) });
|
|
125
124
|
}
|
|
@@ -153,31 +152,6 @@ function NavbarExpandableMenu({ children }) {
|
|
|
153
152
|
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(expandableMenuContext.Provider, { value: [open, toggleOpen], children });
|
|
154
153
|
}
|
|
155
154
|
NavbarExpandableMenu.displayName = "NavbarExpandableMenu";
|
|
156
|
-
var RenderButton = (0, import_react2.forwardRef)(
|
|
157
|
-
(_a, ref) => {
|
|
158
|
-
var _b = _a, { className, innerRef, open = false, text, title, toggleOpen, width } = _b, rest = __objRest(_b, ["className", "innerRef", "open", "text", "title", "toggleOpen", "width"]);
|
|
159
|
-
const icon = open ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CloseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MenuIcon, {});
|
|
160
|
-
const style = width ? { width } : {};
|
|
161
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
162
|
-
"button",
|
|
163
|
-
__spreadProps(__spreadValues({
|
|
164
|
-
className: (0, import_typed_classname.clsx)("hds-navbar__item", className),
|
|
165
|
-
onClick: toggleOpen,
|
|
166
|
-
ref: ref != null ? ref : innerRef,
|
|
167
|
-
style,
|
|
168
|
-
title,
|
|
169
|
-
type: "button"
|
|
170
|
-
}, rest), {
|
|
171
|
-
children: [
|
|
172
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { className: (0, import_typed_classname.clsx)("hds-navbar__item-responsive-text"), children: text }),
|
|
173
|
-
" ",
|
|
174
|
-
icon
|
|
175
|
-
]
|
|
176
|
-
})
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
);
|
|
180
|
-
RenderButton.displayName = "Navbar.RenderButton";
|
|
181
155
|
var NavbarExpandableMenuTrigger = (0, import_react2.forwardRef)(
|
|
182
156
|
(_a, ref) => {
|
|
183
157
|
var _b = _a, {
|
|
@@ -185,75 +159,78 @@ var NavbarExpandableMenuTrigger = (0, import_react2.forwardRef)(
|
|
|
185
159
|
whenClosedHelperTitle,
|
|
186
160
|
whenOpenText,
|
|
187
161
|
whenOpenHelperTitle,
|
|
162
|
+
style,
|
|
188
163
|
className
|
|
189
164
|
} = _b, rest = __objRest(_b, [
|
|
190
165
|
"whenClosedText",
|
|
191
166
|
"whenClosedHelperTitle",
|
|
192
167
|
"whenOpenText",
|
|
193
168
|
"whenOpenHelperTitle",
|
|
169
|
+
"style",
|
|
194
170
|
"className"
|
|
195
171
|
]);
|
|
196
172
|
const [open, toggleOpen] = useNavbarExpendableMenuContext();
|
|
197
|
-
const [
|
|
198
|
-
const
|
|
173
|
+
const [textWidth, setTextWidth] = (0, import_react2.useState)(void 0);
|
|
174
|
+
const measurementId = (0, import_react2.useId)();
|
|
175
|
+
(0, import_react2.useEffect)(() => {
|
|
176
|
+
var _a2, _b2, _c, _d;
|
|
177
|
+
const widthWhenOpen = (_b2 = (_a2 = document.getElementById(`${measurementId}-when-open`)) == null ? void 0 : _a2.getBoundingClientRect().width) != null ? _b2 : 0;
|
|
178
|
+
const widthWhenClosed = (_d = (_c = document.getElementById(`${measurementId}-when-closed`)) == null ? void 0 : _c.getBoundingClientRect().width) != null ? _d : 0;
|
|
179
|
+
setTextWidth(widthWhenOpen < widthWhenClosed ? widthWhenClosed : widthWhenOpen);
|
|
180
|
+
}, [measurementId]);
|
|
199
181
|
const text = open ? whenOpenText : whenClosedText;
|
|
200
182
|
const title = open ? whenOpenHelperTitle : whenClosedHelperTitle;
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
root.render(element);
|
|
208
|
-
const getWidth = () => {
|
|
209
|
-
var _a2, _b2;
|
|
210
|
-
callback((_b2 = (_a2 = measureButtonRef.current) == null ? void 0 : _a2.offsetWidth) != null ? _b2 : 0);
|
|
211
|
-
root.unmount();
|
|
212
|
-
c.removeChild(container);
|
|
213
|
-
};
|
|
214
|
-
setTimeout(getWidth, 0);
|
|
215
|
-
};
|
|
216
|
-
(0, import_react2.useEffect)(() => {
|
|
217
|
-
measureButton(
|
|
218
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
219
|
-
RenderButton,
|
|
220
|
-
__spreadValues({
|
|
221
|
-
className,
|
|
222
|
-
innerRef: measureButtonRef,
|
|
223
|
-
text: whenClosedText,
|
|
224
|
-
title
|
|
225
|
-
}, rest)
|
|
226
|
-
),
|
|
227
|
-
(closedWidth) => {
|
|
228
|
-
measureButton(
|
|
229
|
-
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
230
|
-
RenderButton,
|
|
231
|
-
__spreadValues({
|
|
232
|
-
className,
|
|
233
|
-
innerRef: measureButtonRef,
|
|
234
|
-
open: true,
|
|
235
|
-
text: whenOpenText,
|
|
236
|
-
title
|
|
237
|
-
}, rest)
|
|
238
|
-
),
|
|
239
|
-
(openWidth) => {
|
|
240
|
-
setWidth(Math.max(openWidth, closedWidth));
|
|
241
|
-
}
|
|
242
|
-
);
|
|
243
|
-
}
|
|
244
|
-
);
|
|
245
|
-
}, [className, rest, title, whenClosedText, whenOpenText]);
|
|
246
|
-
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
247
|
-
RenderButton,
|
|
248
|
-
__spreadValues({
|
|
249
|
-
className,
|
|
250
|
-
open,
|
|
183
|
+
const icon = open ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(CloseIcon, {}) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(MenuIcon, {});
|
|
184
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
185
|
+
"button",
|
|
186
|
+
__spreadProps(__spreadValues({
|
|
187
|
+
className: (0, import_typed_classname.clsx)("hds-navbar__item", className),
|
|
188
|
+
onClick: toggleOpen,
|
|
251
189
|
ref,
|
|
252
|
-
text,
|
|
253
190
|
title,
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
}, rest)
|
|
191
|
+
type: "button",
|
|
192
|
+
style: __spreadValues({ position: "relative" }, style)
|
|
193
|
+
}, rest), {
|
|
194
|
+
children: [
|
|
195
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
196
|
+
"span",
|
|
197
|
+
{
|
|
198
|
+
id: `${measurementId}-when-closed`,
|
|
199
|
+
"aria-hidden": true,
|
|
200
|
+
style: {
|
|
201
|
+
position: "absolute",
|
|
202
|
+
visibility: "hidden",
|
|
203
|
+
pointerEvents: "none",
|
|
204
|
+
whiteSpace: "nowrap"
|
|
205
|
+
},
|
|
206
|
+
children: whenOpenText
|
|
207
|
+
}
|
|
208
|
+
),
|
|
209
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
210
|
+
"span",
|
|
211
|
+
{
|
|
212
|
+
id: `${measurementId}-when-open`,
|
|
213
|
+
"aria-hidden": true,
|
|
214
|
+
style: {
|
|
215
|
+
position: "absolute",
|
|
216
|
+
visibility: "hidden",
|
|
217
|
+
pointerEvents: "none",
|
|
218
|
+
whiteSpace: "nowrap"
|
|
219
|
+
},
|
|
220
|
+
children: whenClosedText
|
|
221
|
+
}
|
|
222
|
+
),
|
|
223
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
224
|
+
"span",
|
|
225
|
+
{
|
|
226
|
+
style: { width: textWidth, whiteSpace: "nowrap" },
|
|
227
|
+
className: (0, import_typed_classname.clsx)("hds-navbar__item-responsive-text"),
|
|
228
|
+
children: text
|
|
229
|
+
}
|
|
230
|
+
),
|
|
231
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("span", { style: { width: 32, height: 32 }, children: icon })
|
|
232
|
+
]
|
|
233
|
+
})
|
|
257
234
|
);
|
|
258
235
|
}
|
|
259
236
|
);
|
|
@@ -266,7 +243,7 @@ var NavbarExpandableMenuContent = (0, import_react2.forwardRef)((_a, ref) => {
|
|
|
266
243
|
__spreadProps(__spreadValues(__spreadProps(__spreadValues({}, rest), {
|
|
267
244
|
className: (0, import_typed_classname.clsx)("hds-navbar__expandable-menu-content", className),
|
|
268
245
|
"data-state": open ? "open" : "closed"
|
|
269
|
-
}), { inert: open ? "true"
|
|
246
|
+
}), { inert: open ? void 0 : "true" }), {
|
|
270
247
|
ref,
|
|
271
248
|
children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: (0, import_typed_classname.clsx)("hds-navbar__expandable-menu-content-inner"), children })
|
|
272
249
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/navbar/navbar-expandable-menu.tsx","../../src/utils.ts","../../src/navbar/icons.tsx"],"sourcesContent":["import { createContext, useContext, forwardRef, useState, useRef, useEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { clsx } from \"@postenbring/hedwig-css/typed-classname\";\nimport { focusTrap } from \"../utils\";\nimport { CloseIcon, MenuIcon } from \"./icons\";\n\nconst expandableMenuContext = createContext([\n false as boolean,\n () => {\n // Empty\n },\n] as const);\nexport const useNavbarExpendableMenuContext = () => useContext(expandableMenuContext);\n\nexport interface NavbarExpandableMenuProps {\n children: React.ReactNode;\n}\n\n/**\n * Expandable Menu Provider\n * Handles scroll and focus locking,\n * as well as scrolling the user to the top of the page.\n *\n * If we want a sticky header in the future the scrolling should be configurable\n */\nexport function NavbarExpandableMenu({ children }: NavbarExpandableMenuProps) {\n const [open, setOpen] = useState(false);\n function toggleOpen() {\n setOpen((prev) => !prev);\n }\n\n useEffect(() => {\n if (open) {\n window.scrollTo(0, 0);\n document.body.classList.add(clsx(\"hds-navbar-scroll-lock\"));\n const releaseFocusTrap = focusTrap(\n document.getElementsByClassName(clsx(\"hds-navbar\"))[0] as HTMLElement,\n );\n\n return () => {\n document.body.classList.remove(clsx(\"hds-navbar-scroll-lock\"));\n releaseFocusTrap();\n };\n }\n }, [open]);\n\n return (\n <expandableMenuContext.Provider value={[open, toggleOpen]}>\n {children}\n </expandableMenuContext.Provider>\n );\n}\nNavbarExpandableMenu.displayName = \"NavbarExpandableMenu\";\n\ninterface ButtonInterface extends React.HTMLAttributes<HTMLButtonElement> {\n open?: boolean;\n innerRef?: React.RefObject<HTMLButtonElement>;\n text: React.ReactNode;\n title?: string;\n toggleOpen?: () => void;\n width?: number;\n}\n\nconst RenderButton = forwardRef<HTMLButtonElement, ButtonInterface>(\n ({ className, innerRef, open = false, text, title, toggleOpen, width, ...rest }, ref) => {\n const icon = open ? <CloseIcon /> : <MenuIcon />;\n const style = width ? { width } : {};\n return (\n <button\n className={clsx(\"hds-navbar__item\", className as undefined)}\n onClick={toggleOpen}\n ref={ref ?? innerRef}\n style={style}\n title={title}\n type=\"button\"\n {...rest}\n >\n <span className={clsx(\"hds-navbar__item-responsive-text\")}>{text}</span> {icon}\n </button>\n );\n },\n);\nRenderButton.displayName = \"Navbar.RenderButton\";\n\n/**\n * Trigger\n */\nexport interface NavbarExpandableMenuTriggerProps\n extends Omit<React.HTMLAttributes<HTMLButtonElement>, \"children\"> {\n whenClosedText: React.ReactNode;\n whenClosedHelperTitle?: string;\n\n whenOpenText: React.ReactNode;\n whenOpenHelperTitle?: string;\n}\nexport const NavbarExpandableMenuTrigger = forwardRef<\n HTMLButtonElement,\n NavbarExpandableMenuTriggerProps\n>(\n (\n {\n whenClosedText,\n whenClosedHelperTitle,\n\n whenOpenText,\n whenOpenHelperTitle,\n\n className,\n ...rest\n },\n ref,\n ) => {\n const [open, toggleOpen] = useNavbarExpendableMenuContext();\n const [width, setWidth] = useState(0);\n const measureButtonRef = useRef<HTMLButtonElement>(null);\n\n const text: React.ReactNode = open ? whenOpenText : whenClosedText;\n const title = open ? whenOpenHelperTitle : whenClosedHelperTitle;\n\n /**\n *\n * @param element - Button to measure\n * @param callback - report the width back\n */\n const measureButton = (element: React.ReactNode, callback: (width: number) => void) => {\n // Create an empty div to render the Button in\n const container = document.createElement(\"div\");\n container.style.cssText = \"display: inline-block; position: absolute; visibility: hidden\";\n\n // Attach the empty div inside the navigation section\n const c = document.getElementsByClassName(\"hds-navbar__navigation\")[0];\n c.appendChild(container);\n\n // Render the Button here\n const root = createRoot(container as HTMLElement);\n root.render(element);\n\n /**\n * Get the offsetWidth now that it is rendered\n * Also clean up after us\n */\n const getWidth = () => {\n callback(measureButtonRef.current?.offsetWidth ?? 0);\n root.unmount();\n c.removeChild(container);\n };\n // This is instead of the callback that used to be on ReactDOM.render()\n setTimeout(getWidth, 0);\n };\n\n useEffect(() => {\n measureButton(\n <RenderButton\n className={className}\n innerRef={measureButtonRef}\n text={whenClosedText}\n title={title}\n {...rest}\n />,\n (closedWidth: number) => {\n measureButton(\n <RenderButton\n className={className}\n innerRef={measureButtonRef}\n open\n text={whenOpenText}\n title={title}\n {...rest}\n />,\n (openWidth: number) => {\n setWidth(Math.max(openWidth, closedWidth));\n },\n );\n },\n );\n }, [className, rest, title, whenClosedText, whenOpenText]);\n\n return (\n <RenderButton\n className={className}\n open={open}\n ref={ref}\n text={text as string}\n title={title}\n toggleOpen={toggleOpen}\n width={width}\n {...rest}\n />\n );\n },\n);\nNavbarExpandableMenuTrigger.displayName = \"Navbar.ExpandableMenuTrigger\";\n\n/**\n * Content\n */\nexport interface NavbarExpandableMenuContentProps {\n children: React.ReactNode;\n className?: string;\n}\nexport const NavbarExpandableMenuContent = forwardRef<\n HTMLDivElement,\n NavbarExpandableMenuContentProps\n>(({ children, className, ...rest }, ref) => {\n const [open] = useNavbarExpendableMenuContext();\n return (\n <section\n {...rest}\n className={clsx(\"hds-navbar__expandable-menu-content\", className as undefined)}\n data-state={open ? \"open\" : \"closed\"}\n {...{ inert: open ? \"true\" : undefined }}\n ref={ref}\n >\n <div className={clsx(\"hds-navbar__expandable-menu-content-inner\")}>{children}</div>\n </section>\n );\n});\nNavbarExpandableMenuContent.displayName = \"Navbar.ExpandableMenuContent\";\n","import * as React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n/**\n * Merges an array of refs into a single memoized callback ref or `null`.\n * @see https://floating-ui.com/docs/useMergeRefs\n */\nexport function useMergeRefs<Instance>(\n refs: (React.Ref<Instance> | undefined)[],\n): React.RefCallback<Instance> | null {\n return React.useMemo(() => {\n if (refs.every((ref) => ref === null)) {\n return null;\n }\n\n return (value) => {\n refs.forEach((ref) => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref !== null) {\n (ref as React.MutableRefObject<Instance | null>).current = value;\n }\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, refs);\n}\n\nexport function useResize<Instance extends HTMLElement>(\n ref: React.RefObject<Instance> | undefined | null,\n): { width: number; height: number } {\n const [width, setWidth] = useState<number>(0);\n const [height, setHeight] = useState<number>(0);\n const handleResize = useCallback(() => {\n if (ref?.current !== null) {\n setWidth(ref?.current?.offsetWidth ?? 0);\n setHeight(ref?.current?.offsetHeight ?? 0);\n }\n }, [ref]);\n useEffect(() => {\n window.addEventListener(\"load\", handleResize);\n window.addEventListener(\"resize\", handleResize);\n return () => {\n window.removeEventListener(\"load\", handleResize);\n window.removeEventListener(\"resize\", handleResize);\n };\n }, [ref, handleResize]);\n useEffect(() => {\n handleResize();\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, []);\n return { width, height };\n}\n\nfunction subscribe() {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- It's ok\n return () => {};\n}\n\nexport function useHydrated() {\n return React.useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n );\n}\n\n/**\n * Trap focus inside an element using the `inert` attribute.\n *\n * Adds `inert` to all siblings of the given element, and all their ancestors up to the body.\n * Returns a cleanup function which removes the `inert` property from the elements, effectively giving focus back to rest of the document.\n *\n * NOTE: Does not support portals, i.e. elements outside the DOM hierarchy of the given element.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert\n * @see https://web.dev/articles/inert\n */\nexport function focusTrap(element: HTMLElement) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- NOP on focus trapping the body element\n if (element === document.body) return () => {};\n\n let inertElements: HTMLElement[] = [];\n for (let el: HTMLElement | null = element; el; el = el.parentElement) {\n if (el === document.body) break;\n\n for (const sibling of el.parentElement?.children ?? []) {\n if (sibling === el) continue;\n if (!(sibling instanceof HTMLElement)) continue;\n if (sibling.hasAttribute(\"inert\")) continue;\n\n sibling.setAttribute(\"inert\", \"true\");\n inertElements.push(sibling);\n }\n }\n\n return () => {\n releaseFocusTrap(inertElements);\n inertElements = [];\n };\n}\n\n/**\n * Unset the `inert` attribute on all elements given\n */\nexport function releaseFocusTrap(inertElements: Iterable<HTMLElement>) {\n for (const el of inertElements) {\n el.removeAttribute(\"inert\");\n }\n}\n","export function CloseIcon() {\n return (\n <svg aria-hidden viewBox=\"0 0 384 512\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M345 137c9.4-9.4 9.4-24.6 0-33.9s-24.6-9.4-33.9 0l-119 119L73 103c-9.4-9.4-24.6-9.4-33.9 0s-9.4 24.6 0 33.9l119 119L39 375c-9.4 9.4-9.4 24.6 0 33.9s24.6 9.4 33.9 0l119-119L311 409c9.4 9.4 24.6 9.4 33.9 0s9.4-24.6 0-33.9l-119-119L345 137z\"\n fill=\"currentColor\"\n />\n </svg>\n );\n}\n\nexport function MenuIcon() {\n return (\n <svg role=\"img\" viewBox=\"0 0 448 512\" xmlns=\"http://www.w3.org/2000/svg\">\n <path\n d=\"M0 88C0 74.7 10.7 64 24 64H424c13.3 0 24 10.7 24 24s-10.7 24-24 24H24C10.7 112 0 101.3 0 88zM0 248c0-13.3 10.7-24 24-24H424c13.3 0 24 10.7 24 24s-10.7 24-24 24H24c-13.3 0-24-10.7-24-24zM448 408c0 13.3-10.7 24-24 24H24c-13.3 0-24-10.7-24-24s10.7-24 24-24H424c13.3 0 24 10.7 24 24z\"\n fill=\"currentColor\"\n />\n </svg>\n );\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","releaseFocusTrap","_a","_b"]}
|
|
1
|
+
{"version":3,"sources":["../../src/navbar/navbar-expandable-menu.tsx","../../src/utils.ts","../../src/navbar/icons.tsx"],"sourcesContent":["import { createContext, useContext, forwardRef, useState, useEffect, useId } from \"react\";\nimport { clsx } from \"@postenbring/hedwig-css/typed-classname\";\nimport { focusTrap } from \"../utils\";\nimport { CloseIcon, MenuIcon } from \"./icons\";\n\nconst expandableMenuContext = createContext([\n false as boolean,\n () => {\n // Empty\n },\n] as const);\nexport const useNavbarExpendableMenuContext = () => useContext(expandableMenuContext);\n\nexport interface NavbarExpandableMenuProps {\n children: React.ReactNode;\n}\n\n/**\n * Expandable Menu Provider\n * Handles scroll and focus locking,\n * as well as scrolling the user to the top of the page.\n *\n * If we want a sticky header in the future the scrolling should be configurable\n */\nexport function NavbarExpandableMenu({ children }: NavbarExpandableMenuProps) {\n const [open, setOpen] = useState(false);\n function toggleOpen() {\n setOpen((prev) => !prev);\n }\n\n useEffect(() => {\n if (open) {\n window.scrollTo(0, 0);\n document.body.classList.add(clsx(\"hds-navbar-scroll-lock\"));\n const releaseFocusTrap = focusTrap(\n document.getElementsByClassName(clsx(\"hds-navbar\"))[0] as HTMLElement,\n );\n\n return () => {\n document.body.classList.remove(clsx(\"hds-navbar-scroll-lock\"));\n releaseFocusTrap();\n };\n }\n }, [open]);\n\n return (\n <expandableMenuContext.Provider value={[open, toggleOpen]}>\n {children}\n </expandableMenuContext.Provider>\n );\n}\nNavbarExpandableMenu.displayName = \"NavbarExpandableMenu\";\n\n/**\n * Trigger\n */\nexport interface NavbarExpandableMenuTriggerProps\n extends Omit<React.HTMLAttributes<HTMLButtonElement>, \"children\"> {\n whenClosedText: React.ReactNode;\n whenClosedHelperTitle?: string;\n\n whenOpenText: React.ReactNode;\n whenOpenHelperTitle?: string;\n}\nexport const NavbarExpandableMenuTrigger = forwardRef<\n HTMLButtonElement,\n NavbarExpandableMenuTriggerProps\n>(\n (\n {\n whenClosedText,\n whenClosedHelperTitle,\n\n whenOpenText,\n whenOpenHelperTitle,\n\n style,\n className,\n ...rest\n },\n ref,\n ) => {\n const [open, toggleOpen] = useNavbarExpendableMenuContext();\n\n // Measure the width of the text when open and closed and choose the widest one\n // This is to ensure that the button doesn't change size when the text changes\n const [textWidth, setTextWidth] = useState<number | undefined>(undefined);\n const measurementId = useId();\n useEffect(() => {\n const widthWhenOpen =\n document.getElementById(`${measurementId}-when-open`)?.getBoundingClientRect().width ?? 0;\n const widthWhenClosed =\n document.getElementById(`${measurementId}-when-closed`)?.getBoundingClientRect().width ?? 0;\n\n setTextWidth(widthWhenOpen < widthWhenClosed ? widthWhenClosed : widthWhenOpen);\n }, [measurementId]);\n\n const text = open ? whenOpenText : whenClosedText;\n const title = open ? whenOpenHelperTitle : whenClosedHelperTitle;\n const icon = open ? <CloseIcon /> : <MenuIcon />;\n\n return (\n <button\n className={clsx(\"hds-navbar__item\", className as undefined)}\n onClick={toggleOpen}\n ref={ref}\n title={title}\n type=\"button\"\n style={{ position: \"relative\", ...style }}\n {...rest}\n >\n {/* Measurement elements, not shown to the user */}\n <span\n id={`${measurementId}-when-closed`}\n aria-hidden\n style={{\n position: \"absolute\",\n visibility: \"hidden\",\n pointerEvents: \"none\",\n whiteSpace: \"nowrap\",\n }}\n >\n {whenOpenText}\n </span>\n <span\n id={`${measurementId}-when-open`}\n aria-hidden\n style={{\n position: \"absolute\",\n visibility: \"hidden\",\n pointerEvents: \"none\",\n whiteSpace: \"nowrap\",\n }}\n >\n {whenClosedText}\n </span>\n\n {/* Actual content */}\n <span\n style={{ width: textWidth, whiteSpace: \"nowrap\" }}\n className={clsx(\"hds-navbar__item-responsive-text\")}\n >\n {text}\n </span>\n <span style={{ width: 32, height: 32 }}>{icon}</span>\n </button>\n );\n },\n);\nNavbarExpandableMenuTrigger.displayName = \"Navbar.ExpandableMenuTrigger\";\n\n/**\n * Content\n */\nexport interface NavbarExpandableMenuContentProps {\n children: React.ReactNode;\n className?: string;\n}\nexport const NavbarExpandableMenuContent = forwardRef<\n HTMLDivElement,\n NavbarExpandableMenuContentProps\n>(({ children, className, ...rest }, ref) => {\n const [open] = useNavbarExpendableMenuContext();\n return (\n <section\n {...rest}\n className={clsx(\"hds-navbar__expandable-menu-content\", className as undefined)}\n data-state={open ? \"open\" : \"closed\"}\n {...{ inert: open ? undefined : \"true\" }}\n ref={ref}\n >\n <div className={clsx(\"hds-navbar__expandable-menu-content-inner\")}>{children}</div>\n </section>\n );\n});\nNavbarExpandableMenuContent.displayName = \"Navbar.ExpandableMenuContent\";\n","import * as React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n/**\n * Merges an array of refs into a single memoized callback ref or `null`.\n * @see https://floating-ui.com/docs/useMergeRefs\n */\nexport function useMergeRefs<Instance>(\n refs: (React.Ref<Instance> | undefined)[],\n): React.RefCallback<Instance> | null {\n return React.useMemo(() => {\n if (refs.every((ref) => ref === null)) {\n return null;\n }\n\n return (value) => {\n refs.forEach((ref) => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref !== null) {\n (ref as React.MutableRefObject<Instance | null>).current = value;\n }\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, refs);\n}\n\nexport function useResize<Instance extends HTMLElement>(\n ref: React.RefObject<Instance> | undefined | null,\n): { width: number; height: number } {\n const [width, setWidth] = useState<number>(0);\n const [height, setHeight] = useState<number>(0);\n const handleResize = useCallback(() => {\n if (ref?.current !== null) {\n setWidth(ref?.current?.offsetWidth ?? 0);\n setHeight(ref?.current?.offsetHeight ?? 0);\n }\n }, [ref]);\n useEffect(() => {\n window.addEventListener(\"load\", handleResize);\n window.addEventListener(\"resize\", handleResize);\n return () => {\n window.removeEventListener(\"load\", handleResize);\n window.removeEventListener(\"resize\", handleResize);\n };\n }, [ref, handleResize]);\n useEffect(() => {\n handleResize();\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, []);\n return { width, height };\n}\n\nfunction subscribe() {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- It's ok\n return () => {};\n}\n\nexport function useHydrated() {\n return React.useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n );\n}\n\n/**\n * Trap focus inside an element using the `inert` attribute.\n *\n * Adds `inert` to all siblings of the given element, and all their ancestors up to the body.\n * Returns a cleanup function which removes the `inert` property from the elements, effectively giving focus back to rest of the document.\n *\n * NOTE: Does not support portals, i.e. elements outside the DOM hierarchy of the given element.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert\n * @see https://web.dev/articles/inert\n */\nexport function focusTrap(element: HTMLElement) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- NOP on focus trapping the body element\n if (element === document.body) return () => {};\n\n let inertElements: HTMLElement[] = [];\n for (let el: HTMLElement | null = element; el; el = el.parentElement) {\n if (el === document.body) break;\n\n for (const sibling of el.parentElement?.children ?? []) {\n if (sibling === el) continue;\n if (!(sibling instanceof HTMLElement)) continue;\n if (sibling.hasAttribute(\"inert\")) continue;\n\n sibling.setAttribute(\"inert\", \"true\");\n inertElements.push(sibling);\n }\n }\n\n return () => {\n releaseFocusTrap(inertElements);\n inertElements = [];\n };\n}\n\n/**\n * Unset the `inert` attribute on all elements given\n */\nexport function releaseFocusTrap(inertElements: Iterable<HTMLElement>) {\n for (const el of inertElements) {\n el.removeAttribute(\"inert\");\n }\n}\n","export function CloseIcon() {\n return (\n <svg aria-hidden xmlns=\"http://www.w3.org/2000/svg\" width={24} height={24}>\n <path\n fill=\"currentColor\"\n d=\"m13.16 12.25 3.621-3.586.739-.738c.105-.106.105-.281 0-.422l-.774-.774c-.14-.105-.316-.105-.422 0L12 11.09 7.64 6.73c-.105-.105-.28-.105-.421 0l-.774.774c-.105.14-.105.316 0 .422l4.36 4.324-4.36 4.36c-.105.105-.105.28 0 .421l.774.774c.14.105.316.105.422 0L12 13.445l3.586 3.621.738.739c.106.105.281.105.422 0l.774-.774c.105-.14.105-.316 0-.422l-4.36-4.359Z\"\n />\n </svg>\n );\n}\n\nexport function MenuIcon() {\n return (\n <svg aria-hidden xmlns=\"http://www.w3.org/2000/svg\" width={32} height={32}>\n <path\n fill=\"currentColor\"\n d=\"M25.938 10.146a.57.57 0 0 0 .562-.563v-1.5c0-.281-.281-.562-.563-.562H6.063a.57.57 0 0 0-.562.562v1.5c0 .328.234.563.563.563h19.875Zm0 7.5a.57.57 0 0 0 .562-.563v-1.5c0-.281-.281-.563-.563-.563H6.063a.57.57 0 0 0-.562.563v1.5c0 .328.234.563.563.563h19.875Zm0 7.5a.57.57 0 0 0 .562-.563v-1.5c0-.281-.281-.563-.563-.563H6.063a.57.57 0 0 0-.562.563v1.5c0 .328.234.563.563.563h19.875Z\"\n />\n </svg>\n );\n}\n"],"mappings":"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","names":["import_react","import_jsx_runtime","releaseFocusTrap","_a","_b"]}
|
|
@@ -3,8 +3,8 @@ import {
|
|
|
3
3
|
NavbarExpandableMenuContent,
|
|
4
4
|
NavbarExpandableMenuTrigger,
|
|
5
5
|
useNavbarExpendableMenuContext
|
|
6
|
-
} from "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
} from "../chunk-6R7AXKBR.mjs";
|
|
7
|
+
import "../chunk-MOU6WBT2.mjs";
|
|
8
8
|
import "../chunk-DEX36MFK.mjs";
|
|
9
9
|
import "../chunk-R4SQKVDQ.mjs";
|
|
10
10
|
export {
|
package/dist/show-more/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/show-more/index.ts","../../src/utilities/auto-animate-height.tsx","../../src/utils.ts","../../src/show-more/show-more.tsx"],"sourcesContent":["export { AutoAnimateHeight } from \"../utilities/auto-animate-height\";\nexport type * from \"../utilities/auto-animate-height\";\n\nexport { ShowMoreButton } from \"./show-more\";\nexport type * from \"./show-more\";\n","import { cloneElement, forwardRef, useEffect, useRef, useState } from \"react\";\nimport { useMergeRefs } from \"../utils\";\n\nexport interface AutoAnimateHeightProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Time of the animation, using the hedwig animation tokens\n * quick: 0.1s\n * normal: 0.3s\n * slow: 0.7s\n *\n * default is \"quick\"\n */\n animationDuration?: \"quick\" | \"normal\" | \"slow\";\n\n /**\n * Callback fired when animiation transition ends\n * Use this to do effects after resizing is done, e.g. scrolling to the element\n * using `element.scrollIntoView()`\n */\n onTransitionEnd?: () => void;\n\n /**\n * Which hedwig easing function to use, default is \"normal\"\n */\n animationEasing?: \"in\" | \"out\" | \"normal\";\n children: React.ReactNode;\n style?: React.CSSProperties;\n}\n\n/**\n * Helper component to animate the height of the children when they change\n * It's done by rendering two versions of the passed children,\n * one hidden to measure the height and one visible to only changes after the height is measured.\n *\n * **IMPORTANT** Do not pass any components with effects (like data fetching), as they will trigger twice.\n */\nexport const AutoAnimateHeight = forwardRef<HTMLDivElement, AutoAnimateHeightProps>(\n (\n {\n children,\n style,\n animationDuration = \"quick\",\n animationEasing = \"normal\",\n onTransitionEnd,\n ...rest\n },\n ref,\n ) => {\n const rootRef = useRef<HTMLDivElement>(null);\n const mergedRef = useMergeRefs([rootRef, ref]);\n const measurementRef = useRef<HTMLDivElement>(null);\n const [height, setHeight] = useState<{ height: number; shouldAnimate: boolean } | undefined>(\n undefined,\n );\n const [clonedChildren, setClonedChildren] = useState<React.ReactNode>(() =>\n cloneElement(<>{children}</>, {}),\n );\n\n useEffect(() => {\n if (!rootRef.current) return;\n if (!measurementRef.current) return;\n if (document.body.scrollHeight === 0) return;\n const currentMeasurement = measurementRef.current;\n const { height: newHeight } = currentMeasurement.getBoundingClientRect();\n\n // Listen for resize events on the measurement element\n // Keep the children in sync with the height\n // But don't animate it.\n let previouslyObservedHeight = newHeight;\n const resizeObserver = new ResizeObserver(() => {\n const { height: resizedHeight } = currentMeasurement.getBoundingClientRect();\n if (resizedHeight === previouslyObservedHeight) return;\n previouslyObservedHeight = resizedHeight;\n setHeight({ height: resizedHeight, shouldAnimate: false });\n });\n resizeObserver.observe(currentMeasurement); // This is cleaned up down below in the return functions\n\n // Set the new height when children changes\n setHeight({ height: newHeight, shouldAnimate: true });\n\n // Update children\n const nextClonedChildren = cloneElement(<>{children}</>, {});\n\n // When increasing in height update immediately so the new content is shown during the animation\n if (newHeight >= (height?.height ?? 0)) {\n setClonedChildren(nextClonedChildren);\n return () => {\n resizeObserver.disconnect();\n };\n }\n\n // When decreasing in height, wait until the animation is done so that we don't get a sudden flash of empty content\n const currentRoot = rootRef.current;\n function onTransitionEndHandler(e: TransitionEvent) {\n if (e.propertyName !== \"height\") return;\n setClonedChildren(nextClonedChildren);\n }\n currentRoot.addEventListener(\"transitionend\", onTransitionEndHandler);\n return () => {\n resizeObserver.disconnect();\n currentRoot.removeEventListener(\"transitionend\", onTransitionEndHandler);\n };\n\n // eslint-disable-next-line react-hooks/exhaustive-deps -- I know better\n }, [children]);\n\n return (\n <div\n ref={mergedRef}\n onTransitionEnd={onTransitionEnd}\n style={{\n position: \"relative\",\n overflow: \"hidden\",\n height: height?.height ?? measurementRef.current?.getBoundingClientRect().height,\n transitionProperty: height?.shouldAnimate ? \"height\" : \"none\",\n transitionDuration: `var(--hds-micro-animation-duration-${animationDuration})`,\n transitionTimingFunction: `var(--hds-micro-animation-easing-${animationEasing})`,\n willChange: \"height\",\n ...style,\n }}\n {...rest}\n >\n <div\n aria-hidden\n ref={measurementRef}\n style={{\n position: \"absolute\",\n visibility: \"hidden\",\n }}\n >\n {children}\n </div>\n {clonedChildren}\n </div>\n );\n },\n);\nAutoAnimateHeight.displayName = \"AutoAnimateHeight\";\n","import * as React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n/**\n * Merges an array of refs into a single memoized callback ref or `null`.\n * @see https://floating-ui.com/docs/useMergeRefs\n */\nexport function useMergeRefs<Instance>(\n refs: (React.Ref<Instance> | undefined)[],\n): React.RefCallback<Instance> | null {\n return React.useMemo(() => {\n if (refs.every((ref) => ref === null)) {\n return null;\n }\n\n return (value) => {\n refs.forEach((ref) => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref !== null) {\n (ref as React.MutableRefObject<Instance | null>).current = value;\n }\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, refs);\n}\n\nexport function useResize<Instance extends HTMLElement>(\n ref: React.RefObject<Instance> | undefined | null,\n): { width: number; height: number } {\n const [width, setWidth] = useState<number>(0);\n const [height, setHeight] = useState<number>(0);\n const handleResize = useCallback(() => {\n if (ref?.current !== null) {\n setWidth(ref?.current?.offsetWidth ?? 0);\n setHeight(ref?.current?.offsetHeight ?? 0);\n }\n }, [ref]);\n useEffect(() => {\n window.addEventListener(\"load\", handleResize);\n window.addEventListener(\"resize\", handleResize);\n return () => {\n window.removeEventListener(\"load\", handleResize);\n window.removeEventListener(\"resize\", handleResize);\n };\n }, [ref, handleResize]);\n useEffect(() => {\n handleResize();\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, []);\n return { width, height };\n}\n\nfunction subscribe() {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- It's ok\n return () => {};\n}\n\nexport function useHydrated() {\n return React.useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n );\n}\n\n/**\n * Trap focus inside an element using the `inert` attribute.\n *\n * Adds `inert` to all siblings of the given element, and all their ancestors up to the body.\n * Returns a cleanup function which removes the `inert` property from the elements, effectively giving focus back to rest of the document.\n *\n * NOTE: Does not support portals, i.e. elements outside the DOM hierarchy of the given element.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert\n * @see https://web.dev/articles/inert\n */\nexport function focusTrap(element: HTMLElement) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- NOP on focus trapping the body element\n if (element === document.body) return () => {};\n\n let inertElements: HTMLElement[] = [];\n for (let el: HTMLElement | null = element; el; el = el.parentElement) {\n if (el === document.body) break;\n\n for (const sibling of el.parentElement?.children ?? []) {\n if (sibling === el) continue;\n if (!(sibling instanceof HTMLElement)) continue;\n if (sibling.hasAttribute(\"inert\")) continue;\n\n sibling.setAttribute(\"inert\", \"true\");\n inertElements.push(sibling);\n }\n }\n\n return () => {\n releaseFocusTrap(inertElements);\n inertElements = [];\n };\n}\n\n/**\n * Unset the `inert` attribute on all elements given\n */\nexport function releaseFocusTrap(inertElements: Iterable<HTMLElement>) {\n for (const el of inertElements) {\n el.removeAttribute(\"inert\");\n }\n}\n","import { clsx } from \"@postenbring/hedwig-css/typed-classname\";\nimport { forwardRef } from \"react\";\n\ntype Variant =\n | {\n variant?: \"show-more\";\n expanded?: never;\n }\n | {\n variant: \"show-more-show-less\";\n expanded: boolean;\n };\n\nexport type ShowMoreProps = React.HTMLAttributes<HTMLButtonElement> & {\n text: React.ReactNode;\n} & Variant;\n\n/**\n * Simple button for triggering more content.\n *\n * You have to add the logic for what happens when the button is clicked yourself.\n *\n * @example\n * ```tsx\n * function Content() {\n * const [items, fetchMoreItems, moreItemsAvailable] = useYourData();\n * function onShowMoreItems() {\n * fetchMoreItems();\n * }\n * return (\n * <>\n * <ul>\n * {items.map((item) => (\n * <li key={item.id}>{item.text}</li>\n * ))}\n * </ul>\n * {moreItemsAvailable ?\n * <ShowMoreButton className=\"mt-8\" onClick={onShowMoreItems} lang=\"en\" /> :\n * null\n * }\n * </>\n * )\n * }\n * ```\n */\nexport const ShowMoreButton = forwardRef<HTMLButtonElement, ShowMoreProps>(\n ({ text, variant, expanded, className, ...rest }, ref) => {\n return (\n <button\n ref={ref}\n className={clsx(\n \"hds-show-more\",\n variant === \"show-more-show-less\" && \"hds-show-more--show-less\",\n className as undefined,\n )}\n data-state={expanded ? \"expanded\" : undefined}\n type=\"button\"\n {...rest}\n >\n {text}\n <span className={clsx(\"hds-show-more__icon\")} />\n </button>\n );\n },\n);\nShowMoreButton.displayName = \"ShowMoreButton\";\n"],"mappings":"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","names":["import_react","_a","_b","import_react","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../../src/show-more/index.ts","../../src/utilities/auto-animate-height.tsx","../../src/utils.ts","../../src/show-more/show-more.tsx"],"sourcesContent":["export { AutoAnimateHeight } from \"../utilities/auto-animate-height\";\nexport type * from \"../utilities/auto-animate-height\";\n\nexport { ShowMoreButton } from \"./show-more\";\nexport type * from \"./show-more\";\n","import { cloneElement, forwardRef, useEffect, useRef, useState } from \"react\";\nimport { useMergeRefs } from \"../utils\";\n\nexport interface AutoAnimateHeightProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Time of the animation, using the hedwig animation tokens\n * quick: 0.1s\n * normal: 0.3s\n * slow: 0.7s\n *\n * default is \"quick\"\n */\n animationDuration?: \"quick\" | \"normal\" | \"slow\";\n\n /**\n * Callback fired when animiation transition ends\n * Use this to do effects after resizing is done, e.g. scrolling to the element\n * using `element.scrollIntoView()`\n */\n onTransitionEnd?: () => void;\n\n /**\n * Which hedwig easing function to use, default is \"normal\"\n */\n animationEasing?: \"in\" | \"out\" | \"normal\";\n children: React.ReactNode;\n style?: React.CSSProperties;\n}\n\n/**\n * Helper component to animate the height of the children when they change\n * It's done by rendering two versions of the passed children,\n * one hidden to measure the height and one visible to only changes after the height is measured.\n *\n * **IMPORTANT** Do not pass any components with effects (like data fetching), as they will trigger twice.\n */\nexport const AutoAnimateHeight = forwardRef<HTMLDivElement, AutoAnimateHeightProps>(\n (\n {\n children,\n style,\n animationDuration = \"quick\",\n animationEasing = \"normal\",\n onTransitionEnd,\n ...rest\n },\n ref,\n ) => {\n const rootRef = useRef<HTMLDivElement>(null);\n const mergedRef = useMergeRefs([rootRef, ref]);\n const measurementRef = useRef<HTMLDivElement>(null);\n const [height, setHeight] = useState<{ height: number; shouldAnimate: boolean } | undefined>(\n undefined,\n );\n const [clonedChildren, setClonedChildren] = useState<React.ReactNode>(() =>\n cloneElement(<>{children}</>, {}),\n );\n\n useEffect(() => {\n if (!rootRef.current) return;\n if (!measurementRef.current) return;\n if (document.body.scrollHeight === 0) return;\n const currentMeasurement = measurementRef.current;\n const { height: newHeight } = currentMeasurement.getBoundingClientRect();\n\n // Listen for resize events on the measurement element\n // Keep the children in sync with the height\n // But don't animate it.\n let previouslyObservedHeight = newHeight;\n const resizeObserver = new ResizeObserver(() => {\n const { height: resizedHeight } = currentMeasurement.getBoundingClientRect();\n if (resizedHeight === previouslyObservedHeight) return;\n previouslyObservedHeight = resizedHeight;\n setHeight({ height: resizedHeight, shouldAnimate: false });\n });\n resizeObserver.observe(currentMeasurement); // This is cleaned up down below in the return functions\n\n // Set the new height when children changes\n setHeight({ height: newHeight, shouldAnimate: true });\n\n // Update children\n const nextClonedChildren = cloneElement(<>{children}</>, {});\n\n // When increasing in height update immediately so the new content is shown during the animation\n if (newHeight >= (height?.height ?? 0)) {\n setClonedChildren(nextClonedChildren);\n return () => {\n resizeObserver.disconnect();\n };\n }\n\n // When decreasing in height, wait until the animation is done so that we don't get a sudden flash of empty content\n const currentRoot = rootRef.current;\n function onTransitionEndHandler(e: TransitionEvent) {\n if (e.propertyName !== \"height\") return;\n setClonedChildren(nextClonedChildren);\n }\n currentRoot.addEventListener(\"transitionend\", onTransitionEndHandler);\n return () => {\n resizeObserver.disconnect();\n currentRoot.removeEventListener(\"transitionend\", onTransitionEndHandler);\n };\n\n // eslint-disable-next-line react-hooks/exhaustive-deps -- I know better\n }, [children]);\n\n return (\n <div\n ref={mergedRef}\n onTransitionEnd={onTransitionEnd}\n style={{\n position: \"relative\",\n overflow: \"hidden\",\n height: height?.height ?? measurementRef.current?.getBoundingClientRect().height,\n transitionProperty: height?.shouldAnimate ? \"height\" : \"none\",\n transitionDuration: `var(--hds-micro-animation-duration-${animationDuration})`,\n transitionTimingFunction: `var(--hds-micro-animation-easing-${animationEasing})`,\n willChange: \"height\",\n ...style,\n }}\n {...rest}\n >\n <div\n aria-hidden\n ref={measurementRef}\n style={{\n position: \"absolute\",\n visibility: \"hidden\",\n pointerEvents: \"none\",\n }}\n >\n {children}\n </div>\n {clonedChildren}\n </div>\n );\n },\n);\nAutoAnimateHeight.displayName = \"AutoAnimateHeight\";\n","import * as React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n/**\n * Merges an array of refs into a single memoized callback ref or `null`.\n * @see https://floating-ui.com/docs/useMergeRefs\n */\nexport function useMergeRefs<Instance>(\n refs: (React.Ref<Instance> | undefined)[],\n): React.RefCallback<Instance> | null {\n return React.useMemo(() => {\n if (refs.every((ref) => ref === null)) {\n return null;\n }\n\n return (value) => {\n refs.forEach((ref) => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref !== null) {\n (ref as React.MutableRefObject<Instance | null>).current = value;\n }\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, refs);\n}\n\nexport function useResize<Instance extends HTMLElement>(\n ref: React.RefObject<Instance> | undefined | null,\n): { width: number; height: number } {\n const [width, setWidth] = useState<number>(0);\n const [height, setHeight] = useState<number>(0);\n const handleResize = useCallback(() => {\n if (ref?.current !== null) {\n setWidth(ref?.current?.offsetWidth ?? 0);\n setHeight(ref?.current?.offsetHeight ?? 0);\n }\n }, [ref]);\n useEffect(() => {\n window.addEventListener(\"load\", handleResize);\n window.addEventListener(\"resize\", handleResize);\n return () => {\n window.removeEventListener(\"load\", handleResize);\n window.removeEventListener(\"resize\", handleResize);\n };\n }, [ref, handleResize]);\n useEffect(() => {\n handleResize();\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, []);\n return { width, height };\n}\n\nfunction subscribe() {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- It's ok\n return () => {};\n}\n\nexport function useHydrated() {\n return React.useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n );\n}\n\n/**\n * Trap focus inside an element using the `inert` attribute.\n *\n * Adds `inert` to all siblings of the given element, and all their ancestors up to the body.\n * Returns a cleanup function which removes the `inert` property from the elements, effectively giving focus back to rest of the document.\n *\n * NOTE: Does not support portals, i.e. elements outside the DOM hierarchy of the given element.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert\n * @see https://web.dev/articles/inert\n */\nexport function focusTrap(element: HTMLElement) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- NOP on focus trapping the body element\n if (element === document.body) return () => {};\n\n let inertElements: HTMLElement[] = [];\n for (let el: HTMLElement | null = element; el; el = el.parentElement) {\n if (el === document.body) break;\n\n for (const sibling of el.parentElement?.children ?? []) {\n if (sibling === el) continue;\n if (!(sibling instanceof HTMLElement)) continue;\n if (sibling.hasAttribute(\"inert\")) continue;\n\n sibling.setAttribute(\"inert\", \"true\");\n inertElements.push(sibling);\n }\n }\n\n return () => {\n releaseFocusTrap(inertElements);\n inertElements = [];\n };\n}\n\n/**\n * Unset the `inert` attribute on all elements given\n */\nexport function releaseFocusTrap(inertElements: Iterable<HTMLElement>) {\n for (const el of inertElements) {\n el.removeAttribute(\"inert\");\n }\n}\n","import { clsx } from \"@postenbring/hedwig-css/typed-classname\";\nimport { forwardRef } from \"react\";\n\ntype Variant =\n | {\n variant?: \"show-more\";\n expanded?: never;\n }\n | {\n variant: \"show-more-show-less\";\n expanded: boolean;\n };\n\nexport type ShowMoreProps = React.HTMLAttributes<HTMLButtonElement> & {\n text: React.ReactNode;\n} & Variant;\n\n/**\n * Simple button for triggering more content.\n *\n * You have to add the logic for what happens when the button is clicked yourself.\n *\n * @example\n * ```tsx\n * function Content() {\n * const [items, fetchMoreItems, moreItemsAvailable] = useYourData();\n * function onShowMoreItems() {\n * fetchMoreItems();\n * }\n * return (\n * <>\n * <ul>\n * {items.map((item) => (\n * <li key={item.id}>{item.text}</li>\n * ))}\n * </ul>\n * {moreItemsAvailable ?\n * <ShowMoreButton className=\"mt-8\" onClick={onShowMoreItems} lang=\"en\" /> :\n * null\n * }\n * </>\n * )\n * }\n * ```\n */\nexport const ShowMoreButton = forwardRef<HTMLButtonElement, ShowMoreProps>(\n ({ text, variant, expanded, className, ...rest }, ref) => {\n return (\n <button\n ref={ref}\n className={clsx(\n \"hds-show-more\",\n variant === \"show-more-show-less\" && \"hds-show-more--show-less\",\n className as undefined,\n )}\n data-state={expanded ? \"expanded\" : undefined}\n type=\"button\"\n {...rest}\n >\n {text}\n <span className={clsx(\"hds-show-more__icon\")} />\n </button>\n );\n },\n);\nShowMoreButton.displayName = \"ShowMoreButton\";\n"],"mappings":"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","names":["import_react","_a","_b","import_react","import_jsx_runtime"]}
|
package/dist/show-more/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/auto-animate-height.tsx","../../src/utils.ts"],"sourcesContent":["import { cloneElement, forwardRef, useEffect, useRef, useState } from \"react\";\nimport { useMergeRefs } from \"../utils\";\n\nexport interface AutoAnimateHeightProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Time of the animation, using the hedwig animation tokens\n * quick: 0.1s\n * normal: 0.3s\n * slow: 0.7s\n *\n * default is \"quick\"\n */\n animationDuration?: \"quick\" | \"normal\" | \"slow\";\n\n /**\n * Callback fired when animiation transition ends\n * Use this to do effects after resizing is done, e.g. scrolling to the element\n * using `element.scrollIntoView()`\n */\n onTransitionEnd?: () => void;\n\n /**\n * Which hedwig easing function to use, default is \"normal\"\n */\n animationEasing?: \"in\" | \"out\" | \"normal\";\n children: React.ReactNode;\n style?: React.CSSProperties;\n}\n\n/**\n * Helper component to animate the height of the children when they change\n * It's done by rendering two versions of the passed children,\n * one hidden to measure the height and one visible to only changes after the height is measured.\n *\n * **IMPORTANT** Do not pass any components with effects (like data fetching), as they will trigger twice.\n */\nexport const AutoAnimateHeight = forwardRef<HTMLDivElement, AutoAnimateHeightProps>(\n (\n {\n children,\n style,\n animationDuration = \"quick\",\n animationEasing = \"normal\",\n onTransitionEnd,\n ...rest\n },\n ref,\n ) => {\n const rootRef = useRef<HTMLDivElement>(null);\n const mergedRef = useMergeRefs([rootRef, ref]);\n const measurementRef = useRef<HTMLDivElement>(null);\n const [height, setHeight] = useState<{ height: number; shouldAnimate: boolean } | undefined>(\n undefined,\n );\n const [clonedChildren, setClonedChildren] = useState<React.ReactNode>(() =>\n cloneElement(<>{children}</>, {}),\n );\n\n useEffect(() => {\n if (!rootRef.current) return;\n if (!measurementRef.current) return;\n if (document.body.scrollHeight === 0) return;\n const currentMeasurement = measurementRef.current;\n const { height: newHeight } = currentMeasurement.getBoundingClientRect();\n\n // Listen for resize events on the measurement element\n // Keep the children in sync with the height\n // But don't animate it.\n let previouslyObservedHeight = newHeight;\n const resizeObserver = new ResizeObserver(() => {\n const { height: resizedHeight } = currentMeasurement.getBoundingClientRect();\n if (resizedHeight === previouslyObservedHeight) return;\n previouslyObservedHeight = resizedHeight;\n setHeight({ height: resizedHeight, shouldAnimate: false });\n });\n resizeObserver.observe(currentMeasurement); // This is cleaned up down below in the return functions\n\n // Set the new height when children changes\n setHeight({ height: newHeight, shouldAnimate: true });\n\n // Update children\n const nextClonedChildren = cloneElement(<>{children}</>, {});\n\n // When increasing in height update immediately so the new content is shown during the animation\n if (newHeight >= (height?.height ?? 0)) {\n setClonedChildren(nextClonedChildren);\n return () => {\n resizeObserver.disconnect();\n };\n }\n\n // When decreasing in height, wait until the animation is done so that we don't get a sudden flash of empty content\n const currentRoot = rootRef.current;\n function onTransitionEndHandler(e: TransitionEvent) {\n if (e.propertyName !== \"height\") return;\n setClonedChildren(nextClonedChildren);\n }\n currentRoot.addEventListener(\"transitionend\", onTransitionEndHandler);\n return () => {\n resizeObserver.disconnect();\n currentRoot.removeEventListener(\"transitionend\", onTransitionEndHandler);\n };\n\n // eslint-disable-next-line react-hooks/exhaustive-deps -- I know better\n }, [children]);\n\n return (\n <div\n ref={mergedRef}\n onTransitionEnd={onTransitionEnd}\n style={{\n position: \"relative\",\n overflow: \"hidden\",\n height: height?.height ?? measurementRef.current?.getBoundingClientRect().height,\n transitionProperty: height?.shouldAnimate ? \"height\" : \"none\",\n transitionDuration: `var(--hds-micro-animation-duration-${animationDuration})`,\n transitionTimingFunction: `var(--hds-micro-animation-easing-${animationEasing})`,\n willChange: \"height\",\n ...style,\n }}\n {...rest}\n >\n <div\n aria-hidden\n ref={measurementRef}\n style={{\n position: \"absolute\",\n visibility: \"hidden\",\n }}\n >\n {children}\n </div>\n {clonedChildren}\n </div>\n );\n },\n);\nAutoAnimateHeight.displayName = \"AutoAnimateHeight\";\n","import * as React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n/**\n * Merges an array of refs into a single memoized callback ref or `null`.\n * @see https://floating-ui.com/docs/useMergeRefs\n */\nexport function useMergeRefs<Instance>(\n refs: (React.Ref<Instance> | undefined)[],\n): React.RefCallback<Instance> | null {\n return React.useMemo(() => {\n if (refs.every((ref) => ref === null)) {\n return null;\n }\n\n return (value) => {\n refs.forEach((ref) => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref !== null) {\n (ref as React.MutableRefObject<Instance | null>).current = value;\n }\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, refs);\n}\n\nexport function useResize<Instance extends HTMLElement>(\n ref: React.RefObject<Instance> | undefined | null,\n): { width: number; height: number } {\n const [width, setWidth] = useState<number>(0);\n const [height, setHeight] = useState<number>(0);\n const handleResize = useCallback(() => {\n if (ref?.current !== null) {\n setWidth(ref?.current?.offsetWidth ?? 0);\n setHeight(ref?.current?.offsetHeight ?? 0);\n }\n }, [ref]);\n useEffect(() => {\n window.addEventListener(\"load\", handleResize);\n window.addEventListener(\"resize\", handleResize);\n return () => {\n window.removeEventListener(\"load\", handleResize);\n window.removeEventListener(\"resize\", handleResize);\n };\n }, [ref, handleResize]);\n useEffect(() => {\n handleResize();\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, []);\n return { width, height };\n}\n\nfunction subscribe() {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- It's ok\n return () => {};\n}\n\nexport function useHydrated() {\n return React.useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n );\n}\n\n/**\n * Trap focus inside an element using the `inert` attribute.\n *\n * Adds `inert` to all siblings of the given element, and all their ancestors up to the body.\n * Returns a cleanup function which removes the `inert` property from the elements, effectively giving focus back to rest of the document.\n *\n * NOTE: Does not support portals, i.e. elements outside the DOM hierarchy of the given element.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert\n * @see https://web.dev/articles/inert\n */\nexport function focusTrap(element: HTMLElement) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- NOP on focus trapping the body element\n if (element === document.body) return () => {};\n\n let inertElements: HTMLElement[] = [];\n for (let el: HTMLElement | null = element; el; el = el.parentElement) {\n if (el === document.body) break;\n\n for (const sibling of el.parentElement?.children ?? []) {\n if (sibling === el) continue;\n if (!(sibling instanceof HTMLElement)) continue;\n if (sibling.hasAttribute(\"inert\")) continue;\n\n sibling.setAttribute(\"inert\", \"true\");\n inertElements.push(sibling);\n }\n }\n\n return () => {\n releaseFocusTrap(inertElements);\n inertElements = [];\n };\n}\n\n/**\n * Unset the `inert` attribute on all elements given\n */\nexport function releaseFocusTrap(inertElements: Iterable<HTMLElement>) {\n for (const el of inertElements) {\n el.removeAttribute(\"inert\");\n }\n}\n"],"mappings":"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","names":["import_react","_a","_b"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/auto-animate-height.tsx","../../src/utils.ts"],"sourcesContent":["import { cloneElement, forwardRef, useEffect, useRef, useState } from \"react\";\nimport { useMergeRefs } from \"../utils\";\n\nexport interface AutoAnimateHeightProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Time of the animation, using the hedwig animation tokens\n * quick: 0.1s\n * normal: 0.3s\n * slow: 0.7s\n *\n * default is \"quick\"\n */\n animationDuration?: \"quick\" | \"normal\" | \"slow\";\n\n /**\n * Callback fired when animiation transition ends\n * Use this to do effects after resizing is done, e.g. scrolling to the element\n * using `element.scrollIntoView()`\n */\n onTransitionEnd?: () => void;\n\n /**\n * Which hedwig easing function to use, default is \"normal\"\n */\n animationEasing?: \"in\" | \"out\" | \"normal\";\n children: React.ReactNode;\n style?: React.CSSProperties;\n}\n\n/**\n * Helper component to animate the height of the children when they change\n * It's done by rendering two versions of the passed children,\n * one hidden to measure the height and one visible to only changes after the height is measured.\n *\n * **IMPORTANT** Do not pass any components with effects (like data fetching), as they will trigger twice.\n */\nexport const AutoAnimateHeight = forwardRef<HTMLDivElement, AutoAnimateHeightProps>(\n (\n {\n children,\n style,\n animationDuration = \"quick\",\n animationEasing = \"normal\",\n onTransitionEnd,\n ...rest\n },\n ref,\n ) => {\n const rootRef = useRef<HTMLDivElement>(null);\n const mergedRef = useMergeRefs([rootRef, ref]);\n const measurementRef = useRef<HTMLDivElement>(null);\n const [height, setHeight] = useState<{ height: number; shouldAnimate: boolean } | undefined>(\n undefined,\n );\n const [clonedChildren, setClonedChildren] = useState<React.ReactNode>(() =>\n cloneElement(<>{children}</>, {}),\n );\n\n useEffect(() => {\n if (!rootRef.current) return;\n if (!measurementRef.current) return;\n if (document.body.scrollHeight === 0) return;\n const currentMeasurement = measurementRef.current;\n const { height: newHeight } = currentMeasurement.getBoundingClientRect();\n\n // Listen for resize events on the measurement element\n // Keep the children in sync with the height\n // But don't animate it.\n let previouslyObservedHeight = newHeight;\n const resizeObserver = new ResizeObserver(() => {\n const { height: resizedHeight } = currentMeasurement.getBoundingClientRect();\n if (resizedHeight === previouslyObservedHeight) return;\n previouslyObservedHeight = resizedHeight;\n setHeight({ height: resizedHeight, shouldAnimate: false });\n });\n resizeObserver.observe(currentMeasurement); // This is cleaned up down below in the return functions\n\n // Set the new height when children changes\n setHeight({ height: newHeight, shouldAnimate: true });\n\n // Update children\n const nextClonedChildren = cloneElement(<>{children}</>, {});\n\n // When increasing in height update immediately so the new content is shown during the animation\n if (newHeight >= (height?.height ?? 0)) {\n setClonedChildren(nextClonedChildren);\n return () => {\n resizeObserver.disconnect();\n };\n }\n\n // When decreasing in height, wait until the animation is done so that we don't get a sudden flash of empty content\n const currentRoot = rootRef.current;\n function onTransitionEndHandler(e: TransitionEvent) {\n if (e.propertyName !== \"height\") return;\n setClonedChildren(nextClonedChildren);\n }\n currentRoot.addEventListener(\"transitionend\", onTransitionEndHandler);\n return () => {\n resizeObserver.disconnect();\n currentRoot.removeEventListener(\"transitionend\", onTransitionEndHandler);\n };\n\n // eslint-disable-next-line react-hooks/exhaustive-deps -- I know better\n }, [children]);\n\n return (\n <div\n ref={mergedRef}\n onTransitionEnd={onTransitionEnd}\n style={{\n position: \"relative\",\n overflow: \"hidden\",\n height: height?.height ?? measurementRef.current?.getBoundingClientRect().height,\n transitionProperty: height?.shouldAnimate ? \"height\" : \"none\",\n transitionDuration: `var(--hds-micro-animation-duration-${animationDuration})`,\n transitionTimingFunction: `var(--hds-micro-animation-easing-${animationEasing})`,\n willChange: \"height\",\n ...style,\n }}\n {...rest}\n >\n <div\n aria-hidden\n ref={measurementRef}\n style={{\n position: \"absolute\",\n visibility: \"hidden\",\n pointerEvents: \"none\",\n }}\n >\n {children}\n </div>\n {clonedChildren}\n </div>\n );\n },\n);\nAutoAnimateHeight.displayName = \"AutoAnimateHeight\";\n","import * as React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n/**\n * Merges an array of refs into a single memoized callback ref or `null`.\n * @see https://floating-ui.com/docs/useMergeRefs\n */\nexport function useMergeRefs<Instance>(\n refs: (React.Ref<Instance> | undefined)[],\n): React.RefCallback<Instance> | null {\n return React.useMemo(() => {\n if (refs.every((ref) => ref === null)) {\n return null;\n }\n\n return (value) => {\n refs.forEach((ref) => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref !== null) {\n (ref as React.MutableRefObject<Instance | null>).current = value;\n }\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, refs);\n}\n\nexport function useResize<Instance extends HTMLElement>(\n ref: React.RefObject<Instance> | undefined | null,\n): { width: number; height: number } {\n const [width, setWidth] = useState<number>(0);\n const [height, setHeight] = useState<number>(0);\n const handleResize = useCallback(() => {\n if (ref?.current !== null) {\n setWidth(ref?.current?.offsetWidth ?? 0);\n setHeight(ref?.current?.offsetHeight ?? 0);\n }\n }, [ref]);\n useEffect(() => {\n window.addEventListener(\"load\", handleResize);\n window.addEventListener(\"resize\", handleResize);\n return () => {\n window.removeEventListener(\"load\", handleResize);\n window.removeEventListener(\"resize\", handleResize);\n };\n }, [ref, handleResize]);\n useEffect(() => {\n handleResize();\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, []);\n return { width, height };\n}\n\nfunction subscribe() {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- It's ok\n return () => {};\n}\n\nexport function useHydrated() {\n return React.useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n );\n}\n\n/**\n * Trap focus inside an element using the `inert` attribute.\n *\n * Adds `inert` to all siblings of the given element, and all their ancestors up to the body.\n * Returns a cleanup function which removes the `inert` property from the elements, effectively giving focus back to rest of the document.\n *\n * NOTE: Does not support portals, i.e. elements outside the DOM hierarchy of the given element.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert\n * @see https://web.dev/articles/inert\n */\nexport function focusTrap(element: HTMLElement) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- NOP on focus trapping the body element\n if (element === document.body) return () => {};\n\n let inertElements: HTMLElement[] = [];\n for (let el: HTMLElement | null = element; el; el = el.parentElement) {\n if (el === document.body) break;\n\n for (const sibling of el.parentElement?.children ?? []) {\n if (sibling === el) continue;\n if (!(sibling instanceof HTMLElement)) continue;\n if (sibling.hasAttribute(\"inert\")) continue;\n\n sibling.setAttribute(\"inert\", \"true\");\n inertElements.push(sibling);\n }\n }\n\n return () => {\n releaseFocusTrap(inertElements);\n inertElements = [];\n };\n}\n\n/**\n * Unset the `inert` attribute on all elements given\n */\nexport function releaseFocusTrap(inertElements: Iterable<HTMLElement>) {\n for (const el of inertElements) {\n el.removeAttribute(\"inert\");\n }\n}\n"],"mappings":"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","names":["import_react","_a","_b"]}
|
package/dist/utilities/index.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/utilities/index.ts","../../src/utilities/auto-animate-height.tsx","../../src/utils.ts"],"sourcesContent":["export { AutoAnimateHeight } from \"./auto-animate-height\";\nexport type * from \"./auto-animate-height\";\n","import { cloneElement, forwardRef, useEffect, useRef, useState } from \"react\";\nimport { useMergeRefs } from \"../utils\";\n\nexport interface AutoAnimateHeightProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Time of the animation, using the hedwig animation tokens\n * quick: 0.1s\n * normal: 0.3s\n * slow: 0.7s\n *\n * default is \"quick\"\n */\n animationDuration?: \"quick\" | \"normal\" | \"slow\";\n\n /**\n * Callback fired when animiation transition ends\n * Use this to do effects after resizing is done, e.g. scrolling to the element\n * using `element.scrollIntoView()`\n */\n onTransitionEnd?: () => void;\n\n /**\n * Which hedwig easing function to use, default is \"normal\"\n */\n animationEasing?: \"in\" | \"out\" | \"normal\";\n children: React.ReactNode;\n style?: React.CSSProperties;\n}\n\n/**\n * Helper component to animate the height of the children when they change\n * It's done by rendering two versions of the passed children,\n * one hidden to measure the height and one visible to only changes after the height is measured.\n *\n * **IMPORTANT** Do not pass any components with effects (like data fetching), as they will trigger twice.\n */\nexport const AutoAnimateHeight = forwardRef<HTMLDivElement, AutoAnimateHeightProps>(\n (\n {\n children,\n style,\n animationDuration = \"quick\",\n animationEasing = \"normal\",\n onTransitionEnd,\n ...rest\n },\n ref,\n ) => {\n const rootRef = useRef<HTMLDivElement>(null);\n const mergedRef = useMergeRefs([rootRef, ref]);\n const measurementRef = useRef<HTMLDivElement>(null);\n const [height, setHeight] = useState<{ height: number; shouldAnimate: boolean } | undefined>(\n undefined,\n );\n const [clonedChildren, setClonedChildren] = useState<React.ReactNode>(() =>\n cloneElement(<>{children}</>, {}),\n );\n\n useEffect(() => {\n if (!rootRef.current) return;\n if (!measurementRef.current) return;\n if (document.body.scrollHeight === 0) return;\n const currentMeasurement = measurementRef.current;\n const { height: newHeight } = currentMeasurement.getBoundingClientRect();\n\n // Listen for resize events on the measurement element\n // Keep the children in sync with the height\n // But don't animate it.\n let previouslyObservedHeight = newHeight;\n const resizeObserver = new ResizeObserver(() => {\n const { height: resizedHeight } = currentMeasurement.getBoundingClientRect();\n if (resizedHeight === previouslyObservedHeight) return;\n previouslyObservedHeight = resizedHeight;\n setHeight({ height: resizedHeight, shouldAnimate: false });\n });\n resizeObserver.observe(currentMeasurement); // This is cleaned up down below in the return functions\n\n // Set the new height when children changes\n setHeight({ height: newHeight, shouldAnimate: true });\n\n // Update children\n const nextClonedChildren = cloneElement(<>{children}</>, {});\n\n // When increasing in height update immediately so the new content is shown during the animation\n if (newHeight >= (height?.height ?? 0)) {\n setClonedChildren(nextClonedChildren);\n return () => {\n resizeObserver.disconnect();\n };\n }\n\n // When decreasing in height, wait until the animation is done so that we don't get a sudden flash of empty content\n const currentRoot = rootRef.current;\n function onTransitionEndHandler(e: TransitionEvent) {\n if (e.propertyName !== \"height\") return;\n setClonedChildren(nextClonedChildren);\n }\n currentRoot.addEventListener(\"transitionend\", onTransitionEndHandler);\n return () => {\n resizeObserver.disconnect();\n currentRoot.removeEventListener(\"transitionend\", onTransitionEndHandler);\n };\n\n // eslint-disable-next-line react-hooks/exhaustive-deps -- I know better\n }, [children]);\n\n return (\n <div\n ref={mergedRef}\n onTransitionEnd={onTransitionEnd}\n style={{\n position: \"relative\",\n overflow: \"hidden\",\n height: height?.height ?? measurementRef.current?.getBoundingClientRect().height,\n transitionProperty: height?.shouldAnimate ? \"height\" : \"none\",\n transitionDuration: `var(--hds-micro-animation-duration-${animationDuration})`,\n transitionTimingFunction: `var(--hds-micro-animation-easing-${animationEasing})`,\n willChange: \"height\",\n ...style,\n }}\n {...rest}\n >\n <div\n aria-hidden\n ref={measurementRef}\n style={{\n position: \"absolute\",\n visibility: \"hidden\",\n }}\n >\n {children}\n </div>\n {clonedChildren}\n </div>\n );\n },\n);\nAutoAnimateHeight.displayName = \"AutoAnimateHeight\";\n","import * as React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n/**\n * Merges an array of refs into a single memoized callback ref or `null`.\n * @see https://floating-ui.com/docs/useMergeRefs\n */\nexport function useMergeRefs<Instance>(\n refs: (React.Ref<Instance> | undefined)[],\n): React.RefCallback<Instance> | null {\n return React.useMemo(() => {\n if (refs.every((ref) => ref === null)) {\n return null;\n }\n\n return (value) => {\n refs.forEach((ref) => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref !== null) {\n (ref as React.MutableRefObject<Instance | null>).current = value;\n }\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, refs);\n}\n\nexport function useResize<Instance extends HTMLElement>(\n ref: React.RefObject<Instance> | undefined | null,\n): { width: number; height: number } {\n const [width, setWidth] = useState<number>(0);\n const [height, setHeight] = useState<number>(0);\n const handleResize = useCallback(() => {\n if (ref?.current !== null) {\n setWidth(ref?.current?.offsetWidth ?? 0);\n setHeight(ref?.current?.offsetHeight ?? 0);\n }\n }, [ref]);\n useEffect(() => {\n window.addEventListener(\"load\", handleResize);\n window.addEventListener(\"resize\", handleResize);\n return () => {\n window.removeEventListener(\"load\", handleResize);\n window.removeEventListener(\"resize\", handleResize);\n };\n }, [ref, handleResize]);\n useEffect(() => {\n handleResize();\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, []);\n return { width, height };\n}\n\nfunction subscribe() {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- It's ok\n return () => {};\n}\n\nexport function useHydrated() {\n return React.useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n );\n}\n\n/**\n * Trap focus inside an element using the `inert` attribute.\n *\n * Adds `inert` to all siblings of the given element, and all their ancestors up to the body.\n * Returns a cleanup function which removes the `inert` property from the elements, effectively giving focus back to rest of the document.\n *\n * NOTE: Does not support portals, i.e. elements outside the DOM hierarchy of the given element.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert\n * @see https://web.dev/articles/inert\n */\nexport function focusTrap(element: HTMLElement) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- NOP on focus trapping the body element\n if (element === document.body) return () => {};\n\n let inertElements: HTMLElement[] = [];\n for (let el: HTMLElement | null = element; el; el = el.parentElement) {\n if (el === document.body) break;\n\n for (const sibling of el.parentElement?.children ?? []) {\n if (sibling === el) continue;\n if (!(sibling instanceof HTMLElement)) continue;\n if (sibling.hasAttribute(\"inert\")) continue;\n\n sibling.setAttribute(\"inert\", \"true\");\n inertElements.push(sibling);\n }\n }\n\n return () => {\n releaseFocusTrap(inertElements);\n inertElements = [];\n };\n}\n\n/**\n * Unset the `inert` attribute on all elements given\n */\nexport function releaseFocusTrap(inertElements: Iterable<HTMLElement>) {\n for (const el of inertElements) {\n el.removeAttribute(\"inert\");\n }\n}\n"],"mappings":"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","names":["import_react","_a","_b"]}
|
|
1
|
+
{"version":3,"sources":["../../src/utilities/index.ts","../../src/utilities/auto-animate-height.tsx","../../src/utils.ts"],"sourcesContent":["export { AutoAnimateHeight } from \"./auto-animate-height\";\nexport type * from \"./auto-animate-height\";\n","import { cloneElement, forwardRef, useEffect, useRef, useState } from \"react\";\nimport { useMergeRefs } from \"../utils\";\n\nexport interface AutoAnimateHeightProps extends React.HTMLAttributes<HTMLDivElement> {\n /**\n * Time of the animation, using the hedwig animation tokens\n * quick: 0.1s\n * normal: 0.3s\n * slow: 0.7s\n *\n * default is \"quick\"\n */\n animationDuration?: \"quick\" | \"normal\" | \"slow\";\n\n /**\n * Callback fired when animiation transition ends\n * Use this to do effects after resizing is done, e.g. scrolling to the element\n * using `element.scrollIntoView()`\n */\n onTransitionEnd?: () => void;\n\n /**\n * Which hedwig easing function to use, default is \"normal\"\n */\n animationEasing?: \"in\" | \"out\" | \"normal\";\n children: React.ReactNode;\n style?: React.CSSProperties;\n}\n\n/**\n * Helper component to animate the height of the children when they change\n * It's done by rendering two versions of the passed children,\n * one hidden to measure the height and one visible to only changes after the height is measured.\n *\n * **IMPORTANT** Do not pass any components with effects (like data fetching), as they will trigger twice.\n */\nexport const AutoAnimateHeight = forwardRef<HTMLDivElement, AutoAnimateHeightProps>(\n (\n {\n children,\n style,\n animationDuration = \"quick\",\n animationEasing = \"normal\",\n onTransitionEnd,\n ...rest\n },\n ref,\n ) => {\n const rootRef = useRef<HTMLDivElement>(null);\n const mergedRef = useMergeRefs([rootRef, ref]);\n const measurementRef = useRef<HTMLDivElement>(null);\n const [height, setHeight] = useState<{ height: number; shouldAnimate: boolean } | undefined>(\n undefined,\n );\n const [clonedChildren, setClonedChildren] = useState<React.ReactNode>(() =>\n cloneElement(<>{children}</>, {}),\n );\n\n useEffect(() => {\n if (!rootRef.current) return;\n if (!measurementRef.current) return;\n if (document.body.scrollHeight === 0) return;\n const currentMeasurement = measurementRef.current;\n const { height: newHeight } = currentMeasurement.getBoundingClientRect();\n\n // Listen for resize events on the measurement element\n // Keep the children in sync with the height\n // But don't animate it.\n let previouslyObservedHeight = newHeight;\n const resizeObserver = new ResizeObserver(() => {\n const { height: resizedHeight } = currentMeasurement.getBoundingClientRect();\n if (resizedHeight === previouslyObservedHeight) return;\n previouslyObservedHeight = resizedHeight;\n setHeight({ height: resizedHeight, shouldAnimate: false });\n });\n resizeObserver.observe(currentMeasurement); // This is cleaned up down below in the return functions\n\n // Set the new height when children changes\n setHeight({ height: newHeight, shouldAnimate: true });\n\n // Update children\n const nextClonedChildren = cloneElement(<>{children}</>, {});\n\n // When increasing in height update immediately so the new content is shown during the animation\n if (newHeight >= (height?.height ?? 0)) {\n setClonedChildren(nextClonedChildren);\n return () => {\n resizeObserver.disconnect();\n };\n }\n\n // When decreasing in height, wait until the animation is done so that we don't get a sudden flash of empty content\n const currentRoot = rootRef.current;\n function onTransitionEndHandler(e: TransitionEvent) {\n if (e.propertyName !== \"height\") return;\n setClonedChildren(nextClonedChildren);\n }\n currentRoot.addEventListener(\"transitionend\", onTransitionEndHandler);\n return () => {\n resizeObserver.disconnect();\n currentRoot.removeEventListener(\"transitionend\", onTransitionEndHandler);\n };\n\n // eslint-disable-next-line react-hooks/exhaustive-deps -- I know better\n }, [children]);\n\n return (\n <div\n ref={mergedRef}\n onTransitionEnd={onTransitionEnd}\n style={{\n position: \"relative\",\n overflow: \"hidden\",\n height: height?.height ?? measurementRef.current?.getBoundingClientRect().height,\n transitionProperty: height?.shouldAnimate ? \"height\" : \"none\",\n transitionDuration: `var(--hds-micro-animation-duration-${animationDuration})`,\n transitionTimingFunction: `var(--hds-micro-animation-easing-${animationEasing})`,\n willChange: \"height\",\n ...style,\n }}\n {...rest}\n >\n <div\n aria-hidden\n ref={measurementRef}\n style={{\n position: \"absolute\",\n visibility: \"hidden\",\n pointerEvents: \"none\",\n }}\n >\n {children}\n </div>\n {clonedChildren}\n </div>\n );\n },\n);\nAutoAnimateHeight.displayName = \"AutoAnimateHeight\";\n","import * as React from \"react\";\nimport { useCallback, useEffect, useState } from \"react\";\n\n/**\n * Merges an array of refs into a single memoized callback ref or `null`.\n * @see https://floating-ui.com/docs/useMergeRefs\n */\nexport function useMergeRefs<Instance>(\n refs: (React.Ref<Instance> | undefined)[],\n): React.RefCallback<Instance> | null {\n return React.useMemo(() => {\n if (refs.every((ref) => ref === null)) {\n return null;\n }\n\n return (value) => {\n refs.forEach((ref) => {\n if (typeof ref === \"function\") {\n ref(value);\n } else if (ref !== null) {\n (ref as React.MutableRefObject<Instance | null>).current = value;\n }\n });\n };\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, refs);\n}\n\nexport function useResize<Instance extends HTMLElement>(\n ref: React.RefObject<Instance> | undefined | null,\n): { width: number; height: number } {\n const [width, setWidth] = useState<number>(0);\n const [height, setHeight] = useState<number>(0);\n const handleResize = useCallback(() => {\n if (ref?.current !== null) {\n setWidth(ref?.current?.offsetWidth ?? 0);\n setHeight(ref?.current?.offsetHeight ?? 0);\n }\n }, [ref]);\n useEffect(() => {\n window.addEventListener(\"load\", handleResize);\n window.addEventListener(\"resize\", handleResize);\n return () => {\n window.removeEventListener(\"load\", handleResize);\n window.removeEventListener(\"resize\", handleResize);\n };\n }, [ref, handleResize]);\n useEffect(() => {\n handleResize();\n // eslint-disable-next-line react-hooks/exhaustive-deps -- It's ok\n }, []);\n return { width, height };\n}\n\nfunction subscribe() {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- It's ok\n return () => {};\n}\n\nexport function useHydrated() {\n return React.useSyncExternalStore(\n subscribe,\n () => true,\n () => false,\n );\n}\n\n/**\n * Trap focus inside an element using the `inert` attribute.\n *\n * Adds `inert` to all siblings of the given element, and all their ancestors up to the body.\n * Returns a cleanup function which removes the `inert` property from the elements, effectively giving focus back to rest of the document.\n *\n * NOTE: Does not support portals, i.e. elements outside the DOM hierarchy of the given element.\n *\n * @see https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/inert\n * @see https://web.dev/articles/inert\n */\nexport function focusTrap(element: HTMLElement) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function -- NOP on focus trapping the body element\n if (element === document.body) return () => {};\n\n let inertElements: HTMLElement[] = [];\n for (let el: HTMLElement | null = element; el; el = el.parentElement) {\n if (el === document.body) break;\n\n for (const sibling of el.parentElement?.children ?? []) {\n if (sibling === el) continue;\n if (!(sibling instanceof HTMLElement)) continue;\n if (sibling.hasAttribute(\"inert\")) continue;\n\n sibling.setAttribute(\"inert\", \"true\");\n inertElements.push(sibling);\n }\n }\n\n return () => {\n releaseFocusTrap(inertElements);\n inertElements = [];\n };\n}\n\n/**\n * Unset the `inert` attribute on all elements given\n */\nexport function releaseFocusTrap(inertElements: Iterable<HTMLElement>) {\n for (const el of inertElements) {\n el.removeAttribute(\"inert\");\n }\n}\n"],"mappings":"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","names":["import_react","_a","_b"]}
|
package/dist/utilities/index.mjs
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@postenbring/hedwig-react",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.81",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"module": "./dist/index.mjs",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -19,8 +19,8 @@
|
|
|
19
19
|
"react-dom": "18.3.1",
|
|
20
20
|
"tsup": "^8.0.1",
|
|
21
21
|
"typescript": "^5.4.5",
|
|
22
|
-
"
|
|
23
|
-
"
|
|
22
|
+
"eslint-config-custom": "0.0.1",
|
|
23
|
+
"hedwig-tsconfig": "0.0.0"
|
|
24
24
|
},
|
|
25
25
|
"peerDependencies": {
|
|
26
26
|
"@types/react": "^17.0.0 || ^18.0.0",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@radix-ui/react-popover": "1.0.7",
|
|
33
33
|
"@radix-ui/react-slot": "1.0.2",
|
|
34
|
-
"@postenbring/hedwig-css": "0.0.
|
|
34
|
+
"@postenbring/hedwig-css": "0.0.69"
|
|
35
35
|
},
|
|
36
36
|
"publishConfig": {
|
|
37
37
|
"access": "public"
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/navbar/navbar-expandable-menu.tsx"],"sourcesContent":["import { createContext, useContext, forwardRef, useState, useRef, useEffect } from \"react\";\nimport { createRoot } from \"react-dom/client\";\nimport { clsx } from \"@postenbring/hedwig-css/typed-classname\";\nimport { focusTrap } from \"../utils\";\nimport { CloseIcon, MenuIcon } from \"./icons\";\n\nconst expandableMenuContext = createContext([\n false as boolean,\n () => {\n // Empty\n },\n] as const);\nexport const useNavbarExpendableMenuContext = () => useContext(expandableMenuContext);\n\nexport interface NavbarExpandableMenuProps {\n children: React.ReactNode;\n}\n\n/**\n * Expandable Menu Provider\n * Handles scroll and focus locking,\n * as well as scrolling the user to the top of the page.\n *\n * If we want a sticky header in the future the scrolling should be configurable\n */\nexport function NavbarExpandableMenu({ children }: NavbarExpandableMenuProps) {\n const [open, setOpen] = useState(false);\n function toggleOpen() {\n setOpen((prev) => !prev);\n }\n\n useEffect(() => {\n if (open) {\n window.scrollTo(0, 0);\n document.body.classList.add(clsx(\"hds-navbar-scroll-lock\"));\n const releaseFocusTrap = focusTrap(\n document.getElementsByClassName(clsx(\"hds-navbar\"))[0] as HTMLElement,\n );\n\n return () => {\n document.body.classList.remove(clsx(\"hds-navbar-scroll-lock\"));\n releaseFocusTrap();\n };\n }\n }, [open]);\n\n return (\n <expandableMenuContext.Provider value={[open, toggleOpen]}>\n {children}\n </expandableMenuContext.Provider>\n );\n}\nNavbarExpandableMenu.displayName = \"NavbarExpandableMenu\";\n\ninterface ButtonInterface extends React.HTMLAttributes<HTMLButtonElement> {\n open?: boolean;\n innerRef?: React.RefObject<HTMLButtonElement>;\n text: React.ReactNode;\n title?: string;\n toggleOpen?: () => void;\n width?: number;\n}\n\nconst RenderButton = forwardRef<HTMLButtonElement, ButtonInterface>(\n ({ className, innerRef, open = false, text, title, toggleOpen, width, ...rest }, ref) => {\n const icon = open ? <CloseIcon /> : <MenuIcon />;\n const style = width ? { width } : {};\n return (\n <button\n className={clsx(\"hds-navbar__item\", className as undefined)}\n onClick={toggleOpen}\n ref={ref ?? innerRef}\n style={style}\n title={title}\n type=\"button\"\n {...rest}\n >\n <span className={clsx(\"hds-navbar__item-responsive-text\")}>{text}</span> {icon}\n </button>\n );\n },\n);\nRenderButton.displayName = \"Navbar.RenderButton\";\n\n/**\n * Trigger\n */\nexport interface NavbarExpandableMenuTriggerProps\n extends Omit<React.HTMLAttributes<HTMLButtonElement>, \"children\"> {\n whenClosedText: React.ReactNode;\n whenClosedHelperTitle?: string;\n\n whenOpenText: React.ReactNode;\n whenOpenHelperTitle?: string;\n}\nexport const NavbarExpandableMenuTrigger = forwardRef<\n HTMLButtonElement,\n NavbarExpandableMenuTriggerProps\n>(\n (\n {\n whenClosedText,\n whenClosedHelperTitle,\n\n whenOpenText,\n whenOpenHelperTitle,\n\n className,\n ...rest\n },\n ref,\n ) => {\n const [open, toggleOpen] = useNavbarExpendableMenuContext();\n const [width, setWidth] = useState(0);\n const measureButtonRef = useRef<HTMLButtonElement>(null);\n\n const text: React.ReactNode = open ? whenOpenText : whenClosedText;\n const title = open ? whenOpenHelperTitle : whenClosedHelperTitle;\n\n /**\n *\n * @param element - Button to measure\n * @param callback - report the width back\n */\n const measureButton = (element: React.ReactNode, callback: (width: number) => void) => {\n // Create an empty div to render the Button in\n const container = document.createElement(\"div\");\n container.style.cssText = \"display: inline-block; position: absolute; visibility: hidden\";\n\n // Attach the empty div inside the navigation section\n const c = document.getElementsByClassName(\"hds-navbar__navigation\")[0];\n c.appendChild(container);\n\n // Render the Button here\n const root = createRoot(container as HTMLElement);\n root.render(element);\n\n /**\n * Get the offsetWidth now that it is rendered\n * Also clean up after us\n */\n const getWidth = () => {\n callback(measureButtonRef.current?.offsetWidth ?? 0);\n root.unmount();\n c.removeChild(container);\n };\n // This is instead of the callback that used to be on ReactDOM.render()\n setTimeout(getWidth, 0);\n };\n\n useEffect(() => {\n measureButton(\n <RenderButton\n className={className}\n innerRef={measureButtonRef}\n text={whenClosedText}\n title={title}\n {...rest}\n />,\n (closedWidth: number) => {\n measureButton(\n <RenderButton\n className={className}\n innerRef={measureButtonRef}\n open\n text={whenOpenText}\n title={title}\n {...rest}\n />,\n (openWidth: number) => {\n setWidth(Math.max(openWidth, closedWidth));\n },\n );\n },\n );\n }, [className, rest, title, whenClosedText, whenOpenText]);\n\n return (\n <RenderButton\n className={className}\n open={open}\n ref={ref}\n text={text as string}\n title={title}\n toggleOpen={toggleOpen}\n width={width}\n {...rest}\n />\n );\n },\n);\nNavbarExpandableMenuTrigger.displayName = \"Navbar.ExpandableMenuTrigger\";\n\n/**\n * Content\n */\nexport interface NavbarExpandableMenuContentProps {\n children: React.ReactNode;\n className?: string;\n}\nexport const NavbarExpandableMenuContent = forwardRef<\n HTMLDivElement,\n NavbarExpandableMenuContentProps\n>(({ children, className, ...rest }, ref) => {\n const [open] = useNavbarExpendableMenuContext();\n return (\n <section\n {...rest}\n className={clsx(\"hds-navbar__expandable-menu-content\", className as undefined)}\n data-state={open ? \"open\" : \"closed\"}\n {...{ inert: open ? \"true\" : undefined }}\n ref={ref}\n >\n <div className={clsx(\"hds-navbar__expandable-menu-content-inner\")}>{children}</div>\n </section>\n );\n});\nNavbarExpandableMenuContent.displayName = \"Navbar.ExpandableMenuContent\";\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,eAAe,YAAY,YAAY,UAAU,QAAQ,iBAAiB;AACnF,SAAS,kBAAkB;AAC3B,SAAS,YAAY;AA6CjB,cAqBE,YArBF;AAzCJ,IAAM,wBAAwB,cAAc;AAAA,EAC1C;AAAA,EACA,MAAM;AAAA,EAEN;AACF,CAAU;AACH,IAAM,iCAAiC,MAAM,WAAW,qBAAqB;AAa7E,SAAS,qBAAqB,EAAE,SAAS,GAA8B;AAC5E,QAAM,CAAC,MAAM,OAAO,IAAI,SAAS,KAAK;AACtC,WAAS,aAAa;AACpB,YAAQ,CAAC,SAAS,CAAC,IAAI;AAAA,EACzB;AAEA,YAAU,MAAM;AACd,QAAI,MAAM;AACR,aAAO,SAAS,GAAG,CAAC;AACpB,eAAS,KAAK,UAAU,IAAI,KAAK,wBAAwB,CAAC;AAC1D,YAAM,mBAAmB;AAAA,QACvB,SAAS,uBAAuB,KAAK,YAAY,CAAC,EAAE,CAAC;AAAA,MACvD;AAEA,aAAO,MAAM;AACX,iBAAS,KAAK,UAAU,OAAO,KAAK,wBAAwB,CAAC;AAC7D,yBAAiB;AAAA,MACnB;AAAA,IACF;AAAA,EACF,GAAG,CAAC,IAAI,CAAC;AAET,SACE,oBAAC,sBAAsB,UAAtB,EAA+B,OAAO,CAAC,MAAM,UAAU,GACrD,UACH;AAEJ;AACA,qBAAqB,cAAc;AAWnC,IAAM,eAAe;AAAA,EACnB,CAAC,IAAgF,QAAQ;AAAxF,iBAAE,aAAW,UAAU,OAAO,OAAO,MAAM,OAAO,YAAY,MAhEjE,IAgEG,IAAwE,iBAAxE,IAAwE,CAAtE,aAAW,YAAU,QAAc,QAAM,SAAO,cAAY;AAC7D,UAAM,OAAO,OAAO,oBAAC,aAAU,IAAK,oBAAC,YAAS;AAC9C,UAAM,QAAQ,QAAQ,EAAE,MAAM,IAAI,CAAC;AACnC,WACE;AAAA,MAAC;AAAA;AAAA,QACC,WAAW,KAAK,oBAAoB,SAAsB;AAAA,QAC1D,SAAS;AAAA,QACT,KAAK,oBAAO;AAAA,QACZ;AAAA,QACA;AAAA,QACA,MAAK;AAAA,SACD,OAPL;AAAA,QASC;AAAA,8BAAC,UAAK,WAAW,KAAK,kCAAkC,GAAI,gBAAK;AAAA,UAAO;AAAA,UAAE;AAAA;AAAA;AAAA,IAC5E;AAAA,EAEJ;AACF;AACA,aAAa,cAAc;AAapB,IAAM,8BAA8B;AAAA,EAIzC,CACE,IAUA,QACG;AAXH,iBACE;AAAA;AAAA,MACA;AAAA,MAEA;AAAA,MACA;AAAA,MAEA;AAAA,IA3GN,IAoGI,IAQK,iBARL,IAQK;AAAA,MAPH;AAAA,MACA;AAAA,MAEA;AAAA,MACA;AAAA,MAEA;AAAA;AAKF,UAAM,CAAC,MAAM,UAAU,IAAI,+BAA+B;AAC1D,UAAM,CAAC,OAAO,QAAQ,IAAI,SAAS,CAAC;AACpC,UAAM,mBAAmB,OAA0B,IAAI;AAEvD,UAAM,OAAwB,OAAO,eAAe;AACpD,UAAM,QAAQ,OAAO,sBAAsB;AAO3C,UAAM,gBAAgB,CAAC,SAA0B,aAAsC;AAErF,YAAM,YAAY,SAAS,cAAc,KAAK;AAC9C,gBAAU,MAAM,UAAU;AAG1B,YAAM,IAAI,SAAS,uBAAuB,wBAAwB,EAAE,CAAC;AACrE,QAAE,YAAY,SAAS;AAGvB,YAAM,OAAO,WAAW,SAAwB;AAChD,WAAK,OAAO,OAAO;AAMnB,YAAM,WAAW,MAAM;AA7I7B,YAAAA,KAAAC;AA8IQ,kBAASA,OAAAD,MAAA,iBAAiB,YAAjB,gBAAAA,IAA0B,gBAA1B,OAAAC,MAAyC,CAAC;AACnD,aAAK,QAAQ;AACb,UAAE,YAAY,SAAS;AAAA,MACzB;AAEA,iBAAW,UAAU,CAAC;AAAA,IACxB;AAEA,cAAU,MAAM;AACd;AAAA,QACE;AAAA,UAAC;AAAA;AAAA,YACC;AAAA,YACA,UAAU;AAAA,YACV,MAAM;AAAA,YACN;AAAA,aACI;AAAA,QACN;AAAA,QACA,CAAC,gBAAwB;AACvB;AAAA,YACE;AAAA,cAAC;AAAA;AAAA,gBACC;AAAA,gBACA,UAAU;AAAA,gBACV,MAAI;AAAA,gBACJ,MAAM;AAAA,gBACN;AAAA,iBACI;AAAA,YACN;AAAA,YACA,CAAC,cAAsB;AACrB,uBAAS,KAAK,IAAI,WAAW,WAAW,CAAC;AAAA,YAC3C;AAAA,UACF;AAAA,QACF;AAAA,MACF;AAAA,IACF,GAAG,CAAC,WAAW,MAAM,OAAO,gBAAgB,YAAY,CAAC;AAEzD,WACE;AAAA,MAAC;AAAA;AAAA,QACC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,SACI;AAAA,IACN;AAAA,EAEJ;AACF;AACA,4BAA4B,cAAc;AASnC,IAAM,8BAA8B,WAGzC,CAAC,IAAkC,QAAQ;AAA1C,eAAE,YAAU,UA3Mf,IA2MG,IAA0B,iBAA1B,IAA0B,CAAxB,YAAU;AACb,QAAM,CAAC,IAAI,IAAI,+BAA+B;AAC9C,SACE;AAAA,IAAC;AAAA,kEACK,OADL;AAAA,MAEC,WAAW,KAAK,uCAAuC,SAAsB;AAAA,MAC7E,cAAY,OAAO,SAAS;AAAA,QACxB,EAAE,OAAO,OAAO,SAAS,OAAU,IAJxC;AAAA,MAKC;AAAA,MAEA,8BAAC,SAAI,WAAW,KAAK,2CAA2C,GAAI,UAAS;AAAA;AAAA,EAC/E;AAEJ,CAAC;AACD,4BAA4B,cAAc;","names":["_a","_b"]}
|