@reykjavik/hanna-react 0.10.159 → 0.10.161
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/ButtonTertiary.d.ts +1 -1
- package/ButtonTertiary.js +1 -2
- package/CHANGELOG.md +23 -0
- package/DropdownButton.d.ts +69 -13
- package/DropdownButton.js +34 -14
- package/Icon.d.ts +7 -0
- package/Icon.js +11 -0
- package/MainMenu2.d.ts +16 -11
- package/MainMenu2.js +29 -22
- package/ReadSpeakerPlayer.js +2 -2
- package/ReykjavikWaves.d.ts +8 -0
- package/ReykjavikWaves.js +8 -0
- package/SearchInput.js +1 -1
- package/_abstract/_Button.d.ts +8 -6
- package/_abstract/_Button.js +8 -11
- package/esm/ButtonTertiary.d.ts +1 -1
- package/esm/ButtonTertiary.js +1 -2
- package/esm/DropdownButton.d.ts +69 -13
- package/esm/DropdownButton.js +35 -15
- package/esm/Icon.d.ts +7 -0
- package/esm/Icon.js +7 -0
- package/esm/MainMenu2.d.ts +16 -11
- package/esm/MainMenu2.js +29 -22
- package/esm/ReadSpeakerPlayer.js +3 -3
- package/esm/ReykjavikWaves.d.ts +8 -0
- package/esm/ReykjavikWaves.js +3 -0
- package/esm/SearchInput.js +1 -1
- package/esm/_abstract/_Button.d.ts +8 -6
- package/esm/_abstract/_Button.js +8 -11
- package/esm/index.d.ts +2 -0
- package/index.d.ts +2 -0
- package/package.json +10 -2
package/ButtonTertiary.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { ButtonProps, ButtonVariantProps } from './_abstract/_Button.js';
|
|
3
3
|
type TertiarySize = Extract<ButtonVariantProps['size'], 'normal' | 'small'>;
|
|
4
|
-
type TertiaryIcon = Extract<ButtonVariantProps['icon'], '
|
|
4
|
+
type TertiaryIcon = Extract<ButtonVariantProps['icon'], 'go-back'>;
|
|
5
5
|
export type ButtonTertiaryProps = ButtonProps & Omit<ButtonVariantProps, 'icon' | 'size'> & {
|
|
6
6
|
size?: TertiarySize;
|
|
7
7
|
icon?: TertiaryIcon;
|
package/ButtonTertiary.js
CHANGED
|
@@ -9,13 +9,12 @@ const sizes = {
|
|
|
9
9
|
small: 'small',
|
|
10
10
|
};
|
|
11
11
|
const icons = {
|
|
12
|
-
none: 'none',
|
|
13
12
|
'go-back': 'go-back',
|
|
14
13
|
};
|
|
15
14
|
// NOTE: As a `_abstract/_Button.tsx`-derived component, all `<button/>` and
|
|
16
15
|
// `<a/>` props are allowed directly, so adding `wrapperProps` makes no sense.
|
|
17
16
|
const ButtonTertiary = (props) => {
|
|
18
|
-
const { size = 'normal', icon = '
|
|
17
|
+
const { size = 'normal', icon = '' } = props;
|
|
19
18
|
return react_1.default.createElement(_Button_js_1.Button, Object.assign({ bem: "ButtonTertiary" }, props, { size: sizes[size], icon: icons[icon] }));
|
|
20
19
|
};
|
|
21
20
|
exports.ButtonTertiary = ButtonTertiary;
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,29 @@
|
|
|
4
4
|
|
|
5
5
|
- ... <!-- Add new lines here. -->
|
|
6
6
|
|
|
7
|
+
## 0.10.161
|
|
8
|
+
|
|
9
|
+
_2026-01-15_
|
|
10
|
+
|
|
11
|
+
- feat: Add component `Icon`
|
|
12
|
+
- feat: Add component `ReykjavikWaves`
|
|
13
|
+
- `DropdownButton`:
|
|
14
|
+
- feat: Add prop `Toggler` for custom toggler content
|
|
15
|
+
- feat: Add "customitem" object with `Content`, `modifier` and `current`
|
|
16
|
+
- `MainMenu2`:
|
|
17
|
+
- feat: Add prop `redhot` to `MainMenu2Props.items.hot` items to always show
|
|
18
|
+
them on mobile
|
|
19
|
+
- feat: Add "customitem" object with `Content`, `modifier` and `current`
|
|
20
|
+
- docs: Minor JSDoc corrections for on-click handlers
|
|
21
|
+
- fix: Clicking `.SearchInput__button` always passed empty string as value
|
|
22
|
+
|
|
23
|
+
## 0.10.160
|
|
24
|
+
|
|
25
|
+
_2025-10-13_
|
|
26
|
+
|
|
27
|
+
- `ReadSpeakerPlayer`:
|
|
28
|
+
- fix: Default `lang` to Hanna's `DEFAULT_LANG` value, like other components
|
|
29
|
+
|
|
7
30
|
## 0.10.159
|
|
8
31
|
|
|
9
32
|
_2025-10-09_
|
package/DropdownButton.d.ts
CHANGED
|
@@ -1,29 +1,85 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { HTMLAttributeAnchorTarget, ReactElement } from 'react';
|
|
2
|
+
import { IconToken } from '@reykjavik/hanna-css';
|
|
3
|
+
import { ClassNameModifiers, EitherObj } from '@reykjavik/hanna-utils';
|
|
4
|
+
import { IconName_old } from '../../hanna-css/src/lib/icons.js';
|
|
3
5
|
import { ButtonVariantProps } from './_abstract/_Button.js';
|
|
4
6
|
import { MainMenu2Item } from './MainMenu2.js';
|
|
5
7
|
import { SSRSupportProps, WrapperElmProps } from './utils.js';
|
|
6
8
|
type Prefix<record extends Record<string, unknown>, prefix extends string> = {
|
|
7
9
|
[K in keyof record as `${prefix}${Capitalize<string & K>}`]: record[K];
|
|
8
10
|
};
|
|
9
|
-
export type DropdownButtonItem =
|
|
10
|
-
|
|
11
|
+
export type DropdownButtonItem = {
|
|
12
|
+
/** Visible label text */
|
|
13
|
+
label: string | ReactElement;
|
|
14
|
+
/** Un-abbreviated label set as `aria-label=""` */
|
|
15
|
+
labelLong?: string;
|
|
16
|
+
/** Language of the link label */
|
|
17
|
+
lang?: string;
|
|
18
|
+
/** Languge of the linked resource */
|
|
19
|
+
hrefLang?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Puts a modifier className for the menu __item <li/> element.
|
|
22
|
+
* */
|
|
23
|
+
modifier?: ClassNameModifiers;
|
|
24
|
+
/** Signifies if the menu item is part of the page's breadcrumb trail */
|
|
25
|
+
current?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The URL the link points to.
|
|
28
|
+
*
|
|
29
|
+
* If neither `href` nor `onClick` is passed, then the item is not rendered
|
|
30
|
+
* at all.
|
|
31
|
+
*/
|
|
32
|
+
href?: string;
|
|
33
|
+
/** Sets `target=""` on anchor tags with a `href` attribute. */
|
|
34
|
+
target?: HTMLAttributeAnchorTarget;
|
|
35
|
+
/**
|
|
36
|
+
* Adding `onClick` automatically results in a <button/> element being
|
|
37
|
+
* rendered. If `href` is also passed, then a <a href/> element is rendered
|
|
38
|
+
* during initial (server-side) render, which then gets replaced by a
|
|
39
|
+
* <button/> element during the first client-side
|
|
40
|
+
*
|
|
41
|
+
* NOTE: Clicking a menu item will automatically close tghe menu
|
|
42
|
+
* … unless the `onClick` function explicitly returns `false`.
|
|
43
|
+
*/
|
|
44
|
+
onClick?: (item: MainMenu2Item) => void | boolean;
|
|
45
|
+
/** Sets `aria-controls=""` on `<button/>`s with `onClick` */
|
|
46
|
+
controlsId?: string;
|
|
47
|
+
Content?: never;
|
|
48
|
+
/** Seldom used flag for buttons that do destruction */
|
|
49
|
+
destructive?: boolean;
|
|
50
|
+
icon?: IconToken | IconName_old;
|
|
11
51
|
};
|
|
12
|
-
|
|
52
|
+
type DropdownButtonCustomItemFn = (props: {
|
|
13
53
|
closeMenu: () => void;
|
|
14
|
-
}) =>
|
|
54
|
+
}) => ReactElement;
|
|
55
|
+
export type DropdownButtonCustomItem = Pick<DropdownButtonItem, 'modifier' | 'current'> & {
|
|
56
|
+
Content: DropdownButtonCustomItemFn;
|
|
57
|
+
};
|
|
58
|
+
/** Renders a divider line between `Dropdown*Item`s with an optional legend */
|
|
59
|
+
export type DropdownButtonItemDivider = {
|
|
60
|
+
divider: true;
|
|
61
|
+
label?: string;
|
|
62
|
+
};
|
|
15
63
|
export type DropdownButtonProps = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
items: Array<DropdownButtonItem | DropdownButtonCustomItem>;
|
|
64
|
+
/** The items to display inside the dropdown menu */
|
|
65
|
+
items: Array<DropdownButtonItem | DropdownButtonCustomItem | DropdownButtonItemDivider>;
|
|
19
66
|
/**
|
|
20
|
-
* NOTE: Clicking a
|
|
21
|
-
* "Hamburger menu" (a.k.a. "Mobile menu")
|
|
67
|
+
* NOTE: Clicking a DropdownButton item will automatically close the drropdown
|
|
22
68
|
* … unless the `onItemClick` function explicitly returns `false`.
|
|
23
69
|
*/
|
|
24
70
|
onItemClick?: (item: MainMenu2Item) => void | boolean;
|
|
25
|
-
|
|
71
|
+
} & EitherObj<{
|
|
72
|
+
/** Label for the toggler button */
|
|
73
|
+
label: string | ReactElement;
|
|
74
|
+
/** Longer accessible toggler label text */
|
|
75
|
+
labelLong?: string;
|
|
76
|
+
/** Default: `"secondary"` */
|
|
26
77
|
buttonType?: 'primary' | 'secondary';
|
|
27
|
-
} & Prefix<Omit<ButtonVariantProps, 'small'>, 'button'
|
|
78
|
+
} & Prefix<Omit<ButtonVariantProps, 'small'>, 'button'>, {
|
|
79
|
+
/** Custom toggler rendering function component */
|
|
80
|
+
Toggler: (props: {
|
|
81
|
+
isOpen: boolean;
|
|
82
|
+
}) => ReactElement;
|
|
83
|
+
}> & WrapperElmProps<'details', 'open' | 'name'> & SSRSupportProps;
|
|
28
84
|
export declare const DropdownButton: (props: DropdownButtonProps) => JSX.Element;
|
|
29
85
|
export {};
|
package/DropdownButton.js
CHANGED
|
@@ -15,7 +15,7 @@ const DropdownButton = (props) => {
|
|
|
15
15
|
const [isOpen, setIsOpen] = (0, useLaggedState_js_1.useLaggedState)(false, 10);
|
|
16
16
|
const isBrowser = (0, utils_js_1.useIsBrowserSide)(props.ssr);
|
|
17
17
|
const [isHovering, setIsHovering] = (0, react_1.useState)(false);
|
|
18
|
-
const wrapperRef = react_1.
|
|
18
|
+
const wrapperRef = (0, react_1.useRef)(null);
|
|
19
19
|
const closeMenuStat = () => setIsOpen(false, 0);
|
|
20
20
|
(0, useOnClickOutside_js_1.useOnClickOutside)(wrapperRef, isOpen && closeMenuStat);
|
|
21
21
|
(0, useCallbackOnEsc_js_1.useCallbackOnEsc)(isOpen && closeMenuStat);
|
|
@@ -25,6 +25,10 @@ const DropdownButton = (props) => {
|
|
|
25
25
|
whileElementsMounted: react_2.autoUpdate,
|
|
26
26
|
});
|
|
27
27
|
const { onItemClick, wrapperProps = {} } = props;
|
|
28
|
+
const toggle = (e) => {
|
|
29
|
+
e.preventDefault();
|
|
30
|
+
setIsOpen(!isOpen, 0);
|
|
31
|
+
};
|
|
28
32
|
return (react_1.default.createElement("details", Object.assign({}, wrapperProps, { className: (0, hanna_utils_1.modifiedClass)('DropdownButton', isOpen && 'open', wrapperProps.className), open: isOpen, onBlur: (e) => {
|
|
29
33
|
var _a;
|
|
30
34
|
if (!isHovering) {
|
|
@@ -39,10 +43,8 @@ const DropdownButton = (props) => {
|
|
|
39
43
|
refs.setReference(elm.querySelector('.DropdownButton__toggler'));
|
|
40
44
|
refs.setFloating(elm.querySelector('.DropdownButton__menu'));
|
|
41
45
|
} }),
|
|
42
|
-
react_1.default.createElement(
|
|
43
|
-
|
|
44
|
-
setIsOpen(!isOpen, 0);
|
|
45
|
-
} }, props.label),
|
|
46
|
+
props.Toggler ? (react_1.default.createElement("summary", { className: "DropdownButton__toggler", onClick: toggle },
|
|
47
|
+
react_1.default.createElement(props.Toggler, { isOpen: isOpen }))) : (react_1.default.createElement(_Button_js_1.Button, { as: "summary", className: "DropdownButton__toggler", bem: props.buttonType === 'primary' ? 'ButtonPrimary' : 'ButtonSecondary', icon: props.buttonIcon, size: props.buttonSize, variant: props.buttonVariant, "aria-label": props.labelLong, onClick: toggle }, props.label)),
|
|
46
48
|
react_1.default.createElement("ul", { className: "DropdownButton__menu", onMouseEnter: () => {
|
|
47
49
|
setIsHovering(true);
|
|
48
50
|
}, onMouseLeave: () => {
|
|
@@ -55,26 +57,44 @@ const DropdownButton = (props) => {
|
|
|
55
57
|
'--DropdownButton-pos-x': `${x}px`,
|
|
56
58
|
}
|
|
57
59
|
: undefined },
|
|
58
|
-
props.items.map(
|
|
60
|
+
props.items.map(
|
|
61
|
+
// eslint-disable-next-line complexity
|
|
62
|
+
(item, i) => {
|
|
63
|
+
if ('divider' in item) {
|
|
64
|
+
if ((i === 0 && !item.label) || i === props.items.length - 1) {
|
|
65
|
+
// Gracefully omit pointless dividers
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
return (react_1.default.createElement("li", { key: i, className: (0, hanna_utils_1.modifiedClass)('DropdownButton__itemDivider', item.label && 'labelled') }, item.label || false));
|
|
69
|
+
}
|
|
59
70
|
if (typeof item === 'function') {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
71
|
+
item = { Content: item };
|
|
72
|
+
}
|
|
73
|
+
const itemProps = {
|
|
74
|
+
className: (0, hanna_utils_1.modifiedClass)('DropdownButton__item', item.modifier),
|
|
75
|
+
'aria-current': item.current || undefined,
|
|
76
|
+
};
|
|
77
|
+
if ('Content' in item && item.Content) {
|
|
78
|
+
return (react_1.default.createElement("li", Object.assign({ key: i, "data-customitem": "" }, itemProps),
|
|
79
|
+
react_1.default.createElement(item.Content, { closeMenu: closeMenuStat })));
|
|
63
80
|
}
|
|
64
|
-
const { label, onClick, href } = item;
|
|
81
|
+
const { label, onClick, href, destructive } = item;
|
|
65
82
|
const commonProps = {
|
|
66
|
-
className: 'DropdownButton__itembutton',
|
|
83
|
+
className: (0, hanna_utils_1.modifiedClass)('DropdownButton__itembutton', destructive && 'destructive'),
|
|
67
84
|
lang: item.lang,
|
|
68
85
|
'data-icon': item.icon,
|
|
69
86
|
'arial-label': item.labelLong,
|
|
70
87
|
};
|
|
71
88
|
const doRenderButton = isBrowser && (onClick || (onItemClick && href == null));
|
|
89
|
+
// TypeScript type-narrowing helper for the onClick callbacks below — because
|
|
90
|
+
// `item` is a variable and could hypothetically change before the click occurs
|
|
91
|
+
const _item = item;
|
|
72
92
|
return (react_1.default.createElement("li", { key: i, className: (0, hanna_utils_1.modifiedClass)('DropdownButton__item', item.modifier), "aria-current": item.current || undefined }, doRenderButton ? (react_1.default.createElement("button", Object.assign({}, commonProps, { type: "button", "aria-controls": item.controlsId, onClick: () => {
|
|
73
|
-
const keepOpen1 = onClick && onClick(
|
|
74
|
-
const keepOpen2 = onItemClick && onItemClick(
|
|
93
|
+
const keepOpen1 = onClick && onClick(_item) === false;
|
|
94
|
+
const keepOpen2 = onItemClick && onItemClick(_item) === false;
|
|
75
95
|
!(keepOpen1 || keepOpen2) && closeMenuStat();
|
|
76
96
|
} }), label)) : href != null ? (react_1.default.createElement("a", Object.assign({}, commonProps, { href: href, hrefLang: item.hrefLang, target: item.target, onClick: () => {
|
|
77
|
-
const keepOpen = onItemClick && onItemClick(
|
|
97
|
+
const keepOpen = onItemClick && onItemClick(_item) === false;
|
|
78
98
|
!keepOpen && closeMenuStat();
|
|
79
99
|
} }), label)) : null));
|
|
80
100
|
}),
|
package/Icon.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { IconToken } from '../../hanna-css/src/iconfontTokens.js';
|
|
2
|
+
import { HTMLProps } from './utils.js';
|
|
3
|
+
export type IconProps = {
|
|
4
|
+
type: IconToken;
|
|
5
|
+
size?: 'small' | 'medium' | 'large';
|
|
6
|
+
} & HTMLProps<'span'>;
|
|
7
|
+
export declare const Icon: (props: IconProps) => JSX.Element;
|
package/Icon.js
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Icon = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const react_1 = tslib_1.__importDefault(require("react"));
|
|
6
|
+
const hanna_utils_1 = require("@reykjavik/hanna-utils");
|
|
7
|
+
const Icon = (props) => {
|
|
8
|
+
const { className, type, size = 'medium' } = props, rest = tslib_1.__rest(props, ["className", "type", "size"]);
|
|
9
|
+
return (react_1.default.createElement("span", Object.assign({}, rest, { className: (0, hanna_utils_1.classes)('Icon', props.className), "data-icon": type, "data-icon-size": size !== 'medium' ? size : undefined })));
|
|
10
|
+
};
|
|
11
|
+
exports.Icon = Icon;
|
package/MainMenu2.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { ReactElement } from 'react';
|
|
2
|
+
import { IconName, IconToken } from '@reykjavik/hanna-css';
|
|
2
3
|
import { ClassNameModifiers, Cleanup, EitherObj } from '@reykjavik/hanna-utils';
|
|
3
4
|
import { Illustration } from '@reykjavik/hanna-utils/assets';
|
|
4
5
|
import { DefaultTexts } from '@reykjavik/hanna-utils/i18n';
|
|
@@ -44,32 +45,35 @@ export type MainMenu2Item = {
|
|
|
44
45
|
* during initial (server-side) render, which then gets replaced by a
|
|
45
46
|
* <button/> element during the first client-side
|
|
46
47
|
*
|
|
47
|
-
* NOTE: Clicking a menu item will automatically close
|
|
48
|
-
* "Hamburger menu" (a.k.a. "Mobile menu")
|
|
48
|
+
* NOTE: Clicking a menu item will automatically close tghe menu
|
|
49
49
|
* … unless the `onClick` function explicitly returns `false`.
|
|
50
50
|
*/
|
|
51
51
|
onClick?: (item: MainMenu2Item) => void | boolean;
|
|
52
52
|
/** Sets `aria-controls=""` on `<button/>`s with `onClick` */
|
|
53
53
|
controlsId?: string;
|
|
54
|
+
Content?: never;
|
|
54
55
|
};
|
|
55
56
|
export type MainMenu2ButtonItem = MainMenu2Item & {
|
|
56
|
-
icon?:
|
|
57
|
+
icon?: IconToken | IconName;
|
|
57
58
|
};
|
|
58
|
-
|
|
59
|
+
type MainMenu2CustomItemFn = (props: {
|
|
59
60
|
closeMenu: () => void;
|
|
60
61
|
openMenu: () => void;
|
|
61
62
|
}) => ReactElement;
|
|
63
|
+
export type MainMenu2CustomItem = Pick<MainMenu2Item, 'modifier' | 'current'> & {
|
|
64
|
+
Content: MainMenu2CustomItemFn;
|
|
65
|
+
};
|
|
62
66
|
export type MainMenu2SubMenuItem = MainMenu2Item & {
|
|
63
67
|
descr?: string;
|
|
64
68
|
};
|
|
65
69
|
export type MainMenu2SubMenu = {
|
|
66
70
|
title: string;
|
|
67
71
|
current?: boolean;
|
|
68
|
-
subItems: Array<MainMenu2SubMenuItem | MainMenu2CustomItem | Falseish>;
|
|
72
|
+
subItems: Array<MainMenu2SubMenuItem | MainMenu2CustomItem | MainMenu2CustomItemFn | Falseish>;
|
|
69
73
|
};
|
|
70
|
-
export type MainMenu2ItemList = Array<MainMenu2Item | MainMenu2CustomItem | Falseish>;
|
|
71
|
-
export type MainMenu2ButtonItemList = Array<MainMenu2ButtonItem | MainMenu2CustomItem | Falseish>;
|
|
72
|
-
export type MainMenu2SubMenuItemList = Array<MainMenu2SubMenuItem | MainMenu2CustomItem | Falseish>;
|
|
74
|
+
export type MainMenu2ItemList = Array<MainMenu2Item | MainMenu2CustomItem | MainMenu2CustomItemFn | Falseish>;
|
|
75
|
+
export type MainMenu2ButtonItemList<Extra = unknown> = Array<(MainMenu2ButtonItem & Extra) | (MainMenu2CustomItem & Extra) | MainMenu2CustomItemFn | Falseish>;
|
|
76
|
+
export type MainMenu2SubMenuItemList = Array<MainMenu2SubMenuItem | MainMenu2CustomItem | MainMenu2CustomItemFn | Falseish>;
|
|
73
77
|
export type MainMenu2Props = {
|
|
74
78
|
/**
|
|
75
79
|
* URL for the mandatory (usually screen-reader-only) homepage Link.
|
|
@@ -96,7 +100,9 @@ export type MainMenu2Props = {
|
|
|
96
100
|
* "Open Menu" button. Make sure to only use 2–3 items, and remember that
|
|
97
101
|
* they may be hidden on smaller screens.
|
|
98
102
|
*/
|
|
99
|
-
hot?: MainMenu2ButtonItemList
|
|
103
|
+
hot?: MainMenu2ButtonItemList<{
|
|
104
|
+
redhot?: true;
|
|
105
|
+
}>;
|
|
100
106
|
extra?: MainMenu2ButtonItemList;
|
|
101
107
|
relatedTitle?: string;
|
|
102
108
|
related?: MainMenu2ButtonItemList;
|
|
@@ -104,8 +110,7 @@ export type MainMenu2Props = {
|
|
|
104
110
|
/** Visual type */
|
|
105
111
|
variant?: 'default' | 'light';
|
|
106
112
|
/**
|
|
107
|
-
* NOTE: Clicking a MainMenu2 item will automatically close
|
|
108
|
-
* "Hamburger menu" (a.k.a. "Mobile menu")
|
|
113
|
+
* NOTE: Clicking a `MainMenu2` item will automatically close the menu
|
|
109
114
|
* … unless the `onItemClick` function explicitly returns `false`.
|
|
110
115
|
*/
|
|
111
116
|
onItemClick?: (item: MainMenu2Item) => void | boolean;
|
package/MainMenu2.js
CHANGED
|
@@ -47,15 +47,6 @@ exports.defaultMainMenu2Texts = {
|
|
|
47
47
|
},
|
|
48
48
|
};
|
|
49
49
|
// ---------------------------------------------------------------------------
|
|
50
|
-
const iconMap = {
|
|
51
|
-
alert: 'info',
|
|
52
|
-
globe: undefined,
|
|
53
|
-
search: 'search',
|
|
54
|
-
user: 'user',
|
|
55
|
-
// NOTE: We're temporarily coerceing `IconName` to `ButtonIcon`
|
|
56
|
-
// TODO: Remove this once Hanna icons (and `ButtonIcons` sperifically)
|
|
57
|
-
// have been expanded better standardised.
|
|
58
|
-
};
|
|
59
50
|
/**
|
|
60
51
|
* Function that turns menu item props/objects into HTML
|
|
61
52
|
* rendering <a/> or <button/> elements depending on the context,
|
|
@@ -63,18 +54,29 @@ const iconMap = {
|
|
|
63
54
|
*/
|
|
64
55
|
const getRenderers = (props) => {
|
|
65
56
|
const { onItemClick, closeMenu, openMenu, isBrowser } = props;
|
|
57
|
+
// eslint-disable-next-line complexity
|
|
66
58
|
const renderItem = (classPrefix, item, opts = {}) => {
|
|
67
59
|
if (!item) {
|
|
68
60
|
return;
|
|
69
61
|
}
|
|
70
62
|
const { key, Tag = 'li', button } = opts;
|
|
71
63
|
if (typeof item === 'function') {
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
64
|
+
item = { Content: item };
|
|
65
|
+
}
|
|
66
|
+
const itemProps = {
|
|
67
|
+
key,
|
|
68
|
+
className: (0, hanna_utils_1.modifiedClass)(`${classPrefix}item`, item.modifier),
|
|
69
|
+
'aria-current': item.current || undefined,
|
|
70
|
+
};
|
|
71
|
+
if ('Content' in item && item.Content) {
|
|
72
|
+
return (react_1.default.createElement(Tag, Object.assign({ "data-customitem": "" }, itemProps),
|
|
73
|
+
react_1.default.createElement(item.Content, { closeMenu: closeMenu, openMenu: openMenu })));
|
|
75
74
|
}
|
|
76
75
|
const linkClassName = `${classPrefix}link`;
|
|
77
76
|
const { label, labelLong, href, target, lang, controlsId, onClick, descr, icon } = item;
|
|
77
|
+
// TypeScript type-narrowing helper for the onClick callbacks below — because
|
|
78
|
+
// `item` is a variable and could hypothetically change before the click occurs
|
|
79
|
+
const _item = item;
|
|
78
80
|
const itemDescr = descr && (react_1.default.createElement(react_1.default.Fragment, null,
|
|
79
81
|
' ',
|
|
80
82
|
react_1.default.createElement("small", { className: `${linkClassName}__descr` }, descr)));
|
|
@@ -82,26 +84,25 @@ const getRenderers = (props) => {
|
|
|
82
84
|
const LinkTag = button ? ButtonSecondary_js_1.default : _Link_js_1.Link;
|
|
83
85
|
const commonProps = {
|
|
84
86
|
className: linkClassName,
|
|
85
|
-
'data-icon': icon
|
|
87
|
+
'data-icon': icon,
|
|
86
88
|
'arial-label': labelLong,
|
|
87
89
|
title: labelLong,
|
|
88
90
|
lang,
|
|
89
91
|
};
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
: undefined;
|
|
92
|
+
if (button) {
|
|
93
|
+
console.log('Rendering button for menu item:', commonProps);
|
|
94
|
+
}
|
|
95
|
+
const buttonCompProps = button ? { size: 'small' } : undefined;
|
|
95
96
|
const doRenderButton = isBrowser && (onClick || (onItemClick && href == null));
|
|
96
97
|
return (react_1.default.createElement(Tag, { key: key, className: (0, hanna_utils_1.modifiedClass)(`${classPrefix}item`, item.modifier), "aria-current": item.current || undefined }, doRenderButton ? (react_1.default.createElement(ButtonTag, Object.assign({}, commonProps, { type: "button", "aria-controls": controlsId, onClick: () => {
|
|
97
|
-
const keepOpen1 = onClick && onClick(
|
|
98
|
-
const keepOpen2 = onItemClick && onItemClick(
|
|
98
|
+
const keepOpen1 = onClick && onClick(_item) === false;
|
|
99
|
+
const keepOpen2 = onItemClick && onItemClick(_item) === false;
|
|
99
100
|
!(keepOpen1 || keepOpen2) && closeMenu();
|
|
100
101
|
} }, buttonCompProps),
|
|
101
102
|
label,
|
|
102
103
|
" ",
|
|
103
104
|
itemDescr)) : href != null ? (react_1.default.createElement(LinkTag, Object.assign({}, commonProps, { href: href, hrefLang: item.hrefLang, target: target, onClick: () => {
|
|
104
|
-
const keepOpen = onItemClick && onItemClick(
|
|
105
|
+
const keepOpen = onItemClick && onItemClick(_item) === false;
|
|
105
106
|
!keepOpen && closeMenu();
|
|
106
107
|
} }, buttonCompProps),
|
|
107
108
|
label,
|
|
@@ -119,6 +120,7 @@ const getRenderers = (props) => {
|
|
|
119
120
|
};
|
|
120
121
|
// eslint-disable-next-line complexity
|
|
121
122
|
const MainMenu2 = (props) => {
|
|
123
|
+
var _a;
|
|
122
124
|
const { homeLink = '/', items, onItemClick, illustration, imageUrl, variant, wrapperProps = {}, } = props;
|
|
123
125
|
const domid = (0, utils_js_1.useDomid)(wrapperProps.id);
|
|
124
126
|
const isBrowser = (0, utils_js_1.useIsBrowserSide)(props.ssr);
|
|
@@ -239,7 +241,12 @@ const MainMenu2 = (props) => {
|
|
|
239
241
|
}
|
|
240
242
|
return renderItem('MainMenu2__main__', mainItem, { key: i, Tag: 'div' });
|
|
241
243
|
}))),
|
|
242
|
-
renderList('MainMenu2__hot__', items.hot
|
|
244
|
+
renderList('MainMenu2__hot__', (_a = items.hot) === null || _a === void 0 ? void 0 : _a.map((i) => {
|
|
245
|
+
if (i && 'redhot' in i) {
|
|
246
|
+
return Object.assign(Object.assign({}, i), { modifier: i.modifier ? [i.modifier, 'redhot'] : 'redhot' });
|
|
247
|
+
}
|
|
248
|
+
return i;
|
|
249
|
+
}), { buttons: true }),
|
|
243
250
|
renderList('MainMenu2__extra__', items.extra, { buttons: true }),
|
|
244
251
|
items.related && items.related.length > 0 && (react_1.default.createElement("div", { className: "MainMenu2__related" },
|
|
245
252
|
items.relatedTitle && (react_1.default.createElement("h3", { className: "MainMenu2__related__title" }, items.relatedTitle)),
|
package/ReadSpeakerPlayer.js
CHANGED
|
@@ -26,8 +26,8 @@ exports.defaultReadSpeakerPlayerTexts = {
|
|
|
26
26
|
* @see https://docs.typo3.org/p/readspeaker/readspeaker-services/main/en-us/Configuration/Index.html
|
|
27
27
|
*/
|
|
28
28
|
const ReadSpeakerPlayer = (props) => {
|
|
29
|
-
const { align, float, customerId = '11315', lang =
|
|
30
|
-
const { linkText, linkLabel } = (0, i18n_1.getTexts)(
|
|
29
|
+
const { align, float, customerId = '11315', lang = i18n_1.DEFAULT_LANG, voice = /^is(?:_is)?$/i.test(lang) ? 'is_dora' : '', readId = '', readClass = readId ? '' : 'Layout__main', wrapperProps, } = props;
|
|
30
|
+
const { linkText, linkLabel } = (0, i18n_1.getTexts)(props, exports.defaultReadSpeakerPlayerTexts);
|
|
31
31
|
(0, react_1.useEffect)(() => {
|
|
32
32
|
var _a, _b;
|
|
33
33
|
if (buttons === 0) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { WrapperElmProps } from './utils.js';
|
|
3
|
+
export type ReykjavikWavesProps = {
|
|
4
|
+
small?: boolean;
|
|
5
|
+
className?: string;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
} & WrapperElmProps<'div', 'className'>;
|
|
8
|
+
export declare const ReykjavikWaves: (props: ReykjavikWavesProps) => JSX.Element;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ReykjavikWaves = void 0;
|
|
4
|
+
const tslib_1 = require("tslib");
|
|
5
|
+
const react_1 = tslib_1.__importDefault(require("react"));
|
|
6
|
+
const hanna_utils_1 = require("@reykjavik/hanna-utils");
|
|
7
|
+
const ReykjavikWaves = (props) => (react_1.default.createElement("div", Object.assign({}, props.wrapperProps, { className: (0, hanna_utils_1.modifiedClass)('ReykjavikWaves', props.small && 'small', props.className) }), props.children));
|
|
8
|
+
exports.ReykjavikWaves = ReykjavikWaves;
|
package/SearchInput.js
CHANGED
|
@@ -30,7 +30,7 @@ const SearchInput = (props) => {
|
|
|
30
30
|
: onKeyDown }, inputElementProps, { ref: inputRef })),
|
|
31
31
|
' ',
|
|
32
32
|
showButton && (react_1.default.createElement("button", { className: "SearchInput__button", type: "submit", onClick: handleButtonClick &&
|
|
33
|
-
((e) => !handleButtonClick(e.currentTarget.value) && e.preventDefault()), title: buttonText, ref: buttonRef, disabled: props.disabled || props.readOnly }, buttonText)))) })));
|
|
33
|
+
((e) => !handleButtonClick(e.currentTarget.parentElement.querySelector('input.SearchInput__input').value) && e.preventDefault()), title: buttonText, ref: buttonRef, disabled: props.disabled || props.readOnly }, buttonText)))) })));
|
|
34
34
|
};
|
|
35
35
|
exports.SearchInput = SearchInput;
|
|
36
36
|
exports.default = exports.SearchInput;
|
package/_abstract/_Button.d.ts
CHANGED
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import { ComponentProps, ReactElement, ReactNode } from 'react';
|
|
2
|
+
import { IconToken } from '../../../hanna-css/src/iconfontTokens.js';
|
|
2
3
|
import { BemModifierProps, BemProps } from '../utils/types.js';
|
|
3
4
|
type ButtonElmProps = {
|
|
4
5
|
href?: never;
|
|
5
|
-
} & BemModifierProps &
|
|
6
|
+
} & BemModifierProps & ComponentProps<'button'>;
|
|
6
7
|
type AnchorElmProps = {
|
|
7
8
|
href: string;
|
|
8
9
|
type?: never;
|
|
9
10
|
name?: never;
|
|
10
11
|
value?: never;
|
|
11
|
-
} & BemModifierProps &
|
|
12
|
+
} & BemModifierProps & ComponentProps<'a'>;
|
|
12
13
|
export type ButtonProps = {
|
|
13
14
|
/** Label takes preference over `children` */
|
|
14
|
-
label?: string |
|
|
15
|
+
label?: string | ReactElement;
|
|
15
16
|
} & (ButtonElmProps | AnchorElmProps);
|
|
16
17
|
declare const sizes: {
|
|
17
18
|
readonly normal: "";
|
|
@@ -24,12 +25,13 @@ declare const variants: {
|
|
|
24
25
|
readonly destructive: "destructive";
|
|
25
26
|
};
|
|
26
27
|
type ButtonVariant = keyof typeof variants;
|
|
27
|
-
type NavigationFlag = '
|
|
28
|
+
type NavigationFlag = 'go-back' | 'go-forward';
|
|
29
|
+
/** @deprecated (Will be removed in v0.11) */
|
|
28
30
|
export type ButtonIcon = 'edit';
|
|
29
31
|
export type ButtonVariantProps = {
|
|
30
32
|
size?: ButtonSize;
|
|
31
33
|
variant?: ButtonVariant;
|
|
32
|
-
icon?:
|
|
34
|
+
icon?: NavigationFlag | IconToken;
|
|
33
35
|
/** @deprecated Use `size="small"` instead (Will be removed in v0.11) */
|
|
34
36
|
small?: boolean;
|
|
35
37
|
};
|
package/_abstract/_Button.js
CHANGED
|
@@ -17,30 +17,27 @@ const variants = {
|
|
|
17
17
|
destructive: 'destructive',
|
|
18
18
|
};
|
|
19
19
|
const navigationFlags = {
|
|
20
|
-
none: '',
|
|
21
20
|
'go-back': 'go--back',
|
|
22
21
|
'go-forward': 'go--forward',
|
|
23
22
|
};
|
|
24
|
-
const icons = {
|
|
25
|
-
// TODO: insert icons
|
|
26
|
-
edit: 'edit',
|
|
27
|
-
};
|
|
28
23
|
// NOTE: As this component already accepts all `<button/>` and `<a/>` props
|
|
29
24
|
// directly, it makes little sense to add support for `wrapperProps` on top.
|
|
30
25
|
const Button = (props) => {
|
|
31
26
|
const { as: CustomTag, bem, small, // eslint-disable-line deprecation/deprecation
|
|
32
|
-
size = small ? 'small' : 'normal', modifier, children, variant = 'normal', icon
|
|
27
|
+
size = small ? 'small' : 'normal', modifier, children, variant = 'normal', icon, label = children } = props, buttonProps = tslib_1.__rest(props, ["as", "bem", "small", "size", "modifier", "children", "variant", "icon", "label"]);
|
|
33
28
|
const className = bem &&
|
|
34
|
-
(0, hanna_utils_1.modifiedClass)(bem, [modifier, variants[variant], sizes[size], navigationFlags[icon]], props.className);
|
|
35
|
-
const iconProp =
|
|
29
|
+
(0, hanna_utils_1.modifiedClass)(bem, [modifier, variants[variant], sizes[size], navigationFlags[icon || '']], props.className);
|
|
30
|
+
const iconProp = icon && !(icon in navigationFlags)
|
|
31
|
+
? icon
|
|
32
|
+
: props['data-icon'];
|
|
36
33
|
if (CustomTag) {
|
|
37
|
-
return (react_1.default.createElement(CustomTag, Object.assign({}, buttonProps, { className: className
|
|
34
|
+
return (react_1.default.createElement(CustomTag, Object.assign({}, buttonProps, { className: className, "data-icon": iconProp }), label));
|
|
38
35
|
}
|
|
39
36
|
if (buttonProps.href != null) {
|
|
40
|
-
return (react_1.default.createElement(_Link_js_1.Link, Object.assign({}, buttonProps, { className: className
|
|
37
|
+
return (react_1.default.createElement(_Link_js_1.Link, Object.assign({}, buttonProps, { className: className, "data-icon": iconProp }), label));
|
|
41
38
|
}
|
|
42
39
|
else {
|
|
43
|
-
return (react_1.default.createElement("button", Object.assign({ type: "button" }, buttonProps, { className: className
|
|
40
|
+
return (react_1.default.createElement("button", Object.assign({ type: "button" }, buttonProps, { className: className, "data-icon": iconProp }), label));
|
|
44
41
|
}
|
|
45
42
|
};
|
|
46
43
|
exports.Button = Button;
|
package/esm/ButtonTertiary.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { ReactNode } from 'react';
|
|
2
2
|
import { ButtonProps, ButtonVariantProps } from './_abstract/_Button.js';
|
|
3
3
|
type TertiarySize = Extract<ButtonVariantProps['size'], 'normal' | 'small'>;
|
|
4
|
-
type TertiaryIcon = Extract<ButtonVariantProps['icon'], '
|
|
4
|
+
type TertiaryIcon = Extract<ButtonVariantProps['icon'], 'go-back'>;
|
|
5
5
|
export type ButtonTertiaryProps = ButtonProps & Omit<ButtonVariantProps, 'icon' | 'size'> & {
|
|
6
6
|
size?: TertiarySize;
|
|
7
7
|
icon?: TertiaryIcon;
|
package/esm/ButtonTertiary.js
CHANGED
|
@@ -5,13 +5,12 @@ const sizes = {
|
|
|
5
5
|
small: 'small',
|
|
6
6
|
};
|
|
7
7
|
const icons = {
|
|
8
|
-
none: 'none',
|
|
9
8
|
'go-back': 'go-back',
|
|
10
9
|
};
|
|
11
10
|
// NOTE: As a `_abstract/_Button.tsx`-derived component, all `<button/>` and
|
|
12
11
|
// `<a/>` props are allowed directly, so adding `wrapperProps` makes no sense.
|
|
13
12
|
export const ButtonTertiary = (props) => {
|
|
14
|
-
const { size = 'normal', icon = '
|
|
13
|
+
const { size = 'normal', icon = '' } = props;
|
|
15
14
|
return React.createElement(Button, Object.assign({ bem: "ButtonTertiary" }, props, { size: sizes[size], icon: icons[icon] }));
|
|
16
15
|
};
|
|
17
16
|
export default ButtonTertiary;
|
package/esm/DropdownButton.d.ts
CHANGED
|
@@ -1,29 +1,85 @@
|
|
|
1
|
-
import
|
|
2
|
-
import {
|
|
1
|
+
import { HTMLAttributeAnchorTarget, ReactElement } from 'react';
|
|
2
|
+
import { IconToken } from '@reykjavik/hanna-css';
|
|
3
|
+
import { ClassNameModifiers, EitherObj } from '@reykjavik/hanna-utils';
|
|
4
|
+
import { IconName_old } from '../../hanna-css/src/lib/icons.js';
|
|
3
5
|
import { ButtonVariantProps } from './_abstract/_Button.js';
|
|
4
6
|
import { MainMenu2Item } from './MainMenu2.js';
|
|
5
7
|
import { SSRSupportProps, WrapperElmProps } from './utils.js';
|
|
6
8
|
type Prefix<record extends Record<string, unknown>, prefix extends string> = {
|
|
7
9
|
[K in keyof record as `${prefix}${Capitalize<string & K>}`]: record[K];
|
|
8
10
|
};
|
|
9
|
-
export type DropdownButtonItem =
|
|
10
|
-
|
|
11
|
+
export type DropdownButtonItem = {
|
|
12
|
+
/** Visible label text */
|
|
13
|
+
label: string | ReactElement;
|
|
14
|
+
/** Un-abbreviated label set as `aria-label=""` */
|
|
15
|
+
labelLong?: string;
|
|
16
|
+
/** Language of the link label */
|
|
17
|
+
lang?: string;
|
|
18
|
+
/** Languge of the linked resource */
|
|
19
|
+
hrefLang?: string;
|
|
20
|
+
/**
|
|
21
|
+
* Puts a modifier className for the menu __item <li/> element.
|
|
22
|
+
* */
|
|
23
|
+
modifier?: ClassNameModifiers;
|
|
24
|
+
/** Signifies if the menu item is part of the page's breadcrumb trail */
|
|
25
|
+
current?: boolean;
|
|
26
|
+
/**
|
|
27
|
+
* The URL the link points to.
|
|
28
|
+
*
|
|
29
|
+
* If neither `href` nor `onClick` is passed, then the item is not rendered
|
|
30
|
+
* at all.
|
|
31
|
+
*/
|
|
32
|
+
href?: string;
|
|
33
|
+
/** Sets `target=""` on anchor tags with a `href` attribute. */
|
|
34
|
+
target?: HTMLAttributeAnchorTarget;
|
|
35
|
+
/**
|
|
36
|
+
* Adding `onClick` automatically results in a <button/> element being
|
|
37
|
+
* rendered. If `href` is also passed, then a <a href/> element is rendered
|
|
38
|
+
* during initial (server-side) render, which then gets replaced by a
|
|
39
|
+
* <button/> element during the first client-side
|
|
40
|
+
*
|
|
41
|
+
* NOTE: Clicking a menu item will automatically close tghe menu
|
|
42
|
+
* … unless the `onClick` function explicitly returns `false`.
|
|
43
|
+
*/
|
|
44
|
+
onClick?: (item: MainMenu2Item) => void | boolean;
|
|
45
|
+
/** Sets `aria-controls=""` on `<button/>`s with `onClick` */
|
|
46
|
+
controlsId?: string;
|
|
47
|
+
Content?: never;
|
|
48
|
+
/** Seldom used flag for buttons that do destruction */
|
|
49
|
+
destructive?: boolean;
|
|
50
|
+
icon?: IconToken | IconName_old;
|
|
11
51
|
};
|
|
12
|
-
|
|
52
|
+
type DropdownButtonCustomItemFn = (props: {
|
|
13
53
|
closeMenu: () => void;
|
|
14
|
-
}) =>
|
|
54
|
+
}) => ReactElement;
|
|
55
|
+
export type DropdownButtonCustomItem = Pick<DropdownButtonItem, 'modifier' | 'current'> & {
|
|
56
|
+
Content: DropdownButtonCustomItemFn;
|
|
57
|
+
};
|
|
58
|
+
/** Renders a divider line between `Dropdown*Item`s with an optional legend */
|
|
59
|
+
export type DropdownButtonItemDivider = {
|
|
60
|
+
divider: true;
|
|
61
|
+
label?: string;
|
|
62
|
+
};
|
|
15
63
|
export type DropdownButtonProps = {
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
items: Array<DropdownButtonItem | DropdownButtonCustomItem>;
|
|
64
|
+
/** The items to display inside the dropdown menu */
|
|
65
|
+
items: Array<DropdownButtonItem | DropdownButtonCustomItem | DropdownButtonItemDivider>;
|
|
19
66
|
/**
|
|
20
|
-
* NOTE: Clicking a
|
|
21
|
-
* "Hamburger menu" (a.k.a. "Mobile menu")
|
|
67
|
+
* NOTE: Clicking a DropdownButton item will automatically close the drropdown
|
|
22
68
|
* … unless the `onItemClick` function explicitly returns `false`.
|
|
23
69
|
*/
|
|
24
70
|
onItemClick?: (item: MainMenu2Item) => void | boolean;
|
|
25
|
-
|
|
71
|
+
} & EitherObj<{
|
|
72
|
+
/** Label for the toggler button */
|
|
73
|
+
label: string | ReactElement;
|
|
74
|
+
/** Longer accessible toggler label text */
|
|
75
|
+
labelLong?: string;
|
|
76
|
+
/** Default: `"secondary"` */
|
|
26
77
|
buttonType?: 'primary' | 'secondary';
|
|
27
|
-
} & Prefix<Omit<ButtonVariantProps, 'small'>, 'button'
|
|
78
|
+
} & Prefix<Omit<ButtonVariantProps, 'small'>, 'button'>, {
|
|
79
|
+
/** Custom toggler rendering function component */
|
|
80
|
+
Toggler: (props: {
|
|
81
|
+
isOpen: boolean;
|
|
82
|
+
}) => ReactElement;
|
|
83
|
+
}> & WrapperElmProps<'details', 'open' | 'name'> & SSRSupportProps;
|
|
28
84
|
export declare const DropdownButton: (props: DropdownButtonProps) => JSX.Element;
|
|
29
85
|
export {};
|
package/esm/DropdownButton.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import React, { useState } from 'react';
|
|
1
|
+
import React, { useRef, useState, } from 'react';
|
|
2
2
|
import { autoUpdate, flip, shift, useFloating } from '@floating-ui/react';
|
|
3
3
|
import { modifiedClass } from '@reykjavik/hanna-utils';
|
|
4
4
|
import { Button } from './_abstract/_Button.js';
|
|
@@ -11,7 +11,7 @@ export const DropdownButton = (props) => {
|
|
|
11
11
|
const [isOpen, setIsOpen] = useLaggedState(false, 10);
|
|
12
12
|
const isBrowser = useIsBrowserSide(props.ssr);
|
|
13
13
|
const [isHovering, setIsHovering] = useState(false);
|
|
14
|
-
const wrapperRef =
|
|
14
|
+
const wrapperRef = useRef(null);
|
|
15
15
|
const closeMenuStat = () => setIsOpen(false, 0);
|
|
16
16
|
useOnClickOutside(wrapperRef, isOpen && closeMenuStat);
|
|
17
17
|
useCallbackOnEsc(isOpen && closeMenuStat);
|
|
@@ -21,6 +21,10 @@ export const DropdownButton = (props) => {
|
|
|
21
21
|
whileElementsMounted: autoUpdate,
|
|
22
22
|
});
|
|
23
23
|
const { onItemClick, wrapperProps = {} } = props;
|
|
24
|
+
const toggle = (e) => {
|
|
25
|
+
e.preventDefault();
|
|
26
|
+
setIsOpen(!isOpen, 0);
|
|
27
|
+
};
|
|
24
28
|
return (React.createElement("details", Object.assign({}, wrapperProps, { className: modifiedClass('DropdownButton', isOpen && 'open', wrapperProps.className), open: isOpen, onBlur: (e) => {
|
|
25
29
|
var _a;
|
|
26
30
|
if (!isHovering) {
|
|
@@ -35,10 +39,8 @@ export const DropdownButton = (props) => {
|
|
|
35
39
|
refs.setReference(elm.querySelector('.DropdownButton__toggler'));
|
|
36
40
|
refs.setFloating(elm.querySelector('.DropdownButton__menu'));
|
|
37
41
|
} }),
|
|
38
|
-
React.createElement(
|
|
39
|
-
|
|
40
|
-
setIsOpen(!isOpen, 0);
|
|
41
|
-
} }, props.label),
|
|
42
|
+
props.Toggler ? (React.createElement("summary", { className: "DropdownButton__toggler", onClick: toggle },
|
|
43
|
+
React.createElement(props.Toggler, { isOpen: isOpen }))) : (React.createElement(Button, { as: "summary", className: "DropdownButton__toggler", bem: props.buttonType === 'primary' ? 'ButtonPrimary' : 'ButtonSecondary', icon: props.buttonIcon, size: props.buttonSize, variant: props.buttonVariant, "aria-label": props.labelLong, onClick: toggle }, props.label)),
|
|
42
44
|
React.createElement("ul", { className: "DropdownButton__menu", onMouseEnter: () => {
|
|
43
45
|
setIsHovering(true);
|
|
44
46
|
}, onMouseLeave: () => {
|
|
@@ -51,26 +53,44 @@ export const DropdownButton = (props) => {
|
|
|
51
53
|
'--DropdownButton-pos-x': `${x}px`,
|
|
52
54
|
}
|
|
53
55
|
: undefined },
|
|
54
|
-
props.items.map(
|
|
56
|
+
props.items.map(
|
|
57
|
+
// eslint-disable-next-line complexity
|
|
58
|
+
(item, i) => {
|
|
59
|
+
if ('divider' in item) {
|
|
60
|
+
if ((i === 0 && !item.label) || i === props.items.length - 1) {
|
|
61
|
+
// Gracefully omit pointless dividers
|
|
62
|
+
return null;
|
|
63
|
+
}
|
|
64
|
+
return (React.createElement("li", { key: i, className: modifiedClass('DropdownButton__itemDivider', item.label && 'labelled') }, item.label || false));
|
|
65
|
+
}
|
|
55
66
|
if (typeof item === 'function') {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
67
|
+
item = { Content: item };
|
|
68
|
+
}
|
|
69
|
+
const itemProps = {
|
|
70
|
+
className: modifiedClass('DropdownButton__item', item.modifier),
|
|
71
|
+
'aria-current': item.current || undefined,
|
|
72
|
+
};
|
|
73
|
+
if ('Content' in item && item.Content) {
|
|
74
|
+
return (React.createElement("li", Object.assign({ key: i, "data-customitem": "" }, itemProps),
|
|
75
|
+
React.createElement(item.Content, { closeMenu: closeMenuStat })));
|
|
59
76
|
}
|
|
60
|
-
const { label, onClick, href } = item;
|
|
77
|
+
const { label, onClick, href, destructive } = item;
|
|
61
78
|
const commonProps = {
|
|
62
|
-
className: 'DropdownButton__itembutton',
|
|
79
|
+
className: modifiedClass('DropdownButton__itembutton', destructive && 'destructive'),
|
|
63
80
|
lang: item.lang,
|
|
64
81
|
'data-icon': item.icon,
|
|
65
82
|
'arial-label': item.labelLong,
|
|
66
83
|
};
|
|
67
84
|
const doRenderButton = isBrowser && (onClick || (onItemClick && href == null));
|
|
85
|
+
// TypeScript type-narrowing helper for the onClick callbacks below — because
|
|
86
|
+
// `item` is a variable and could hypothetically change before the click occurs
|
|
87
|
+
const _item = item;
|
|
68
88
|
return (React.createElement("li", { key: i, className: modifiedClass('DropdownButton__item', item.modifier), "aria-current": item.current || undefined }, doRenderButton ? (React.createElement("button", Object.assign({}, commonProps, { type: "button", "aria-controls": item.controlsId, onClick: () => {
|
|
69
|
-
const keepOpen1 = onClick && onClick(
|
|
70
|
-
const keepOpen2 = onItemClick && onItemClick(
|
|
89
|
+
const keepOpen1 = onClick && onClick(_item) === false;
|
|
90
|
+
const keepOpen2 = onItemClick && onItemClick(_item) === false;
|
|
71
91
|
!(keepOpen1 || keepOpen2) && closeMenuStat();
|
|
72
92
|
} }), label)) : href != null ? (React.createElement("a", Object.assign({}, commonProps, { href: href, hrefLang: item.hrefLang, target: item.target, onClick: () => {
|
|
73
|
-
const keepOpen = onItemClick && onItemClick(
|
|
93
|
+
const keepOpen = onItemClick && onItemClick(_item) === false;
|
|
74
94
|
!keepOpen && closeMenuStat();
|
|
75
95
|
} }), label)) : null));
|
|
76
96
|
}),
|
package/esm/Icon.d.ts
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { IconToken } from '../../hanna-css/src/iconfontTokens.js';
|
|
2
|
+
import { HTMLProps } from './utils.js';
|
|
3
|
+
export type IconProps = {
|
|
4
|
+
type: IconToken;
|
|
5
|
+
size?: 'small' | 'medium' | 'large';
|
|
6
|
+
} & HTMLProps<'span'>;
|
|
7
|
+
export declare const Icon: (props: IconProps) => JSX.Element;
|
package/esm/Icon.js
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { __rest } from "tslib";
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { classes } from '@reykjavik/hanna-utils';
|
|
4
|
+
export const Icon = (props) => {
|
|
5
|
+
const { className, type, size = 'medium' } = props, rest = __rest(props, ["className", "type", "size"]);
|
|
6
|
+
return (React.createElement("span", Object.assign({}, rest, { className: classes('Icon', props.className), "data-icon": type, "data-icon-size": size !== 'medium' ? size : undefined })));
|
|
7
|
+
};
|
package/esm/MainMenu2.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import React, { ReactElement } from 'react';
|
|
2
|
+
import { IconName, IconToken } from '@reykjavik/hanna-css';
|
|
2
3
|
import { ClassNameModifiers, Cleanup, EitherObj } from '@reykjavik/hanna-utils';
|
|
3
4
|
import { Illustration } from '@reykjavik/hanna-utils/assets';
|
|
4
5
|
import { DefaultTexts } from '@reykjavik/hanna-utils/i18n';
|
|
@@ -44,32 +45,35 @@ export type MainMenu2Item = {
|
|
|
44
45
|
* during initial (server-side) render, which then gets replaced by a
|
|
45
46
|
* <button/> element during the first client-side
|
|
46
47
|
*
|
|
47
|
-
* NOTE: Clicking a menu item will automatically close
|
|
48
|
-
* "Hamburger menu" (a.k.a. "Mobile menu")
|
|
48
|
+
* NOTE: Clicking a menu item will automatically close tghe menu
|
|
49
49
|
* … unless the `onClick` function explicitly returns `false`.
|
|
50
50
|
*/
|
|
51
51
|
onClick?: (item: MainMenu2Item) => void | boolean;
|
|
52
52
|
/** Sets `aria-controls=""` on `<button/>`s with `onClick` */
|
|
53
53
|
controlsId?: string;
|
|
54
|
+
Content?: never;
|
|
54
55
|
};
|
|
55
56
|
export type MainMenu2ButtonItem = MainMenu2Item & {
|
|
56
|
-
icon?:
|
|
57
|
+
icon?: IconToken | IconName;
|
|
57
58
|
};
|
|
58
|
-
|
|
59
|
+
type MainMenu2CustomItemFn = (props: {
|
|
59
60
|
closeMenu: () => void;
|
|
60
61
|
openMenu: () => void;
|
|
61
62
|
}) => ReactElement;
|
|
63
|
+
export type MainMenu2CustomItem = Pick<MainMenu2Item, 'modifier' | 'current'> & {
|
|
64
|
+
Content: MainMenu2CustomItemFn;
|
|
65
|
+
};
|
|
62
66
|
export type MainMenu2SubMenuItem = MainMenu2Item & {
|
|
63
67
|
descr?: string;
|
|
64
68
|
};
|
|
65
69
|
export type MainMenu2SubMenu = {
|
|
66
70
|
title: string;
|
|
67
71
|
current?: boolean;
|
|
68
|
-
subItems: Array<MainMenu2SubMenuItem | MainMenu2CustomItem | Falseish>;
|
|
72
|
+
subItems: Array<MainMenu2SubMenuItem | MainMenu2CustomItem | MainMenu2CustomItemFn | Falseish>;
|
|
69
73
|
};
|
|
70
|
-
export type MainMenu2ItemList = Array<MainMenu2Item | MainMenu2CustomItem | Falseish>;
|
|
71
|
-
export type MainMenu2ButtonItemList = Array<MainMenu2ButtonItem | MainMenu2CustomItem | Falseish>;
|
|
72
|
-
export type MainMenu2SubMenuItemList = Array<MainMenu2SubMenuItem | MainMenu2CustomItem | Falseish>;
|
|
74
|
+
export type MainMenu2ItemList = Array<MainMenu2Item | MainMenu2CustomItem | MainMenu2CustomItemFn | Falseish>;
|
|
75
|
+
export type MainMenu2ButtonItemList<Extra = unknown> = Array<(MainMenu2ButtonItem & Extra) | (MainMenu2CustomItem & Extra) | MainMenu2CustomItemFn | Falseish>;
|
|
76
|
+
export type MainMenu2SubMenuItemList = Array<MainMenu2SubMenuItem | MainMenu2CustomItem | MainMenu2CustomItemFn | Falseish>;
|
|
73
77
|
export type MainMenu2Props = {
|
|
74
78
|
/**
|
|
75
79
|
* URL for the mandatory (usually screen-reader-only) homepage Link.
|
|
@@ -96,7 +100,9 @@ export type MainMenu2Props = {
|
|
|
96
100
|
* "Open Menu" button. Make sure to only use 2–3 items, and remember that
|
|
97
101
|
* they may be hidden on smaller screens.
|
|
98
102
|
*/
|
|
99
|
-
hot?: MainMenu2ButtonItemList
|
|
103
|
+
hot?: MainMenu2ButtonItemList<{
|
|
104
|
+
redhot?: true;
|
|
105
|
+
}>;
|
|
100
106
|
extra?: MainMenu2ButtonItemList;
|
|
101
107
|
relatedTitle?: string;
|
|
102
108
|
related?: MainMenu2ButtonItemList;
|
|
@@ -104,8 +110,7 @@ export type MainMenu2Props = {
|
|
|
104
110
|
/** Visual type */
|
|
105
111
|
variant?: 'default' | 'light';
|
|
106
112
|
/**
|
|
107
|
-
* NOTE: Clicking a MainMenu2 item will automatically close
|
|
108
|
-
* "Hamburger menu" (a.k.a. "Mobile menu")
|
|
113
|
+
* NOTE: Clicking a `MainMenu2` item will automatically close the menu
|
|
109
114
|
* … unless the `onItemClick` function explicitly returns `false`.
|
|
110
115
|
*/
|
|
111
116
|
onItemClick?: (item: MainMenu2Item) => void | boolean;
|
package/esm/MainMenu2.js
CHANGED
|
@@ -43,15 +43,6 @@ export const defaultMainMenu2Texts = {
|
|
|
43
43
|
},
|
|
44
44
|
};
|
|
45
45
|
// ---------------------------------------------------------------------------
|
|
46
|
-
const iconMap = {
|
|
47
|
-
alert: 'info',
|
|
48
|
-
globe: undefined,
|
|
49
|
-
search: 'search',
|
|
50
|
-
user: 'user',
|
|
51
|
-
// NOTE: We're temporarily coerceing `IconName` to `ButtonIcon`
|
|
52
|
-
// TODO: Remove this once Hanna icons (and `ButtonIcons` sperifically)
|
|
53
|
-
// have been expanded better standardised.
|
|
54
|
-
};
|
|
55
46
|
/**
|
|
56
47
|
* Function that turns menu item props/objects into HTML
|
|
57
48
|
* rendering <a/> or <button/> elements depending on the context,
|
|
@@ -59,18 +50,29 @@ const iconMap = {
|
|
|
59
50
|
*/
|
|
60
51
|
const getRenderers = (props) => {
|
|
61
52
|
const { onItemClick, closeMenu, openMenu, isBrowser } = props;
|
|
53
|
+
// eslint-disable-next-line complexity
|
|
62
54
|
const renderItem = (classPrefix, item, opts = {}) => {
|
|
63
55
|
if (!item) {
|
|
64
56
|
return;
|
|
65
57
|
}
|
|
66
58
|
const { key, Tag = 'li', button } = opts;
|
|
67
59
|
if (typeof item === 'function') {
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
60
|
+
item = { Content: item };
|
|
61
|
+
}
|
|
62
|
+
const itemProps = {
|
|
63
|
+
key,
|
|
64
|
+
className: modifiedClass(`${classPrefix}item`, item.modifier),
|
|
65
|
+
'aria-current': item.current || undefined,
|
|
66
|
+
};
|
|
67
|
+
if ('Content' in item && item.Content) {
|
|
68
|
+
return (React.createElement(Tag, Object.assign({ "data-customitem": "" }, itemProps),
|
|
69
|
+
React.createElement(item.Content, { closeMenu: closeMenu, openMenu: openMenu })));
|
|
71
70
|
}
|
|
72
71
|
const linkClassName = `${classPrefix}link`;
|
|
73
72
|
const { label, labelLong, href, target, lang, controlsId, onClick, descr, icon } = item;
|
|
73
|
+
// TypeScript type-narrowing helper for the onClick callbacks below — because
|
|
74
|
+
// `item` is a variable and could hypothetically change before the click occurs
|
|
75
|
+
const _item = item;
|
|
74
76
|
const itemDescr = descr && (React.createElement(React.Fragment, null,
|
|
75
77
|
' ',
|
|
76
78
|
React.createElement("small", { className: `${linkClassName}__descr` }, descr)));
|
|
@@ -78,26 +80,25 @@ const getRenderers = (props) => {
|
|
|
78
80
|
const LinkTag = button ? ButtonSecondary : Link;
|
|
79
81
|
const commonProps = {
|
|
80
82
|
className: linkClassName,
|
|
81
|
-
'data-icon': icon
|
|
83
|
+
'data-icon': icon,
|
|
82
84
|
'arial-label': labelLong,
|
|
83
85
|
title: labelLong,
|
|
84
86
|
lang,
|
|
85
87
|
};
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
: undefined;
|
|
88
|
+
if (button) {
|
|
89
|
+
console.log('Rendering button for menu item:', commonProps);
|
|
90
|
+
}
|
|
91
|
+
const buttonCompProps = button ? { size: 'small' } : undefined;
|
|
91
92
|
const doRenderButton = isBrowser && (onClick || (onItemClick && href == null));
|
|
92
93
|
return (React.createElement(Tag, { key: key, className: modifiedClass(`${classPrefix}item`, item.modifier), "aria-current": item.current || undefined }, doRenderButton ? (React.createElement(ButtonTag, Object.assign({}, commonProps, { type: "button", "aria-controls": controlsId, onClick: () => {
|
|
93
|
-
const keepOpen1 = onClick && onClick(
|
|
94
|
-
const keepOpen2 = onItemClick && onItemClick(
|
|
94
|
+
const keepOpen1 = onClick && onClick(_item) === false;
|
|
95
|
+
const keepOpen2 = onItemClick && onItemClick(_item) === false;
|
|
95
96
|
!(keepOpen1 || keepOpen2) && closeMenu();
|
|
96
97
|
} }, buttonCompProps),
|
|
97
98
|
label,
|
|
98
99
|
" ",
|
|
99
100
|
itemDescr)) : href != null ? (React.createElement(LinkTag, Object.assign({}, commonProps, { href: href, hrefLang: item.hrefLang, target: target, onClick: () => {
|
|
100
|
-
const keepOpen = onItemClick && onItemClick(
|
|
101
|
+
const keepOpen = onItemClick && onItemClick(_item) === false;
|
|
101
102
|
!keepOpen && closeMenu();
|
|
102
103
|
} }, buttonCompProps),
|
|
103
104
|
label,
|
|
@@ -115,6 +116,7 @@ const getRenderers = (props) => {
|
|
|
115
116
|
};
|
|
116
117
|
// eslint-disable-next-line complexity
|
|
117
118
|
export const MainMenu2 = (props) => {
|
|
119
|
+
var _a;
|
|
118
120
|
const { homeLink = '/', items, onItemClick, illustration, imageUrl, variant, wrapperProps = {}, } = props;
|
|
119
121
|
const domid = useDomid(wrapperProps.id);
|
|
120
122
|
const isBrowser = useIsBrowserSide(props.ssr);
|
|
@@ -235,7 +237,12 @@ export const MainMenu2 = (props) => {
|
|
|
235
237
|
}
|
|
236
238
|
return renderItem('MainMenu2__main__', mainItem, { key: i, Tag: 'div' });
|
|
237
239
|
}))),
|
|
238
|
-
renderList('MainMenu2__hot__', items.hot
|
|
240
|
+
renderList('MainMenu2__hot__', (_a = items.hot) === null || _a === void 0 ? void 0 : _a.map((i) => {
|
|
241
|
+
if (i && 'redhot' in i) {
|
|
242
|
+
return Object.assign(Object.assign({}, i), { modifier: i.modifier ? [i.modifier, 'redhot'] : 'redhot' });
|
|
243
|
+
}
|
|
244
|
+
return i;
|
|
245
|
+
}), { buttons: true }),
|
|
239
246
|
renderList('MainMenu2__extra__', items.extra, { buttons: true }),
|
|
240
247
|
items.related && items.related.length > 0 && (React.createElement("div", { className: "MainMenu2__related" },
|
|
241
248
|
items.relatedTitle && (React.createElement("h3", { className: "MainMenu2__related__title" }, items.relatedTitle)),
|
package/esm/ReadSpeakerPlayer.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect } from 'react';
|
|
2
2
|
import { modifiedClass } from '@reykjavik/hanna-utils';
|
|
3
|
-
import { getTexts } from '@reykjavik/hanna-utils/i18n';
|
|
3
|
+
import { DEFAULT_LANG, getTexts } from '@reykjavik/hanna-utils/i18n';
|
|
4
4
|
const scriptTagId = 'rs_req_Init';
|
|
5
5
|
const scriptTagSelector = `script#${scriptTagId}`;
|
|
6
6
|
/**
|
|
@@ -22,8 +22,8 @@ export const defaultReadSpeakerPlayerTexts = {
|
|
|
22
22
|
* @see https://docs.typo3.org/p/readspeaker/readspeaker-services/main/en-us/Configuration/Index.html
|
|
23
23
|
*/
|
|
24
24
|
export const ReadSpeakerPlayer = (props) => {
|
|
25
|
-
const { align, float, customerId = '11315', lang =
|
|
26
|
-
const { linkText, linkLabel } = getTexts(
|
|
25
|
+
const { align, float, customerId = '11315', lang = DEFAULT_LANG, voice = /^is(?:_is)?$/i.test(lang) ? 'is_dora' : '', readId = '', readClass = readId ? '' : 'Layout__main', wrapperProps, } = props;
|
|
26
|
+
const { linkText, linkLabel } = getTexts(props, defaultReadSpeakerPlayerTexts);
|
|
27
27
|
useEffect(() => {
|
|
28
28
|
var _a, _b;
|
|
29
29
|
if (buttons === 0) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { ReactNode } from 'react';
|
|
2
|
+
import { WrapperElmProps } from './utils.js';
|
|
3
|
+
export type ReykjavikWavesProps = {
|
|
4
|
+
small?: boolean;
|
|
5
|
+
className?: string;
|
|
6
|
+
children?: ReactNode;
|
|
7
|
+
} & WrapperElmProps<'div', 'className'>;
|
|
8
|
+
export declare const ReykjavikWaves: (props: ReykjavikWavesProps) => JSX.Element;
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { modifiedClass } from '@reykjavik/hanna-utils';
|
|
3
|
+
export const ReykjavikWaves = (props) => (React.createElement("div", Object.assign({}, props.wrapperProps, { className: modifiedClass('ReykjavikWaves', props.small && 'small', props.className) }), props.children));
|
package/esm/SearchInput.js
CHANGED
|
@@ -27,6 +27,6 @@ export const SearchInput = (props) => {
|
|
|
27
27
|
: onKeyDown }, inputElementProps, { ref: inputRef })),
|
|
28
28
|
' ',
|
|
29
29
|
showButton && (React.createElement("button", { className: "SearchInput__button", type: "submit", onClick: handleButtonClick &&
|
|
30
|
-
((e) => !handleButtonClick(e.currentTarget.value) && e.preventDefault()), title: buttonText, ref: buttonRef, disabled: props.disabled || props.readOnly }, buttonText)))) })));
|
|
30
|
+
((e) => !handleButtonClick(e.currentTarget.parentElement.querySelector('input.SearchInput__input').value) && e.preventDefault()), title: buttonText, ref: buttonRef, disabled: props.disabled || props.readOnly }, buttonText)))) })));
|
|
31
31
|
};
|
|
32
32
|
export default SearchInput;
|
|
@@ -1,17 +1,18 @@
|
|
|
1
|
-
import { ReactNode } from 'react';
|
|
1
|
+
import { ComponentProps, ReactElement, ReactNode } from 'react';
|
|
2
|
+
import { IconToken } from '../../../hanna-css/src/iconfontTokens.js';
|
|
2
3
|
import { BemModifierProps, BemProps } from '../utils/types.js';
|
|
3
4
|
type ButtonElmProps = {
|
|
4
5
|
href?: never;
|
|
5
|
-
} & BemModifierProps &
|
|
6
|
+
} & BemModifierProps & ComponentProps<'button'>;
|
|
6
7
|
type AnchorElmProps = {
|
|
7
8
|
href: string;
|
|
8
9
|
type?: never;
|
|
9
10
|
name?: never;
|
|
10
11
|
value?: never;
|
|
11
|
-
} & BemModifierProps &
|
|
12
|
+
} & BemModifierProps & ComponentProps<'a'>;
|
|
12
13
|
export type ButtonProps = {
|
|
13
14
|
/** Label takes preference over `children` */
|
|
14
|
-
label?: string |
|
|
15
|
+
label?: string | ReactElement;
|
|
15
16
|
} & (ButtonElmProps | AnchorElmProps);
|
|
16
17
|
declare const sizes: {
|
|
17
18
|
readonly normal: "";
|
|
@@ -24,12 +25,13 @@ declare const variants: {
|
|
|
24
25
|
readonly destructive: "destructive";
|
|
25
26
|
};
|
|
26
27
|
type ButtonVariant = keyof typeof variants;
|
|
27
|
-
type NavigationFlag = '
|
|
28
|
+
type NavigationFlag = 'go-back' | 'go-forward';
|
|
29
|
+
/** @deprecated (Will be removed in v0.11) */
|
|
28
30
|
export type ButtonIcon = 'edit';
|
|
29
31
|
export type ButtonVariantProps = {
|
|
30
32
|
size?: ButtonSize;
|
|
31
33
|
variant?: ButtonVariant;
|
|
32
|
-
icon?:
|
|
34
|
+
icon?: NavigationFlag | IconToken;
|
|
33
35
|
/** @deprecated Use `size="small"` instead (Will be removed in v0.11) */
|
|
34
36
|
small?: boolean;
|
|
35
37
|
};
|
package/esm/_abstract/_Button.js
CHANGED
|
@@ -14,29 +14,26 @@ const variants = {
|
|
|
14
14
|
destructive: 'destructive',
|
|
15
15
|
};
|
|
16
16
|
const navigationFlags = {
|
|
17
|
-
none: '',
|
|
18
17
|
'go-back': 'go--back',
|
|
19
18
|
'go-forward': 'go--forward',
|
|
20
19
|
};
|
|
21
|
-
const icons = {
|
|
22
|
-
// TODO: insert icons
|
|
23
|
-
edit: 'edit',
|
|
24
|
-
};
|
|
25
20
|
// NOTE: As this component already accepts all `<button/>` and `<a/>` props
|
|
26
21
|
// directly, it makes little sense to add support for `wrapperProps` on top.
|
|
27
22
|
export const Button = (props) => {
|
|
28
23
|
const { as: CustomTag, bem, small, // eslint-disable-line deprecation/deprecation
|
|
29
|
-
size = small ? 'small' : 'normal', modifier, children, variant = 'normal', icon
|
|
24
|
+
size = small ? 'small' : 'normal', modifier, children, variant = 'normal', icon, label = children } = props, buttonProps = __rest(props, ["as", "bem", "small", "size", "modifier", "children", "variant", "icon", "label"]);
|
|
30
25
|
const className = bem &&
|
|
31
|
-
modifiedClass(bem, [modifier, variants[variant], sizes[size], navigationFlags[icon]], props.className);
|
|
32
|
-
const iconProp =
|
|
26
|
+
modifiedClass(bem, [modifier, variants[variant], sizes[size], navigationFlags[icon || '']], props.className);
|
|
27
|
+
const iconProp = icon && !(icon in navigationFlags)
|
|
28
|
+
? icon
|
|
29
|
+
: props['data-icon'];
|
|
33
30
|
if (CustomTag) {
|
|
34
|
-
return (React.createElement(CustomTag, Object.assign({}, buttonProps, { className: className
|
|
31
|
+
return (React.createElement(CustomTag, Object.assign({}, buttonProps, { className: className, "data-icon": iconProp }), label));
|
|
35
32
|
}
|
|
36
33
|
if (buttonProps.href != null) {
|
|
37
|
-
return (React.createElement(Link, Object.assign({}, buttonProps, { className: className
|
|
34
|
+
return (React.createElement(Link, Object.assign({}, buttonProps, { className: className, "data-icon": iconProp }), label));
|
|
38
35
|
}
|
|
39
36
|
else {
|
|
40
|
-
return (React.createElement("button", Object.assign({ type: "button" }, buttonProps, { className: className
|
|
37
|
+
return (React.createElement("button", Object.assign({ type: "button" }, buttonProps, { className: className, "data-icon": iconProp }), label));
|
|
41
38
|
}
|
|
42
39
|
};
|
package/esm/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
/// <reference path="./SearchInput.d.tsx" />
|
|
27
27
|
/// <reference path="./RowBlockColumn.d.tsx" />
|
|
28
28
|
/// <reference path="./RowBlock.d.tsx" />
|
|
29
|
+
/// <reference path="./ReykjavikWaves.d.tsx" />
|
|
29
30
|
/// <reference path="./RelatedLinks.d.tsx" />
|
|
30
31
|
/// <reference path="./ReadSpeakerPlayer.d.tsx" />
|
|
31
32
|
/// <reference path="./RadioGroup.d.tsx" />
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
/// <reference path="./IframedLayout.d.tsx" />
|
|
58
59
|
/// <reference path="./IframeBlockClientScript.d.tsx" />
|
|
59
60
|
/// <reference path="./IframeBlock.d.tsx" />
|
|
61
|
+
/// <reference path="./Icon.d.tsx" />
|
|
60
62
|
/// <reference path="./HeroBlock.d.tsx" />
|
|
61
63
|
/// <reference path="./Heading.d.tsx" />
|
|
62
64
|
/// <reference path="./HannaCssLink.d.tsx" />
|
package/index.d.ts
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
/// <reference path="./SearchInput.d.tsx" />
|
|
27
27
|
/// <reference path="./RowBlockColumn.d.tsx" />
|
|
28
28
|
/// <reference path="./RowBlock.d.tsx" />
|
|
29
|
+
/// <reference path="./ReykjavikWaves.d.tsx" />
|
|
29
30
|
/// <reference path="./RelatedLinks.d.tsx" />
|
|
30
31
|
/// <reference path="./ReadSpeakerPlayer.d.tsx" />
|
|
31
32
|
/// <reference path="./RadioGroup.d.tsx" />
|
|
@@ -57,6 +58,7 @@
|
|
|
57
58
|
/// <reference path="./IframedLayout.d.tsx" />
|
|
58
59
|
/// <reference path="./IframeBlockClientScript.d.tsx" />
|
|
59
60
|
/// <reference path="./IframeBlock.d.tsx" />
|
|
61
|
+
/// <reference path="./Icon.d.tsx" />
|
|
60
62
|
/// <reference path="./HeroBlock.d.tsx" />
|
|
61
63
|
/// <reference path="./Heading.d.tsx" />
|
|
62
64
|
/// <reference path="./HannaCssLink.d.tsx" />
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reykjavik/hanna-react",
|
|
3
|
-
"version": "0.10.
|
|
3
|
+
"version": "0.10.161",
|
|
4
4
|
"author": "Reykjavík (http://www.reykjavik.is)",
|
|
5
5
|
"contributors": [
|
|
6
6
|
"Hugsmiðjan ehf (http://www.hugsmidjan.is)",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"@floating-ui/react": "^0.19.2",
|
|
18
18
|
"@hugsmidjan/qj": "^4.22.1",
|
|
19
19
|
"@hugsmidjan/react": "^0.4.32",
|
|
20
|
-
"@reykjavik/hanna-css": "^0.4.
|
|
20
|
+
"@reykjavik/hanna-css": "^0.4.24",
|
|
21
21
|
"@reykjavik/hanna-utils": "^0.2.21",
|
|
22
22
|
"@types/react-autosuggest": "^10.1.0",
|
|
23
23
|
"@types/react-datepicker": "^4.8.0",
|
|
@@ -157,6 +157,10 @@
|
|
|
157
157
|
"import": "./esm/RowBlock.js",
|
|
158
158
|
"require": "./RowBlock.js"
|
|
159
159
|
},
|
|
160
|
+
"./ReykjavikWaves": {
|
|
161
|
+
"import": "./esm/ReykjavikWaves.js",
|
|
162
|
+
"require": "./ReykjavikWaves.js"
|
|
163
|
+
},
|
|
160
164
|
"./RelatedLinks": {
|
|
161
165
|
"import": "./esm/RelatedLinks.js",
|
|
162
166
|
"require": "./RelatedLinks.js"
|
|
@@ -281,6 +285,10 @@
|
|
|
281
285
|
"import": "./esm/IframeBlock.js",
|
|
282
286
|
"require": "./IframeBlock.js"
|
|
283
287
|
},
|
|
288
|
+
"./Icon": {
|
|
289
|
+
"import": "./esm/Icon.js",
|
|
290
|
+
"require": "./Icon.js"
|
|
291
|
+
},
|
|
284
292
|
"./HeroBlock": {
|
|
285
293
|
"import": "./esm/HeroBlock.js",
|
|
286
294
|
"require": "./HeroBlock.js"
|