@reykjavik/hanna-react 0.10.80 → 0.10.82

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,26 @@
4
4
 
5
5
  - ... <!-- Add new lines here. -->
6
6
 
7
+ ## 0.10.82
8
+
9
+ _2023-03-03_
10
+
11
+ - `MainMenu`:
12
+ - feat: Add prop `homeLink` to `MainMenuProps` for convenience
13
+ - feat: Add built-in support for Polish language to `MainMenu`
14
+ - feat: Add optional `homeLink` prop to `MainMenuI18n`
15
+ - feat: Convert newlines in `ContactBubbleItem.extraLabel` into `<br/>`s
16
+ - fix: Remove `role="group"` off all Checkbox and Radio Group containers
17
+
18
+ ## 0.10.81
19
+
20
+ _2023-02-16_
21
+
22
+ - feat: Add component `Tooltip`
23
+ - fix(ts): Make `MainMenu` onClick handlers return `void`, not `undefined`
24
+ - fix: Suppress accidental dev-mode warnings for
25
+ `CheckboxButton`/`RadioButtonGroup`s
26
+
7
27
  ## 0.10.80
8
28
 
9
29
  _2023-02-08_
package/ContactBubble.js CHANGED
@@ -9,6 +9,7 @@ const getBemClass_1 = tslib_1.__importDefault(require("@hugsmidjan/react/utils/g
9
9
  const hanna_utils_1 = require("@reykjavik/hanna-utils");
10
10
  const i18n_1 = require("@reykjavik/hanna-utils/i18n");
11
11
  const _Link_1 = require("./_abstract/_Link");
12
+ const breakOnNL_1 = require("./_abstract/breakOnNL");
12
13
  const utils_1 = require("./utils");
13
14
  exports.defaultTexts = {
14
15
  is: {
@@ -141,7 +142,7 @@ const ContactBubble = (props) => {
141
142
  ' ',
142
143
  label,
143
144
  '\n',
144
- extraLabel && react_1.default.createElement("small", { key: "\uD83E\uDD21" }, extraLabel),
145
+ extraLabel && react_1.default.createElement("small", { key: "\uD83E\uDD21" }, (0, breakOnNL_1.breakOnNL)(extraLabel)),
145
146
  '\n',
146
147
  ];
147
148
  return (react_1.default.createElement("li", { key: i, className: itemClass }, href && href !== '#' ? (react_1.default.createElement(_Link_1.Link, { className: "ContactBubble__link", href: href, onClick: onClickHandler, target: target }, content)) : (react_1.default.createElement("button", { className: "ContactBubble__link", onClick: onClickHandler, type: "button" }, content))));
package/FormField.js CHANGED
@@ -76,7 +76,7 @@ const FormField = (props) => {
76
76
  isBrowser && empty && 'empty',
77
77
  isBrowser && filled && 'filled',
78
78
  isBrowser && focused && 'focused',
79
- ], className), role: group ? 'group' : undefined, ref: props.wrapperRef },
79
+ ], className), ref: props.wrapperRef },
80
80
  LabelTag ? (react_1.default.createElement(LabelTag, { className: "FormField__label", id: labelId },
81
81
  ' ',
82
82
  reqStar,
@@ -1,4 +1,4 @@
1
- import type { MegaMenuPanel } from '../MainMenu';
1
+ import { MegaMenuPanel } from './_PrimaryPanel';
2
2
  export type AuxilaryPanelIllustration = 'hanna-veitiggi' | 'hanna-vandro' | 'hanna-hugsi' | 'hanna-hissa' | 'hanna-hahaha' | 'hanna-hae' | 'hanna-god_spurning' | 'hanna-gjuggiborg' | 'hanna-benda';
3
3
  export type AuxiliaryPanelProps = MegaMenuPanel & {
4
4
  image?: AuxilaryPanelIllustration;
@@ -1,11 +1,27 @@
1
1
  import { RefObject } from 'react';
2
- import { MainMenuI18n, MegaMenuPanel } from '../MainMenu';
2
+ export type PrimaryPanelI18n = {
3
+ backToMenu: string;
4
+ backToMenuLong?: string;
5
+ };
6
+ export type MegaMenuItem = {
7
+ label: string;
8
+ summary?: string;
9
+ href: string;
10
+ lang?: string;
11
+ current?: boolean;
12
+ target?: string;
13
+ };
14
+ export type MegaMenuPanel = {
15
+ title: string;
16
+ items: Array<MegaMenuItem>;
17
+ id: string;
18
+ };
3
19
  type PrimaryPanelProps = {
4
20
  setActivePanel: (panel: MegaMenuPanel | undefined, setFocus?: boolean) => void;
5
21
  isParent?: boolean;
6
22
  isActive?: boolean;
7
23
  panel: MegaMenuPanel;
8
- texts: MainMenuI18n;
24
+ texts: PrimaryPanelI18n;
9
25
  activeRef: RefObject<HTMLLIElement>;
10
26
  isBrowser: true | undefined;
11
27
  };
package/MainMenu.d.ts CHANGED
@@ -1,17 +1,27 @@
1
+ import { Cleanup } from '@reykjavik/hanna-utils';
1
2
  import { DefaultTexts } from '@reykjavik/hanna-utils/i18n';
2
3
  import { AuxilaryPanelIllustration, AuxiliaryPanelProps } from './MainMenu/_Auxiliary';
4
+ import { MegaMenuItem, MegaMenuPanel, PrimaryPanelI18n } from './MainMenu/_PrimaryPanel';
3
5
  import { SSRSupport } from './utils';
4
- export type MainMenuI18n = {
6
+ export type MainMenuI18n = Cleanup<{
5
7
  lang?: string;
6
- backToMenu: string;
7
- backToMenuLong?: string;
8
- };
9
- export declare const defaultMainMenuTexts: DefaultTexts<MainMenuI18n>;
10
- export type { AuxilaryPanelIllustration, AuxiliaryPanelProps };
8
+ homeLabel?: string;
9
+ } & PrimaryPanelI18n>;
10
+ export declare const defaultMainMenuTexts: DefaultTexts<Required<MainMenuI18n>>;
11
+ export type { AuxilaryPanelIllustration, AuxiliaryPanelProps, MegaMenuItem, MegaMenuPanel, };
11
12
  export type MainMenuItem = {
12
13
  label: string;
13
14
  labelLong?: string;
14
15
  lang?: string;
16
+ /**
17
+ * Puts a modifier className on the menu item element.
18
+ *
19
+ * Example:
20
+ *
21
+ * ```html
22
+ * <li class="MainMenu__item MainMenu__item--${modifier}">
23
+ * ```
24
+ * */
15
25
  modifier?: string;
16
26
  current?: boolean;
17
27
  href?: string;
@@ -22,27 +32,19 @@ export type MainMenuItem = {
22
32
  * "Hamburger menu" (a.k.a. "Mobile menu")
23
33
  * … unless the `onClick` function explicitly returns `false`.
24
34
  */
25
- onClick?: (index: number, item: MainMenuItem) => undefined | boolean;
35
+ onClick?: (index: number, item: MainMenuItem) => void | boolean;
26
36
  controlsId?: string;
27
37
  };
28
38
  export type MainMenuSeparator = '---';
29
39
  export type MainMenuItemList = Array<MainMenuItem | MainMenuSeparator>;
30
- export type MegaMenuItem = {
31
- label: string;
32
- summary?: string;
33
- href: string;
34
- lang?: string;
35
- current?: boolean;
36
- target?: string;
37
- };
38
- export type MegaMenuPanel = {
39
- title: string;
40
- items: Array<MegaMenuItem>;
41
- id: string;
42
- };
43
40
  export type MainMenuProps = {
44
41
  title: string;
45
42
  items: MainMenuItemList;
43
+ /**
44
+ * Link for the homepage - defaults to `"/"` adding a
45
+ * generic sounding "Home"/"Forsíða" label
46
+ */
47
+ homeLink?: string | Omit<MainMenuItem, 'modifier'>;
46
48
  megaPanels?: Array<MegaMenuPanel>;
47
49
  auxiliaryPanel?: AuxiliaryPanelProps;
48
50
  /**
@@ -50,7 +52,7 @@ export type MainMenuProps = {
50
52
  * "Hamburger menu" (a.k.a. "Mobile menu")
51
53
  * … unless the `onItemClick` function explicitly returns `false`.
52
54
  */
53
- onItemClick?: (index: number, item: MainMenuItem) => undefined | boolean;
55
+ onItemClick?: (index: number, item: MainMenuItem) => void | boolean;
54
56
  activePanelId?: string;
55
57
  texts?: MainMenuI18n;
56
58
  lang?: string;
package/MainMenu.js CHANGED
@@ -16,11 +16,77 @@ const useFormatMonitor_1 = require("./utils/useFormatMonitor");
16
16
  const utils_1 = require("./utils");
17
17
  const findActivePanel = (megaPanels, activeId) => activeId ? megaPanels.find((panel) => activeId === panel.id) : undefined;
18
18
  exports.defaultMainMenuTexts = {
19
- is: { lang: 'is', backToMenu: 'Loka', backToMenuLong: 'Til baka í valmynd' },
20
- en: { lang: 'en', backToMenu: 'Close', backToMenuLong: 'Close and return to menu' },
19
+ is: {
20
+ lang: 'is',
21
+ homeLabel: 'Forsíða',
22
+ backToMenu: 'Loka',
23
+ backToMenuLong: 'Til baka í valmynd',
24
+ },
25
+ en: {
26
+ lang: 'en',
27
+ homeLabel: 'Home page',
28
+ backToMenu: 'Close',
29
+ backToMenuLong: 'Close and return to menu',
30
+ },
31
+ pl: {
32
+ lang: 'pl',
33
+ homeLabel: 'Strona główna',
34
+ backToMenu: 'Zamknij',
35
+ backToMenuLong: 'Zamknij i wróć do menu',
36
+ },
37
+ };
38
+ // ---------------------------------------------------------------------------
39
+ const _issueHomeLinkWarnings = (hasHomeItem, hasHomeLinkProp) => {
40
+ const bothDefined = hasHomeItem && hasHomeLinkProp;
41
+ const neitherDefined = !hasHomeItem && !hasHomeLinkProp;
42
+ if (bothDefined) {
43
+ console.warn('Ignoring a redundant `MainMenuProps.homeLink` value. ' +
44
+ '(As `MainMenuProps.items` already starts with a "Home" item.)');
45
+ }
46
+ else if (neitherDefined) {
47
+ console.warn('`MainMenuProps.homeLink` is missing. Auto-inserting a generic "home link" with `href="/"`.');
48
+ }
49
+ };
50
+ const normalizeMenuItems = (itemsProp, megaPanels, homeLink, texts) => {
51
+ const items = itemsProp.map((item) => {
52
+ if (item === '---') {
53
+ return item;
54
+ }
55
+ const href = item.href;
56
+ const controlsId = item.controlsId || (href && /^#/.test(href) && href.slice(1)) || undefined;
57
+ const megaPanel = controlsId
58
+ ? megaPanels.find((panel) => panel.id === controlsId)
59
+ : undefined;
60
+ return Object.assign(Object.assign({}, item), { controlsId, megaPanel });
61
+ });
62
+ const firstItem = items[0];
63
+ if (firstItem) {
64
+ // Prepend menu item list with a "home link", unless it's already there
65
+ const hasHomeItem = typeof firstItem === 'object' && firstItem.modifier === 'home';
66
+ if (process.env.NODE_ENV !== 'production') {
67
+ _issueHomeLinkWarnings(hasHomeItem, !!homeLink);
68
+ }
69
+ if (!hasHomeItem) {
70
+ if (!homeLink || typeof homeLink === 'string') {
71
+ let label = texts.homeLabel;
72
+ let lang;
73
+ if (label == null) {
74
+ const def = exports.defaultMainMenuTexts[texts.lang || 'en'] ||
75
+ exports.defaultMainMenuTexts.en ||
76
+ exports.defaultMainMenuTexts.is;
77
+ label = def.homeLabel;
78
+ lang = def.lang;
79
+ }
80
+ homeLink = { href: homeLink || '/', label, lang };
81
+ }
82
+ items.unshift(Object.assign(Object.assign({}, homeLink), { modifier: 'home' }));
83
+ }
84
+ }
85
+ return items;
21
86
  };
22
87
  const MainMenu = (props) => {
23
88
  const { title, megaPanels = [], onItemClick, ssr, auxiliaryPanel } = props;
89
+ const texts = (0, i18n_1.getTexts)(props, exports.defaultMainMenuTexts);
24
90
  const { closeHamburgerMenu } = (0, HannaUIState_1.useHannaUIState)();
25
91
  const isBrowser = (0, utils_1.useIsBrowserSide)(ssr);
26
92
  const menuElmRef = (0, react_1.useRef)(null);
@@ -73,14 +139,7 @@ const MainMenu = (props) => {
73
139
  }
74
140
  });
75
141
  const hasActivePanel = !!activePanel;
76
- const menuItems = (0, react_1.useMemo)(() => props.items.map((item) => {
77
- if (item === '---') {
78
- return item;
79
- }
80
- const href = item.href;
81
- const controlsId = item.controlsId || (href && /^#/.test(href) && href.slice(1)) || undefined;
82
- return Object.assign(Object.assign({}, item), { controlsId, megaPanel: controlsId && megaPanels.find((panel) => panel.id === controlsId) });
83
- }), [props.items, megaPanels]);
142
+ const menuItems = (0, react_1.useMemo)(() => normalizeMenuItems(props.items, megaPanels, props.homeLink, texts), [props.items, props.homeLink, megaPanels, texts]);
84
143
  (0, react_1.useEffect)(() => {
85
144
  setActivePanel(findActivePanel(megaPanels, props.activePanelId));
86
145
  }, [props.activePanelId, megaPanels, setActivePanel]);
@@ -157,7 +216,7 @@ const MainMenu = (props) => {
157
216
  }
158
217
  const isActive = activePanel === panel || laggyActivePanel === panel || undefined;
159
218
  const isParent = !!panel.items.find((item) => item.current);
160
- return (react_1.default.createElement(_PrimaryPanel_1.PrimaryPanel, { key: i, isParent: isParent, isActive: isActive, panel: panel, isBrowser: isBrowser, setActivePanel: setActivePanel, texts: (0, i18n_1.getTexts)(props, exports.defaultMainMenuTexts), activeRef: activePanelRef }));
219
+ return (react_1.default.createElement(_PrimaryPanel_1.PrimaryPanel, { key: i, isParent: isParent, isActive: isActive, panel: panel, isBrowser: isBrowser, setActivePanel: setActivePanel, texts: texts, activeRef: activePanelRef }));
161
220
  }),
162
221
  auxiliaryPanel && react_1.default.createElement(_Auxiliary_1.AuxiliaryPanel, Object.assign({}, auxiliaryPanel)))))));
163
222
  };
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;
@@ -8,5 +8,5 @@ const breakOnNL = (text) => text &&
8
8
  .trim()
9
9
  .replace(/(?:\n\s*)+/g, '\n\n')
10
10
  .split(/\n/)
11
- .map((bit) => bit || react_1.default.createElement("br", null));
11
+ .map((bit, i) => bit || react_1.default.createElement("br", { key: i }));
12
12
  exports.breakOnNL = breakOnNL;
package/assets.js CHANGED
@@ -14,7 +14,9 @@ Object.defineProperty(exports, "illustrations", { enumerable: true, get: functio
14
14
  /** @deprecated Use `getCssBundleUrl` from '@reykjavik/hanna-css' instead (Will be reomved in v0.11) */
15
15
  const getCssBundleUrl = (cssTokens,
16
16
  /** If you want to pin your CSS files to a specific version */
17
- version) => (0, hanna_css_1.getCssBundleUrl)(cssTokens, { version: version });
17
+ version) => (0, hanna_css_1.getCssBundleUrl)(cssTokens, {
18
+ version: version,
19
+ });
18
20
  exports.getCssBundleUrl = getCssBundleUrl;
19
21
  // ---------------------------------------------------------------------------
20
22
  // Based on "https://styles.reykjavik.is/assets/efnistakn/menu/files.json"
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.80",
3
+ "version": "0.10.82",
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.13",
19
- "@reykjavik/hanna-utils": "^0.2.1",
19
+ "@reykjavik/hanna-css": "^0.3.14",
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",