@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.
Files changed (56) hide show
  1. package/components.d.ts +10 -0
  2. package/components.js +10 -0
  3. package/css/badge.css +21 -3
  4. package/css/menu.css +113 -0
  5. package/dialog/dialog.d.ts +3 -2
  6. package/dialog/dialog.js +2 -2
  7. package/input/input-element.d.ts +3 -1
  8. package/input/input-element.js +1 -1
  9. package/menu/README.md +29 -0
  10. package/menu/components/menu-item-button.d.ts +17 -0
  11. package/menu/components/menu-item-button.js +27 -0
  12. package/menu/components/menu-item.d.ts +12 -0
  13. package/menu/components/menu-item.js +67 -0
  14. package/menu/index.d.ts +2 -0
  15. package/menu/index.js +5 -0
  16. package/menu/menu.d.ts +15 -0
  17. package/menu/menu.js +23 -0
  18. package/menu/menu.spec.d.ts +1 -0
  19. package/menu/menu.spec.js +9 -0
  20. package/menu/menu.stories.d.ts +2 -0
  21. package/menu/menu.stories.js +41 -0
  22. package/menu/types.d.ts +18 -0
  23. package/menu/types.js +2 -0
  24. package/modal/README.md +187 -7
  25. package/modal/constants.d.ts +2 -0
  26. package/modal/constants.js +11 -0
  27. package/modal/index.d.ts +6 -2
  28. package/modal/index.js +13 -2
  29. package/modal/modal-layer-config.d.ts +12 -0
  30. package/modal/modal-layer-config.js +60 -0
  31. package/modal/modal-provider.d.ts +2 -2
  32. package/modal/modal-provider.js +68 -26
  33. package/modal/modal-service.d.ts +19 -4
  34. package/modal/modal-service.js +46 -10
  35. package/modal/modal.js +1 -1
  36. package/modal/modal.stories.d.ts +1 -0
  37. package/modal/modal.stories.js +53 -8
  38. package/modal/theme.d.ts +4 -0
  39. package/modal/types.d.ts +38 -2
  40. package/package.json +1 -1
  41. package/popover/README.md +10 -0
  42. package/popover/popover.d.ts +71 -0
  43. package/popover/popover.js +123 -0
  44. package/popover/popover.stories.d.ts +2 -0
  45. package/popover/popover.stories.js +15 -0
  46. package/readmes.d.ts +2 -0
  47. package/readmes.js +46 -42
  48. package/text-input/text-input.d.ts +3 -1
  49. package/text-input/text-input.js +1 -1
  50. package/tw-tokens/tw-colors.d.ts +5 -0
  51. package/tw-tokens/tw-colors.js +5 -0
  52. package/tw-tokens/tw-z-index.d.ts +1 -0
  53. package/tw-tokens/tw-z-index.js +2 -1
  54. package/types/path-params.d.ts +1 -0
  55. package/types/path-params.js +2 -0
  56. 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
- @apply h-6 min-w-[24px] rounded-[12px] px-1.5 text-sm;
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
- @apply h-8 min-w-[32px] rounded-[16px] px-2 text-base;
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
- @apply h-10 min-w-[40px] rounded-[20px] px-2 text-lg;
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
+ }
@@ -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,
@@ -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;
@@ -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
+ }
@@ -0,0 +1,2 @@
1
+ export { Menu } from "./menu";
2
+ export type { MenuProps } from "./menu";
package/menu/index.js ADDED
@@ -0,0 +1,5 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Menu = void 0;
4
+ var menu_1 = require("./menu");
5
+ Object.defineProperty(exports, "Menu", { enumerable: true, get: function () { return menu_1.Menu; } });
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,2 @@
1
+ import React from "react";
2
+ export declare function Default(): React.JSX.Element;
@@ -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
+ }
@@ -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
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });