@uxf/ui 11.100.1 → 11.102.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/components.d.ts +10 -0
- package/components.js +10 -0
- package/css/badge.css +21 -3
- package/css/menu.css +113 -0
- package/dialog/dialog.d.ts +3 -2
- package/dialog/dialog.js +2 -2
- package/input/input-element.d.ts +3 -1
- package/input/input-element.js +1 -1
- package/menu/README.md +29 -0
- package/menu/components/menu-item-button.d.ts +17 -0
- package/menu/components/menu-item-button.js +27 -0
- package/menu/components/menu-item.d.ts +12 -0
- package/menu/components/menu-item.js +67 -0
- package/menu/index.d.ts +2 -0
- package/menu/index.js +5 -0
- package/menu/menu.d.ts +15 -0
- package/menu/menu.js +23 -0
- package/menu/menu.spec.d.ts +1 -0
- package/menu/menu.spec.js +9 -0
- package/menu/menu.stories.d.ts +2 -0
- package/menu/menu.stories.js +41 -0
- package/menu/types.d.ts +18 -0
- package/menu/types.js +2 -0
- package/modal/README.md +187 -7
- package/modal/constants.d.ts +2 -0
- package/modal/constants.js +11 -0
- package/modal/index.d.ts +6 -2
- package/modal/index.js +13 -2
- package/modal/modal-layer-config.d.ts +12 -0
- package/modal/modal-layer-config.js +60 -0
- package/modal/modal-provider.d.ts +2 -2
- package/modal/modal-provider.js +68 -26
- package/modal/modal-service.d.ts +19 -4
- package/modal/modal-service.js +46 -10
- package/modal/modal.js +1 -1
- package/modal/modal.stories.d.ts +1 -0
- package/modal/modal.stories.js +53 -8
- package/modal/theme.d.ts +4 -0
- package/modal/types.d.ts +38 -2
- package/package.json +1 -1
- package/popover/README.md +10 -0
- package/popover/popover.d.ts +71 -0
- package/popover/popover.js +123 -0
- package/popover/popover.stories.d.ts +2 -0
- package/popover/popover.stories.js +15 -0
- package/readmes.d.ts +2 -0
- package/readmes.js +46 -42
- package/text-input/text-input.d.ts +3 -1
- package/text-input/text-input.js +1 -1
- package/tw-tokens/tw-colors.d.ts +5 -0
- package/tw-tokens/tw-colors.js +5 -0
- package/tw-tokens/tw-z-index.d.ts +1 -0
- package/tw-tokens/tw-z-index.js +2 -1
- package/types/path-params.d.ts +1 -0
- package/types/path-params.js +2 -0
- package/utils/tailwind-config.js +6 -0
package/components.d.ts
CHANGED
|
@@ -40,6 +40,7 @@ import * as lightboxStories from "./lightbox/lightbox.stories";
|
|
|
40
40
|
import * as listItemStories from "./list-item/list-item.stories";
|
|
41
41
|
import * as loaderStories from "./loader/loader.stories";
|
|
42
42
|
import * as lozengeStories from "./lozenge/lozenge.stories";
|
|
43
|
+
import * as menuStories from "./menu/menu.stories";
|
|
43
44
|
import * as messageStories from "./message/message.stories";
|
|
44
45
|
import * as modalStories from "./modal/modal.stories";
|
|
45
46
|
import * as modalDialogStories from "./modal-dialog/modal-dialog.stories";
|
|
@@ -48,6 +49,7 @@ import * as multiComboboxStories from "./multi-combobox/multi-combobox.stories";
|
|
|
48
49
|
import * as multiSelectStories from "./multi-select/multi-select.stories";
|
|
49
50
|
import * as paginationStories from "./pagination/pagination.stories";
|
|
50
51
|
import * as paperStories from "./paper/paper.stories";
|
|
52
|
+
import * as popoverStories from "./popover/popover.stories";
|
|
51
53
|
import * as radioStories from "./radio/radio.stories";
|
|
52
54
|
import * as radioGroupStories from "./radio-group/radio-group.stories";
|
|
53
55
|
import * as rasterImageStories from "./raster-image/raster-image.stories";
|
|
@@ -230,6 +232,10 @@ export declare const components: {
|
|
|
230
232
|
readonly title: "Lozenge";
|
|
231
233
|
readonly stories: typeof lozengeStories;
|
|
232
234
|
};
|
|
235
|
+
readonly menu: {
|
|
236
|
+
readonly title: "Menu";
|
|
237
|
+
readonly stories: typeof menuStories;
|
|
238
|
+
};
|
|
233
239
|
readonly message: {
|
|
234
240
|
readonly title: "Message";
|
|
235
241
|
readonly stories: typeof messageStories;
|
|
@@ -262,6 +268,10 @@ export declare const components: {
|
|
|
262
268
|
readonly title: "Paper";
|
|
263
269
|
readonly stories: typeof paperStories;
|
|
264
270
|
};
|
|
271
|
+
readonly popover: {
|
|
272
|
+
readonly title: "Popover";
|
|
273
|
+
readonly stories: typeof popoverStories;
|
|
274
|
+
};
|
|
265
275
|
readonly radio: {
|
|
266
276
|
readonly title: "Radio";
|
|
267
277
|
readonly stories: typeof radioStories;
|
package/components.js
CHANGED
|
@@ -77,6 +77,7 @@ const lightboxStories = __importStar(require("./lightbox/lightbox.stories"));
|
|
|
77
77
|
const listItemStories = __importStar(require("./list-item/list-item.stories"));
|
|
78
78
|
const loaderStories = __importStar(require("./loader/loader.stories"));
|
|
79
79
|
const lozengeStories = __importStar(require("./lozenge/lozenge.stories"));
|
|
80
|
+
const menuStories = __importStar(require("./menu/menu.stories"));
|
|
80
81
|
const messageStories = __importStar(require("./message/message.stories"));
|
|
81
82
|
const modalStories = __importStar(require("./modal/modal.stories"));
|
|
82
83
|
const modalDialogStories = __importStar(require("./modal-dialog/modal-dialog.stories"));
|
|
@@ -85,6 +86,7 @@ const multiComboboxStories = __importStar(require("./multi-combobox/multi-combob
|
|
|
85
86
|
const multiSelectStories = __importStar(require("./multi-select/multi-select.stories"));
|
|
86
87
|
const paginationStories = __importStar(require("./pagination/pagination.stories"));
|
|
87
88
|
const paperStories = __importStar(require("./paper/paper.stories"));
|
|
89
|
+
const popoverStories = __importStar(require("./popover/popover.stories"));
|
|
88
90
|
const radioStories = __importStar(require("./radio/radio.stories"));
|
|
89
91
|
const radioGroupStories = __importStar(require("./radio-group/radio-group.stories"));
|
|
90
92
|
const rasterImageStories = __importStar(require("./raster-image/raster-image.stories"));
|
|
@@ -267,6 +269,10 @@ exports.components = {
|
|
|
267
269
|
title: "Lozenge",
|
|
268
270
|
stories: lozengeStories
|
|
269
271
|
},
|
|
272
|
+
"menu": {
|
|
273
|
+
title: "Menu",
|
|
274
|
+
stories: menuStories
|
|
275
|
+
},
|
|
270
276
|
"message": {
|
|
271
277
|
title: "Message",
|
|
272
278
|
stories: messageStories
|
|
@@ -299,6 +305,10 @@ exports.components = {
|
|
|
299
305
|
title: "Paper",
|
|
300
306
|
stories: paperStories
|
|
301
307
|
},
|
|
308
|
+
"popover": {
|
|
309
|
+
title: "Popover",
|
|
310
|
+
stories: popoverStories
|
|
311
|
+
},
|
|
302
312
|
"radio": {
|
|
303
313
|
title: "Radio",
|
|
304
314
|
stories: radioStories
|
package/css/badge.css
CHANGED
|
@@ -1,16 +1,34 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--uxf-badge-size: theme("space.8");
|
|
3
|
+
--uxf-badge-font-size: theme("fontSize.base");
|
|
4
|
+
}
|
|
5
|
+
|
|
1
6
|
.uxf-badge {
|
|
2
7
|
@apply bg-primary inline-flex shrink-0 items-center justify-center font-bold text-white;
|
|
3
8
|
|
|
9
|
+
font-size: var(--uxf-badge-font-size);
|
|
10
|
+
height: var(--uxf-badge-size);
|
|
11
|
+
min-width: var(--uxf-badge-size);
|
|
12
|
+
|
|
4
13
|
&--size-small {
|
|
5
|
-
|
|
14
|
+
--uxf-badge-size: theme("space.6");
|
|
15
|
+
--uxf-badge-font-size: theme("fontSize.sm");
|
|
16
|
+
|
|
17
|
+
@apply rounded-[12px] px-1.5;
|
|
6
18
|
}
|
|
7
19
|
|
|
8
20
|
&--size-medium {
|
|
9
|
-
|
|
21
|
+
--uxf-badge-size: theme("space.8");
|
|
22
|
+
--uxf-badge-font-size: theme("fontSize.base");
|
|
23
|
+
|
|
24
|
+
@apply rounded-[16px] px-2;
|
|
10
25
|
}
|
|
11
26
|
|
|
12
27
|
&--size-large {
|
|
13
|
-
|
|
28
|
+
--uxf-badge-size: theme("space.10");
|
|
29
|
+
--uxf-badge-font-size: theme("fontSize.lg");
|
|
30
|
+
|
|
31
|
+
@apply rounded-[20px] px-2;
|
|
14
32
|
}
|
|
15
33
|
|
|
16
34
|
:root .dark & {
|
package/css/menu.css
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
:root {
|
|
2
|
+
--uxf-menu-item-background-color: inherit;
|
|
3
|
+
--uxf-menu-icon-size: 16px;
|
|
4
|
+
--uxf-menu-item-gap: 12px;
|
|
5
|
+
--uxf-menu-item-px: 12px;
|
|
6
|
+
--uxf-menu-item-border-radius: 8px;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
.uxf-menu {
|
|
10
|
+
font-size: theme("fontSize.sm");
|
|
11
|
+
padding: 4px;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
.uxf-menu-item {
|
|
15
|
+
align-items: center;
|
|
16
|
+
background-color: var(--uxf-menu-item-background-color);
|
|
17
|
+
border-radius: var(--uxf-menu-item-border-radius);
|
|
18
|
+
color: black;
|
|
19
|
+
cursor: pointer;
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: row;
|
|
22
|
+
gap: var(--uxf-menu-item-gap);
|
|
23
|
+
height: 44px;
|
|
24
|
+
margin-bottom: 2px;
|
|
25
|
+
padding: 4px var(--uxf-menu-item-px);
|
|
26
|
+
width: 100%;
|
|
27
|
+
|
|
28
|
+
&:hover {
|
|
29
|
+
--uxf-menu-item-background-color: var(--uxf-color-base-surface-main-hover);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
&.is-active:not(.uxf-menu-item--level-1),
|
|
33
|
+
&.is-active.has-no-children {
|
|
34
|
+
--uxf-menu-item-background-color: #e5e7eb;
|
|
35
|
+
|
|
36
|
+
font-weight: 500;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
.uxf-menu-item__right-element {
|
|
41
|
+
align-items: center;
|
|
42
|
+
display: flex;
|
|
43
|
+
|
|
44
|
+
.uxf-badge {
|
|
45
|
+
--uxf-badge-size: 16px;
|
|
46
|
+
--uxf-badge-font-size: 12px;
|
|
47
|
+
|
|
48
|
+
font-weight: normal;
|
|
49
|
+
padding: 0 4px;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.uxf-menu-item__icon {
|
|
54
|
+
height: var(--uxf-menu-icon-size);
|
|
55
|
+
width: var(--uxf-menu-icon-size);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
.uxf-menu-item__label {
|
|
59
|
+
flex-grow: 1;
|
|
60
|
+
text-align: left;
|
|
61
|
+
|
|
62
|
+
@apply truncate;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
.uxf-menu-item__open-icon {
|
|
66
|
+
@apply transition-transform duration-200;
|
|
67
|
+
|
|
68
|
+
height: 20px;
|
|
69
|
+
width: 20px;
|
|
70
|
+
|
|
71
|
+
&.is-close {
|
|
72
|
+
transform: rotate(-90deg);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
.uxf-menu__submenu {
|
|
77
|
+
padding-left: calc(var(--uxf-menu-item-gap) + var(--uxf-menu-icon-size));
|
|
78
|
+
position: relative;
|
|
79
|
+
|
|
80
|
+
&::before {
|
|
81
|
+
background-color: #e5e7eb;
|
|
82
|
+
bottom: 0;
|
|
83
|
+
content: "";
|
|
84
|
+
left: calc(var(--uxf-menu-item-px) + var(--uxf-menu-icon-size) / 2);
|
|
85
|
+
position: absolute;
|
|
86
|
+
top: 0;
|
|
87
|
+
width: 1px;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.uxf-menu-item {
|
|
91
|
+
position: relative;
|
|
92
|
+
|
|
93
|
+
&:hover {
|
|
94
|
+
&::before {
|
|
95
|
+
background-color: #0f1012;
|
|
96
|
+
bottom: 0;
|
|
97
|
+
content: "";
|
|
98
|
+
left: calc(-1 * (var(--uxf-menu-icon-size) / 2));
|
|
99
|
+
position: absolute;
|
|
100
|
+
top: 0;
|
|
101
|
+
width: 2px;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
.uxf-menu__popover {
|
|
108
|
+
background-color: var(--uxf-color-base-surface-main);
|
|
109
|
+
border: 1px solid var(--uxf-color-base-border-border);
|
|
110
|
+
border-radius: 8px;
|
|
111
|
+
box-shadow: theme("boxShadow.sm");
|
|
112
|
+
padding: 4px;
|
|
113
|
+
}
|
package/dialog/dialog.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { UseFloatingReturn } from "@floating-ui/react";
|
|
2
2
|
import { DialogVariants } from "@uxf/ui/dialog/theme";
|
|
3
|
-
import React, { FC, HTMLProps, ReactNode, Ref } from "react";
|
|
3
|
+
import React, { CSSProperties, FC, HTMLProps, ReactNode, Ref } from "react";
|
|
4
4
|
type GetFloatingElementProps = (userProps?: HTMLProps<HTMLElement>) => Partial<Record<string, unknown>>;
|
|
5
5
|
export type DialogVariant = keyof DialogVariants;
|
|
6
6
|
export interface DialogProps {
|
|
@@ -10,9 +10,10 @@ export interface DialogProps {
|
|
|
10
10
|
forwardedRef: Ref<HTMLDivElement>;
|
|
11
11
|
getFloatingProps: GetFloatingElementProps;
|
|
12
12
|
isOpen: boolean;
|
|
13
|
+
style?: CSSProperties;
|
|
13
14
|
variant?: DialogVariant;
|
|
14
15
|
}
|
|
15
|
-
type DialogProviderProps = Pick<DialogProps, "variant" | "className" | "children">;
|
|
16
|
+
type DialogProviderProps = Pick<DialogProps, "variant" | "className" | "children" | "style">;
|
|
16
17
|
export declare const Dialog: React.NamedExoticComponent<DialogProps>;
|
|
17
18
|
type DialogCloseHandler = () => void;
|
|
18
19
|
export declare function useDialog(dialogConfig?: {
|
package/dialog/dialog.js
CHANGED
|
@@ -45,7 +45,7 @@ exports.Dialog = (0, react_2.memo)((props) => {
|
|
|
45
45
|
var _a, _b;
|
|
46
46
|
return (react_2.default.createElement(react_1.FloatingPortal, null,
|
|
47
47
|
react_2.default.createElement(show_1.Show, { when: props.isOpen },
|
|
48
|
-
react_2.default.createElement(react_1.FloatingOverlay, { className: `uxf-dialog uxf-dialog__backdrop uxf-dialog--variant-${(_a = props.variant) !== null && _a !== void 0 ? _a : "default"} ${(_b = props.className) !== null && _b !== void 0 ? _b : ""}`, lockScroll: true },
|
|
48
|
+
react_2.default.createElement(react_1.FloatingOverlay, { className: `uxf-dialog uxf-dialog__backdrop uxf-dialog--variant-${(_a = props.variant) !== null && _a !== void 0 ? _a : "default"} ${(_b = props.className) !== null && _b !== void 0 ? _b : ""}`, lockScroll: true, style: props.style },
|
|
49
49
|
react_2.default.createElement(react_1.FloatingFocusManager, { context: props.context, guards: true, initialFocus: 0, modal: true },
|
|
50
50
|
react_2.default.createElement("div", { className: "uxf-dialog__wrapper" },
|
|
51
51
|
react_2.default.createElement("div", { className: "uxf-dialog__body" },
|
|
@@ -78,7 +78,7 @@ function useDialog(dialogConfig) {
|
|
|
78
78
|
const { getReferenceProps, getFloatingProps } = (0, react_1.useInteractions)([click, role, dismiss]);
|
|
79
79
|
const latestContext = (0, use_latest_1.useLatest)(context);
|
|
80
80
|
const DialogProvider = (0, react_2.useCallback)((config) => {
|
|
81
|
-
return (react_2.default.createElement(exports.Dialog, { className: config === null || config === void 0 ? void 0 : config.className, context: latestContext.current, forwardedRef: refs.setFloating, getFloatingProps: getFloatingProps, isOpen: Boolean(content), variant: config === null || config === void 0 ? void 0 : config.variant }, content));
|
|
81
|
+
return (react_2.default.createElement(exports.Dialog, { className: config === null || config === void 0 ? void 0 : config.className, context: latestContext.current, forwardedRef: refs.setFloating, getFloatingProps: getFloatingProps, isOpen: Boolean(content), style: config === null || config === void 0 ? void 0 : config.style, variant: config === null || config === void 0 ? void 0 : config.variant }, content));
|
|
82
82
|
}, [latestContext, refs.setFloating, getFloatingProps, content]);
|
|
83
83
|
return {
|
|
84
84
|
openDialog: setContent,
|
package/input/input-element.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { FormControlProps } from "@uxf/ui/types/form-control-props";
|
|
2
|
-
import React, { CSSProperties, KeyboardEventHandler } from "react";
|
|
2
|
+
import React, { ClipboardEventHandler, CSSProperties, InputEventHandler, KeyboardEventHandler } from "react";
|
|
3
3
|
type InputMode = "none" | "text" | "tel" | "url" | "email" | "numeric" | "decimal" | "search" | undefined;
|
|
4
4
|
export interface InputElementProps extends FormControlProps<string> {
|
|
5
5
|
"aria-describedby"?: string;
|
|
@@ -9,7 +9,9 @@ export interface InputElementProps extends FormControlProps<string> {
|
|
|
9
9
|
form?: string;
|
|
10
10
|
id?: string;
|
|
11
11
|
inputMode?: InputMode;
|
|
12
|
+
onBeforeInput?: InputEventHandler<HTMLInputElement>;
|
|
12
13
|
onKeyDown?: KeyboardEventHandler<HTMLInputElement>;
|
|
14
|
+
onPaste?: ClipboardEventHandler<HTMLInputElement>;
|
|
13
15
|
placeholder?: string;
|
|
14
16
|
type?: "email" | "number" | "password" | "search" | "tel" | "text" | "url" | "time";
|
|
15
17
|
maxLength?: number | undefined;
|
package/input/input-element.js
CHANGED
|
@@ -39,6 +39,6 @@ const react_1 = __importStar(require("react"));
|
|
|
39
39
|
exports.InputElement = (0, react_1.forwardRef)((props, ref) => {
|
|
40
40
|
const onChange = (event) => props.onChange(event.target.value, event);
|
|
41
41
|
const ariaAndDataAttrs = (0, filter_aria_and_data_attrs_1.filterAriaAndDataAttrs)(props);
|
|
42
|
-
return (react_1.default.createElement("input", { ...ariaAndDataAttrs, "aria-describedby": props["aria-describedby"], "aria-invalid": props.isInvalid, autoComplete: props.autoComplete, autoFocus: props.autoFocus, className: "uxf-input__element", disabled: props.isDisabled, form: props.form, id: props.id, inputMode: props.inputMode, max: props.max, maxLength: props.maxLength, min: props.min, minLength: props.minLength, name: props.name, onBlur: props.onBlur, onChange: onChange, onFocus: props.onFocus, onKeyDown: props.onKeyDown, pattern: props.pattern, placeholder: props.placeholder, readOnly: props.isReadOnly, ref: ref, step: props.step, style: props.style, tabIndex: props.isReadOnly ? -1 : undefined, type: props.type, value: props.value }));
|
|
42
|
+
return (react_1.default.createElement("input", { ...ariaAndDataAttrs, "aria-describedby": props["aria-describedby"], "aria-invalid": props.isInvalid, autoComplete: props.autoComplete, autoFocus: props.autoFocus, className: "uxf-input__element", disabled: props.isDisabled, form: props.form, id: props.id, inputMode: props.inputMode, max: props.max, maxLength: props.maxLength, min: props.min, minLength: props.minLength, name: props.name, onBeforeInput: props.onBeforeInput, onBlur: props.onBlur, onChange: onChange, onFocus: props.onFocus, onKeyDown: props.onKeyDown, onPaste: props.onPaste, pattern: props.pattern, placeholder: props.placeholder, readOnly: props.isReadOnly, ref: ref, step: props.step, style: props.style, tabIndex: props.isReadOnly ? -1 : undefined, type: props.type, value: props.value }));
|
|
43
43
|
});
|
|
44
44
|
exports.InputElement.displayName = "UxfUiInputElement";
|
package/menu/README.md
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Menu
|
|
2
|
+
|
|
3
|
+
## CSS dependencies
|
|
4
|
+
|
|
5
|
+
```css
|
|
6
|
+
@import url("@uxf/ui/css/menu.css");
|
|
7
|
+
```
|
|
8
|
+
|
|
9
|
+
## How to use active item
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
// app directory
|
|
13
|
+
import { usePathname } from "next/navigation";
|
|
14
|
+
import { usePageParams } from "@app-routes";
|
|
15
|
+
|
|
16
|
+
const pathname = usePathname();
|
|
17
|
+
const pathParams = usePageParams();
|
|
18
|
+
|
|
19
|
+
<Menu configuration={...} router={{pathname, pathParams}}/>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
```tsx
|
|
23
|
+
// pages directory
|
|
24
|
+
import { useRouter } from "next/router";
|
|
25
|
+
|
|
26
|
+
const router = useRouter();
|
|
27
|
+
|
|
28
|
+
<Menu configuration={...} router={router}
|
|
29
|
+
```
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { ReactElement } from "react";
|
|
2
|
+
import { IconName } from "../../icon/types";
|
|
3
|
+
import { NextLink } from "../../utils/next-link";
|
|
4
|
+
interface MenuItemButtonProps {
|
|
5
|
+
icon?: IconName;
|
|
6
|
+
label: string;
|
|
7
|
+
hasChildren?: boolean;
|
|
8
|
+
LinkComponent?: NextLink | "a";
|
|
9
|
+
href?: string;
|
|
10
|
+
onClick?: () => void;
|
|
11
|
+
isOpen?: boolean;
|
|
12
|
+
level?: number;
|
|
13
|
+
isActive?: boolean;
|
|
14
|
+
badge?: ReactElement;
|
|
15
|
+
}
|
|
16
|
+
export declare function MenuItemButton(props: MenuItemButtonProps): ReactElement;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.MenuItemButton = MenuItemButton;
|
|
7
|
+
const cx_1 = require("@uxf/core/utils/cx");
|
|
8
|
+
const is_nil_1 = require("@uxf/core/utils/is-nil");
|
|
9
|
+
const is_not_nil_1 = require("@uxf/core/utils/is-not-nil");
|
|
10
|
+
const icon_1 = require("@uxf/ui/icon");
|
|
11
|
+
const react_1 = __importDefault(require("react"));
|
|
12
|
+
function MenuItemButton(props) {
|
|
13
|
+
var _a;
|
|
14
|
+
const LinkComponent = (_a = props.LinkComponent) !== null && _a !== void 0 ? _a : "a";
|
|
15
|
+
const className = (0, cx_1.cx)(`uxf-menu-item uxf-menu-item--level-${props.level}`, props.isOpen ? "is-open" : "is-close", props.isActive && "is-active", props.hasChildren ? "has-children" : "has-no-children", (0, is_nil_1.isNil)(props.icon) && "uxf-menu-item--no-icon");
|
|
16
|
+
const content = (react_1.default.createElement(react_1.default.Fragment, null,
|
|
17
|
+
(0, is_not_nil_1.isNotNil)(props.icon) && (react_1.default.createElement("div", { className: "uxf-menu-item__icon-wrapper" },
|
|
18
|
+
react_1.default.createElement(icon_1.Icon, { className: "uxf-menu-item__icon", name: props.icon }))),
|
|
19
|
+
react_1.default.createElement("div", { className: "uxf-menu-item__label" }, props.label),
|
|
20
|
+
react_1.default.createElement("div", { className: "uxf-menu-item__right-element" },
|
|
21
|
+
(0, is_not_nil_1.isNotNil)(props.badge) && props.badge,
|
|
22
|
+
props.hasChildren && (react_1.default.createElement(icon_1.Icon, { className: (0, cx_1.cx)("uxf-menu-item__open-icon", props.isOpen ? "is-open" : "is-close", props.isActive && "is-active"), name: "caretDown" })))));
|
|
23
|
+
if ((0, is_not_nil_1.isNotNil)(props.href)) {
|
|
24
|
+
return (react_1.default.createElement(LinkComponent, { className: className, href: props.href, onClick: props.onClick, title: props.label }, content));
|
|
25
|
+
}
|
|
26
|
+
return (react_1.default.createElement("button", { className: className, onClick: props.onClick, title: props.label }, content));
|
|
27
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { NextLink } from "../../utils/next-link";
|
|
3
|
+
import { ActiveTreeNode, MenuItemConfiguration } from "../types";
|
|
4
|
+
interface Props {
|
|
5
|
+
item: MenuItemConfiguration;
|
|
6
|
+
activeTree: ActiveTreeNode;
|
|
7
|
+
LinkComponent?: NextLink | "a";
|
|
8
|
+
level: number;
|
|
9
|
+
isPopoverEnabled?: boolean;
|
|
10
|
+
}
|
|
11
|
+
export declare function MenuItem(props: Props): React.JSX.Element;
|
|
12
|
+
export {};
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.MenuItem = MenuItem;
|
|
37
|
+
const is_empty_1 = require("@uxf/core/utils/is-empty");
|
|
38
|
+
const is_nil_1 = require("@uxf/core/utils/is-nil");
|
|
39
|
+
const is_not_empty_1 = require("@uxf/core/utils/is-not-empty");
|
|
40
|
+
const is_not_nil_1 = require("@uxf/core/utils/is-not-nil");
|
|
41
|
+
const throw_error_1 = require("@uxf/core/utils/throw-error");
|
|
42
|
+
const react_1 = __importStar(require("react"));
|
|
43
|
+
const popover_1 = require("../../popover/popover");
|
|
44
|
+
const menu_item_button_1 = require("./menu-item-button");
|
|
45
|
+
function MenuItem(props) {
|
|
46
|
+
var _a, _b;
|
|
47
|
+
const [isOpen, setIsOpen] = (0, react_1.useState)(props.activeTree.isActive);
|
|
48
|
+
const button = (react_1.default.createElement(menu_item_button_1.MenuItemButton, { LinkComponent: (_b = (_a = props.item.as) !== null && _a !== void 0 ? _a : props.LinkComponent) !== null && _b !== void 0 ? _b : "a", badge: props.item.badge, hasChildren: (0, is_not_nil_1.isNotNil)(props.item.children) && (0, is_not_empty_1.isNotEmpty)(props.item.children), href: props.item.href, icon: props.item.icon, isActive: props.activeTree.isActive, isOpen: isOpen, label: props.item.label, level: props.level, onClick: () => setIsOpen((prev) => !prev) }));
|
|
49
|
+
if ((0, is_nil_1.isNil)(props.item.children) || (0, is_empty_1.isEmpty)(props.item.children)) {
|
|
50
|
+
return button;
|
|
51
|
+
}
|
|
52
|
+
const children = props.item.children.map((item, index) => {
|
|
53
|
+
var _a, _b;
|
|
54
|
+
return (react_1.default.createElement(MenuItem, { LinkComponent: props.LinkComponent, activeTree: (_b = (_a = props.activeTree.children) === null || _a === void 0 ? void 0 : _a.at(index)) !== null && _b !== void 0 ? _b : (0, throw_error_1.throwError)("Active tree is not defined"), isPopoverEnabled: props.isPopoverEnabled, item: item, key: item.label, level: props.level + 1 }));
|
|
55
|
+
});
|
|
56
|
+
if (!props.isPopoverEnabled) {
|
|
57
|
+
return (react_1.default.createElement("div", null,
|
|
58
|
+
button,
|
|
59
|
+
react_1.default.createElement("div", { className: "uxf-menu__submenu" }, isOpen && children)));
|
|
60
|
+
}
|
|
61
|
+
return (react_1.default.createElement(popover_1.Popover, { placement: "right-start", trigger: "hover" },
|
|
62
|
+
react_1.default.createElement(popover_1.Popover.Trigger, null,
|
|
63
|
+
react_1.default.createElement("div", null,
|
|
64
|
+
button,
|
|
65
|
+
isOpen && react_1.default.createElement("div", { className: "uxf-menu__submenu" }, children))),
|
|
66
|
+
!isOpen && react_1.default.createElement(popover_1.Popover.Content, { className: "uxf-menu__popover" }, children)));
|
|
67
|
+
}
|
package/menu/index.d.ts
ADDED
package/menu/index.js
ADDED
package/menu/menu.d.ts
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import React from "react";
|
|
2
|
+
import { RouterPathParams } from "../types/path-params";
|
|
3
|
+
import { MenuItemConfiguration } from "./types";
|
|
4
|
+
export type MenuConfiguration = MenuItemConfiguration[];
|
|
5
|
+
export type SimpleRouter = {
|
|
6
|
+
pathname: string | null;
|
|
7
|
+
pathParams?: RouterPathParams | null;
|
|
8
|
+
};
|
|
9
|
+
export interface MenuProps {
|
|
10
|
+
configuration: MenuConfiguration;
|
|
11
|
+
router: SimpleRouter;
|
|
12
|
+
className?: string;
|
|
13
|
+
isPopoverEnabled?: boolean;
|
|
14
|
+
}
|
|
15
|
+
export declare function Menu(props: MenuProps): React.JSX.Element;
|
package/menu/menu.js
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Menu = Menu;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const menu_item_1 = require("./components/menu-item");
|
|
9
|
+
function createActiveTree(menuItem, router) {
|
|
10
|
+
var _a, _b, _c, _d;
|
|
11
|
+
const children = (_a = menuItem.children) === null || _a === void 0 ? void 0 : _a.map((child) => createActiveTree(child, router));
|
|
12
|
+
return {
|
|
13
|
+
isActive: (children === null || children === void 0 ? void 0 : children.some((child) => child.isActive)) ||
|
|
14
|
+
((_b = menuItem.routeMatcher) === null || _b === void 0 ? void 0 : _b.call(menuItem, (_c = router.pathname) !== null && _c !== void 0 ? _c : "", (_d = router.pathParams) !== null && _d !== void 0 ? _d : {})) ||
|
|
15
|
+
false,
|
|
16
|
+
children,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function Menu(props) {
|
|
20
|
+
var _a;
|
|
21
|
+
const activeTree = props.configuration.map((item) => createActiveTree(item, props.router));
|
|
22
|
+
return (react_1.default.createElement("div", { className: `uxf-menu ${(_a = props.className) !== null && _a !== void 0 ? _a : ""}` }, props.configuration.map((item, index) => (react_1.default.createElement(menu_item_1.MenuItem, { activeTree: activeTree[index], isPopoverEnabled: props.isPopoverEnabled, item: item, key: item.label, level: 1 })))));
|
|
23
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
const react_1 = __importDefault(require("react"));
|
|
7
|
+
const snap_test_1 = require("../utils/snap-test");
|
|
8
|
+
const menu_stories_1 = require("./menu.stories");
|
|
9
|
+
(0, snap_test_1.snapTest)("render stories", react_1.default.createElement(menu_stories_1.Default, null));
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.Default = Default;
|
|
7
|
+
const react_1 = __importDefault(require("react"));
|
|
8
|
+
const badge_1 = require("../badge");
|
|
9
|
+
const menu_1 = require("./menu");
|
|
10
|
+
const CONFIGURATION = [
|
|
11
|
+
{
|
|
12
|
+
label: "UXF Web",
|
|
13
|
+
href: "https://www.uxf.cz",
|
|
14
|
+
icon: "eye",
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
label: "Link without children",
|
|
18
|
+
href: "/examples/ui/menu/Default",
|
|
19
|
+
icon: "camera",
|
|
20
|
+
badge: react_1.default.createElement("div", { className: "text-xs font-semibold text-neutral-500" }, "12"),
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
label: "Link with children",
|
|
24
|
+
icon: "file",
|
|
25
|
+
badge: react_1.default.createElement(badge_1.Badge, null, "5"),
|
|
26
|
+
children: [
|
|
27
|
+
{ label: "Link 1", icon: "bars" },
|
|
28
|
+
{ label: "Link 2", href: "/examples", icon: "copy" },
|
|
29
|
+
{ label: "Link with very very long text", href: "/examples", icon: "copy" },
|
|
30
|
+
],
|
|
31
|
+
},
|
|
32
|
+
{
|
|
33
|
+
label: "Link with very very long text",
|
|
34
|
+
icon: "file",
|
|
35
|
+
badge: react_1.default.createElement(badge_1.Badge, null, "5"),
|
|
36
|
+
},
|
|
37
|
+
];
|
|
38
|
+
function Default() {
|
|
39
|
+
return (react_1.default.createElement("div", { className: "fixed inset-y-0 left-0 w-60 bg-white p-1" },
|
|
40
|
+
react_1.default.createElement(menu_1.Menu, { configuration: CONFIGURATION, router: { pathname: "", pathParams: {} } })));
|
|
41
|
+
}
|
package/menu/types.d.ts
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { ReactElement } from "react";
|
|
2
|
+
import { IconName } from "../icon/types";
|
|
3
|
+
import { RouterPathParams } from "../types/path-params";
|
|
4
|
+
import { NextLink } from "../utils/next-link";
|
|
5
|
+
export type ActiveTreeNode = {
|
|
6
|
+
isActive: boolean;
|
|
7
|
+
children?: ActiveTreeNode[];
|
|
8
|
+
};
|
|
9
|
+
export interface MenuItemConfiguration {
|
|
10
|
+
as?: NextLink | "a";
|
|
11
|
+
href?: string;
|
|
12
|
+
onClick?: () => void;
|
|
13
|
+
icon?: IconName;
|
|
14
|
+
label: string;
|
|
15
|
+
children?: MenuItemConfiguration[];
|
|
16
|
+
routeMatcher?: (pathname: string, pathParams?: RouterPathParams) => boolean;
|
|
17
|
+
badge?: ReactElement;
|
|
18
|
+
}
|
package/menu/types.js
ADDED