@energysage/es-ds-components 5.1.0 → 5.3.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 (98) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/app/components/es-logo.vue +50 -0
  3. package/app/components/es-mobile-nav-content.vue +308 -0
  4. package/app/components/es-mobile-nav-custom-item.vue +9 -0
  5. package/app/components/es-mobile-nav-link.vue +62 -0
  6. package/app/components/es-mobile-nav-trigger.vue +29 -0
  7. package/app/components/es-mobile-nav.vue +219 -0
  8. package/app/components/es-mobile-sub-nav.vue +243 -0
  9. package/app/components/es-nav-cta-card.vue +113 -0
  10. package/app/components/es-sticky-bar.vue +3 -1
  11. package/app/components/icon/arrow-clockwise.vue +13 -48
  12. package/app/components/icon/arrow-counter-clockwise.vue +13 -34
  13. package/app/components/icon/arrow-cycle.vue +13 -40
  14. package/app/components/icon/arrow-down.vue +16 -0
  15. package/app/components/icon/arrow-left.vue +13 -36
  16. package/app/components/icon/arrow-right.vue +13 -36
  17. package/app/components/icon/arrow-up.vue +13 -36
  18. package/app/components/icon/battery-charging-vertical.vue +16 -0
  19. package/app/components/icon/bell.vue +13 -39
  20. package/app/components/icon/block.vue +13 -43
  21. package/app/components/icon/bolt.vue +12 -34
  22. package/app/components/icon/calculator.vue +13 -34
  23. package/app/components/icon/calendar.vue +13 -43
  24. package/app/components/icon/cart.vue +13 -43
  25. package/app/components/icon/charging-station.vue +16 -0
  26. package/app/components/icon/chat-bubble.vue +13 -37
  27. package/app/components/icon/chat-dots.vue +13 -39
  28. package/app/components/icon/check.vue +13 -36
  29. package/app/components/icon/chevron-down.vue +13 -36
  30. package/app/components/icon/chevron-horizontal.vue +13 -36
  31. package/app/components/icon/chevron-left.vue +13 -36
  32. package/app/components/icon/chevron-right.vue +13 -36
  33. package/app/components/icon/chevron-up.vue +13 -36
  34. package/app/components/icon/circle-alert.vue +13 -43
  35. package/app/components/icon/circle-check.vue +13 -43
  36. package/app/components/icon/circle-chevron-down.vue +12 -43
  37. package/app/components/icon/circle-chevron-left.vue +13 -43
  38. package/app/components/icon/circle-chevron-right.vue +13 -43
  39. package/app/components/icon/circle-chevron-up.vue +13 -43
  40. package/app/components/icon/circle-minus.vue +13 -43
  41. package/app/components/icon/circle-plus.vue +13 -43
  42. package/app/components/icon/circle-x.vue +13 -43
  43. package/app/components/icon/circle.vue +13 -39
  44. package/app/components/icon/clock.vue +13 -43
  45. package/app/components/icon/copy.vue +13 -42
  46. package/app/components/icon/envelope.vue +13 -42
  47. package/app/components/icon/eye.vue +13 -38
  48. package/app/components/icon/flag.vue +13 -34
  49. package/app/components/icon/folder.vue +13 -38
  50. package/app/components/icon/gear.vue +12 -33
  51. package/app/components/icon/hamburger.vue +13 -35
  52. package/app/components/icon/heart.vue +13 -36
  53. package/app/components/icon/house-line.vue +16 -0
  54. package/app/components/icon/image.vue +13 -42
  55. package/app/components/icon/info.vue +13 -42
  56. package/app/components/icon/light-bulb.vue +13 -33
  57. package/app/components/icon/link-out.vue +13 -35
  58. package/app/components/icon/location.vue +13 -42
  59. package/app/components/icon/lock-off.vue +13 -42
  60. package/app/components/icon/lock-on.vue +13 -45
  61. package/app/components/icon/minus.vue +13 -35
  62. package/app/components/icon/money-bag.vue +12 -33
  63. package/app/components/icon/open-envelope.vue +13 -42
  64. package/app/components/icon/paper.vue +13 -42
  65. package/app/components/icon/pencil.vue +13 -42
  66. package/app/components/icon/person-add.vue +13 -38
  67. package/app/components/icon/person-check.vue +13 -38
  68. package/app/components/icon/person-cross.vue +13 -38
  69. package/app/components/icon/person.vue +13 -38
  70. package/app/components/icon/phone.vue +13 -38
  71. package/app/components/icon/play.vue +13 -38
  72. package/app/components/icon/plus.vue +13 -35
  73. package/app/components/icon/roof.vue +12 -33
  74. package/app/components/icon/search.vue +12 -39
  75. package/app/components/icon/settings-horizontal.vue +13 -54
  76. package/app/components/icon/settings-vertical.vue +13 -54
  77. package/app/components/icon/shield-with-check.vue +12 -34
  78. package/app/components/icon/shield.vue +13 -38
  79. package/app/components/icon/shopping-bag.vue +13 -36
  80. package/app/components/icon/solar.vue +12 -33
  81. package/app/components/icon/sound-down.vue +13 -42
  82. package/app/components/icon/sound-off.vue +13 -38
  83. package/app/components/icon/sound-on.vue +13 -38
  84. package/app/components/icon/sound-up.vue +13 -38
  85. package/app/components/icon/star-empty.vue +13 -35
  86. package/app/components/icon/star-full.vue +15 -33
  87. package/app/components/icon/star-half.vue +15 -52
  88. package/app/components/icon/thumbs-up.vue +13 -36
  89. package/app/components/icon/trash-can.vue +13 -38
  90. package/app/components/icon/tree.vue +12 -33
  91. package/app/components/icon/upload.vue +12 -37
  92. package/app/components/icon/verified.vue +13 -37
  93. package/app/components/icon/wifi.vue +13 -35
  94. package/app/components/icon/x.vue +13 -35
  95. package/app/composables/breakpoints.ts +75 -0
  96. package/nuxt.config.ts +1 -1
  97. package/package.json +3 -2
  98. package/app/components/icon/arrow.vue +0 -68
package/CHANGELOG.md CHANGED
@@ -1,5 +1,33 @@
1
1
  # Changelog
2
2
 
3
+ ## [5.3.0](https://github.com/EnergySage/es-ds/compare/es-ds-components-v5.2.0...es-ds-components-v5.3.0) (2026-04-06)
4
+
5
+
6
+ ### Features
7
+
8
+ * add EsMobileNav ([#1722](https://github.com/EnergySage/es-ds/issues/1722)) ([29fcfad](https://github.com/EnergySage/es-ds/commit/29fcfada66e6386df1f58d10f783b29be416ec2e))
9
+ * added EsNavCtaCard for use in mobile/desktop navs ([#1723](https://github.com/EnergySage/es-ds/issues/1723)) ([74b3bbd](https://github.com/EnergySage/es-ds/commit/74b3bbd68c293b9606f9012b5d94ba1c06c60f59))
10
+ * added the breakpoints composable ([#1724](https://github.com/EnergySage/es-ds/issues/1724)) ([de6aa05](https://github.com/EnergySage/es-ds/commit/de6aa058e0783b865930c668b729329333994d3c))
11
+
12
+
13
+ ### Dependencies
14
+
15
+ * The following workspace dependencies were updated
16
+ * dependencies
17
+ * @energysage/es-ds-styles bumped from ^3.3.4 to ^3.4.0
18
+
19
+ ## [5.2.0](https://github.com/EnergySage/es-ds/compare/es-ds-components-v5.1.0...es-ds-components-v5.2.0) (2026-03-27)
20
+
21
+
22
+ ### Features
23
+
24
+ * update to Phosphor icon library ([#1689](https://github.com/EnergySage/es-ds/issues/1689)) ([78bd39b](https://github.com/EnergySage/es-ds/commit/78bd39b2e3325722bfa71e660961cba0de2766ed))
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * ensure shadow is not visible when sticky bar is hidden ([#1720](https://github.com/EnergySage/es-ds/issues/1720)) ([68a7396](https://github.com/EnergySage/es-ds/commit/68a73966dba1eabb170eb0e0cb8ae5a22b6057f7))
30
+
3
31
  ## [5.1.0](https://github.com/EnergySage/es-ds/compare/es-ds-components-v5.0.2...es-ds-components-v5.1.0) (2026-03-27)
4
32
 
5
33
 
@@ -0,0 +1,50 @@
1
+ <script setup lang="ts">
2
+ // for dark background, set class="text-white" on this component
3
+ interface IProps {
4
+ alt?: string;
5
+ height?: string;
6
+ width?: string;
7
+ }
8
+ withDefaults(defineProps<IProps>(), {
9
+ alt: 'EnergySage',
10
+ height: '45px',
11
+ width: '205px',
12
+ });
13
+ </script>
14
+
15
+ <template>
16
+ <svg
17
+ class="es-logo"
18
+ :style="{
19
+ height: height,
20
+ width: width,
21
+ }"
22
+ xmlns="http://www.w3.org/2000/svg"
23
+ viewBox="0 0 205 45"
24
+ width="205"
25
+ height="45"
26
+ fill="none">
27
+ <title v-if="alt">
28
+ {{ alt }}
29
+ </title>
30
+ <!-- eslint-disable -->
31
+ <path
32
+ d="M47.929 17.293c-4.967 0-8.435 3.573-8.435 8.694 0 5.12 3.533 8.533 8.593 8.533 3.42 0 5.972-1.308 6.895-2.524a.308.308 0 0 0 .029-.327l-.114-.214a.311.311 0 0 0-.466-.097 7.103 7.103 0 0 1-4.452 1.56c-2.536 0-4.866-1.33-6.077-3.47a5.229 5.229 0 0 1-.843-2.076c-.02-.129-.06-.355-.081-.605H55.22c.17 0 .308-.138.308-.307v-.832c0-4.152-2.35-8.344-7.596-8.344v.009h-.004Zm-4.78 6.534c.635-2.306 2.337-3.619 4.715-3.619 2.71 0 3.744 1.89 4.124 3.619h-8.844.004ZM83.472 17.293c-4.967 0-8.435 3.573-8.435 8.694 0 5.12 3.533 8.533 8.593 8.533 3.42 0 5.972-1.308 6.896-2.524a.309.309 0 0 0 .028-.327l-.113-.214a.311.311 0 0 0-.466-.097 7.103 7.103 0 0 1-4.453 1.56c-2.536 0-4.866-1.33-6.077-3.47a5.23 5.23 0 0 1-.843-2.076c-.02-.129-.06-.355-.08-.605h12.243c.17 0 .308-.138.308-.307v-.832c0-4.152-2.35-8.344-7.597-8.344v.009h-.004Zm-4.784 6.534c.636-2.306 2.337-3.619 4.715-3.619 2.71 0 3.744 1.89 4.125 3.619h-8.845.004ZM197.399 17.293c-4.967 0-8.435 3.573-8.435 8.694 0 5.12 3.533 8.533 8.593 8.533 3.42 0 5.972-1.308 6.896-2.524a.308.308 0 0 0 .028-.327l-.113-.214a.311.311 0 0 0-.466-.097 7.103 7.103 0 0 1-4.452 1.56c-2.537 0-4.866-1.33-6.078-3.47-.004-.008-.008-.016-.016-.024a5.219 5.219 0 0 1-.826-2.052c-.021-.129-.061-.355-.081-.605h12.243c.17 0 .308-.138.308-.307v-.832c0-4.152-2.35-8.344-7.596-8.344v.009h-.005Zm-.064 2.915c2.71 0 3.743 1.89 4.124 3.619h-8.844c.636-2.306 2.337-3.619 4.716-3.619h.004ZM27.96 29.287h-3.7a.154.154 0 0 0-.154.153v4.693c0 .084.07.153.154.153h3.7a.154.154 0 0 0 .153-.153V29.44a.154.154 0 0 0-.154-.154Z"
33
+ fill="currentColor" />
34
+ <path
35
+ d="M25.553 17.228h-9.61a.306.306 0 0 1-.304-.303V.303A.306.306 0 0 0 15.335 0H13.35a.454.454 0 0 0-.414.267L3.918 20.37l8.475-13.303v17.668l3.258-4.272h8.062L11.214 40.08V26.197H1.406L0 29.3h7.973V44.7c0 .166.134.299.3.299h3.484a.294.294 0 0 0 .256-.141l13.576-21.311 9.63 10.18-9.67-16.496.004-.004Z"
36
+ fill="#FF9133" />
37
+ <path
38
+ d="M120.195 19.182c-1.945-1.272-4.145-1.89-6.73-1.89-2.37 0-4.57.78-6.186 2.197-1.791 1.567-2.735 3.772-2.735 6.381 0 3.166 1.325 5.165 2.435 6.28 1.43 1.438 3.375 2.261 5.332 2.261 1.523 0 3.184-.634 4.391-1.635a.166.166 0 0 1 .272.129v1.963c0 3.17-1.471 4.777-4.376 4.777-2.256 0-4.29-.856-5.352-1.587-.056-.04-.129-.053-.194-.04a.25.25 0 0 0-.162.117l-1.293 2.18a.166.166 0 0 0 .049.223c1.053.714 3.415 1.974 6.952 1.974 1.455 0 3.537-.29 5.198-1.684 1.641-1.377 2.476-3.489 2.476-6.283V19.324a.171.171 0 0 0-.077-.142Zm-3.221 1.975v7.988a.19.19 0 0 1-.024.093c-.843 1.325-2.496 2.245-4.06 2.245-1.953 0-4.858-1.494-4.858-5.609 0-2.617 1.43-5.674 5.47-5.674 1.6 0 2.787.517 3.387.816a.162.162 0 0 1 .089.15v-.009h-.004ZM139.123 17.943a.249.249 0 0 0-.215-.117h-3.233a.166.166 0 0 0-.149.092l-4.996 10.347a.168.168 0 0 1-.304-.004l-4.55-10.286a.252.252 0 0 0-.226-.15h-3.148a.246.246 0 0 0-.227.351l6.466 13.966c.02.048.02.1-.004.15l-5.202 9.68a.251.251 0 0 0 .004.245.243.243 0 0 0 .214.122h3.084a.25.25 0 0 0 .222-.138l12.272-24.016a.244.244 0 0 0-.008-.242ZM145.796 24.279c-1.531-.743-2.856-1.39-2.856-2.577 0-.864.688-1.736 2.224-1.736 1.535 0 2.889 1.127 3.253 1.518a.251.251 0 0 0 .405-.044l1.155-2.04a.158.158 0 0 0-.024-.193c-.855-.885-2.743-1.947-5.142-1.947-3.034 0-5.323 1.995-5.323 4.636 0 2.504 2.289 3.691 4.307 4.741 1.6.832 2.985 1.551 2.985 2.718 0 2.177-2.021 2.346-2.641 2.346-2.236 0-3.768-1.457-4.185-1.906a.244.244 0 0 0-.207-.076.25.25 0 0 0-.186.117l-1.289 2.076a.17.17 0 0 0 .029.21c.899.831 2.909 2.29 5.972 2.29 3.549 0 5.935-2.056 5.935-5.117 0-2.876-2.342-4.01-4.404-5.016h-.008ZM167.475 19.251a11.564 11.564 0 0 0-6.409-1.954c-4.546 0-9.148 2.935-9.148 8.54 0 4.871 3.2 8.542 7.446 8.542 1.832 0 3.651-.787 4.741-1.97a.167.167 0 0 1 .291.1l.089 1.333a.162.162 0 0 0 .166.153h2.731a.164.164 0 0 0 .166-.165V19.392a.162.162 0 0 0-.073-.137v-.004Zm-3.285 1.914v7.576s0 .04-.013.06c-.474 1.281-2.272 2.678-4.173 2.678-3.16 0-4.602-2.79-4.602-5.387 0-3.4 2.326-5.868 5.534-5.868 1.268 0 2.464.384 3.173.8a.16.16 0 0 1 .081.141ZM186.1 19.182c-1.945-1.272-4.145-1.89-6.725-1.89-2.371 0-4.571.78-6.187 2.197-1.791 1.567-2.735 3.772-2.735 6.381 0 3.166 1.325 5.165 2.435 6.28 1.43 1.438 3.375 2.261 5.332 2.261 1.523 0 3.184-.634 4.392-1.635a.166.166 0 0 1 .271.129v1.963c0 3.17-1.471 4.777-4.376 4.777-2.256 0-4.29-.856-5.351-1.587-.057-.04-.126-.053-.195-.04a.25.25 0 0 0-.162.117l-1.292 2.18a.165.165 0 0 0 .048.223c1.054.714 3.416 1.974 6.952 1.974 1.455 0 3.537-.29 5.199-1.684 1.64-1.377 2.475-3.489 2.475-6.283V19.324a.169.169 0 0 0-.077-.142h-.004Zm-7.305 12.297c-1.953 0-4.858-1.494-4.858-5.609 0-2.617 1.431-5.674 5.47-5.674 1.6 0 2.787.517 3.387.816a.162.162 0 0 1 .089.15v7.983a.19.19 0 0 1-.024.093c-.843 1.325-2.496 2.245-4.06 2.245l-.004-.004ZM72.821 34.294h-3.08a.164.164 0 0 1-.165-.165l-.073-8.8v-.012a5.33 5.33 0 0 0-.584-2.16 4.783 4.783 0 0 0-1.73-1.866c-.753-.477-1.697-.715-2.8-.715-.712 0-1.251.028-1.6.084-.291.049-.579.118-.854.21a.456.456 0 0 0-.316.433V34.12a.168.168 0 0 1-.17.166c-1.102-.008-2.111-.012-3.075-.012a.164.164 0 0 1-.167-.166v-15.33c0-.072.045-.137.114-.157.636-.202 1.454-.4 2.435-.59a19.63 19.63 0 0 1 3.699-.327c1.738 0 3.261.364 4.533 1.075 1.268.71 2.265 1.688 2.954 2.895.672 1.175 1.02 2.504 1.041 3.954v8.492a.164.164 0 0 1-.166.166v.008h.004ZM104.524 18.936a.167.167 0 0 0-.069-.226c-2.95-1.623-7.48-1.006-10.53-.085a.161.161 0 0 0-.113.158v15.33c0 .092.073.165.166.165.964 0 1.977.008 3.075.012.093 0 .17-.073.17-.165V21.307c0-.198.126-.372.312-.432.875-.271 1.305-.283 2.455-.295 1.102 0 2.046.242 2.8.715.129.08.308.258.425.064l1.305-2.427h.004v.004Z"
39
+ fill="currentColor" />
40
+ <!-- eslint-enable -->
41
+ </svg>
42
+ </template>
43
+
44
+ <style lang="scss" scoped>
45
+ @use '@energysage/es-ds-styles/scss/variables';
46
+
47
+ .es-logo {
48
+ color: variables.$blue-900;
49
+ }
50
+ </style>
@@ -0,0 +1,308 @@
1
+ <script setup lang="ts">
2
+ import { NavigationMenuContent, NavigationMenuList } from 'reka-ui';
3
+ import type { ShallowRef } from 'vue';
4
+
5
+ const mobileNavParentElement = useTemplateRef('mobileNavParentElement');
6
+
7
+ // reactive flags set by hidden sr-only buttons in submenus to simulate focus on visible header buttons
8
+ const backButtonSimulatedFocus = ref(false);
9
+ const closeButtonSimulatedFocus = ref(false);
10
+
11
+ // injected variables provided from EsMobileNav
12
+ const animationDuration = inject('animationDuration', 200);
13
+ const closeMenu: (...args: any[]) => void = inject('closeMenu', () => {});
14
+ const currentDepth = inject(
15
+ 'currentDepth',
16
+ computed(() => 0),
17
+ );
18
+ const displayedName = inject('displayedName', '');
19
+ const from = inject('from', ref<'left' | 'right'>('left'));
20
+ const goBack = inject('goBack', () => {});
21
+ const width = inject('width', ref(400));
22
+ const widthPx = inject(
23
+ 'widthPx',
24
+ computed(() => '400px'),
25
+ );
26
+
27
+ // derived values for CSS custom properties set directly on the element
28
+ const animationDurationMs = `${animationDuration}ms`;
29
+ const contentPaneTransformXs = computed(() => `translateX(${currentDepth.value * -100}vw)`);
30
+ const contentPaneTransformSm = computed(() => `translateX(${currentDepth.value * -1 * width.value}px)`);
31
+ const left = computed(() => (from.value === 'left' ? '0' : 'auto'));
32
+ const menuClosedTranslateX = computed(() => (from.value === 'right' ? '100%' : '-100%'));
33
+ const registerScrollableContentArea = inject<
34
+ (areaTemplateRef: Readonly<ShallowRef<InstanceType<typeof NavigationMenuContent> | null>>) => void
35
+ >('registerScrollableContentArea', () => {});
36
+ const right = computed(() => (from.value === 'right' ? '0' : 'auto'));
37
+
38
+ // directional animation of the submenu titles as you're going down and back up
39
+ const direction = ref(1);
40
+ const transitionName = computed(() =>
41
+ direction.value > 0 ? 'es-mobile-nav-title-forward' : 'es-mobile-nav-title-back',
42
+ );
43
+ watch(
44
+ currentDepth,
45
+ (newVal, oldVal) => {
46
+ direction.value = newVal > oldVal ? 1 : -1;
47
+ },
48
+ { flush: 'sync' },
49
+ );
50
+
51
+ const focusOutside = (e: any) => {
52
+ // if focus is on something outside of the overall menu, close it
53
+ if (!isElementWithinMenu(e.target)) {
54
+ closeMenu();
55
+ }
56
+ };
57
+
58
+ const isElementWithinMenu = (element: any) => {
59
+ if (!mobileNavParentElement?.value?.$el) {
60
+ return false;
61
+ }
62
+
63
+ const parent: Node = mobileNavParentElement.value.$el;
64
+ return parent.contains(element);
65
+ };
66
+
67
+ onMounted(() => {
68
+ // give EsMobileNav a reference to the scrollable content area so it can set the scroll position
69
+ registerScrollableContentArea(mobileNavParentElement);
70
+ });
71
+
72
+ provide('backButtonSimulatedFocus', backButtonSimulatedFocus);
73
+ provide('closeButtonSimulatedFocus', closeButtonSimulatedFocus);
74
+ provide('isElementWithinMenu', isElementWithinMenu);
75
+ </script>
76
+
77
+ <template>
78
+ <!-- teleported to body to escape any ancestor stacking context (e.g. es-sticky-bar) -->
79
+ <teleport to="body">
80
+ <navigation-menu-content
81
+ v-bind="$attrs"
82
+ ref="mobileNavParentElement"
83
+ class="es-mobile-nav-content bg-white d-flex flex-column pb-50 position-fixed px-50"
84
+ :style="{
85
+ '--es-mobile-nav-animation-duration': animationDurationMs,
86
+ '--es-mobile-nav-closed-translate-x': menuClosedTranslateX,
87
+ '--es-mobile-nav-content-pane-transform-sm': contentPaneTransformSm,
88
+ '--es-mobile-nav-content-pane-transform-xs': contentPaneTransformXs,
89
+ '--es-mobile-nav-left': left,
90
+ '--es-mobile-nav-right': right,
91
+ '--es-mobile-nav-width': widthPx,
92
+ }"
93
+ @focus-outside.prevent.stop="focusOutside"
94
+ @interact-outside.prevent.stop
95
+ @pointer-down-outside.prevent.stop>
96
+ <div
97
+ class="es-mobile-nav-content-header align-items-center bg-white d-flex flex-shrink-0 justify-content-center position-sticky w-100">
98
+ <!-- back button -->
99
+ <transition name="es-mobile-nav-back">
100
+ <es-button
101
+ v-if="currentDepth > 0"
102
+ class="es-mobile-nav-back-button p-50 position-absolute text-blue-900"
103
+ :class="{ focus: backButtonSimulatedFocus }"
104
+ inline
105
+ variant="link"
106
+ @click="goBack">
107
+ <icon-arrow-left aria-hidden="true" />
108
+ <span class="sr-only">back</span>
109
+ </es-button>
110
+ </transition>
111
+
112
+ <!-- logo or subnav title, with smooth transition between them -->
113
+ <div class="es-mobile-nav-title-outer-container">
114
+ <transition :name="transitionName">
115
+ <div
116
+ :key="displayedName || 'logo'"
117
+ class="es-mobile-nav-title-inner-container align-items-center d-flex justify-content-center">
118
+ <div
119
+ v-if="displayedName"
120
+ class="font-weight-bolder">
121
+ {{ displayedName }}
122
+ </div>
123
+ <es-logo
124
+ v-else
125
+ alt=""
126
+ aria-hidden="true"
127
+ height="30px"
128
+ width="135px" />
129
+ </div>
130
+ </transition>
131
+ </div>
132
+
133
+ <!-- close button -->
134
+ <es-button
135
+ class="es-mobile-nav-close-button p-50 position-absolute text-blue-900"
136
+ :class="{ focus: closeButtonSimulatedFocus }"
137
+ inline
138
+ variant="link"
139
+ @click="closeMenu">
140
+ <icon-x />
141
+ <span class="sr-only">close</span>
142
+ </es-button>
143
+ </div>
144
+ <div
145
+ class="es-mobile-nav-content-pane d-flex flex-column flex-grow-1 justify-content-between position-relative">
146
+ <navigation-menu-list class="list-unstyled">
147
+ <slot />
148
+ </navigation-menu-list>
149
+ <div class="es-mobile-nav-content-footer">
150
+ <slot name="footer" />
151
+ </div>
152
+ </div>
153
+ </navigation-menu-content>
154
+ </teleport>
155
+ </template>
156
+
157
+ <!-- unscoped: targets Reka UI-rendered elements that don't receive Vue's scoped attribute -->
158
+ <style lang="scss">
159
+ @use '@energysage/es-ds-styles/scss/mixins/breakpoints' as breakpoints;
160
+ @use '@energysage/es-ds-styles/scss/variables';
161
+
162
+ @keyframes esMobileNavOpen {
163
+ from {
164
+ transform: translateX(var(--es-mobile-nav-closed-translate-x));
165
+ }
166
+ to {
167
+ transform: translateX(0);
168
+ }
169
+ }
170
+
171
+ @keyframes esMobileNavClose {
172
+ from {
173
+ transform: translateX(0);
174
+ }
175
+ to {
176
+ transform: translateX(var(--es-mobile-nav-closed-translate-x));
177
+ }
178
+ }
179
+
180
+ .es-mobile-nav-content {
181
+ --es-mobile-nav-header-height: 88px;
182
+
183
+ bottom: 0;
184
+ box-shadow: 0 0 6px 0 rgba(34, 38, 51, 0.2);
185
+ left: 0;
186
+ overflow-x: hidden;
187
+ position: fixed;
188
+ right: 0;
189
+ top: 0;
190
+ z-index: 1020;
191
+
192
+ @media not (prefers-reduced-motion) {
193
+ &[data-state='open'] {
194
+ animation: esMobileNavOpen var(--es-mobile-nav-animation-duration) ease-out;
195
+ }
196
+
197
+ &[data-state='closed'] {
198
+ animation: esMobileNavClose var(--es-mobile-nav-animation-duration) ease-in;
199
+ }
200
+ }
201
+
202
+ @include breakpoints.media-breakpoint-up(sm) {
203
+ left: var(--es-mobile-nav-left);
204
+ right: var(--es-mobile-nav-right);
205
+ width: var(--es-mobile-nav-width);
206
+ }
207
+ }
208
+
209
+ .es-mobile-nav-content-header {
210
+ border-bottom: 1px solid variables.$gray-100;
211
+ height: var(--es-mobile-nav-header-height);
212
+ left: 0;
213
+ right: 0;
214
+ top: 0;
215
+ z-index: 2;
216
+ }
217
+
218
+ .es-mobile-nav-back-button {
219
+ left: variables.$spacer * 0.5;
220
+ transform: translateY(-50%);
221
+ top: 50%;
222
+
223
+ svg {
224
+ height: 20px !important;
225
+ width: 20px !important;
226
+ }
227
+ }
228
+
229
+ .es-mobile-nav-close-button {
230
+ right: variables.$spacer;
231
+ transform: translateY(-50%);
232
+ top: 50%;
233
+
234
+ svg {
235
+ height: 20px !important;
236
+ width: 20px !important;
237
+ }
238
+ }
239
+
240
+ .es-mobile-nav-content-pane {
241
+ transform: var(--es-mobile-nav-content-pane-transform-xs);
242
+
243
+ @media not (prefers-reduced-motion) {
244
+ transition: transform var(--es-mobile-nav-animation-duration) ease-in-out;
245
+ }
246
+
247
+ @include breakpoints.media-breakpoint-up(sm) {
248
+ transform: var(--es-mobile-nav-content-pane-transform-sm);
249
+ }
250
+ }
251
+ </style>
252
+
253
+ <!-- scoped: targets native elements directly in this component's template -->
254
+ <style lang="scss" scoped>
255
+ .es-mobile-nav-back-enter-from,
256
+ .es-mobile-nav-back-leave-to {
257
+ opacity: 0;
258
+ }
259
+
260
+ @media not (prefers-reduced-motion) {
261
+ .es-mobile-nav-back-enter-active,
262
+ .es-mobile-nav-back-leave-active {
263
+ transition: opacity var(--es-mobile-nav-animation-duration) ease-in-out;
264
+ }
265
+ }
266
+
267
+ .es-mobile-nav-title-outer-container {
268
+ /* necessary to keep the logo/titles centered as they show/hide and animate */
269
+ display: grid;
270
+ }
271
+
272
+ .es-mobile-nav-title-inner-container {
273
+ /* necessary to keep the logo/titles centered as they show/hide and animate */
274
+ grid-area: 1 / 1;
275
+ height: 30px;
276
+ }
277
+
278
+ .es-mobile-nav-title-back-enter-from {
279
+ opacity: 0;
280
+ transform: translateX(-20px);
281
+ }
282
+
283
+ .es-mobile-nav-title-back-leave-to {
284
+ opacity: 0;
285
+ transform: translateX(20px);
286
+ }
287
+
288
+ .es-mobile-nav-title-forward-enter-from {
289
+ opacity: 0;
290
+ transform: translateX(20px);
291
+ }
292
+
293
+ .es-mobile-nav-title-forward-leave-to {
294
+ opacity: 0;
295
+ transform: translateX(-20px);
296
+ }
297
+
298
+ @media not (prefers-reduced-motion) {
299
+ .es-mobile-nav-title-forward-enter-active,
300
+ .es-mobile-nav-title-forward-leave-active,
301
+ .es-mobile-nav-title-back-enter-active,
302
+ .es-mobile-nav-title-back-leave-active {
303
+ transition:
304
+ opacity var(--es-mobile-nav-animation-duration) ease,
305
+ transform var(--es-mobile-nav-animation-duration) ease;
306
+ }
307
+ }
308
+ </style>
@@ -0,0 +1,9 @@
1
+ <script setup lang="ts">
2
+ import { NavigationMenuItem } from 'reka-ui';
3
+ </script>
4
+
5
+ <template>
6
+ <navigation-menu-item class="my-150">
7
+ <slot />
8
+ </navigation-menu-item>
9
+ </template>
@@ -0,0 +1,62 @@
1
+ <script setup lang="ts">
2
+ import { NavigationMenuItem, NavigationMenuLink } from 'reka-ui';
3
+
4
+ interface IProps {
5
+ href: string;
6
+ name: string;
7
+ target?: string;
8
+ }
9
+ withDefaults(defineProps<IProps>(), {
10
+ target: '_self',
11
+ });
12
+ </script>
13
+
14
+ <template>
15
+ <navigation-menu-item>
16
+ <navigation-menu-link
17
+ as-child
18
+ @select.prevent.stop>
19
+ <a
20
+ class="es-mobile-nav-link d-block font-size-75 font-weight-bolder"
21
+ :href="href"
22
+ :target="target">
23
+ <span class="d-block p-100 rounded-sm">
24
+ {{ name }}
25
+ </span>
26
+ </a>
27
+ </navigation-menu-link>
28
+ </navigation-menu-item>
29
+ </template>
30
+
31
+ <style lang="scss" scoped>
32
+ @use '@energysage/es-ds-styles/scss/variables';
33
+
34
+ $transition-duration: 100ms;
35
+
36
+ .es-mobile-nav-link {
37
+ /* specify color here so it can be overridden in active state */
38
+ color: variables.$dark-blue;
39
+
40
+ &:hover {
41
+ text-decoration: none;
42
+ }
43
+
44
+ @media not (prefers-reduced-motion) {
45
+ transition:
46
+ background-color $transition-duration ease-in-out,
47
+ color $transition-duration ease-in-out;
48
+
49
+ & > span {
50
+ transition: background-color $transition-duration ease-in-out;
51
+ }
52
+ }
53
+
54
+ &:active {
55
+ color: variables.$white;
56
+
57
+ & > span {
58
+ background-color: variables.$dark-blue;
59
+ }
60
+ }
61
+ }
62
+ </style>
@@ -0,0 +1,29 @@
1
+ <script setup lang="ts">
2
+ import { NavigationMenuTrigger } from 'reka-ui';
3
+
4
+ interface IProps {
5
+ unstyled?: boolean;
6
+ }
7
+ withDefaults(defineProps<IProps>(), {
8
+ unstyled: false,
9
+ });
10
+
11
+ const triggerRef = useTemplateRef<InstanceType<typeof NavigationMenuTrigger>>('triggerRef');
12
+ const registerNavTrigger = inject<(el: HTMLElement) => void>('registerNavTrigger');
13
+
14
+ onMounted(() => {
15
+ if (registerNavTrigger && triggerRef.value) {
16
+ registerNavTrigger(triggerRef.value.$el);
17
+ }
18
+ });
19
+ </script>
20
+
21
+ <template>
22
+ <!-- styles defined with :deep() in EsMobileNav due to Reka UI's rendering method -->
23
+ <navigation-menu-trigger
24
+ ref="triggerRef"
25
+ v-bind="$attrs"
26
+ :class="{ 'es-mobile-nav-trigger': !unstyled }">
27
+ <slot />
28
+ </navigation-menu-trigger>
29
+ </template>