@reykjavik/hanna-react 0.10.79 → 0.10.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/CHANGELOG.md CHANGED
@@ -4,6 +4,29 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
+ ## 0.10.81
8
+
9
+ _2023-02-16_
10
+
11
+ - feat: Add component `Tooltip`
12
+ - fix(ts): Make `MainMenu` onClick handlers return `void`, not `undefined`
13
+ - fix: Suppress accidental dev-mode warnings for
14
+ `CheckboxButton`/`RadioButtonGroup`s
15
+
16
+ ## 0.10.80
17
+
18
+ _2023-02-08_
19
+
20
+ - feat: Automatically close "Hamburger menu" when user clicks `MainMenu` links
21
+ - feat: Support `MainMenuProps.onItemClick` `MainMenuItem.onClick` returning
22
+ `false` to prevent auto-closing of "Hamburger menu".
23
+ - feat: Add `useHannaUIState` hook to "utils" — exposing `isHamburgerMenuOpen`
24
+ and `closeHamburgerMenu()`.
25
+ - fix: Toggle `MainMenu` mega panels on repeated top-level link clicks
26
+ - fix: Close the currently open `MainMenu` mega panels on intra-link click
27
+ - fix: Close `ContactBubble` on intra-link/button click
28
+ - fix: Update `react-datepicker` for latest upstream bugfixes
29
+
7
30
  ## 0.10.79
8
31
 
9
32
  _2023-01-23_
package/ContactBubble.js CHANGED
@@ -128,13 +128,15 @@ const ContactBubble = (props) => {
128
128
  const { href, label, extraLabel, target, onClick } = linkInfo;
129
129
  const icon = (0, exports.ensureIcon)(linkInfo.icon);
130
130
  const itemClass = (0, getBemClass_1.default)('ContactBubble__item', icon && 'type--' + icon);
131
- const onClickHandler = onClick &&
132
- ((e) => {
133
- if (!onClick()) {
131
+ const onClickHandler = (e) => {
132
+ if (onClick) {
133
+ const doPreventDefault = onClick() !== true;
134
+ if (doPreventDefault) {
134
135
  e.preventDefault();
135
- closeBubble(false);
136
136
  }
137
- });
137
+ }
138
+ closeBubble(false);
139
+ };
138
140
  const content = [
139
141
  ' ',
140
142
  label,
package/Datepicker.js CHANGED
@@ -66,16 +66,7 @@ const Datepicker = (props) => {
66
66
  (elm === null || elm === void 0 ? void 0 : elm.querySelector('input')) || undefined;
67
67
  return elm;
68
68
  }) }, addFocusProps()),
69
- react_1.default.createElement(react_datepicker_1.default, Object.assign({ id: domid, required: inputProps.required, disabled: inputProps.disabled, readOnly: inputProps.readOnly, selected: value, name: name, locale: localeCode, dateFormat:
70
- // NOTE: Force all dateFormat values into Array<string> to temporarily work around
71
- // a bug in the current version of react-datepicker where invalid **string** values
72
- // are re-parsed with `new Date()`, causing surprising behavior in certain situations
73
- // AND wheree Arrayed formats get parsed in order of "increasing priority".
74
- // Revert back to the plain `dateFormat={dateFormat}` pass-through once this PR has been
75
- // accepted and released: https://github.com/Hacker0x01/react-datepicker/pull/3903
76
- typeof dateFormat === 'string'
77
- ? [dateFormat]
78
- : dateFormat.slice(0).reverse(), onChange: (date) => {
69
+ react_1.default.createElement(react_datepicker_1.default, Object.assign({ id: domid, required: inputProps.required, disabled: inputProps.disabled, readOnly: inputProps.readOnly, selected: value, name: name, locale: localeCode, dateFormat: dateFormat, onChange: (date) => {
79
70
  onChange(date || undefined);
80
71
  const inputElm = inputRef === null || inputRef === void 0 ? void 0 : inputRef.current;
81
72
  if (inputElm) {
@@ -1,9 +1,9 @@
1
- import React from 'react';
1
+ /// <reference types="react" />
2
2
  import { GalleryProps } from '../Gallery';
3
3
  import { GalleryItemProps } from './_GalleryItem';
4
4
  type ModalContextProps = {
5
5
  setCurrentImage: (item: GalleryItemProps | undefined) => void;
6
6
  currentImage: GalleryItemProps | undefined;
7
7
  } & GalleryProps;
8
- export declare const GalleryModalContext: React.Context<ModalContextProps>;
8
+ export declare const GalleryModalContext: import("react").Context<ModalContextProps>;
9
9
  export {};
@@ -1,6 +1,5 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.GalleryModalContext = void 0;
4
- const tslib_1 = require("tslib");
5
- const react_1 = tslib_1.__importDefault(require("react"));
6
- exports.GalleryModalContext = react_1.default.createContext({});
4
+ const react_1 = require("react");
5
+ exports.GalleryModalContext = (0, react_1.createContext)({});
package/Layout.js CHANGED
@@ -8,6 +8,7 @@ const assets_1 = require("@reykjavik/hanna-utils/assets");
8
8
  const i18n_1 = require("@reykjavik/hanna-utils/i18n");
9
9
  const _Image_1 = require("./_abstract/_Image");
10
10
  const _Link_1 = require("./_abstract/_Link");
11
+ const HannaUIState_1 = require("./utils/HannaUIState");
11
12
  const useMenuToggling_1 = require("./utils/useMenuToggling");
12
13
  const useScrollbarWidthCSSVar_1 = require("./utils/useScrollbarWidthCSSVar");
13
14
  const utils_1 = require("./utils");
@@ -28,7 +29,7 @@ exports.defaultLayoutTexts = {
28
29
  const Layout = (props) => {
29
30
  (0, useScrollbarWidthCSSVar_1.useScrollbarWidthCSSVar)();
30
31
  const { ssr, globalAlerts, mainChildren, navChildren, footerChildren, colorTheme, children, siteName = 'Reykjavík', logoLink = '/', } = props;
31
- const { isMenuActive, closeMenu, toggleMenu } = (0, useMenuToggling_1.useMenuToggling)(ssr !== 'ssr-only');
32
+ const { isMenuActive, isMenuOpen, closeMenu, toggleMenu } = (0, useMenuToggling_1.useMenuToggling)(ssr !== 'ssr-only');
32
33
  const isBrowser = (0, utils_1.useIsBrowserSide)( /* ssr */);
33
34
  const txt = (0, i18n_1.getTexts)(props, exports.defaultLayoutTexts);
34
35
  return (react_1.default.createElement("div", { className: (0, getBemClass_1.default)('Layout', props.modifier), "data-sprinkled": isBrowser, "data-color-theme": colorTheme },
@@ -49,7 +50,10 @@ const Layout = (props) => {
49
50
  }), "aria-label": txt.skipLinkLabel }, txt.skipLinkLabel))),
50
51
  react_1.default.createElement("div", { className: "Layout__main", role: "main" }, mainChildren || children),
51
52
  navChildren && (react_1.default.createElement("div", { className: "Layout__nav", id: "pagenav", role: "navigation" },
52
- navChildren,
53
+ react_1.default.createElement(HannaUIState_1.HannaUIState, { value: {
54
+ closeHamburgerMenu: closeMenu,
55
+ isHamburgerMenuOpen: isMenuOpen,
56
+ } }, navChildren),
53
57
  isMenuActive && (react_1.default.createElement("button", { className: "Layout__nav__closebutton", onClick: closeMenu, "aria-label": txt.closeMenuLabelLong, type: "button" }, txt.closeMenuLabel)))),
54
58
  react_1.default.createElement("div", { className: "Layout__footer", role: "complementary" }, footerChildren))));
55
59
  };
package/MainMenu.d.ts CHANGED
@@ -15,7 +15,14 @@ export type MainMenuItem = {
15
15
  modifier?: string;
16
16
  current?: boolean;
17
17
  href?: string;
18
- onClick?: (index: number, item: MainMenuItem) => void;
18
+ /**
19
+ * Adding `onClick` automatically results in a <button/> element being rendered.
20
+ *
21
+ * NOTE: Clicking a MainMenu item will automatically close HannaUIState's
22
+ * "Hamburger menu" (a.k.a. "Mobile menu")
23
+ * … unless the `onClick` function explicitly returns `false`.
24
+ */
25
+ onClick?: (index: number, item: MainMenuItem) => void | boolean;
19
26
  controlsId?: string;
20
27
  };
21
28
  export type MainMenuSeparator = '---';
@@ -38,7 +45,12 @@ export type MainMenuProps = {
38
45
  items: MainMenuItemList;
39
46
  megaPanels?: Array<MegaMenuPanel>;
40
47
  auxiliaryPanel?: AuxiliaryPanelProps;
41
- onItemClick?: (index: number, item: MainMenuItem) => void;
48
+ /**
49
+ * NOTE: Clicking a MainMenu item will automatically close HannaUIState's
50
+ * "Hamburger menu" (a.k.a. "Mobile menu")
51
+ * … unless the `onItemClick` function explicitly returns `false`.
52
+ */
53
+ onItemClick?: (index: number, item: MainMenuItem) => void | boolean;
42
54
  activePanelId?: string;
43
55
  texts?: MainMenuI18n;
44
56
  lang?: string;
package/MainMenu.js CHANGED
@@ -11,6 +11,7 @@ const i18n_1 = require("@reykjavik/hanna-utils/i18n");
11
11
  const _Link_1 = require("./_abstract/_Link");
12
12
  const _Auxiliary_1 = require("./MainMenu/_Auxiliary");
13
13
  const _PrimaryPanel_1 = require("./MainMenu/_PrimaryPanel");
14
+ const HannaUIState_1 = require("./utils/HannaUIState");
14
15
  const useFormatMonitor_1 = require("./utils/useFormatMonitor");
15
16
  const utils_1 = require("./utils");
16
17
  const findActivePanel = (megaPanels, activeId) => activeId ? megaPanels.find((panel) => activeId === panel.id) : undefined;
@@ -20,52 +21,52 @@ exports.defaultMainMenuTexts = {
20
21
  };
21
22
  const MainMenu = (props) => {
22
23
  const { title, megaPanels = [], onItemClick, ssr, auxiliaryPanel } = props;
24
+ const { closeHamburgerMenu } = (0, HannaUIState_1.useHannaUIState)();
23
25
  const isBrowser = (0, utils_1.useIsBrowserSide)(ssr);
24
26
  const menuElmRef = (0, react_1.useRef)(null);
25
27
  const pressedLinkRef = (0, react_1.useRef)(null);
26
28
  const activePanelRef = (0, react_1.useRef)(null);
27
29
  const [activePanel, _setActivePanel] = (0, react_1.useState)(() => isBrowser && findActivePanel(megaPanels, props.activePanelId));
28
30
  const [laggyActivePanel, setLaggyActivePanel] = (0, useShortState_1.default)();
29
- const setActivePanel = (0, react_1.useCallback)((newActive, setFocus = true) => {
30
- if (!isBrowser) {
31
- return;
31
+ const setActivePanel = (0, react_1.useMemo)(() => isBrowser
32
+ ? (newActive, setFocus = true) => {
33
+ const htmlElmDataset = document.documentElement.dataset;
34
+ // const menuElm = menuElmRef.current as HTMLElement;
35
+ _setActivePanel((activePanel) => {
36
+ const scrollElm = (0, hanna_utils_1.getPageScrollElm)();
37
+ if (!newActive) {
38
+ activePanel && setLaggyActivePanel(activePanel, 1000);
39
+ scrollElm.scrollTop = parseInt(htmlElmDataset.scrollTop || '') || 0;
40
+ delete htmlElmDataset.scrollTop;
41
+ delete htmlElmDataset.megaPanelActive;
42
+ }
43
+ else {
44
+ setLaggyActivePanel(undefined, 0);
45
+ htmlElmDataset.scrollTop = String(scrollElm.scrollTop);
46
+ scrollElm.scrollTop = 0;
47
+ htmlElmDataset.megaPanelActive = '';
48
+ }
49
+ if (setFocus) {
50
+ const pressedLinkElm = pressedLinkRef.current; // pressedLinkElm will be undefined when setTimeout fires
51
+ setTimeout(() => {
52
+ if (!newActive) {
53
+ // const buttonElm = menuElm.querySelector<HTMLButtonElement>(
54
+ // 'button.MainMenu__link[aria-pressed="true"]'
55
+ // );
56
+ (0, focusElm_1.default)(pressedLinkElm);
57
+ }
58
+ else if (newActive !== activePanel) {
59
+ // const panelElm = menuElm.querySelector<HTMLButtonElement>(
60
+ // '.PrimaryPanel--active'
61
+ // );
62
+ (0, focusElm_1.default)(activePanelRef.current);
63
+ }
64
+ }, 100);
65
+ }
66
+ return newActive;
67
+ });
32
68
  }
33
- const htmlElmDataset = document.documentElement.dataset;
34
- // const menuElm = menuElmRef.current as HTMLElement;
35
- _setActivePanel((activePanel) => {
36
- const scrollElm = (0, hanna_utils_1.getPageScrollElm)();
37
- if (!newActive) {
38
- activePanel && setLaggyActivePanel(activePanel, 1000);
39
- scrollElm.scrollTop = parseInt(htmlElmDataset.scrollTop || '') || 0;
40
- delete htmlElmDataset.scrollTop;
41
- delete htmlElmDataset.megaPanelActive;
42
- }
43
- else {
44
- setLaggyActivePanel(undefined, 0);
45
- htmlElmDataset.scrollTop = String(scrollElm.scrollTop);
46
- scrollElm.scrollTop = 0;
47
- htmlElmDataset.megaPanelActive = '';
48
- }
49
- if (setFocus) {
50
- const pressedLinkElm = pressedLinkRef.current; // pressedLinkElm will be undefined when setTimeout fires
51
- setTimeout(() => {
52
- if (!newActive) {
53
- // const buttonElm = menuElm.querySelector<HTMLButtonElement>(
54
- // 'button.MainMenu__link[aria-pressed="true"]'
55
- // );
56
- (0, focusElm_1.default)(pressedLinkElm);
57
- }
58
- else if (newActive !== activePanel) {
59
- // const panelElm = menuElm.querySelector<HTMLButtonElement>(
60
- // '.PrimaryPanel--active'
61
- // );
62
- (0, focusElm_1.default)(activePanelRef.current);
63
- }
64
- }, 100);
65
- }
66
- return newActive;
67
- });
68
- }, [setLaggyActivePanel, isBrowser]);
69
+ : () => undefined, [setLaggyActivePanel, isBrowser]);
69
70
  (0, useFormatMonitor_1.useFormatMonitor)((media) => {
70
71
  if (media.leftTopmenu) {
71
72
  setActivePanel(undefined);
@@ -108,6 +109,17 @@ const MainMenu = (props) => {
108
109
  if (menuItems.length === 0) {
109
110
  return null;
110
111
  }
112
+ /** Close mega panels on clicks their links. */
113
+ const handleMegaPanelClicks = (e) => {
114
+ if (
115
+ // NOTE: We can NOT check for `e.defaultPrevented` because if the current
116
+ // LinkRenderer is something like Next.js or Remix's <Link/> compponent
117
+ // then default is ALWAYS prevented
118
+ e.target.closest('a[href]')) {
119
+ setActivePanel(undefined);
120
+ closeHamburgerMenu();
121
+ }
122
+ };
111
123
  return (react_1.default.createElement("nav", { className: "MainMenu", "aria-label": title, "data-sprinkled": isBrowser, ref: menuElmRef },
112
124
  react_1.default.createElement("h2", { className: "MainMenu__title" }, title),
113
125
  react_1.default.createElement("ul", { className: "MainMenu__items" }, menuItems.map((item, i) => {
@@ -119,17 +131,26 @@ const MainMenu = (props) => {
119
131
  return (react_1.default.createElement("li", { key: i, className: (0, getBemClass_1.default)('MainMenu__item', item.modifier), "aria-current": item.current || undefined }, onClick || (!!item.megaPanel && (isBrowser || !item.href)) ? (
120
132
  // only print script-driven buttons in the browser
121
133
  react_1.default.createElement("button", { className: "MainMenu__link", onClick: () => {
122
- item.megaPanel && setActivePanel(item.megaPanel);
123
- onClick && onClick(i, item);
124
- onItemClick && onItemClick(i, item);
134
+ const keepOpen1 = onClick && onClick(i, item) === false;
135
+ const keepOpen2 = onItemClick && onItemClick(i, item) === false;
136
+ const { megaPanel } = item;
137
+ if (megaPanel) {
138
+ setActivePanel(megaPanel !== activePanel ? megaPanel : undefined);
139
+ }
140
+ else {
141
+ !(keepOpen1 || keepOpen2) && closeHamburgerMenu();
142
+ }
125
143
  }, ref: pressed && pressedLinkRef, "aria-pressed": pressed, "aria-controls": controlsId, "aria-label": labelLong, title: labelLong, lang: lang, type: "button" }, label)) : item.href != null ? (
126
144
  // always render links server-side
127
- react_1.default.createElement(_Link_1.Link, { className: "MainMenu__link", href: item.href, "aria-label": labelLong, title: labelLong, onClick: onItemClick && (() => onItemClick(i, item)), lang: lang }, label)) : undefined // skip rendering non-link menu items server side
145
+ react_1.default.createElement(_Link_1.Link, { className: "MainMenu__link", href: item.href, "aria-label": labelLong, title: labelLong, onClick: () => {
146
+ const keepOpen = onItemClick && onItemClick(i, item) === false;
147
+ !keepOpen && closeHamburgerMenu();
148
+ }, lang: lang }, label)) : undefined // skip rendering non-link menu items server side
128
149
  ));
129
150
  })),
130
151
  '\n\n',
131
152
  megaPanels.length > 0 && (react_1.default.createElement("div", { className: (0, getBemClass_1.default)('MainMenu__panelsWrap', [activePanel && 'active']) },
132
- react_1.default.createElement("ul", { className: "MainMenu__panels" },
153
+ react_1.default.createElement("ul", { className: "MainMenu__panels", onClick: handleMegaPanelClicks },
133
154
  megaPanels.map((panel, i) => {
134
155
  if (!panel.items.length) {
135
156
  return;
@@ -14,7 +14,9 @@ export type SearchResultsFilter = {
14
14
  };
15
15
  export type { SearchResultsItemProps } from './SearchResults/_SearchResultsItem';
16
16
  export type SearchResultsProps = {
17
+ /** Grand total number of searach results */
17
18
  totalHits?: number;
19
+ /** Grand total number of searach results */
18
20
  hits?: number;
19
21
  query?: string;
20
22
  items?: Array<SearchResultsItemProps>;
@@ -23,7 +25,9 @@ export type SearchResultsProps = {
23
25
  setFilter?: (activeTab: number) => void;
24
26
  status: SearchStatus;
25
27
  errorText?: string | ReactNode;
28
+ /** Number of results returned in each page/batch */
26
29
  pageSize: number;
30
+ /** Total number of pages */
27
31
  pages?: number;
28
32
  loadMore?: () => void;
29
33
  texts?: SearchReesultI18n;
@@ -20,5 +20,5 @@ export type SiteSearchInputProps = {
20
20
  children?: never;
21
21
  ssr?: SSRSupport;
22
22
  } & WrappingProps & InputElmProps;
23
- export declare const SiteSearchInput: React.ForwardRefExoticComponent<Pick<SiteSearchInputProps, "form" | "label" | "slot" | "style" | "title" | "key" | "list" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "nonce" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "children" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "step" | "pattern" | "value" | "ssr" | "autoFocus" | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | "formTarget" | "name" | "size" | "src" | "multiple" | "alt" | "width" | "accept" | "autoComplete" | "capture" | "checked" | "crossOrigin" | "enterKeyHint" | "height" | "max" | "maxLength" | "min" | "minLength" | "onButtonClick" | "buttonText"> & React.RefAttributes<HTMLInputElement>>;
23
+ export declare const SiteSearchInput: React.ForwardRefExoticComponent<Pick<SiteSearchInputProps, "form" | "label" | "slot" | "style" | "title" | "key" | "list" | "defaultChecked" | "defaultValue" | "suppressContentEditableWarning" | "suppressHydrationWarning" | "accessKey" | "contentEditable" | "contextMenu" | "dir" | "draggable" | "hidden" | "id" | "lang" | "nonce" | "placeholder" | "spellCheck" | "tabIndex" | "translate" | "radioGroup" | "role" | "about" | "datatype" | "inlist" | "prefix" | "property" | "resource" | "typeof" | "vocab" | "autoCapitalize" | "autoCorrect" | "autoSave" | "color" | "itemProp" | "itemScope" | "itemType" | "itemID" | "itemRef" | "results" | "security" | "unselectable" | "inputMode" | "is" | "aria-activedescendant" | "aria-atomic" | "aria-autocomplete" | "aria-busy" | "aria-checked" | "aria-colcount" | "aria-colindex" | "aria-colspan" | "aria-controls" | "aria-current" | "aria-describedby" | "aria-details" | "aria-disabled" | "aria-dropeffect" | "aria-errormessage" | "aria-expanded" | "aria-flowto" | "aria-grabbed" | "aria-haspopup" | "aria-hidden" | "aria-invalid" | "aria-keyshortcuts" | "aria-label" | "aria-labelledby" | "aria-level" | "aria-live" | "aria-modal" | "aria-multiline" | "aria-multiselectable" | "aria-orientation" | "aria-owns" | "aria-placeholder" | "aria-posinset" | "aria-pressed" | "aria-readonly" | "aria-relevant" | "aria-required" | "aria-roledescription" | "aria-rowcount" | "aria-rowindex" | "aria-rowspan" | "aria-selected" | "aria-setsize" | "aria-sort" | "aria-valuemax" | "aria-valuemin" | "aria-valuenow" | "aria-valuetext" | "children" | "dangerouslySetInnerHTML" | "onCopy" | "onCopyCapture" | "onCut" | "onCutCapture" | "onPaste" | "onPasteCapture" | "onCompositionEnd" | "onCompositionEndCapture" | "onCompositionStart" | "onCompositionStartCapture" | "onCompositionUpdate" | "onCompositionUpdateCapture" | "onFocus" | "onFocusCapture" | "onBlur" | "onBlurCapture" | "onChange" | "onChangeCapture" | "onBeforeInput" | "onBeforeInputCapture" | "onInput" | "onInputCapture" | "onReset" | "onResetCapture" | "onSubmit" | "onSubmitCapture" | "onInvalid" | "onInvalidCapture" | "onLoad" | "onLoadCapture" | "onError" | "onErrorCapture" | "onKeyDown" | "onKeyDownCapture" | "onKeyPress" | "onKeyPressCapture" | "onKeyUp" | "onKeyUpCapture" | "onAbort" | "onAbortCapture" | "onCanPlay" | "onCanPlayCapture" | "onCanPlayThrough" | "onCanPlayThroughCapture" | "onDurationChange" | "onDurationChangeCapture" | "onEmptied" | "onEmptiedCapture" | "onEncrypted" | "onEncryptedCapture" | "onEnded" | "onEndedCapture" | "onLoadedData" | "onLoadedDataCapture" | "onLoadedMetadata" | "onLoadedMetadataCapture" | "onLoadStart" | "onLoadStartCapture" | "onPause" | "onPauseCapture" | "onPlay" | "onPlayCapture" | "onPlaying" | "onPlayingCapture" | "onProgress" | "onProgressCapture" | "onRateChange" | "onRateChangeCapture" | "onSeeked" | "onSeekedCapture" | "onSeeking" | "onSeekingCapture" | "onStalled" | "onStalledCapture" | "onSuspend" | "onSuspendCapture" | "onTimeUpdate" | "onTimeUpdateCapture" | "onVolumeChange" | "onVolumeChangeCapture" | "onWaiting" | "onWaitingCapture" | "onAuxClick" | "onAuxClickCapture" | "onClick" | "onClickCapture" | "onContextMenu" | "onContextMenuCapture" | "onDoubleClick" | "onDoubleClickCapture" | "onDrag" | "onDragCapture" | "onDragEnd" | "onDragEndCapture" | "onDragEnter" | "onDragEnterCapture" | "onDragExit" | "onDragExitCapture" | "onDragLeave" | "onDragLeaveCapture" | "onDragOver" | "onDragOverCapture" | "onDragStart" | "onDragStartCapture" | "onDrop" | "onDropCapture" | "onMouseDown" | "onMouseDownCapture" | "onMouseEnter" | "onMouseLeave" | "onMouseMove" | "onMouseMoveCapture" | "onMouseOut" | "onMouseOutCapture" | "onMouseOver" | "onMouseOverCapture" | "onMouseUp" | "onMouseUpCapture" | "onSelect" | "onSelectCapture" | "onTouchCancel" | "onTouchCancelCapture" | "onTouchEnd" | "onTouchEndCapture" | "onTouchMove" | "onTouchMoveCapture" | "onTouchStart" | "onTouchStartCapture" | "onPointerDown" | "onPointerDownCapture" | "onPointerMove" | "onPointerMoveCapture" | "onPointerUp" | "onPointerUpCapture" | "onPointerCancel" | "onPointerCancelCapture" | "onPointerEnter" | "onPointerEnterCapture" | "onPointerLeave" | "onPointerLeaveCapture" | "onPointerOver" | "onPointerOverCapture" | "onPointerOut" | "onPointerOutCapture" | "onGotPointerCapture" | "onGotPointerCaptureCapture" | "onLostPointerCapture" | "onLostPointerCaptureCapture" | "onScroll" | "onScrollCapture" | "onWheel" | "onWheelCapture" | "onAnimationStart" | "onAnimationStartCapture" | "onAnimationEnd" | "onAnimationEndCapture" | "onAnimationIteration" | "onAnimationIterationCapture" | "onTransitionEnd" | "onTransitionEndCapture" | "step" | "value" | "pattern" | "ssr" | "autoFocus" | "formAction" | "formEncType" | "formMethod" | "formNoValidate" | "formTarget" | "name" | "size" | "src" | "multiple" | "alt" | "width" | "accept" | "autoComplete" | "capture" | "checked" | "crossOrigin" | "enterKeyHint" | "height" | "max" | "maxLength" | "min" | "minLength" | "onButtonClick" | "buttonText"> & React.RefAttributes<HTMLInputElement>>;
24
24
  export default SiteSearchInput;
package/Tooltip.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ export type TooltipProps = {
2
+ label: string;
3
+ text: string | JSX.Element;
4
+ iconOnly?: boolean;
5
+ };
6
+ declare const ToolTip: (props: TooltipProps) => JSX.Element;
7
+ export default ToolTip;
package/Tooltip.js ADDED
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ const react_1 = tslib_1.__importStar(require("react"));
5
+ const react_2 = require("@floating-ui/react");
6
+ const hooks_1 = require("@hugsmidjan/react/hooks");
7
+ const getBemClass_1 = tslib_1.__importDefault(require("@hugsmidjan/react/utils/getBemClass"));
8
+ const getSide = (placement) => placement.split('-')[0];
9
+ const ToolTip = (props) => {
10
+ const { text, label, iconOnly } = props;
11
+ const arrowRef = (0, react_1.useRef)(null);
12
+ const [isOpen, setIsOpen] = (0, hooks_1.useLaggedState)(false, 300);
13
+ const { x, y, reference, floating, middlewareData, placement } = (0, react_2.useFloating)({
14
+ placement: 'top',
15
+ middleware: [(0, react_2.offset)(10), (0, react_2.flip)(), (0, react_2.shift)(), (0, react_2.arrow)({ element: arrowRef })],
16
+ whileElementsMounted: react_2.autoUpdate,
17
+ });
18
+ const { arrow } = middlewareData;
19
+ const arrowX = arrow === null || arrow === void 0 ? void 0 : arrow.x;
20
+ const arrowY = arrow === null || arrow === void 0 ? void 0 : arrow.y;
21
+ (0, hooks_1.useCallbackOnEsc)(() => {
22
+ setIsOpen(false);
23
+ });
24
+ return (react_1.default.createElement("details", { className: `Tooltip Tooltip--${getSide(placement)}`, open: isOpen, onMouseEnter: () => setIsOpen(true, 100), onFocus: () => {
25
+ if (isOpen) {
26
+ setIsOpen(true, 0);
27
+ }
28
+ }, onBlur: () => setIsOpen(false), onClick: (e) => {
29
+ e.preventDefault();
30
+ setIsOpen(!isOpen, 0);
31
+ }, onMouseDown: () => {
32
+ if (isOpen) {
33
+ setTimeout(() => {
34
+ setIsOpen(true, 0);
35
+ }, 100);
36
+ }
37
+ }, onMouseLeave: (e) => {
38
+ if (e.currentTarget.$contextClicked_firefox_fix) {
39
+ return;
40
+ }
41
+ setIsOpen(false);
42
+ }, onContextMenu: (e) => {
43
+ const elm = e.currentTarget;
44
+ clearTimeout(elm.$contextClicked_firefox_fix);
45
+ elm.$contextClicked_firefox_fix = setTimeout(() => {
46
+ delete elm.$contextClicked_firefox_fix;
47
+ }, 300);
48
+ }, style: x == null
49
+ ? undefined
50
+ : {
51
+ '--tooltip-content-pos-y': `${y}px`,
52
+ '--tooltip-content-pos-x': `${x}px`,
53
+ '--tooltip-arrow-pos-x': `${arrowX}px`,
54
+ '--tooltip-arrow-pos-y': `${arrowY}px`,
55
+ } },
56
+ react_1.default.createElement("summary", { className: (0, getBemClass_1.default)('Tooltip__trigger', iconOnly && 'icononly'), ref: reference }, label),
57
+ react_1.default.createElement("div", { className: "Tooltip__content", onClick: (e) => e.stopPropagation(), ref: floating },
58
+ x !== null && (react_1.default.createElement("div", { "data-floating-ui-hack-plz-ignore": "", style: { position: 'absolute', display: 'none' }, ref: arrowRef })),
59
+ text)));
60
+ };
61
+ exports.default = ToolTip;
@@ -2,7 +2,7 @@
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.TogglerInput = void 0;
4
4
  const tslib_1 = require("tslib");
5
- const react_1 = tslib_1.__importStar(require("react"));
5
+ const react_1 = tslib_1.__importDefault(require("react"));
6
6
  const hooks_1 = require("@hugsmidjan/react/hooks");
7
7
  const getBemClass_1 = tslib_1.__importDefault(require("@hugsmidjan/react/utils/getBemClass"));
8
8
  const TogglerInput = (props) => {
@@ -12,17 +12,16 @@ const TogglerInput = (props) => {
12
12
  const reqStar = required && reqText !== false && (react_1.default.createElement("abbr", { className: bem + '__label__reqstar',
13
13
  // TODO: add mo-better i18n thinking
14
14
  title: (reqText || 'Þarf að haka í') + ': ' }, "*"));
15
- const InnerWrap = innerWrap ? 'span' : react_1.Fragment;
15
+ const labelContent = (react_1.default.createElement(react_1.default.Fragment, null,
16
+ ' ',
17
+ reqStar,
18
+ " ",
19
+ label,
20
+ ' '));
16
21
  return (react_1.default.createElement(Wrapper, { className: (0, getBemClass_1.default)(bem, modifier, className) },
17
22
  react_1.default.createElement("input", Object.assign({ className: bem + '__input', type: type, id: domid, "aria-invalid": invalid || !!errorMessage || undefined, "aria-describedby": errorId }, inputProps)),
18
23
  ' ',
19
- react_1.default.createElement("label", { className: bem + '__label', htmlFor: domid },
20
- react_1.default.createElement(InnerWrap, { className: bem + '__label__wrap' },
21
- ' ',
22
- reqStar,
23
- " ",
24
- label,
25
- ' ')),
24
+ react_1.default.createElement("label", { className: bem + '__label', htmlFor: domid }, innerWrap ? (react_1.default.createElement("span", { className: bem + '__label__wrap' }, labelContent)) : (labelContent)),
26
25
  errorMessage && (react_1.default.createElement("div", { className: bem + '__error', id: errorId }, errorMessage))));
27
26
  };
28
27
  exports.TogglerInput = TogglerInput;
package/index.d.ts CHANGED
@@ -86,6 +86,7 @@
86
86
  /// <reference path="./TextBlock.d.tsx" />
87
87
  /// <reference path="./TextButton.d.tsx" />
88
88
  /// <reference path="./TextInput.d.tsx" />
89
+ /// <reference path="./Tooltip.d.tsx" />
89
90
  /// <reference path="./utils.d.ts" />
90
91
  /// <reference path="./VerticalTabsTOC.d.tsx" />
91
92
  /// <reference path="./VSpacer.d.tsx" />
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reykjavik/hanna-react",
3
- "version": "0.10.79",
3
+ "version": "0.10.81",
4
4
  "author": "Reykjavík (http://www.reykjavik.is)",
5
5
  "contributors": [
6
6
  "Hugsmiðjan ehf (http://www.hugsmidjan.is)",
@@ -13,10 +13,11 @@
13
13
  "homepage": "https://github.com/rvk-utd/hanna/blob/main/modules/hanna-react/README.md",
14
14
  "license": "MIT",
15
15
  "dependencies": {
16
+ "@floating-ui/react": "^0.19.2",
16
17
  "@hugsmidjan/qj": "^4.10.2",
17
18
  "@hugsmidjan/react": "^0.4.23",
18
- "@reykjavik/hanna-css": "^0.3.11",
19
- "@reykjavik/hanna-utils": "^0.2.1",
19
+ "@reykjavik/hanna-css": "^0.3.13",
20
+ "@reykjavik/hanna-utils": "^0.2.3",
20
21
  "@types/react": "^17.0.24",
21
22
  "@types/react-autosuggest": "^10.1.0",
22
23
  "@types/react-datepicker": "^4.8.0",
@@ -24,7 +25,7 @@
24
25
  "@types/react-transition-group": "^4.4.0",
25
26
  "iframe-resizer-react": "^1.1.0",
26
27
  "react-autosuggest": "^10.1.0",
27
- "react-datepicker": "^4.8.0",
28
+ "react-datepicker": "^4.9.0",
28
29
  "react-dropzone": "^10.2.2",
29
30
  "react-intersection-observer": "^8.30.1",
30
31
  "react-transition-group": "^4.4.1"
@@ -0,0 +1,8 @@
1
+ /// <reference types="react" />
2
+ type HannaUIStateProps = {
3
+ closeHamburgerMenu: () => void;
4
+ isHamburgerMenuOpen: boolean | undefined;
5
+ };
6
+ export declare const HannaUIState: import("react").Provider<HannaUIStateProps>;
7
+ export declare const useHannaUIState: () => HannaUIStateProps;
8
+ export {};
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useHannaUIState = exports.HannaUIState = void 0;
4
+ const react_1 = require("react");
5
+ const _HannaUIContext = (0, react_1.createContext)({
6
+ closeHamburgerMenu: () => undefined,
7
+ isHamburgerMenuOpen: undefined,
8
+ });
9
+ exports.HannaUIState = _HannaUIContext.Provider;
10
+ const useHannaUIState = () => (0, react_1.useContext)(_HannaUIContext);
11
+ exports.useHannaUIState = useHannaUIState;
@@ -12,9 +12,9 @@ const noop = () => undefined;
12
12
  const useMenuToggling = (doInitialize = true) => {
13
13
  const [isMenuOpen, setIsMenuOpen] = (0, react_1.useState)(false);
14
14
  const [isMenuActive, setIsMenuActive] = (0, react_1.useState)();
15
- const state = { isMenuOpen, isMenuActive };
16
- const stateRef = (0, react_1.useRef)(state);
17
- stateRef.current = state;
15
+ const _state = { isMenuOpen, isMenuActive };
16
+ const stateRef = (0, react_1.useRef)(_state);
17
+ stateRef.current = _state;
18
18
  const _openMenu = () => {
19
19
  if (!stateRef.current.isMenuOpen) {
20
20
  setIsMenuOpen(true);
package/utils.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export * from './utils/browserSide';
2
2
  export * from './utils/config';
3
+ export * from './utils/HannaUIState';
3
4
  export * from './utils/useDidChange';
4
5
  export * from './utils/useFormatMonitor';
5
6
  export * from './utils/useGetSVGtext';
package/utils.js CHANGED
@@ -3,6 +3,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  const tslib_1 = require("tslib");
4
4
  tslib_1.__exportStar(require("./utils/browserSide"), exports);
5
5
  tslib_1.__exportStar(require("./utils/config"), exports);
6
+ tslib_1.__exportStar(require("./utils/HannaUIState"), exports);
6
7
  tslib_1.__exportStar(require("./utils/useDidChange"), exports);
7
8
  tslib_1.__exportStar(require("./utils/useFormatMonitor"), exports);
8
9
  tslib_1.__exportStar(require("./utils/useGetSVGtext"), exports);