@energysage/es-ds-components 3.0.0-alpha.9 → 3.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +0 -3
- package/components/es-accordion-list.vue +29 -12
- package/components/es-carousel.vue +524 -69
- package/components/es-col.vue +5 -5
- package/components/es-container.vue +10 -1
- package/components/es-data-table-column.vue +20 -0
- package/components/es-data-table.vue +35 -88
- package/components/es-error-page.vue +33 -24
- package/components/es-footer.vue +32 -3
- package/components/es-segmented-control.vue +237 -0
- package/components/es-tabs.vue +10 -3
- package/components/icon/ccpa-opt-out.vue +61 -0
- package/nuxt.config.ts +6 -0
- package/package.json +2 -2
- package/types/es-carousel.ts +12 -0
- package/types/index.ts +1 -0
- package/utils/get-es-footer-content.ts +1 -0
|
@@ -1,5 +1,14 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
interface IProps {
|
|
3
|
+
fluid?: boolean;
|
|
4
|
+
}
|
|
5
|
+
const props = withDefaults(defineProps<IProps>(), {
|
|
6
|
+
fluid: false,
|
|
7
|
+
});
|
|
8
|
+
</script>
|
|
9
|
+
|
|
1
10
|
<template>
|
|
2
|
-
<div class="container">
|
|
11
|
+
<div :class="props.fluid ? 'container-fluid' : 'container'">
|
|
3
12
|
<slot />
|
|
4
13
|
</div>
|
|
5
14
|
</template>
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import Column from 'primevue/column';
|
|
3
|
+
import type { ComponentOptionsMixin } from 'vue';
|
|
4
|
+
|
|
5
|
+
// magic solution for wrapping Column successfully
|
|
6
|
+
// https://www.reddit.com/r/vuejs/comments/1g17u7u/comment/lvjv91h/
|
|
7
|
+
defineOptions({
|
|
8
|
+
name: 'Column',
|
|
9
|
+
extends: (Column as unknown as ComponentOptionsMixin).extends,
|
|
10
|
+
inheritAttrs: false,
|
|
11
|
+
});
|
|
12
|
+
</script>
|
|
13
|
+
|
|
14
|
+
<!-- eslint-disable vue/valid-template-root -->
|
|
15
|
+
<template>
|
|
16
|
+
<!--
|
|
17
|
+
While this file needs to exist to keep Nuxt happy, it doesn't actually do anything.
|
|
18
|
+
Instances of es-data-table-column are placed inside es-data-table, and es-data-table handles their display.
|
|
19
|
+
-->
|
|
20
|
+
</template>
|
|
@@ -1,75 +1,38 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import DataTable from 'primevue/datatable';
|
|
3
|
-
import Column from 'primevue/column';
|
|
4
3
|
|
|
5
4
|
const props = defineProps({
|
|
6
|
-
fields: {
|
|
7
|
-
type: Array,
|
|
8
|
-
default: null,
|
|
9
|
-
},
|
|
10
5
|
items: {
|
|
11
6
|
type: Array,
|
|
12
7
|
required: true,
|
|
13
8
|
},
|
|
14
|
-
|
|
9
|
+
scrollable: {
|
|
15
10
|
type: Boolean,
|
|
16
11
|
default: false,
|
|
17
12
|
},
|
|
18
|
-
|
|
19
|
-
type:
|
|
20
|
-
default:
|
|
13
|
+
scrollHeight: {
|
|
14
|
+
type: String,
|
|
15
|
+
default: '',
|
|
21
16
|
},
|
|
22
17
|
});
|
|
23
18
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
// Code is taken from BootStrap Vue to implemnt <b-table> feature that automatically
|
|
32
|
-
// samples the first row to extract field name and "humanize" by converting kebab-case,
|
|
33
|
-
// snake_case, and camelCase to individual words and capitalizes each word.
|
|
34
|
-
// Doc: https://bootstrap-vue.org/docs/components/table
|
|
35
|
-
// Source Code: https://github.com/bootstrap-vue/bootstrap-vue/blob/5173dd19f6f46dc9d125cd7233fb59ccd2ef9296/src/utils/string.js#L30
|
|
36
|
-
const startCase = (str: string) =>
|
|
37
|
-
str
|
|
38
|
-
.replace(/_/g, ' ')
|
|
39
|
-
.replace(/([a-z])([A-Z])/g, (_, $1, $2) => `${$1} ${$2}`)
|
|
40
|
-
.replace(/(\s|^)(\w)/g, (_, $1, $2) => $1 + $2.toUpperCase());
|
|
41
|
-
|
|
42
|
-
const label = (col: any) => {
|
|
43
|
-
if (typeof col === 'string') {
|
|
44
|
-
return startCase(col);
|
|
19
|
+
// avoid passing in default scrollHeight value when scrollable is false
|
|
20
|
+
// otherwise it will limit the height when we don't want it to
|
|
21
|
+
const defaultScrollHeight = computed(() => {
|
|
22
|
+
if (props.scrollable) {
|
|
23
|
+
return props.scrollHeight || '305px';
|
|
45
24
|
}
|
|
46
|
-
return col?.label ? col.label : startCase(col?.key);
|
|
47
|
-
};
|
|
48
25
|
|
|
49
|
-
const columns = computed(() => {
|
|
50
|
-
if (props.fields) {
|
|
51
|
-
return props.fields;
|
|
52
|
-
}
|
|
53
|
-
// @ts-expect-error not sure
|
|
54
|
-
return Object.keys(props.items[0]);
|
|
55
|
-
});
|
|
56
|
-
|
|
57
|
-
const scrollHeight = computed(() => {
|
|
58
|
-
if (props.stickyHeader) {
|
|
59
|
-
if (typeof props.stickyHeader === 'string') {
|
|
60
|
-
return props.stickyHeader;
|
|
61
|
-
}
|
|
62
|
-
return '300px';
|
|
63
|
-
}
|
|
64
26
|
return '';
|
|
65
27
|
});
|
|
66
28
|
</script>
|
|
29
|
+
|
|
67
30
|
<template>
|
|
68
31
|
<data-table
|
|
69
|
-
|
|
32
|
+
class="border table__data-table table-spacing table"
|
|
70
33
|
:pt="{
|
|
71
34
|
table: {
|
|
72
|
-
class: 'font-size-75 font-size-md-100',
|
|
35
|
+
class: 'font-size-75 font-size-md-100 w-100',
|
|
73
36
|
style: 'border-collapse: separate; border-spacing: 0;',
|
|
74
37
|
},
|
|
75
38
|
wrapper: {
|
|
@@ -79,50 +42,34 @@ const scrollHeight = computed(() => {
|
|
|
79
42
|
style: 'top: 0px; z-index: 2',
|
|
80
43
|
},
|
|
81
44
|
}"
|
|
82
|
-
:
|
|
83
|
-
|
|
84
|
-
:
|
|
85
|
-
|
|
86
|
-
<column
|
|
87
|
-
v-for="(col, index) of columns"
|
|
88
|
-
:key="col?.key ? col.key : col"
|
|
89
|
-
:field="col?.key ? col.key : col"
|
|
90
|
-
:header="label(col)"
|
|
91
|
-
:sortable="col?.sortable"
|
|
92
|
-
:frozen="checkFrozenRow(index)"
|
|
93
|
-
align-frozen="left"
|
|
94
|
-
:pt="{
|
|
95
|
-
sortIcon: {
|
|
96
|
-
style: 'display: none',
|
|
97
|
-
},
|
|
98
|
-
headerCell: (options) => ({
|
|
99
|
-
class: [
|
|
100
|
-
{
|
|
101
|
-
sortable: options.props.sortable,
|
|
102
|
-
stickyFirstColHeader: options.props.frozen,
|
|
103
|
-
},
|
|
104
|
-
],
|
|
105
|
-
}),
|
|
106
|
-
bodyCell: (options) => ({
|
|
107
|
-
class: [
|
|
108
|
-
{
|
|
109
|
-
stickyFirstCol: options.props.frozen,
|
|
110
|
-
},
|
|
111
|
-
],
|
|
112
|
-
}),
|
|
113
|
-
}">
|
|
114
|
-
</column>
|
|
45
|
+
:scrollable="scrollable"
|
|
46
|
+
:scroll-height="defaultScrollHeight"
|
|
47
|
+
:value="items">
|
|
48
|
+
<slot />
|
|
115
49
|
</data-table>
|
|
116
50
|
</template>
|
|
51
|
+
|
|
117
52
|
<style scoped lang="scss">
|
|
118
|
-
:deep(
|
|
119
|
-
|
|
53
|
+
:deep(th[data-p-sortable-column='true']) {
|
|
54
|
+
cursor: pointer;
|
|
55
|
+
user-select: none;
|
|
56
|
+
|
|
57
|
+
div[data-pc-section='headercontent'] {
|
|
58
|
+
align-items: center;
|
|
59
|
+
display: flex;
|
|
60
|
+
}
|
|
61
|
+
span[data-pc-section='sort'] {
|
|
62
|
+
display: inline-block;
|
|
63
|
+
margin-left: 0.5rem;
|
|
64
|
+
}
|
|
120
65
|
}
|
|
121
|
-
|
|
66
|
+
|
|
67
|
+
:deep(td[data-p-frozen-column='true']),
|
|
68
|
+
:deep(th[data-p-frozen-column='true']) {
|
|
122
69
|
position: sticky !important;
|
|
123
|
-
background: inherit;
|
|
124
70
|
}
|
|
125
|
-
|
|
126
|
-
|
|
71
|
+
|
|
72
|
+
:deep(td[data-p-frozen-column='true']) {
|
|
73
|
+
background: inherit;
|
|
127
74
|
}
|
|
128
75
|
</style>
|
|
@@ -4,35 +4,44 @@ import IconCommunitySolar from './icon/community-solar.vue';
|
|
|
4
4
|
import IconInfoBlogPost from './icon/info-blog-post.vue';
|
|
5
5
|
import IconMarketplace from './icon/marketplace.vue';
|
|
6
6
|
|
|
7
|
-
const ES_DOMAIN = process.env.ES_DOMAIN || 'https://www.energysage.com';
|
|
8
|
-
const linkItems = [
|
|
9
|
-
{
|
|
10
|
-
icon: IconContactUs,
|
|
11
|
-
link: 'mailto:hello@energysage.com',
|
|
12
|
-
label: 'Contact us',
|
|
13
|
-
},
|
|
14
|
-
{
|
|
15
|
-
icon: IconCommunitySolar,
|
|
16
|
-
link: `${ES_DOMAIN}/solar/`,
|
|
17
|
-
label: 'Learn about solar',
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
icon: IconInfoBlogPost,
|
|
21
|
-
link: `${ES_DOMAIN}/blog/`,
|
|
22
|
-
label: 'EnergySage blog',
|
|
23
|
-
},
|
|
24
|
-
{
|
|
25
|
-
icon: IconMarketplace,
|
|
26
|
-
link: `${ES_DOMAIN}/shop/home-solar/`,
|
|
27
|
-
label: 'EnergySage marketplace',
|
|
28
|
-
},
|
|
29
|
-
];
|
|
30
|
-
|
|
31
7
|
const props = defineProps({
|
|
32
8
|
errorType: {
|
|
33
9
|
type: String,
|
|
34
10
|
required: true,
|
|
35
11
|
},
|
|
12
|
+
esDomain: {
|
|
13
|
+
type: String,
|
|
14
|
+
default: null,
|
|
15
|
+
},
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const esDomain = computed(() => {
|
|
19
|
+
return props.esDomain || 'https://www.energysage.com';
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const linkItems = computed(() => {
|
|
23
|
+
return [
|
|
24
|
+
{
|
|
25
|
+
icon: IconContactUs,
|
|
26
|
+
link: 'mailto:hello@energysage.com',
|
|
27
|
+
label: 'Contact us',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
icon: IconCommunitySolar,
|
|
31
|
+
link: `${esDomain.value}/solar/`,
|
|
32
|
+
label: 'Learn about solar',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
icon: IconInfoBlogPost,
|
|
36
|
+
link: `${esDomain.value}/blog/`,
|
|
37
|
+
label: 'EnergySage blog',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
icon: IconMarketplace,
|
|
41
|
+
link: `${esDomain.value}/shop/home-solar/`,
|
|
42
|
+
label: 'EnergySage marketplace',
|
|
43
|
+
},
|
|
44
|
+
];
|
|
36
45
|
});
|
|
37
46
|
|
|
38
47
|
interface ErrorCode {
|
package/components/es-footer.vue
CHANGED
|
@@ -25,6 +25,25 @@ export default {
|
|
|
25
25
|
return this.content.copyrightText.replace('{currentYear}', new Date().getFullYear());
|
|
26
26
|
},
|
|
27
27
|
},
|
|
28
|
+
mounted() {
|
|
29
|
+
// https://energysage.atlassian.net/wiki/spaces/FG/pages/1427865649/One-trust+Consent+Initialization+and+GTM
|
|
30
|
+
window.addEventListener('OneTrustLoadedCb', () => {
|
|
31
|
+
(window as any).OneTrust.OnConsentChanged(() => {
|
|
32
|
+
// OneTrust modal should modify cookie values, a hard-refresh will
|
|
33
|
+
// trigger re-loading GTM with updated cookie values, which in turn
|
|
34
|
+
// will only fire tags aligned with new preferences
|
|
35
|
+
window.location.reload();
|
|
36
|
+
return false;
|
|
37
|
+
});
|
|
38
|
+
document.querySelectorAll('.toggle-info-display').forEach((elem) => {
|
|
39
|
+
// Function closure to ensure event only fires on one elem
|
|
40
|
+
elem.addEventListener('click', (e) => {
|
|
41
|
+
e.stopImmediatePropagation();
|
|
42
|
+
(window as any).OneTrust.ToggleInfoDisplay();
|
|
43
|
+
});
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
},
|
|
28
47
|
};
|
|
29
48
|
</script>
|
|
30
49
|
|
|
@@ -120,16 +139,26 @@ export default {
|
|
|
120
139
|
<!-- Trademark Info -->
|
|
121
140
|
<!-- Legal -->
|
|
122
141
|
<hr class="border-top border-blue-500 m-0" />
|
|
123
|
-
<div class="row
|
|
142
|
+
<div class="row pt-100">
|
|
124
143
|
<div
|
|
125
144
|
v-for="link in content.legalLinks"
|
|
126
145
|
:key="link.text"
|
|
127
|
-
class="col col-6 col-lg-auto
|
|
146
|
+
class="col col-6 col-lg-auto mt-25 mb-50 pr-lg-300">
|
|
128
147
|
<es-nav-bar-link
|
|
148
|
+
v-if="link.url"
|
|
129
149
|
:href="link.url"
|
|
130
|
-
class="text-reset font-weight-normal">
|
|
150
|
+
class="text-reset font-weight-normal font-size-75 font-size-md-50 font-weight-md-normal">
|
|
131
151
|
{{ link.text }}
|
|
132
152
|
</es-nav-bar-link>
|
|
153
|
+
<div v-else>
|
|
154
|
+
<icon-ccpa-opt-out />
|
|
155
|
+
<es-button
|
|
156
|
+
inline
|
|
157
|
+
class="toggle-info-display text-reset font-weight-normal font-size-75 font-size-md-50 font-weight-md-normal ml-1"
|
|
158
|
+
variant="link">
|
|
159
|
+
{{ link.text }}
|
|
160
|
+
</es-button>
|
|
161
|
+
</div>
|
|
133
162
|
</div>
|
|
134
163
|
</div>
|
|
135
164
|
<!-- Legal -->
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
import SelectButton from 'primevue/selectbutton';
|
|
3
|
+
|
|
4
|
+
interface SegmentedControlItem {
|
|
5
|
+
label: string;
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
interface Props {
|
|
9
|
+
disabled?: boolean;
|
|
10
|
+
label: string;
|
|
11
|
+
options: SegmentedControlItem[];
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
interface ButtonDimensionsInterface {
|
|
15
|
+
options: Partial<CSSStyleDeclaration>[];
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
19
|
+
disabled: false,
|
|
20
|
+
});
|
|
21
|
+
const model = defineModel<number>();
|
|
22
|
+
const segmentedControlRef = useTemplateRef('segmentedControl');
|
|
23
|
+
let resizeObserver: ResizeObserver | null = null;
|
|
24
|
+
|
|
25
|
+
// needed to avoid Content Layout Shift (CLS)
|
|
26
|
+
// as the inkbar is positioned via left and width attributes which are not set until mount
|
|
27
|
+
// meaning the active state would be initially missing and then pop in on mount, causing CLS
|
|
28
|
+
const isMounted = ref(false);
|
|
29
|
+
|
|
30
|
+
const buttonDimensions: ButtonDimensionsInterface = reactive({
|
|
31
|
+
options: props.options?.length ? props.options.map(() => ({})) : [],
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
const inkbarStyle: ComputedRef<Partial<CSSStyleDeclaration>> = computed(
|
|
35
|
+
() => buttonDimensions.options[model.value || 0],
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
// assume the provided label is unique and convert to an id
|
|
39
|
+
// by lowercasing and replacing all whitespace with dashes
|
|
40
|
+
const labelId = computed(() => {
|
|
41
|
+
return props.label ? props.label.toLowerCase().replace(/\s/g, '-') : '';
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
// return the left/width to use for the active state bubble, for each provided button
|
|
45
|
+
const getButtonDimensions = (buttons: HTMLCollection) => {
|
|
46
|
+
const result = [];
|
|
47
|
+
let runningLeftTotal = 0;
|
|
48
|
+
for (let i = 0; i < buttons.length; i += 1) {
|
|
49
|
+
const button = buttons[i] as unknown as HTMLElement;
|
|
50
|
+
result.push({
|
|
51
|
+
left: `${runningLeftTotal}px`,
|
|
52
|
+
width: `${button.offsetWidth}px`,
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
runningLeftTotal += button.offsetWidth;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return result;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
onMounted(() => {
|
|
62
|
+
isMounted.value = true;
|
|
63
|
+
|
|
64
|
+
if (segmentedControlRef.value) {
|
|
65
|
+
// set the initial button dimensions
|
|
66
|
+
const buttons = segmentedControlRef.value.querySelectorAll(
|
|
67
|
+
'.es-segmented-control-button',
|
|
68
|
+
) as unknown as HTMLCollection;
|
|
69
|
+
buttonDimensions.options = getButtonDimensions(buttons);
|
|
70
|
+
|
|
71
|
+
// set up a resize observer for whenever the segmented control's button group changes width
|
|
72
|
+
// this will most commonly happen when changing between desktop and mobile breakpoints
|
|
73
|
+
// but could also happen if the label text of any options changes
|
|
74
|
+
resizeObserver = new ResizeObserver((groups) => {
|
|
75
|
+
groups.forEach((group) => {
|
|
76
|
+
const updatedButtons = group.target.children;
|
|
77
|
+
buttonDimensions.options = getButtonDimensions(updatedButtons);
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// start the resize observer listening to the segmented control's button group
|
|
82
|
+
const group = segmentedControlRef.value.querySelector('.es-segmented-control-group');
|
|
83
|
+
if (group) {
|
|
84
|
+
resizeObserver.observe(group);
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
onUnmounted(() => {
|
|
90
|
+
if (resizeObserver) {
|
|
91
|
+
resizeObserver.disconnect();
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
</script>
|
|
95
|
+
|
|
96
|
+
<template>
|
|
97
|
+
<div
|
|
98
|
+
v-if="options?.length"
|
|
99
|
+
ref="segmentedControl"
|
|
100
|
+
:aria-hidden="props.disabled || undefined"
|
|
101
|
+
class="es-segmented-control d-flex position-relative"
|
|
102
|
+
:class="{
|
|
103
|
+
disabled: props.disabled,
|
|
104
|
+
'before-mount': !isMounted,
|
|
105
|
+
}">
|
|
106
|
+
<label
|
|
107
|
+
:id="labelId"
|
|
108
|
+
class="sr-only">
|
|
109
|
+
{{ label }}
|
|
110
|
+
</label>
|
|
111
|
+
<span
|
|
112
|
+
class="es-segmented-control-inkbar rounded-lg position-absolute"
|
|
113
|
+
:data-left="buttonDimensions.options[model || 0]?.left || 0"
|
|
114
|
+
:style="inkbarStyle as any" />
|
|
115
|
+
<select-button
|
|
116
|
+
v-model="model"
|
|
117
|
+
:allow-empty="false"
|
|
118
|
+
:aria-labelledby="labelId"
|
|
119
|
+
data-key="label"
|
|
120
|
+
:disabled="props.disabled"
|
|
121
|
+
:option-label="(option) => option.label"
|
|
122
|
+
:option-value="(option) => props.options.findIndex((innerOption) => option.label === innerOption.label)"
|
|
123
|
+
:options="props.options"
|
|
124
|
+
:pt="{
|
|
125
|
+
root: 'es-segmented-control-group bg-gray-100 d-flex rounded-lg',
|
|
126
|
+
button: 'es-segmented-control-button font-size-sm font-weight-bold position-relative px-200 px-lg-300 rounded-lg',
|
|
127
|
+
label: 'es-segmented-control-label position-relative',
|
|
128
|
+
}"
|
|
129
|
+
:unselectable="false" />
|
|
130
|
+
</div>
|
|
131
|
+
</template>
|
|
132
|
+
|
|
133
|
+
<style lang="scss" scoped>
|
|
134
|
+
@use '@energysage/es-ds-styles/scss/variables' as variables;
|
|
135
|
+
|
|
136
|
+
/* avoid Content Layout Shift (CLS) */
|
|
137
|
+
.es-segmented-control.before-mount {
|
|
138
|
+
.es-segmented-control-inkbar {
|
|
139
|
+
/* prevent inkbar with left and width of zero from showing up between first paint and mount */
|
|
140
|
+
opacity: 0;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
:deep(.es-segmented-control-button) {
|
|
144
|
+
position: relative;
|
|
145
|
+
|
|
146
|
+
/* temporarily replicate the inkbar's active state until mount */
|
|
147
|
+
&[data-p-highlight='true'] {
|
|
148
|
+
&::before {
|
|
149
|
+
border-radius: variables.$border-radius-lg;
|
|
150
|
+
content: '';
|
|
151
|
+
left: 0;
|
|
152
|
+
position: absolute;
|
|
153
|
+
width: 100%;
|
|
154
|
+
z-index: 3;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/* active state bubble */
|
|
161
|
+
.es-segmented-control-inkbar,
|
|
162
|
+
.es-segmented-control.before-mount :deep(.es-segmented-control-button[data-p-highlight='true']::before) {
|
|
163
|
+
/* using style for bg instead of bg-white class so it can be overridden for disabled state */
|
|
164
|
+
background-color: variables.$white;
|
|
165
|
+
border: 2px solid variables.$warm-orange;
|
|
166
|
+
bottom: 2px;
|
|
167
|
+
top: 2px;
|
|
168
|
+
/* bring it ~2px away from each side to match the top/bottom */
|
|
169
|
+
transform: scaleX(0.98);
|
|
170
|
+
|
|
171
|
+
@media not (prefers-reduced-motion) {
|
|
172
|
+
transition:
|
|
173
|
+
left 250ms cubic-bezier(0.35, 0, 0.25, 1),
|
|
174
|
+
width 250ms cubic-bezier(0.35, 0, 0.25, 1);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/* each individual option */
|
|
179
|
+
:deep(.es-segmented-control-button) {
|
|
180
|
+
color: variables.$gray-700;
|
|
181
|
+
cursor: pointer;
|
|
182
|
+
/*
|
|
183
|
+
need a non-standard padding to cause a specific height that matches
|
|
184
|
+
better with our "lg" 14px border radius to get the rounded pill look
|
|
185
|
+
*/
|
|
186
|
+
padding-bottom: 6px;
|
|
187
|
+
padding-top: 6px;
|
|
188
|
+
/* don't transition all properties, otherwise responsive bubble width updates will be delayed */
|
|
189
|
+
transition: color 250ms ease-in-out;
|
|
190
|
+
/* prevent text from highlighting when you click a bunch of times */
|
|
191
|
+
user-select: none;
|
|
192
|
+
white-space: nowrap;
|
|
193
|
+
z-index: 4;
|
|
194
|
+
|
|
195
|
+
&:hover {
|
|
196
|
+
color: variables.$gray-900;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
&:focus-visible {
|
|
200
|
+
box-shadow: 0 0 0 3px variables.$warm-orange inset;
|
|
201
|
+
outline: none;
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/* bring the label above the active state bubble */
|
|
206
|
+
:deep(.es-segmented-control-label) {
|
|
207
|
+
z-index: 4;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/* disabled state */
|
|
211
|
+
.es-segmented-control.disabled,
|
|
212
|
+
.es-segmented-control.before-mount.disabled {
|
|
213
|
+
cursor: default;
|
|
214
|
+
pointer-events: none;
|
|
215
|
+
user-select: none;
|
|
216
|
+
|
|
217
|
+
/* active bubble */
|
|
218
|
+
.es-segmented-control-inkbar,
|
|
219
|
+
:deep(.es-segmented-control-button[data-p-highlight='true']::before) {
|
|
220
|
+
background-color: variables.$gray-200;
|
|
221
|
+
border-color: variables.$gray-300;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/* text inside active bubble */
|
|
225
|
+
:deep(.es-segmented-control-button[data-p-highlight='true']) {
|
|
226
|
+
&,
|
|
227
|
+
&:hover {
|
|
228
|
+
color: variables.$gray-500;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/* all other items */
|
|
233
|
+
:deep(.es-segmented-control-button) {
|
|
234
|
+
color: variables.$gray-500;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
</style>
|
package/components/es-tabs.vue
CHANGED
|
@@ -6,7 +6,16 @@ import TabView from 'primevue/tabview';
|
|
|
6
6
|
const model = defineModel<number>();
|
|
7
7
|
|
|
8
8
|
// get the list of elements provided as children to the default slot
|
|
9
|
-
const
|
|
9
|
+
const initialChildren = useSlots().default?.() || [];
|
|
10
|
+
const panels: any[] = [];
|
|
11
|
+
initialChildren.forEach((child) => {
|
|
12
|
+
// unless this has a v-for element and we instead need to access its children
|
|
13
|
+
if (typeof child.type === 'symbol' && Array.isArray(child.children)) {
|
|
14
|
+
panels.push(...child.children);
|
|
15
|
+
} else {
|
|
16
|
+
panels.push(child);
|
|
17
|
+
}
|
|
18
|
+
});
|
|
10
19
|
|
|
11
20
|
// keep track of the active tab index
|
|
12
21
|
// - from above, if v-model is used
|
|
@@ -36,7 +45,6 @@ const updateActiveIndex = (index: number) => {
|
|
|
36
45
|
panelContainer: 'tab-content',
|
|
37
46
|
}"
|
|
38
47
|
@update:active-index="updateActiveIndex">
|
|
39
|
-
<!-- @vue-expect-error -->
|
|
40
48
|
<tab-panel
|
|
41
49
|
v-for="(panel, index) in panels"
|
|
42
50
|
:key="index"
|
|
@@ -60,7 +68,6 @@ const updateActiveIndex = (index: number) => {
|
|
|
60
68
|
],
|
|
61
69
|
}),
|
|
62
70
|
}">
|
|
63
|
-
<!-- @vue-expect-error -->
|
|
64
71
|
<component
|
|
65
72
|
:is="item"
|
|
66
73
|
v-for="(item, idx) in panel.children.default()"
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
export default {
|
|
3
|
+
name: 'IconCcpaOptOut',
|
|
4
|
+
props: {
|
|
5
|
+
/**
|
|
6
|
+
* Width
|
|
7
|
+
*/
|
|
8
|
+
width: {
|
|
9
|
+
type: String,
|
|
10
|
+
default: '30px',
|
|
11
|
+
required: false,
|
|
12
|
+
},
|
|
13
|
+
/**
|
|
14
|
+
* Height
|
|
15
|
+
*/
|
|
16
|
+
height: {
|
|
17
|
+
type: String,
|
|
18
|
+
default: '14px',
|
|
19
|
+
required: false,
|
|
20
|
+
},
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<svg
|
|
27
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
28
|
+
xml:space="preserve"
|
|
29
|
+
id="Layer_1"
|
|
30
|
+
:style="{
|
|
31
|
+
height: height,
|
|
32
|
+
width: width,
|
|
33
|
+
}"
|
|
34
|
+
aria-label="California Consumer Privacy Act (CCPA) Opt-Out Icon"
|
|
35
|
+
role="img"
|
|
36
|
+
x="0"
|
|
37
|
+
y="0"
|
|
38
|
+
version="1.1"
|
|
39
|
+
viewBox="0 0 30 14">
|
|
40
|
+
<g
|
|
41
|
+
id="Final-Copy-2"
|
|
42
|
+
style="display: inline">
|
|
43
|
+
<path
|
|
44
|
+
class="st0"
|
|
45
|
+
d="M7.4,12.8h6.8l3.1-11.6H7.4C4.2,1.2,1.6,3.8,1.6,7S4.2,12.8,7.4,12.8z"
|
|
46
|
+
style="fill: #fff" />
|
|
47
|
+
<path
|
|
48
|
+
id="path2"
|
|
49
|
+
d="M22.6 0H7.4c-3.9 0-7 3.1-7 7s3.1 7 7 7h15.2c3.9 0 7-3.1 7-7s-3.2-7-7-7zm-21 7c0-3.2 2.6-5.8 5.8-5.8h9.9l-3.1 11.6H7.4c-3.2 0-5.8-2.6-5.8-5.8Z"
|
|
50
|
+
style="clip-rule: evenodd; fill: #06f; fill-rule: evenodd" />
|
|
51
|
+
<path
|
|
52
|
+
id="x"
|
|
53
|
+
d="M24.6 4c.2.2.2.6 0 .8L22.5 7l2.2 2.2c.2.2.2.6 0 .8-.2.2-.6.2-.8 0l-2.2-2.2-2.2 2.2c-.2.2-.6.2-.8 0-.2-.2-.2-.6 0-.8L20.8 7l-2.2-2.2c-.2-.2-.2-.6 0-.8.2-.2.6-.2.8 0l2.2 2.2L23.8 4c.2-.2.6-.2.8 0Z"
|
|
54
|
+
style="fill: #fff" />
|
|
55
|
+
<path
|
|
56
|
+
id="y"
|
|
57
|
+
d="M12.7 4.1c.2.2.3.6.1.8L8.6 9.8c-.1.1-.2.2-.3.2-.2.1-.5.1-.7-.1L5.4 7.7c-.2-.2-.2-.6 0-.8.2-.2.6-.2.8 0L8 8.6l3.8-4.5c.2-.2.6-.2.9 0z"
|
|
58
|
+
style="fill: #06f" />
|
|
59
|
+
</g>
|
|
60
|
+
</svg>
|
|
61
|
+
</template>
|
package/nuxt.config.ts
CHANGED
|
@@ -18,6 +18,11 @@ export default defineNuxtConfig({
|
|
|
18
18
|
},
|
|
19
19
|
},
|
|
20
20
|
|
|
21
|
+
// Configure how Nuxt auto-imports composables into your application.
|
|
22
|
+
imports: {
|
|
23
|
+
dirs: ['composables', 'types'],
|
|
24
|
+
},
|
|
25
|
+
|
|
21
26
|
modules: [
|
|
22
27
|
// https://google-fonts.nuxtjs.org/getting-started/setup
|
|
23
28
|
'@nuxtjs/google-fonts',
|
|
@@ -66,6 +71,7 @@ export default defineNuxtConfig({
|
|
|
66
71
|
'primevue/progressbar',
|
|
67
72
|
'primevue/radiobutton',
|
|
68
73
|
'primevue/rating',
|
|
74
|
+
'primevue/selectbutton',
|
|
69
75
|
'primevue/skeleton',
|
|
70
76
|
'primevue/slider',
|
|
71
77
|
'primevue/tabpanel',
|