@ultraviolet/plus 0.10.6 → 0.11.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 (41) hide show
  1. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/Icons.js +53 -0
  2. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/ai.svg.js +20 -0
  3. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/baremetal.svg.js +28 -0
  4. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/billing.svg.js +26 -0
  5. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/compute.svg.js +26 -0
  6. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/console.svg.js +26 -0
  7. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/containers.svg.js +24 -0
  8. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/database.svg.js +22 -0
  9. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/datacenter.svg.js +26 -0
  10. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/dedicated-server.svg.js +24 -0
  11. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/dev-tools.svg.js +32 -0
  12. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/documentation.svg.js +26 -0
  13. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/iot.svg.js +30 -0
  14. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/labs.svg.js +28 -0
  15. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/managed-services.svg.js +22 -0
  16. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/network.svg.js +31 -0
  17. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/observability.svg.js +22 -0
  18. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/pin.svg.js +29 -0
  19. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/security.svg.js +26 -0
  20. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/serverless.svg.js +22 -0
  21. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/storage.svg.js +22 -0
  22. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/tools-services.svg.js +24 -0
  23. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/use-case.svg.js +26 -0
  24. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/vpc.svg.js +60 -0
  25. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/assets/web-hosting.svg.js +24 -0
  26. package/dist/@ultraviolet/icons/dist/components/CategoryIcon/index.js +27 -0
  27. package/dist/index.d.ts +95 -3
  28. package/dist/src/components/EstimateCost/Components/Item.js +1 -2
  29. package/dist/src/components/EstimateCost/EstimateCostContent.js +2 -4
  30. package/dist/src/components/EstimateCost/OverlayComponent.js +2 -4
  31. package/dist/src/components/Navigation/Navigation.js +50 -0
  32. package/dist/src/components/Navigation/NavigationContent.js +230 -0
  33. package/dist/src/components/Navigation/NavigationProvider.js +55 -0
  34. package/dist/src/components/Navigation/components/Group.js +53 -0
  35. package/dist/src/components/Navigation/components/Item.js +437 -0
  36. package/dist/src/components/Navigation/components/PinnedItems.js +30 -0
  37. package/dist/src/components/Navigation/components/Separator.js +12 -0
  38. package/dist/src/components/Navigation/constants.js +37 -0
  39. package/dist/src/components/Navigation/locales/en.js +9 -0
  40. package/dist/src/index.js +1 -0
  41. package/package.json +9 -10
@@ -0,0 +1,26 @@
1
+ import * as React from 'react';
2
+ import { memo } from 'react';
3
+
4
+ var _g;
5
+ var SvgUseCase = function SvgUseCase(props) {
6
+ return /*#__PURE__*/React.createElement("svg", props, _g || (_g = /*#__PURE__*/React.createElement("g", {
7
+ className: "UseCase"
8
+ }, /*#__PURE__*/React.createElement("g", {
9
+ className: "Group 1188"
10
+ }, /*#__PURE__*/React.createElement("path", {
11
+ fill: "#A365F6",
12
+ d: "M13.937 8.867c-.266-.091-.649-.105-1.066.062a1 1 0 0 1-.742-1.857c.84-.337 1.715-.35 2.453-.099a1 1 0 0 1-.645 1.894m0 3.1c-.266-.091-.649-.105-1.066.061a1 1 0 0 1-.742-1.857c.84-.336 1.715-.35 2.453-.098a1 1 0 0 1-.645 1.893Z",
13
+ className: "fillStrong",
14
+ clipRule: "evenodd",
15
+ fillRule: "evenodd"
16
+ }), /*#__PURE__*/React.createElement("path", {
17
+ fill: "#4F0599",
18
+ d: "M4 6.098v7.477c1.527-.356 3.24-.458 5 .132V6.4c-1.773-.86-3.482-.889-5-.303Zm7 .267v7.303c.82-.31 1.634-.48 2.462-.512a8.083 8.083 0 0 1 2.538.323V6.102c-1.576-.594-3.47-.55-5 .263M9.97 4.648c-2.56-1.275-5.16-1.222-7.417-.094A1 1 0 0 0 2 5.448v9.45a1 1 0 0 0 1.316.95c1.982-.661 4.096-.927 6.188.269a1 1 0 0 0 .992 0c1.114-.637 2.096-.927 3.042-.963.95-.037 1.939.18 3.068.664a1 1 0 0 0 1.394-.92v-9.45a1 1 0 0 0-.553-.894c-2.216-1.108-5.126-1.2-7.478.094Z",
19
+ className: "fill",
20
+ clipRule: "evenodd",
21
+ fillRule: "evenodd"
22
+ })))));
23
+ };
24
+ var Memo = /*#__PURE__*/memo(SvgUseCase);
25
+
26
+ export { Memo as ReactComponent };
@@ -0,0 +1,60 @@
1
+ import * as React from 'react';
2
+ import { memo } from 'react';
3
+
4
+ var _g;
5
+ var SvgVpc = function SvgVpc(props) {
6
+ return /*#__PURE__*/React.createElement("svg", props, _g || (_g = /*#__PURE__*/React.createElement("g", {
7
+ className: "VPC"
8
+ }, /*#__PURE__*/React.createElement("path", {
9
+ fill: "#A365F6",
10
+ d: "M9.087 10.408a.993.993 0 0 1 .51-1.32.997.997 0 0 1 1.317.503 1.008 1.008 0 0 1-.51 1.327 1.086 1.086 0 0 1-.407.082.988.988 0 0 1-.91-.592",
11
+ className: "fillStrong",
12
+ clipRule: "evenodd",
13
+ fillRule: "evenodd"
14
+ }), /*#__PURE__*/React.createElement("path", {
15
+ fill: "#4F0599",
16
+ d: "m10 2.345 6.63 3.828v7.654L10 17.655l-6.63-3.828V6.173zm0 2.31L5.37 7.327v5.346L10 15.345l4.63-2.672V7.327z",
17
+ className: "fill",
18
+ clipRule: "evenodd",
19
+ fillRule: "evenodd"
20
+ }), /*#__PURE__*/React.createElement("path", {
21
+ fill: "#A365F6",
22
+ d: "M8.63 17.112a1.49 1.49 0 0 1 .765-1.981c.753-.338 1.639 0 1.976.756a1.513 1.513 0 0 1-.764 1.99 1.63 1.63 0 0 1-.612.123c-.57 0-1.121-.326-1.365-.888",
23
+ className: "fillStrong",
24
+ clipRule: "evenodd",
25
+ fillRule: "evenodd"
26
+ }), /*#__PURE__*/React.createElement("path", {
27
+ fill: "#A365F6",
28
+ d: "M10 17.5a.5.5 0 0 1-.5-.5v-4a.5.5 0 0 1 1 0v4a.5.5 0 0 1-.5.5m4.13-3.388a1.49 1.49 0 0 1 .765-1.981c.753-.338 1.639 0 1.976.756a1.513 1.513 0 0 1-.764 1.99 1.63 1.63 0 0 1-.612.123c-.57 0-1.121-.326-1.365-.888m-11 0a1.49 1.49 0 0 1 .765-1.981c.753-.338 1.639 0 1.976.756a1.513 1.513 0 0 1-.764 1.99 1.63 1.63 0 0 1-.612.123c-.57 0-1.121-.326-1.365-.888",
29
+ className: "fillStrong",
30
+ clipRule: "evenodd",
31
+ fillRule: "evenodd"
32
+ }), /*#__PURE__*/React.createElement("path", {
33
+ fill: "#A365F6",
34
+ d: "M16.416 14.277a.5.5 0 0 1-.693.139l-3-2a.5.5 0 0 1 .554-.832l3 2a.5.5 0 0 1 .139.693m-12.832 0a.5.5 0 0 1 .139-.693l3-2a.5.5 0 1 1 .554.832l-3 2a.5.5 0 0 1-.693-.139M14.13 7.112a1.49 1.49 0 0 1 .765-1.981c.753-.338 1.639 0 1.976.756a1.513 1.513 0 0 1-.764 1.99 1.63 1.63 0 0 1-.612.123c-.57 0-1.121-.326-1.365-.888",
35
+ className: "fillStrong",
36
+ clipRule: "evenodd",
37
+ fillRule: "evenodd"
38
+ }), /*#__PURE__*/React.createElement("path", {
39
+ fill: "#A365F6",
40
+ d: "M16.416 5.723a.5.5 0 0 1-.139.693l-3 2a.5.5 0 1 1-.554-.832l3-2a.5.5 0 0 1 .693.139M3.13 7.112a1.49 1.49 0 0 1 .765-1.981c.753-.338 1.639 0 1.976.756a1.513 1.513 0 0 1-.764 1.99A1.63 1.63 0 0 1 4.495 8c-.57 0-1.121-.326-1.365-.888",
41
+ className: "fillStrong",
42
+ clipRule: "evenodd",
43
+ fillRule: "evenodd"
44
+ }), /*#__PURE__*/React.createElement("path", {
45
+ fill: "#A365F6",
46
+ d: "M3.584 5.723a.5.5 0 0 1 .693-.139l3 2a.5.5 0 0 1-.554.832l-3-2a.5.5 0 0 1-.139-.693M8.63 4.112a1.49 1.49 0 0 1 .765-1.981c.753-.338 1.639 0 1.976.756a1.513 1.513 0 0 1-.764 1.99A1.63 1.63 0 0 1 9.995 5c-.57 0-1.121-.326-1.365-.888",
47
+ className: "fillStrong",
48
+ clipRule: "evenodd",
49
+ fillRule: "evenodd"
50
+ }), /*#__PURE__*/React.createElement("path", {
51
+ fill: "#A365F6",
52
+ d: "M10 3.5a.5.5 0 0 1 .5.5v3a.5.5 0 0 1-1 0V4a.5.5 0 0 1 .5-.5",
53
+ className: "fillStrong",
54
+ clipRule: "evenodd",
55
+ fillRule: "evenodd"
56
+ }))));
57
+ };
58
+ var Memo = /*#__PURE__*/memo(SvgVpc);
59
+
60
+ export { Memo as ReactComponent };
@@ -0,0 +1,24 @@
1
+ import * as React from 'react';
2
+ import { memo } from 'react';
3
+
4
+ var _g;
5
+ var SvgWebHosting = function SvgWebHosting(props) {
6
+ return /*#__PURE__*/React.createElement("svg", props, _g || (_g = /*#__PURE__*/React.createElement("g", {
7
+ className: "Webhosting"
8
+ }, /*#__PURE__*/React.createElement("g", {
9
+ className: "Webhosting-icon"
10
+ }, /*#__PURE__*/React.createElement("path", {
11
+ fill: "#4F0599",
12
+ d: "M3.534 8.221A6.394 6.394 0 0 1 9.92 1.833a6.394 6.394 0 0 1 6.388 6.388c0 2.7-1.36 5.023-2.628 6.618a17.014 17.014 0 0 1-2.728 2.735l-1.031.788-1.032-.788a17.01 17.01 0 0 1-2.729-2.735C4.893 13.244 3.534 10.92 3.534 8.22ZM9.92 4.167A4.06 4.06 0 0 0 5.867 8.22c0 1.93.99 3.743 2.12 5.165a15.407 15.407 0 0 0 1.934 2.016c.44-.38 1.193-1.083 1.934-2.015 1.131-1.423 2.12-3.236 2.12-5.166a4.06 4.06 0 0 0-4.054-4.054Z",
13
+ className: "fill",
14
+ clipRule: "evenodd",
15
+ fillRule: "evenodd"
16
+ }), /*#__PURE__*/React.createElement("path", {
17
+ fill: "#A365F6",
18
+ d: "M9.84 6.043a2.025 2.025 0 1 0 0 4.05 2.025 2.025 0 0 0 0-4.05",
19
+ className: "fillStrong"
20
+ })))));
21
+ };
22
+ var Memo = /*#__PURE__*/memo(SvgWebHosting);
23
+
24
+ export { Memo as ReactComponent };
@@ -0,0 +1,27 @@
1
+ import _styled from '@emotion/styled/base';
2
+ import { CATEGORY_ICONS } from './Icons.js';
3
+ import { jsx } from '@emotion/react/jsx-runtime';
4
+
5
+ const StyledIcon = component => /*#__PURE__*/_styled(component, {
6
+ target: "e1wwql4e0"
7
+ })(".fill{fill:", ({
8
+ theme
9
+ }) => theme.colors.other.icon.category.primary.fill, ";}.fillStrong{fill:", ({
10
+ theme
11
+ }) => theme.colors.other.icon.category.primary.fillStrong, ";}");
12
+ /**
13
+ * CategoryIcon component is used to render category icons, those icons are more complex than system icons
14
+ * as they involve multiple colors that changes depending on theme.
15
+ */
16
+ const CategoryIcon = ({
17
+ name
18
+ }) => {
19
+ const Icon = StyledIcon(CATEGORY_ICONS[name]);
20
+ return jsx(Icon, {
21
+ width: "20",
22
+ height: "20",
23
+ viewBox: "0 0 20 20"
24
+ });
25
+ };
26
+
27
+ export { CategoryIcon };
package/dist/index.d.ts CHANGED
@@ -66,7 +66,7 @@ type CodeEditorProps = {
66
66
  };
67
67
  declare const CodeEditor: ({ title, value, onChange, extensions, onBlur, height, readOnly, }: CodeEditorProps) => _emotion_react_jsx_runtime.JSX.Element;
68
68
 
69
- declare const _default: {
69
+ declare const _default$1: {
70
70
  readonly 'estimate.cost.description': "This summary provides a cost estimation based on your configuration, the amount of time you expect to use the resource for, and the scale of your expected usage. For the purposes of this calculation, we consider that 1 month equals to 730 hours.";
71
71
  readonly 'estimate.cost.label': "Estimated cost";
72
72
  readonly 'estimate.cost.beta.free': "Free During Beta";
@@ -182,7 +182,7 @@ type EstimateCostProps = {
182
182
  /**
183
183
  * Locales for the component. By default, it will use the english locales.
184
184
  */
185
- locales?: typeof _default;
185
+ locales?: typeof _default$1;
186
186
  /**
187
187
  * Defines the currency to be shown in the component.
188
188
  * To find out all currencies checkout https://en.wikipedia.org/wiki/ISO_4217#List_of_ISO_4217_currency_codes section "Code" of the table.
@@ -418,6 +418,98 @@ type MessageProps = {
418
418
  align?: 'left' | 'right';
419
419
  };
420
420
 
421
+ declare const _default: {
422
+ readonly 'navigation.pin.tooltip': "Pin product";
423
+ readonly 'navigation.unpin.tooltip': "Unpin product";
424
+ readonly 'navigation.pinned.item.group.label': "Pinned items";
425
+ readonly 'navigation.expand.button': "Expand sidebar";
426
+ readonly 'navigation.collapse.button': "Collapse sidebar";
427
+ };
428
+
429
+ type NavigationProps = {
430
+ children: ReactNode;
431
+ /**
432
+ * The logo to be displayed in header of the navigation
433
+ * It can be a component or a function. The function will retrun you
434
+ * expanded state of the navigation so you can decide to show/hide
435
+ * some part of your logo
436
+ */
437
+ logo?: ReactNode | ((expanded: boolean) => ReactNode);
438
+ /**
439
+ * This enable / disable the pinned feature of the navigation
440
+ * Pinned allows the use to pin (or favorite) some items in the navigation
441
+ */
442
+ pinnedFeature?: boolean;
443
+ /**
444
+ * This define how many items can be pinned at the same time.
445
+ * If you want to disable the limit just set `Infinity` to this prop
446
+ */
447
+ pinLimit?: number;
448
+ /**
449
+ * The initial pinned items. This should be an array of labels you've set on
450
+ * your `<Navigation.Item>`
451
+ */
452
+ initialPinned?: string[];
453
+ /**
454
+ * The initial expanded state of the navigation. If set to true the
455
+ * navigation will be expanded by default otherwise it will be collapsed
456
+ */
457
+ initialExpanded?: boolean;
458
+ /**
459
+ * This function is triggered when the user click on the pin/unpin button
460
+ * of an item
461
+ */
462
+ onClickPinUnpin?: (pinned: string[]) => void;
463
+ locales?: typeof _default;
464
+ /**
465
+ * This function is triggered when user click on expand button on the footer
466
+ * of the navigation. This is not triggered when the user resize the navigation
467
+ * and it automatically collapse / expand.
468
+ */
469
+ onClickExpand?: (expanded: boolean) => void;
470
+ className?: string;
471
+ /**
472
+ * It defines the initial width of the navigation.
473
+ */
474
+ width?: number;
475
+ /**
476
+ * This function is called when resize occur using the vertical bar on the left of the navigation.
477
+ */
478
+ onWidthResize?: (width: number) => void;
479
+ };
480
+ /**
481
+ * Navigation is a component that allows you to create a sidebar navigation.
482
+ * You can wrap your navigation items like `<Navigation.Item>` with your router
483
+ * to make it work in your application.
484
+ */
485
+ declare const Navigation: {
486
+ ({ children, logo, pinnedFeature, onClickPinUnpin, onClickExpand, initialPinned, initialExpanded, locales, pinLimit, width, onWidthResize, className, }: NavigationProps): _emotion_react_jsx_runtime.JSX.Element;
487
+ Group: ({ children, label }: {
488
+ children: ReactNode;
489
+ label: string;
490
+ }) => _emotion_react_jsx_runtime.JSX.Element | null;
491
+ Item: ({ children, categoryIcon, label, subLabel, badgeText, badgeSentiment, href, onClick, toggle, active, noPinButton, type, hasParents, as, disabled, noExpand, }: {
492
+ children?: ReactNode;
493
+ categoryIcon?: "security" | "console" | "database" | "pin" | "billing" | "storage" | "baremetal" | "webHosting" | "vpc" | "useCase" | "toolsServices" | "serverless" | "observability" | "network" | "managedServices" | "iot" | "documentation" | "dedicatedServer" | "datacenter" | "containers" | "compute" | "ai" | "labs" | "devTools" | undefined;
494
+ label: string;
495
+ subLabel?: string | undefined;
496
+ badgeText?: string | undefined;
497
+ badgeSentiment?: ("neutral" | "danger" | "info" | "primary" | "secondary" | "success" | "warning") | undefined;
498
+ href?: string | undefined;
499
+ onClick?: ((toggle?: boolean | undefined) => void) | undefined;
500
+ toggle?: boolean | undefined;
501
+ active?: boolean | undefined;
502
+ noPinButton?: boolean | undefined;
503
+ type?: ("default" | "pinned" | "pinnedGroup") | undefined;
504
+ hasParents?: boolean | undefined;
505
+ as?: keyof react.JSX.IntrinsicElements | undefined;
506
+ noExpand?: boolean | undefined;
507
+ disabled?: boolean | undefined;
508
+ }) => _emotion_react_jsx_runtime.JSX.Element | null;
509
+ PinnedItems: () => _emotion_react_jsx_runtime.JSX.Element | null;
510
+ Separator: () => _emotion_react_jsx_runtime.JSX.Element;
511
+ };
512
+
421
513
  type FAQProps = {
422
514
  description: string;
423
515
  productIconName?: ComponentProps<typeof ProductIcon>['name'];
@@ -475,4 +567,4 @@ declare const SteppedListContainer: {
475
567
  }) => _emotion_react_jsx_runtime.JSX.Element;
476
568
  };
477
569
 
478
- export { CodeEditor, ContentCard, ContentCardGroup, Conversation, CustomerSatisfaction, EstimateCost, FAQ, SteppedListContainer, _default as estimateCostDefaultLocales };
570
+ export { CodeEditor, ContentCard, ContentCardGroup, Conversation, CustomerSatisfaction, EstimateCost, FAQ, Navigation, SteppedListContainer, _default$1 as estimateCostDefaultLocales };
@@ -2,7 +2,6 @@ import _styled from '@emotion/styled/base';
2
2
  import { css } from '@emotion/react';
3
3
  import { Text, Tooltip, Stack, Icon, zoomIn, Badge } from '@ultraviolet/ui';
4
4
  import { useMemo, useState, useEffect, useCallback, useId, Children, isValidElement, cloneElement } from 'react';
5
- import flattenChildren from 'react-flatten-children';
6
5
  import { useEstimateCost } from '../EstimateCostProvider.js';
7
6
  import { useOverlay } from '../OverlayContext.js';
8
7
  import { Cell, StyledDiv, PriceCell, OverlayRow, StyledTr, StyledLeftSide } from '../componentStyle.js';
@@ -264,7 +263,7 @@ const Item = ({
264
263
  }), jsx(StyledResourceName, {
265
264
  isOverlay: isOverlay,
266
265
  animated: animated,
267
- children: isDefined ? Children.map(flattenChildren(children), child => /*#__PURE__*/isValidElement(child) ? /*#__PURE__*/cloneElement(child, {
266
+ children: isDefined ? Children.map(children, child => /*#__PURE__*/isValidElement(child) ? /*#__PURE__*/cloneElement(child, {
268
267
  itemCallback,
269
268
  amount,
270
269
  maxAmount,
@@ -1,7 +1,6 @@
1
1
  import _styled from '@emotion/styled/base';
2
2
  import { Text, Stack, Alert, Icon } from '@ultraviolet/ui';
3
3
  import { useState, useMemo, useEffect, Children, isValidElement, cloneElement } from 'react';
4
- import flattenChildren from 'react-flatten-children';
5
4
  import { useInView } from 'react-intersection-observer';
6
5
  import { CustomUnitInput } from './Components/CustomUnitInput.js';
7
6
  import { Item } from './Components/Item.js';
@@ -104,7 +103,6 @@ const EstimateCostContent = ({
104
103
  const providerValue = useMemo(() => ({
105
104
  isOverlay: false
106
105
  }), []);
107
- const list = flattenChildren(children);
108
106
  const productsCallback = useMemo(() => ({
109
107
  add: newProduct => {
110
108
  setProducts(total => {
@@ -210,8 +208,8 @@ const EstimateCostContent = ({
210
208
  })]
211
209
  })
212
210
  }) : null, jsx("tbody", {
213
- children: Children.map(list, (child, index) => /*#__PURE__*/isValidElement(child) ? /*#__PURE__*/cloneElement(child, {
214
- isLastElement: index === list.length - 1,
211
+ children: Children.map(children, (child, index) => /*#__PURE__*/isValidElement(child) ? /*#__PURE__*/cloneElement(child, {
212
+ isLastElement: index === Children.count(children) - 1,
215
213
  productsCallback,
216
214
  iteration,
217
215
  discount: discount && !child.props.discount ? discount : child.props.discount
@@ -1,7 +1,6 @@
1
1
  import _styled from '@emotion/styled/base';
2
2
  import { Stack, Icon } from '@ultraviolet/ui';
3
3
  import { useMemo, Children, isValidElement, cloneElement } from 'react';
4
- import flattenChildren from 'react-flatten-children';
5
4
  import { LineThrough } from './Components/LineThrough.js';
6
5
  import { Strong } from './Components/Strong.js';
7
6
  import { useEstimateCost } from './EstimateCostProvider.js';
@@ -55,7 +54,6 @@ const OverlayComponent = ({
55
54
  const value = useMemo(() => ({
56
55
  isOverlay: true
57
56
  }), []);
58
- const list = flattenChildren(children);
59
57
  const totalOverlayPrice = {
60
58
  days: totalPrice.maxOverlayHourly * multiplier.days,
61
59
  hours: totalPrice.maxOverlayHourly,
@@ -81,9 +79,9 @@ const OverlayComponent = ({
81
79
  disabled: disableOverlayLeft,
82
80
  children: locales['estimate.cost.submit.label']
83
81
  })
84
- }) : null, Children.map(list, (child, index) => /*#__PURE__*/isValidElement(child) ? /*#__PURE__*/cloneElement(child, {
82
+ }) : null, Children.map(children, (child, index) => /*#__PURE__*/isValidElement(child) ? /*#__PURE__*/cloneElement(child, {
85
83
  isFirstElement: index === 0,
86
- isLastElement: index === list.length - 1
84
+ isLastElement: index === Children.count(children) - 1
87
85
  }) : null), jsxs(OverlayRow, {
88
86
  children: [jsxs(Stack, {
89
87
  direction: "row",
@@ -0,0 +1,50 @@
1
+ import { NavigationContent } from './NavigationContent.js';
2
+ import { NavigationProvider } from './NavigationProvider.js';
3
+ import { Group } from './components/Group.js';
4
+ import { Item } from './components/Item.js';
5
+ import { PinnedItems } from './components/PinnedItems.js';
6
+ import { Separator } from './components/Separator.js';
7
+ import { NAVIGATION_WIDTH } from './constants.js';
8
+ import NavigationLocales from './locales/en.js';
9
+ import { jsx } from '@emotion/react/jsx-runtime';
10
+
11
+ /**
12
+ * Navigation is a component that allows you to create a sidebar navigation.
13
+ * You can wrap your navigation items like `<Navigation.Item>` with your router
14
+ * to make it work in your application.
15
+ */
16
+ const Navigation = ({
17
+ children,
18
+ logo,
19
+ pinnedFeature = false,
20
+ onClickPinUnpin,
21
+ onClickExpand,
22
+ initialPinned,
23
+ initialExpanded = true,
24
+ locales = NavigationLocales,
25
+ pinLimit = 7,
26
+ width = NAVIGATION_WIDTH,
27
+ onWidthResize,
28
+ className
29
+ }) => jsx(NavigationProvider, {
30
+ onClickPinUnpin: onClickPinUnpin,
31
+ pinnedFeature: pinnedFeature,
32
+ locales: locales,
33
+ initialPinned: initialPinned,
34
+ pinLimit: pinLimit,
35
+ initialExpanded: initialExpanded,
36
+ children: jsx(NavigationContent, {
37
+ onClickExpand: onClickExpand,
38
+ logo: logo,
39
+ className: className,
40
+ width: width,
41
+ onWidthResize: onWidthResize,
42
+ children: children
43
+ })
44
+ });
45
+ Navigation.Group = Group;
46
+ Navigation.Item = Item;
47
+ Navigation.PinnedItems = PinnedItems;
48
+ Navigation.Separator = Separator;
49
+
50
+ export { Navigation };
@@ -0,0 +1,230 @@
1
+ import _styled from '@emotion/styled/base';
2
+ import { Tooltip, Button, Stack } from '@ultraviolet/ui';
3
+ import { useRef, useCallback, useState, useEffect } from 'react';
4
+ import { useNavigation } from './NavigationProvider.js';
5
+ import { ANIMATION_DURATION, NAVIGATION_MAX_WIDTH, NAVIGATION_MIN_WIDTH, NAVIGATION_COLLASPED_WIDTH } from './constants.js';
6
+ import { jsxs, jsx } from '@emotion/react/jsx-runtime';
7
+
8
+ function _EMOTION_STRINGIFIED_CSS_ERROR__() { return "You have tried to stringify object returned from `css` function. It isn't supposed to be used directly (e.g. as value of the `className` prop), but rather handed to emotion so it can handle it (e.g. as value of `css` prop)."; }
9
+ const StyledNav = /*#__PURE__*/_styled("nav", {
10
+ target: "esezfu57"
11
+ })("display:flex;flex-direction:row;position:relative;border-right:1px solid ", ({
12
+ theme
13
+ }) => theme.colors.neutral.borderWeak, ";");
14
+ const Container = /*#__PURE__*/_styled('div', {
15
+ shouldForwardProp: prop => !['width'].includes(prop),
16
+ target: "esezfu56"
17
+ })("background:", ({
18
+ theme
19
+ }) => theme.colors.neutral.background, ";display:flex;flex-direction:column;width:", ({
20
+ width
21
+ }) => width, "px;&[data-expanded='true'][data-animation='false']{max-width:", NAVIGATION_MAX_WIDTH, "px;min-width:", NAVIGATION_MIN_WIDTH, "px;}&[data-expanded='false']{width:", NAVIGATION_COLLASPED_WIDTH, "px;}&[data-animation='expand']{transition:width ", ANIMATION_DURATION, "ms ease-in-out;width:", ({
22
+ width
23
+ }) => width, "px;}&[data-animation='collapse']{transition:width ", ANIMATION_DURATION, "ms ease-in-out;width:", NAVIGATION_COLLASPED_WIDTH, "px;}");
24
+ const StickyFooter = /*#__PURE__*/_styled("div", {
25
+ target: "esezfu55"
26
+ })("display:flex;width:100%;background:", ({
27
+ theme
28
+ }) => theme.colors.neutral.background, ";border-top:1px solid ", ({
29
+ theme
30
+ }) => theme.colors.neutral.borderWeak, ";padding:", ({
31
+ theme
32
+ }) => `${theme.space['1']} ${theme.space['2']}`, ";transition:justify-content ", ANIMATION_DURATION, "ms ease-in-out;box-shadow:", ({
33
+ theme
34
+ }) => theme.shadows.defaultShadow, ";transition:box-shadow 230ms ease-in-out;justify-content:flex-end;&[data-has-overflow-style='false']{box-shadow:none;border:none;}");
35
+ const Header = /*#__PURE__*/_styled("div", {
36
+ target: "esezfu54"
37
+ })("background:", ({
38
+ theme
39
+ }) => theme.colors.neutral.background, ";");
40
+ const LogoContainer = /*#__PURE__*/_styled(Stack, {
41
+ target: "esezfu53"
42
+ })("margin:", ({
43
+ theme
44
+ }) => `${theme.space['3']} ${theme.space['3']} ${theme.space['2']} ${theme.space['3']}`, ";max-width:220px;height:22px;");
45
+ const ContentContainer = /*#__PURE__*/_styled("div", {
46
+ target: "esezfu52"
47
+ })(process.env.NODE_ENV === "production" ? {
48
+ name: "12is9id",
49
+ styles: "overflow:hidden;display:flex;flex-direction:column;flex-grow:1"
50
+ } : {
51
+ name: "12is9id",
52
+ styles: "overflow:hidden;display:flex;flex-direction:column;flex-grow:1",
53
+ toString: _EMOTION_STRINGIFIED_CSS_ERROR__
54
+ });
55
+ const Content = /*#__PURE__*/_styled(Stack, {
56
+ target: "esezfu51"
57
+ })("overflow-y:auto;overflow-x:hidden;flex-grow:1;&[data-is-expanded='false']{padding:", ({
58
+ theme
59
+ }) => theme.space['2'], " 0;}&[data-is-expanded='true'],&[data-animation='expand']{padding:", ({
60
+ theme
61
+ }) => theme.space['2'], ";}");
62
+ const Slider = /*#__PURE__*/_styled("div", {
63
+ target: "esezfu50"
64
+ })("background:transparent;cursor:col-resize;border:2px solid transparent;margin-right:-2px;display:flex;&:hover{border:2px solid ", ({
65
+ theme
66
+ }) => theme.colors.primary.border, ";}");
67
+ const NavigationContent = ({
68
+ children,
69
+ logo,
70
+ onClickExpand,
71
+ width,
72
+ onWidthResize,
73
+ className
74
+ }) => {
75
+ const sliderRef = useRef(null);
76
+ const navigationRef = useRef(null);
77
+ const contentRef = useRef(null);
78
+ const isScrollAtBottom = useCallback(() => {
79
+ if (contentRef.current) {
80
+ if (contentRef.current.scrollTop + contentRef.current.offsetHeight >= contentRef.current.scrollHeight) {
81
+ return false;
82
+ }
83
+ }
84
+ return true;
85
+ }, []);
86
+ const [footerHasOverflowStyle, setFooterHasOverflowStyle] = useState(isScrollAtBottom());
87
+
88
+ // This is for detecting if there is scroll on the content and set the shadow on the footer
89
+ useEffect(() => {
90
+ const scroll = () => {
91
+ const hasOverflow = isScrollAtBottom();
92
+ if (footerHasOverflowStyle !== hasOverflow) {
93
+ setFooterHasOverflowStyle(hasOverflow);
94
+ }
95
+ };
96
+ if (contentRef.current) {
97
+ contentRef.current.addEventListener('scroll', scroll);
98
+ }
99
+ return () => {
100
+ // eslint-disable-next-line react-hooks/exhaustive-deps
101
+ contentRef.current?.removeEventListener('scroll', scroll);
102
+ };
103
+ }, [footerHasOverflowStyle, isScrollAtBottom]);
104
+
105
+ // This will set the shadow on the footer when the component is mounted
106
+ useEffect(() => {
107
+ setFooterHasOverflowStyle(isScrollAtBottom());
108
+ },
109
+ // eslint-disable-next-line react-hooks/exhaustive-deps
110
+ [contentRef.current]);
111
+ const {
112
+ expanded,
113
+ setExpanded,
114
+ animation,
115
+ setAnimation,
116
+ locales
117
+ } = useNavigation();
118
+
119
+ // This function will be triggered when expand/collapse button is clicked
120
+ const toggleExpand = useCallback(() => {
121
+ onClickExpand?.(!expanded);
122
+ if (navigationRef.current) {
123
+ navigationRef.current.style.width = '';
124
+ }
125
+ setAnimation(expanded ? 'collapse' : 'expand');
126
+ setTimeout(() => {
127
+ setExpanded();
128
+ setFooterHasOverflowStyle(isScrollAtBottom());
129
+ setAnimation(false);
130
+ }, ANIMATION_DURATION);
131
+ }, [expanded, isScrollAtBottom, onClickExpand, setAnimation, setExpanded]);
132
+
133
+ // It will handle the resize of the navigation when the user drag the vertical bar
134
+ useEffect(() => {
135
+ let prevX;
136
+ let navRect;
137
+ let shouldCollapseOnMouseUp = false;
138
+ let shouldExpandOnMouseUp = false;
139
+ const mouseMove = event => {
140
+ if (prevX !== undefined) {
141
+ const navWidth = navRect?.width ?? 0;
142
+ const newWidth = navWidth + (event.clientX - prevX);
143
+ if (navigationRef.current && expanded) {
144
+ navigationRef.current.style.width = `${newWidth}px`;
145
+ }
146
+ if (newWidth <= NAVIGATION_MIN_WIDTH) {
147
+ shouldCollapseOnMouseUp = true;
148
+ } else {
149
+ shouldCollapseOnMouseUp = false;
150
+ }
151
+ if (newWidth >= NAVIGATION_COLLASPED_WIDTH && !expanded) {
152
+ shouldExpandOnMouseUp = true;
153
+ } else {
154
+ shouldExpandOnMouseUp = false;
155
+ }
156
+ }
157
+ };
158
+ const mousedown = event => {
159
+ document.body.style.pointerEvents = 'none';
160
+ document.body.style.userSelect = 'none';
161
+ prevX = event.clientX;
162
+ navRect = navigationRef.current?.getBoundingClientRect();
163
+ const mouseup = () => {
164
+ if (shouldCollapseOnMouseUp || shouldExpandOnMouseUp) {
165
+ toggleExpand();
166
+ }
167
+ if (navigationRef.current) {
168
+ if (!shouldCollapseOnMouseUp && !shouldExpandOnMouseUp) {
169
+ onWidthResize?.(navigationRef.current.offsetWidth);
170
+ }
171
+ if (!expanded) {
172
+ navigationRef.current.style.width = '';
173
+ }
174
+ }
175
+ document.removeEventListener('mousemove', mouseMove);
176
+ window.removeEventListener('mouseup', mouseup);
177
+ document.body.style.pointerEvents = '';
178
+ document.body.style.userSelect = '';
179
+ };
180
+ document.addEventListener('mousemove', mouseMove);
181
+ window.addEventListener('mouseup', mouseup);
182
+ };
183
+ sliderRef.current?.addEventListener('mousedown', mousedown);
184
+ return () => {
185
+ // eslint-disable-next-line react-hooks/exhaustive-deps
186
+ sliderRef.current?.removeEventListener('mousedown', mousedown);
187
+ };
188
+ }, [expanded, onWidthResize, toggleExpand]);
189
+ return jsxs(StyledNav, {
190
+ className: className,
191
+ children: [jsxs(Container, {
192
+ ref: navigationRef,
193
+ "data-animation": animation,
194
+ "data-expanded": expanded,
195
+ width: width,
196
+ children: [jsx(Header, {
197
+ children: jsx(LogoContainer, {
198
+ justifyContent: !expanded ? 'center' : undefined,
199
+ alignItems: "start",
200
+ children: typeof logo === 'function' ? logo(animation ? false : expanded) : logo
201
+ })
202
+ }), jsxs(ContentContainer, {
203
+ children: [jsx(Content, {
204
+ ref: contentRef,
205
+ gap: 0.25,
206
+ "data-is-expanded": expanded,
207
+ "data-animation": animation,
208
+ children: children
209
+ }), jsx(StickyFooter, {
210
+ "data-has-overflow-style": footerHasOverflowStyle,
211
+ children: jsx(Tooltip, {
212
+ text: expanded ? locales['navigation.collapse.button'] : locales['navigation.expand.button'],
213
+ placement: "right",
214
+ children: jsx(Button, {
215
+ variant: "ghost",
216
+ sentiment: "neutral",
217
+ size: "small",
218
+ icon: expanded ? 'arrow-left-double' : 'arrow-right-double',
219
+ onClick: toggleExpand
220
+ })
221
+ })
222
+ })]
223
+ })]
224
+ }), jsx(Slider, {
225
+ ref: sliderRef
226
+ })]
227
+ });
228
+ };
229
+
230
+ export { NavigationContent };
@@ -0,0 +1,55 @@
1
+ import { useReducer, useState, useCallback, useMemo, createContext, useContext } from 'react';
2
+ import NavigationLocales from './locales/en.js';
3
+ import { jsx } from '@emotion/react/jsx-runtime';
4
+
5
+ const NavigationContext = /*#__PURE__*/createContext({
6
+ expanded: true,
7
+ setExpanded: () => {},
8
+ animation: false,
9
+ setAnimation: () => {},
10
+ locales: NavigationLocales,
11
+ pinItem: () => {},
12
+ unpinItem: () => {},
13
+ pinnedItems: [],
14
+ pinLimit: 7
15
+ });
16
+ const useNavigation = () => useContext(NavigationContext);
17
+ const NavigationProvider = ({
18
+ children,
19
+ pinnedFeature,
20
+ onClickPinUnpin,
21
+ initialPinned,
22
+ initialExpanded,
23
+ locales,
24
+ pinLimit
25
+ }) => {
26
+ const [expanded, setExpanded] = useReducer(state => !state, initialExpanded);
27
+ const [pinnedItems, setPinnedItems] = useState(initialPinned ?? []);
28
+ const [animation, setAnimation] = useState(false);
29
+ const pinItem = useCallback(item => {
30
+ setPinnedItems([...pinnedItems, item]);
31
+ onClickPinUnpin?.(pinnedItems);
32
+ }, [onClickPinUnpin, pinnedItems]);
33
+ const unpinItem = useCallback(item => {
34
+ setPinnedItems(pinnedItems.filter(localItem => localItem !== item));
35
+ onClickPinUnpin?.(pinnedItems);
36
+ }, [onClickPinUnpin, pinnedItems]);
37
+ const value = useMemo(() => ({
38
+ expanded,
39
+ setExpanded,
40
+ pinnedItems,
41
+ pinItem,
42
+ unpinItem,
43
+ pinnedFeature,
44
+ locales,
45
+ pinLimit,
46
+ animation,
47
+ setAnimation
48
+ }), [expanded, pinnedItems, pinItem, unpinItem, pinnedFeature, locales, pinLimit, animation]);
49
+ return jsx(NavigationContext.Provider, {
50
+ value: value,
51
+ children: children
52
+ });
53
+ };
54
+
55
+ export { NavigationContext, NavigationProvider, useNavigation };