@energysage/es-ds-components 3.0.10 → 3.1.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.
@@ -20,6 +20,7 @@ const props = defineProps({
20
20
  });
21
21
 
22
22
  const userDeterminedState: Ref<boolean | null> = ref(null);
23
+ const uniqueId = useId();
23
24
 
24
25
  // directly determines the expanded/collapsed state of the panel
25
26
  const expanded = computed(() => {
@@ -52,7 +53,7 @@ const onClick = ({ value }: { value: boolean }) => {
52
53
  <template>
53
54
  <panel
54
55
  :collapsed="!expanded"
55
- :header="expanded ? 'click to collapse content' : 'click to expand content'"
56
+ :toggle-button-props="{ 'aria-labelledby': uniqueId }"
56
57
  :pt="{
57
58
  header: 'd-flex flex-column justify-content-center position-relative py-100',
58
59
  icons: 'h-100 position-absolute w-100',
@@ -74,7 +75,9 @@ const onClick = ({ value }: { value: boolean }) => {
74
75
  toggleable
75
76
  @toggle="onClick">
76
77
  <template #header>
77
- <slot name="title" />
78
+ <div :id="uniqueId">
79
+ <slot name="title" />
80
+ </div>
78
81
  </template>
79
82
  <template #togglericon>
80
83
  <icon-chevron-down />
@@ -1,113 +1,141 @@
1
1
  <script setup lang="ts">
2
- import OverlayPanel from 'primevue/overlaypanel';
3
-
4
- const props = defineProps({
5
- target: {
6
- type: String,
7
- required: true,
8
- },
9
- triggers: {
10
- type: String,
11
- required: false,
12
- default: 'focus',
13
- },
14
- show: {
15
- type: Boolean,
16
- required: false,
17
- default: false,
18
- },
19
- variant: {
20
- type: String,
21
- required: false,
22
- default: 'dark',
23
- validator: (val: string) => ['light', 'dark'].includes(val as string),
24
- },
25
- });
2
+ import { PopoverArrow, PopoverClose, PopoverContent, PopoverPortal, PopoverRoot, PopoverTrigger } from 'reka-ui';
26
3
 
27
- const op = ref<InstanceType<typeof OverlayPanel> | null>(null);
28
- const slots = useSlots();
29
- const hasTitle = computed(() => !!slots.title);
30
- const closePanel = () => {
31
- op.value?.hide();
32
- };
33
- const showPanel = (event: Event) => {
34
- op.value?.show(event);
35
- };
36
-
37
- onMounted(() => {
38
- const targetElement = document.getElementById(props.target);
39
- if (targetElement) {
40
- const triggers = props.triggers.split(' ').map((trigger) => (trigger === 'hover' ? 'mouseover' : trigger));
41
-
42
- for (const trigger of triggers) {
43
- targetElement.addEventListener(trigger, showPanel);
44
- }
45
- }
4
+ interface IProps {
5
+ collisionPadding?: number | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>>;
6
+ show?: boolean | undefined;
7
+ side?: 'top' | 'right' | 'bottom' | 'left';
8
+ triggerClass?: string;
9
+ triggerDescription?: string;
10
+ variant?: 'light' | 'dark';
11
+ }
12
+
13
+ withDefaults(defineProps<IProps>(), {
14
+ collisionPadding: 0,
15
+ show: undefined,
16
+ side: 'top',
17
+ triggerClass: '',
18
+ triggerDescription: 'More information',
19
+ variant: 'dark',
46
20
  });
47
21
 
48
- watch(
49
- () => props.show,
50
- (newVal) => {
51
- if (newVal) {
52
- const targetElement = document.getElementById(props.target);
53
- if (targetElement) {
54
- const event = new MouseEvent('click', {
55
- view: window,
56
- bubbles: true,
57
- cancelable: true,
58
- });
59
- targetElement.dispatchEvent(event);
60
- }
61
- } else {
62
- closePanel();
63
- }
64
- },
65
- );
22
+ const emit = defineEmits(['update:show']);
66
23
  </script>
67
24
 
68
25
  <template>
69
- <overlay-panel
70
- ref="op"
71
- :dismissable="true"
72
- append-to="body"
73
- :pt="{
74
- root: {
75
- class: `popover b-popover ${props.variant === 'light' ? 'es-popover-light' : 'es-popover-dark'}`,
76
- },
77
- transition: {
78
- enterFromClass: 'es-popover-enter-from',
79
- enterActiveClass: 'es-popover-enter-active',
80
- leaveActiveClass: 'es-popover-leave-active',
81
- leaveToClass: 'es-popover-leave-to',
82
- },
83
- }">
84
- <div class="arrow"></div>
85
- <template v-if="hasTitle">
86
- <!-- Title slot content -->
87
- <h3 class="popover-header">
88
- <slot name="title" />
89
- <button
90
- class="es-popover-close p-0 float-right bg-transparent"
91
- @click="closePanel">
92
- <icon-x
93
- height="20px"
94
- width="20px" />
95
- </button>
96
- </h3>
97
- </template>
98
- <!-- Popover Content -->
99
- <div class="popover-body">
100
- <div class="d-flex">
101
- <slot />
102
- <button
103
- v-if="!hasTitle"
104
- class="es-popover-close p-0 pl-50 mb-auto bg-transparent"
105
- @click="closePanel">
26
+ <popover-root
27
+ :open="show"
28
+ @update:open="(val) => emit('update:show', val)">
29
+ <popover-trigger
30
+ class="es-popover-trigger p-0"
31
+ :class="{ [triggerClass]: true }">
32
+ <span class="sr-only">{{ triggerDescription }}</span>
33
+ <slot name="trigger" />
34
+ </popover-trigger>
35
+ <popover-portal>
36
+ <popover-content
37
+ class="es-popover-content position-relative"
38
+ :class="{
39
+ 'text-white': variant === 'dark',
40
+ 'es-popover-content--light': variant === 'light',
41
+ }"
42
+ :collision-padding="collisionPadding"
43
+ :side="side">
44
+ <popover-close
45
+ class="es-popover-close position-absolute"
46
+ :class="{ 'es-popover-close--light': variant === 'light' }">
47
+ <span class="sr-only">Close</span>
106
48
  <icon-x
107
49
  height="20px"
108
50
  width="20px" />
109
- </button>
110
- </div>
111
- </div>
112
- </overlay-panel>
51
+ </popover-close>
52
+ <popover-arrow
53
+ class="es-popover-arrow"
54
+ :class="{ 'es-popover-arrow--light': variant === 'light' }"
55
+ :height="12"
56
+ rounded
57
+ :width="22" />
58
+ <div class="pr-300">
59
+ <slot />
60
+ </div>
61
+ <slot name="cta" />
62
+ </popover-content>
63
+ </popover-portal>
64
+ </popover-root>
113
65
  </template>
66
+
67
+ <style lang="scss" scoped>
68
+ @use '@energysage/es-ds-styles/scss/mixins/border-radius' as border-radius;
69
+ @use '@energysage/es-ds-styles/scss/variables' as variables;
70
+
71
+ @keyframes fadeIn {
72
+ from {
73
+ opacity: 0;
74
+ }
75
+ to {
76
+ opacity: 1;
77
+ }
78
+ }
79
+
80
+ @keyframes fadeOut {
81
+ from {
82
+ opacity: 1;
83
+ }
84
+ to {
85
+ opacity: 0;
86
+ }
87
+ }
88
+
89
+ .es-popover-trigger {
90
+ align-items: center;
91
+ background-color: transparent;
92
+ border: none;
93
+ display: inline-flex;
94
+ justify-content: center;
95
+ vertical-align: middle;
96
+ }
97
+
98
+ :deep(.es-popover-content) {
99
+ background-color: variables.$dark-blue;
100
+ @include border-radius.border-radius(variables.$popover-border-radius);
101
+ box-shadow: variables.$popover-box-shadow;
102
+ padding: variables.$popover-header-padding-y variables.$popover-header-padding-x;
103
+ width: 250px;
104
+ z-index: variables.$zindex-popover;
105
+ }
106
+
107
+ :deep(.es-popover-content[data-state='open']) {
108
+ animation: fadeIn 150ms ease;
109
+ }
110
+
111
+ :deep(.es-popover-content[data-state='closed']) {
112
+ animation: fadeOut 150ms ease;
113
+ }
114
+
115
+ :deep(.es-popover-content--light) {
116
+ background-color: variables.$white;
117
+ }
118
+
119
+ :deep(.es-popover-arrow) {
120
+ fill: variables.$dark-blue;
121
+ }
122
+
123
+ :deep(.es-popover-arrow--light) {
124
+ fill: variables.$white;
125
+ }
126
+
127
+ :deep(.es-popover-close) {
128
+ background-color: transparent;
129
+ border: none;
130
+ color: variables.$white;
131
+ height: 28px;
132
+ right: 1rem;
133
+ padding: 0;
134
+ top: 0.75rem;
135
+ width: 28px;
136
+ }
137
+
138
+ :deep(.es-popover-close--light) {
139
+ color: variables.$blue-600;
140
+ }
141
+ </style>
@@ -1,12 +1,15 @@
1
1
  <script setup lang="ts">
2
2
  import TabPanel from 'primevue/tabpanel';
3
3
  import TabView from 'primevue/tabview';
4
+ import { Comment } from 'vue';
4
5
 
5
6
  // allow use of v-model on this component
6
7
  const model = defineModel<number>();
7
8
 
8
- // get the list of elements provided as children to the default slot
9
- const initialChildren = useSlots().default?.() || [];
9
+ // get the list of elements provided as children to the default slot - filtering out comments
10
+ const rawChildren = useSlots().default?.() || [];
11
+ const initialChildren = rawChildren.filter((child) => child.type !== Comment);
12
+
10
13
  const panels: any[] = [];
11
14
  initialChildren.forEach((child) => {
12
15
  // unless this has a v-for element and we instead need to access its children
@@ -0,0 +1,171 @@
1
+ <script setup lang="ts">
2
+ import { TooltipArrow, TooltipContent, TooltipPortal, TooltipProvider, TooltipRoot, TooltipTrigger } from 'reka-ui';
3
+
4
+ interface IProps {
5
+ collisionPadding?: number | Partial<Record<'top' | 'right' | 'bottom' | 'left', number>>;
6
+ delayDuration?: number;
7
+ show?: boolean | undefined;
8
+ side?: 'top' | 'right' | 'bottom' | 'left';
9
+ triggerClass?: string;
10
+ variant?: 'light' | 'dark';
11
+ }
12
+
13
+ withDefaults(defineProps<IProps>(), {
14
+ collisionPadding: 0,
15
+ delayDuration: 0,
16
+ show: undefined,
17
+ side: 'top',
18
+ triggerClass: '',
19
+ variant: 'dark',
20
+ });
21
+
22
+ const emit = defineEmits(['update:show']);
23
+
24
+ const deviceSupportsTouch = ref(false);
25
+ const internalShow = ref(false);
26
+
27
+ // provide a workaround for the case where scroll-flipping causes the tooltip
28
+ // to get in a state where it won't reopen on hover (allow user to click to open)
29
+ const handleClick = () => {
30
+ internalShow.value = true;
31
+ emit('update:show', true);
32
+ };
33
+
34
+ // keep the internal boolean up to date when the state changes
35
+ // and also emit the update event up and out
36
+ const handleUpdateOpen = (val: boolean) => {
37
+ internalShow.value = val;
38
+ emit('update:show', val);
39
+ };
40
+
41
+ onMounted(() => {
42
+ // detect if the device supports touch events or not
43
+ const mediaQueryResult = window.matchMedia('(hover: none)') as MediaQueryList;
44
+ deviceSupportsTouch.value = mediaQueryResult?.matches || false;
45
+ });
46
+ </script>
47
+
48
+ <template>
49
+ <tooltip-provider
50
+ v-if="!deviceSupportsTouch"
51
+ :delay-duration="delayDuration"
52
+ :disable-closing-trigger="true">
53
+ <tooltip-root
54
+ :open="show || internalShow"
55
+ @update:open="handleUpdateOpen">
56
+ <tooltip-trigger
57
+ class="es-tooltip-trigger p-0"
58
+ :class="{ [triggerClass]: true }"
59
+ @click="handleClick">
60
+ <slot name="trigger" />
61
+ </tooltip-trigger>
62
+ <tooltip-portal>
63
+ <tooltip-content
64
+ class="es-tooltip-content"
65
+ :class="{
66
+ 'text-white': variant === 'dark',
67
+ 'es-tooltip-content--light': variant === 'light',
68
+ }"
69
+ :collision-padding="collisionPadding"
70
+ :side="side">
71
+ <tooltip-arrow
72
+ class="es-tooltip-arrow"
73
+ :class="{ 'es-tooltip-arrow--light': variant === 'light' }"
74
+ :height="12"
75
+ rounded
76
+ :width="22" />
77
+ <slot />
78
+ </tooltip-content>
79
+ </tooltip-portal>
80
+ </tooltip-root>
81
+ </tooltip-provider>
82
+ <es-popover
83
+ v-else
84
+ :collision-padding="collisionPadding"
85
+ :show="show"
86
+ :side="side"
87
+ :trigger-class="triggerClass"
88
+ :variant="variant"
89
+ @update:show="(val) => emit('update:show', val)">
90
+ <template #trigger>
91
+ <slot name="trigger" />
92
+ </template>
93
+ <slot />
94
+ </es-popover>
95
+ </template>
96
+
97
+ <style lang="scss" scoped>
98
+ @use '@energysage/es-ds-styles/scss/mixins/border-radius' as border-radius;
99
+ @use '@energysage/es-ds-styles/scss/variables' as variables;
100
+
101
+ @keyframes fadeIn {
102
+ from {
103
+ opacity: 0;
104
+ }
105
+ to {
106
+ opacity: 1;
107
+ }
108
+ }
109
+
110
+ @keyframes fadeOut {
111
+ from {
112
+ opacity: 1;
113
+ }
114
+ to {
115
+ opacity: 0;
116
+ }
117
+ }
118
+
119
+ .es-tooltip-trigger {
120
+ align-items: center;
121
+ background-color: transparent;
122
+ border: none;
123
+ display: inline-flex;
124
+ justify-content: center;
125
+ vertical-align: middle;
126
+ }
127
+
128
+ :deep(.es-tooltip-content) {
129
+ background-color: variables.$dark-blue;
130
+ @include border-radius.border-radius(variables.$popover-border-radius);
131
+ box-shadow: variables.$popover-box-shadow;
132
+ padding: variables.$popover-header-padding-y variables.$popover-header-padding-x;
133
+ width: 250px;
134
+ z-index: variables.$zindex-popover;
135
+ }
136
+
137
+ :deep(.es-tooltip-content[data-state='open']) {
138
+ animation: fadeIn 150ms ease;
139
+ }
140
+
141
+ :deep(.es-tooltip-content[data-state='closed']) {
142
+ animation: fadeOut 150ms ease;
143
+ }
144
+
145
+ :deep(.es-tooltip-content--light) {
146
+ background-color: variables.$white;
147
+ }
148
+
149
+ :deep(.es-tooltip-arrow) {
150
+ fill: variables.$dark-blue;
151
+ }
152
+
153
+ :deep(.es-tooltip-arrow--light) {
154
+ fill: variables.$white;
155
+ }
156
+
157
+ :deep(.es-tooltip-close) {
158
+ background-color: transparent;
159
+ border: none;
160
+ color: variables.$white;
161
+ height: 28px;
162
+ right: 1rem;
163
+ padding: 0;
164
+ top: 0.75rem;
165
+ width: 28px;
166
+ }
167
+
168
+ :deep(.es-tooltip-close--light) {
169
+ color: variables.$blue-600;
170
+ }
171
+ </style>
@@ -12,7 +12,7 @@ const showVideo = ref(false);
12
12
 
13
13
  // Methods
14
14
  function getVideoIdFromUrl(url: string) {
15
- const embedPattern = /https:\/\/www\.youtube\.com\/embed\/(?<videoId>[A-Za-z0-9-]+)/;
15
+ const embedPattern = /https:\/\/www\.youtube\.com\/embed\/(?<videoId>[_A-Za-z0-9-]+)/;
16
16
  const parsedUrl = embedPattern.exec(url);
17
17
  if (parsedUrl) {
18
18
  return parsedUrl?.groups?.videoId;
package/nuxt.config.ts CHANGED
@@ -76,6 +76,7 @@ export default defineNuxtConfig({
76
76
  'primevue/slider',
77
77
  'primevue/tabpanel',
78
78
  'primevue/tabview',
79
+ 'reka-ui',
79
80
  ],
80
81
  },
81
82
  },
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@energysage/es-ds-components",
3
- "version": "3.0.10",
3
+ "version": "3.1.0",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "An EnergySage Vue component library",
@@ -17,7 +17,7 @@
17
17
  "author": "EnergySage",
18
18
  "license": "MIT",
19
19
  "dependencies": {
20
- "@energysage/es-ds-styles": "^3.0.3",
20
+ "@energysage/es-ds-styles": "^3.1.0",
21
21
  "@nuxt/eslint": "^1.0.1",
22
22
  "@nuxtjs/google-fonts": "^3.2.0",
23
23
  "@vuelidate/core": "^2.0.3",
@@ -28,6 +28,7 @@
28
28
  "nuxt-primevue": "^3.0.0",
29
29
  "primevue": "^3.53.0",
30
30
  "prismjs": "^1.29.0",
31
+ "reka-ui": "^2.2.0",
31
32
  "vue": "^3.5.6"
32
33
  },
33
34
  "devDependencies": {