@energie360/ui-library 0.1.1 → 0.1.3
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/base/_input-resets.scss +9 -3
- package/base/_resets.scss +5 -0
- package/components/accordion-item/accordion-item.scss +62 -0
- package/components/accordion-item/u-accordion-item.vue +44 -0
- package/components/card/card.scss +58 -0
- package/components/card/u-card.vue +26 -0
- package/components/card-header/card-header.scss +102 -0
- package/components/card-header/u-card-header.vue +51 -0
- package/components/card-hint/card-hint.scss +13 -0
- package/components/card-hint/u-card-hint.vue +23 -0
- package/components/card-price/card-price.scss +110 -0
- package/components/card-price/u-card-price.vue +47 -0
- package/components/card-table/card-table.scss +76 -0
- package/components/card-table/u-card-table.vue +77 -0
- package/components/card-toggle-switches/card-toggle-switches.scss +13 -0
- package/components/card-toggle-switches/u-card-toggle-switches.vue +30 -0
- package/components/collapsible/collapsible.scss +14 -0
- package/components/collapsible/u-collapsible.vue +81 -0
- package/components/collapsible-group/u-collapsible-group.vue +14 -0
- package/components/icon-teaser/icon-teaser.scss +58 -0
- package/components/icon-teaser/u-icon-teaser.vue +35 -0
- package/components/icon-teaser-group/icon-teaser-group.scss +10 -0
- package/components/icon-teaser-group/u-icon-teaser-group.vue +19 -0
- package/components/icon-text-block/u-icon-text-block.vue +3 -8
- package/components/language-nav/language-nav.scss +32 -0
- package/components/language-nav/u-language-nav.vue +27 -0
- package/components/panel/panel.scss +43 -6
- package/components/panel/u-panel.vue +22 -11
- package/components/progress-bar/u-progress-bar.vue +1 -1
- package/components/richtext/richtext.scss +208 -0
- package/components/richtext/u-richtext.vue +21 -0
- package/components/table/u-cell-ctas.vue +2 -7
- package/components/table/u-cell-icon-group.vue +1 -1
- package/components/table/u-cell-icon-text.vue +1 -1
- package/components/table/u-cell-progress-bar.vue +1 -1
- package/components/table/u-table-cell.vue +3 -13
- package/components/table/u-table-heading.vue +1 -4
- package/components/tooltip/{popover.js → popover.ts} +51 -43
- package/components/tooltip/u-tooltip.vue +40 -60
- package/dist/base-style.css +409 -2
- package/dist/base-style.css.map +1 -0
- package/dist/elements/text-link.css +40 -0
- package/dist/elements/text-link.css.map +1 -0
- package/dist/layout/split.css +124 -0
- package/dist/layout/split.css.map +1 -0
- package/elements/button/u-button.vue +2 -5
- package/elements/button-chip/button-chip.scss +83 -0
- package/elements/button-chip/u-button-chip.vue +45 -0
- package/elements/form-field/form-field-base.scss +2 -3
- package/elements/form-field/form-field.types.ts +8 -0
- package/elements/icon/u-icon.vue +1 -3
- package/elements/image/u-image.vue +2 -2
- package/elements/numeric-stepper/numeric-stepper.scss +110 -0
- package/elements/numeric-stepper/u-numeric-stepper.vue +135 -0
- package/elements/select/select.scss +32 -0
- package/elements/select/u-select.vue +130 -0
- package/elements/select-chip/select-chip.scss +18 -0
- package/elements/select-chip/u-select-chip.vue +50 -0
- package/elements/select-chips/select-chips.scss +5 -0
- package/elements/select-chips/u-select-chips.vue +23 -0
- package/elements/spectro/spectro.scss +1 -4
- package/elements/text-field/u-text-field.vue +43 -27
- package/elements/text-link/text-link.scss +57 -0
- package/elements/toggle-switch/toggle-switch-small.scss +40 -0
- package/elements/toggle-switch/toggle-switch.scss +149 -0
- package/elements/toggle-switch/u-toggle-switch.vue +68 -0
- package/elements/types.ts +7 -0
- package/globals.js +6 -2
- package/helpers/transition-height.vue +39 -0
- package/i18n/i18n.ts +40 -0
- package/layout/grid/grid.mixin.scss +4 -11
- package/layout/split/split.scss +96 -0
- package/modules/footer/footer.scss +161 -0
- package/modules/footer/u-footer.vue +59 -0
- package/package.json +23 -13
- package/utility/elements/text-link.scss +1 -0
- package/utility/layout/split.scss +1 -0
- package/utility/utility-text.js +1 -0
- package/utils/object/deep-get.js +1 -2
- package/utils/translations/translate.js +13 -0
- package/vite.config.ts +1 -0
- package/watch.js +27 -0
- package/wizard/wizard-intro/wizard-intro.scss +4 -0
- package/wizard/wizard-layout/u-wizard-layout-block.vue +1 -1
- package/wizard/wizard-layout/u-wizard-layout-element.vue +1 -1
- package/wizard/wizard-layout/u-wizard-layout.vue +1 -1
- package/wizard/wizard-layout/wizard-layout.scss +6 -6
- package/dist/base-style.js +0 -2
- package/dist/base-style.js.map +0 -1
- package/dist/custom-elements.css +0 -1
- package/dist/custom-elements.js +0 -5185
- package/dist/custom-elements.js.map +0 -1
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
@use '../../base/abstracts/' as a;
|
|
2
|
+
@use '../../elements/text-link/text-link.scss' as t;
|
|
3
|
+
|
|
4
|
+
.richtext {
|
|
5
|
+
// Spacing rules
|
|
6
|
+
* + * {
|
|
7
|
+
margin-top: var(--e-space-3);
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
ul + *,
|
|
11
|
+
ol + * {
|
|
12
|
+
margin-top: var(--e-space-8);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
* + h2,
|
|
16
|
+
* + h3,
|
|
17
|
+
* + .richtext__title-large,
|
|
18
|
+
* + .richtext__title-small {
|
|
19
|
+
margin-top: var(--e-space-16);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
* + ul,
|
|
23
|
+
* + ol {
|
|
24
|
+
margin-top: var(--e-space-6);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
@include a.bp(lg) {
|
|
28
|
+
* + * {
|
|
29
|
+
margin-top: var(--e-space-2);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
ul + *,
|
|
33
|
+
ol + * {
|
|
34
|
+
margin-top: var(--e-space-6);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
* + h2,
|
|
38
|
+
* + h3,
|
|
39
|
+
* + .richtext__title-large,
|
|
40
|
+
* + .richtext__title-small {
|
|
41
|
+
margin-top: var(--e-space-10);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// Element styles
|
|
46
|
+
h2,
|
|
47
|
+
.richtext__title-large {
|
|
48
|
+
@include a.type(700, strong);
|
|
49
|
+
|
|
50
|
+
color: var(--e-c-primary-01-500);
|
|
51
|
+
|
|
52
|
+
@include a.bp(lg) {
|
|
53
|
+
@include a.type(400, strong);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
h3,
|
|
58
|
+
.richtext__title-small {
|
|
59
|
+
@include a.type(400, strong);
|
|
60
|
+
|
|
61
|
+
color: var(--e-c-primary-01-500);
|
|
62
|
+
|
|
63
|
+
@include a.bp(lg) {
|
|
64
|
+
@include a.type(300, strong);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
p {
|
|
69
|
+
@include a.type(300);
|
|
70
|
+
|
|
71
|
+
@include a.bp(lg) {
|
|
72
|
+
@include a.type(200);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
a {
|
|
77
|
+
@include t.text-link;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
ul,
|
|
81
|
+
ol {
|
|
82
|
+
@include a.type(300);
|
|
83
|
+
|
|
84
|
+
padding-left: var(--e-space-4);
|
|
85
|
+
|
|
86
|
+
> li {
|
|
87
|
+
position: relative;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
br {
|
|
91
|
+
font-size: 0; // Firefox hack
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
@include a.bp(lg) {
|
|
95
|
+
@include a.type(200);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
ul {
|
|
100
|
+
> li {
|
|
101
|
+
&::before {
|
|
102
|
+
content: '';
|
|
103
|
+
position: absolute;
|
|
104
|
+
top: var(--e-space-2_5);
|
|
105
|
+
left: calc(var(--e-space-4) * -1);
|
|
106
|
+
display: block;
|
|
107
|
+
width: a.rem(8);
|
|
108
|
+
height: a.rem(8);
|
|
109
|
+
border-radius: 100%;
|
|
110
|
+
background-color: var(--e-c-secondary-05-500);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
@include a.bp(lg) {
|
|
114
|
+
&::before {
|
|
115
|
+
top: var(--e-space-2);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
ol {
|
|
122
|
+
counter-reset: li;
|
|
123
|
+
|
|
124
|
+
> li {
|
|
125
|
+
counter-increment: li;
|
|
126
|
+
|
|
127
|
+
&::before {
|
|
128
|
+
@include a.type(200, strong);
|
|
129
|
+
|
|
130
|
+
content: counter(li);
|
|
131
|
+
position: absolute;
|
|
132
|
+
top: 2px;
|
|
133
|
+
left: calc(var(--e-space-4) * -1);
|
|
134
|
+
display: block;
|
|
135
|
+
color: var(--e-c-secondary-05-500);
|
|
136
|
+
font-weight: 700;
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
@include a.bp(lg) {
|
|
141
|
+
> li::before {
|
|
142
|
+
top: 0;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
u {
|
|
148
|
+
text-underline-offset: a.rem(4);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
p,
|
|
152
|
+
h2,
|
|
153
|
+
h3,
|
|
154
|
+
a,
|
|
155
|
+
li {
|
|
156
|
+
text-wrap: pretty;
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Modifiers
|
|
160
|
+
&.richtext--inverted {
|
|
161
|
+
color: var(--e-c-mono-00);
|
|
162
|
+
|
|
163
|
+
h2,
|
|
164
|
+
h3,
|
|
165
|
+
.richtext__title-small,
|
|
166
|
+
.richtext__title-large {
|
|
167
|
+
color: var(--e-c-mono-00);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
a {
|
|
171
|
+
@include t.text-link--inverted;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
ul > li::before {
|
|
175
|
+
background-color: var(--e-c-mono-00);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
ol > li::before {
|
|
179
|
+
color: var(--e-c-mono-00);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// TODO: Spacing between must also be adjusted for richtext--small.
|
|
184
|
+
&.richtext--small {
|
|
185
|
+
p {
|
|
186
|
+
@include a.type(200);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
ul,
|
|
190
|
+
ol {
|
|
191
|
+
@include a.type(200);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
ul {
|
|
195
|
+
> li {
|
|
196
|
+
&::before {
|
|
197
|
+
top: var(--e-space-2);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
ol {
|
|
203
|
+
> li::before {
|
|
204
|
+
top: 0;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
<script setup lang="ts">
|
|
2
|
+
interface Props {
|
|
3
|
+
text?: string
|
|
4
|
+
small?: boolean
|
|
5
|
+
inverted?: boolean
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
const { small = false, inverted = false } = defineProps<Props>()
|
|
9
|
+
|
|
10
|
+
const classes = ['richtext', { 'richtext--small': small, 'richtext--inverted': inverted }]
|
|
11
|
+
</script>
|
|
12
|
+
|
|
13
|
+
<template>
|
|
14
|
+
<div v-if="text" :class="classes" v-html="text"></div>
|
|
15
|
+
|
|
16
|
+
<div v-else :class="classes"><slot></slot></div>
|
|
17
|
+
</template>
|
|
18
|
+
|
|
19
|
+
<style lang="scss">
|
|
20
|
+
@use './richtext.scss';
|
|
21
|
+
</style>
|
|
@@ -15,13 +15,8 @@ defineProps<Props>()
|
|
|
15
15
|
|
|
16
16
|
<template>
|
|
17
17
|
<div class="cell-ctas">
|
|
18
|
-
<template v-for="cta in ctas">
|
|
19
|
-
<UButton
|
|
20
|
-
variant="plain"
|
|
21
|
-
:icon="cta.icon"
|
|
22
|
-
:href="cta.href"
|
|
23
|
-
:target="cta.target"
|
|
24
|
-
>
|
|
18
|
+
<template v-for="(cta, idx) in ctas" :key="idx">
|
|
19
|
+
<UButton variant="plain" :icon="cta.icon" :href="cta.href" :target="cta.target">
|
|
25
20
|
<span class="button-label">{{ cta.label }}</span>
|
|
26
21
|
</UButton>
|
|
27
22
|
</template>
|
|
@@ -18,7 +18,7 @@ defineProps<Props>()
|
|
|
18
18
|
|
|
19
19
|
<template>
|
|
20
20
|
<div class="cell-icon-group">
|
|
21
|
-
<template v-for="item in icons">
|
|
21
|
+
<template v-for="(item, idx) in icons" :key="idx">
|
|
22
22
|
<UTooltip :title="item.title">
|
|
23
23
|
<UIcon :name="item.icon" :class="[`icon-color-${item.color}`]"></UIcon>
|
|
24
24
|
</UTooltip>
|
|
@@ -12,7 +12,7 @@ const { labelPosition = 'top' } = defineProps<Props>()
|
|
|
12
12
|
|
|
13
13
|
<template>
|
|
14
14
|
<div :class="['cell-progress-bar', `label-position-${labelPosition}`]">
|
|
15
|
-
<p class="progress-bar-label"
|
|
15
|
+
<p v-if="label" class="progress-bar-label">{{ label }}</p>
|
|
16
16
|
<UProgressBar :value="value"></UProgressBar>
|
|
17
17
|
</div>
|
|
18
18
|
</template>
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
|
-
import {
|
|
3
|
-
TableCellBase,
|
|
4
|
-
TableCellHAlign,
|
|
5
|
-
TableCellTextStyle,
|
|
6
|
-
TableCellVAlign,
|
|
7
|
-
} from './table.type'
|
|
2
|
+
import { TableCellBase, TableCellHAlign, TableCellTextStyle, TableCellVAlign } from './table.type'
|
|
8
3
|
import UIcon from '../../elements/icon/u-icon.vue'
|
|
9
4
|
import UTooltip from '../tooltip/u-tooltip.vue'
|
|
10
5
|
|
|
@@ -23,18 +18,13 @@ const {
|
|
|
23
18
|
<template>
|
|
24
19
|
<div
|
|
25
20
|
role="cell"
|
|
26
|
-
:class="[
|
|
27
|
-
'table-cell',
|
|
28
|
-
`h-align-${hAlign}`,
|
|
29
|
-
`v-align-${vAlign}`,
|
|
30
|
-
{ 'has-tooltip': infoText },
|
|
31
|
-
]"
|
|
21
|
+
:class="['table-cell', `h-align-${hAlign}`, `v-align-${vAlign}`, { 'has-tooltip': infoText }]"
|
|
32
22
|
>
|
|
33
23
|
<div :class="['cell-content', `text-${textStyle}`]">
|
|
34
24
|
<slot>{{ text }}</slot>
|
|
35
25
|
</div>
|
|
36
26
|
|
|
37
|
-
<div class="info-tooltip"
|
|
27
|
+
<div v-if="infoText" class="info-tooltip">
|
|
38
28
|
<UTooltip :title="infoText">
|
|
39
29
|
<UIcon name="info"></UIcon>
|
|
40
30
|
</UTooltip>
|
|
@@ -9,10 +9,7 @@ const { hAlign = 'left', vAlign = 'top' } = defineProps<Props>()
|
|
|
9
9
|
</script>
|
|
10
10
|
|
|
11
11
|
<template>
|
|
12
|
-
<div
|
|
13
|
-
role="cell"
|
|
14
|
-
:class="['table-heading', `h-align-${hAlign}`, `v-align-${vAlign}`]"
|
|
15
|
-
>
|
|
12
|
+
<div role="cell" :class="['table-heading', `h-align-${hAlign}`, `v-align-${vAlign}`]">
|
|
16
13
|
<div class="cell-content">
|
|
17
14
|
<slot :text="text">{{ text }}</slot>
|
|
18
15
|
</div>
|
|
@@ -1,6 +1,26 @@
|
|
|
1
1
|
import { getViewportDimensions } from './viewport'
|
|
2
2
|
import { getOffsetParentPosition } from './dom'
|
|
3
3
|
|
|
4
|
+
export type Placement =
|
|
5
|
+
| 'top-right'
|
|
6
|
+
| 'top-left'
|
|
7
|
+
| 'top-center'
|
|
8
|
+
| 'bottom-right'
|
|
9
|
+
| 'bottom-left'
|
|
10
|
+
| 'bottom-center'
|
|
11
|
+
export interface PopoverPositionParams {
|
|
12
|
+
placement?: Placement
|
|
13
|
+
offset?: number
|
|
14
|
+
viewportPadding?: number
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface PopoverPosition {
|
|
18
|
+
top: number
|
|
19
|
+
left: number
|
|
20
|
+
topCorrection: number
|
|
21
|
+
leftCorrection: number
|
|
22
|
+
}
|
|
23
|
+
|
|
4
24
|
const defaultOptions = {
|
|
5
25
|
offset: 0,
|
|
6
26
|
viewportPadding: 0,
|
|
@@ -13,23 +33,25 @@ const defaultOptions = {
|
|
|
13
33
|
*
|
|
14
34
|
* @param {Element} refEl
|
|
15
35
|
* @param {Element} popoverEl
|
|
16
|
-
* @param {Object}
|
|
17
|
-
* @param {number}
|
|
18
|
-
* @param {number}
|
|
19
|
-
* @param {string}
|
|
36
|
+
* @param {Object} opts - Options object
|
|
37
|
+
* @param {number} opts.offset - offset to refEl in px
|
|
38
|
+
* @param {number} opts.viewportPadding - Padding to viewport in px
|
|
39
|
+
* @param {string} opts.placement - One of the following keywords: top-right, top-left, top-center, bottom-right, bottom-left, bottom-center
|
|
20
40
|
*/
|
|
21
|
-
export const getPopoverPosition = (
|
|
22
|
-
|
|
41
|
+
export const getPopoverPosition = (
|
|
42
|
+
refEl: HTMLElement,
|
|
43
|
+
popoverEl: HTMLElement,
|
|
44
|
+
opts: PopoverPositionParams,
|
|
45
|
+
) => {
|
|
46
|
+
const options = Object.assign({}, defaultOptions, opts)
|
|
23
47
|
|
|
24
48
|
const refRect = refEl.getBoundingClientRect()
|
|
25
49
|
let popoverRect = popoverEl.getBoundingClientRect()
|
|
26
50
|
|
|
27
|
-
const
|
|
51
|
+
const { top: offsetTop, left: offsetLeft } = getOffsetParentPosition(refEl)
|
|
28
52
|
|
|
29
|
-
let top
|
|
30
|
-
let left
|
|
31
|
-
const offsetTop = offsetParent.top
|
|
32
|
-
const offsetLeft = offsetParent.left
|
|
53
|
+
let top = 0
|
|
54
|
+
let left = 0
|
|
33
55
|
const { vpWidth } = getViewportDimensions()
|
|
34
56
|
const docHeight = document.documentElement.scrollHeight
|
|
35
57
|
const scrollY = window.scrollY
|
|
@@ -46,7 +68,6 @@ export const getPopoverPosition = (refEl, popoverEl, options = {}) => {
|
|
|
46
68
|
case 'top-right':
|
|
47
69
|
top = offsetTop - popoverRect.height - options.offset
|
|
48
70
|
left = offsetLeft + refRect.width - popoverRect.width
|
|
49
|
-
// console.log(left)
|
|
50
71
|
break
|
|
51
72
|
|
|
52
73
|
case 'top-left':
|
|
@@ -77,10 +98,7 @@ export const getPopoverPosition = (refEl, popoverEl, options = {}) => {
|
|
|
77
98
|
const leftDiff = refRect.left - offsetLeft
|
|
78
99
|
if (left + leftDiff < options.viewportPadding) {
|
|
79
100
|
left = 0 - leftDiff + options.viewportPadding
|
|
80
|
-
} else if (
|
|
81
|
-
left + leftDiff + popoverRect.width >
|
|
82
|
-
vpWidth - options.viewportPadding
|
|
83
|
-
) {
|
|
101
|
+
} else if (left + leftDiff + popoverRect.width > vpWidth - options.viewportPadding) {
|
|
84
102
|
left = vpWidth - leftDiff - popoverRect.width - options.viewportPadding
|
|
85
103
|
}
|
|
86
104
|
|
|
@@ -88,23 +106,15 @@ export const getPopoverPosition = (refEl, popoverEl, options = {}) => {
|
|
|
88
106
|
const topDiff = refRect.top - offsetTop
|
|
89
107
|
if (top + topDiff + scrollY < options.viewportPadding) {
|
|
90
108
|
// popover is too high.
|
|
91
|
-
// console.log('too high')
|
|
92
109
|
// Switch position to bottom
|
|
93
110
|
top = offsetTop + refRect.height + options.offset
|
|
94
|
-
} else if (
|
|
95
|
-
top + topDiff + scrollY + popoverRect.height >
|
|
96
|
-
docHeight - options.viewportPadding
|
|
97
|
-
) {
|
|
111
|
+
} else if (top + topDiff + scrollY + popoverRect.height > docHeight - options.viewportPadding) {
|
|
98
112
|
// popover is too low.
|
|
99
|
-
// console.log('too low')
|
|
100
113
|
// Swith position to top.
|
|
101
114
|
top = offsetTop - popoverRect.height - options.offset
|
|
102
115
|
}
|
|
103
116
|
|
|
104
|
-
return {
|
|
105
|
-
top,
|
|
106
|
-
left,
|
|
107
|
-
}
|
|
117
|
+
return { top, left }
|
|
108
118
|
}
|
|
109
119
|
|
|
110
120
|
/**
|
|
@@ -112,21 +122,26 @@ export const getPopoverPosition = (refEl, popoverEl, options = {}) => {
|
|
|
112
122
|
*
|
|
113
123
|
* @param {Element} refEl
|
|
114
124
|
* @param {Element} popoverEl
|
|
115
|
-
* @param {Object}
|
|
116
|
-
* @param {number}
|
|
117
|
-
* @param {number}
|
|
118
|
-
* @param {string}
|
|
125
|
+
* @param {Object} opts - Options object
|
|
126
|
+
* @param {number} opts.offset - offset to refEl in px
|
|
127
|
+
* @param {number} opts.viewportPadding - Padding to viewport in px
|
|
128
|
+
* @param {string} opts.placement - One of the following keywords: top-right, top-left, top-center, bottom-right, bottom-left, bottom-center
|
|
119
129
|
*/
|
|
120
|
-
export
|
|
121
|
-
|
|
130
|
+
export function getPopoverPositionBody(
|
|
131
|
+
refEl: HTMLElement,
|
|
132
|
+
popoverEl: HTMLElement,
|
|
133
|
+
opts: PopoverPositionParams,
|
|
134
|
+
): PopoverPosition {
|
|
135
|
+
const options = Object.assign({}, defaultOptions, opts)
|
|
122
136
|
|
|
123
137
|
const refRect = refEl.getBoundingClientRect()
|
|
124
138
|
let popoverRect = popoverEl.getBoundingClientRect()
|
|
125
139
|
|
|
126
|
-
let top
|
|
127
|
-
let left
|
|
140
|
+
let top = 0
|
|
141
|
+
let left = 0
|
|
128
142
|
const topCorrection = 0
|
|
129
143
|
let leftCorrection = 0
|
|
144
|
+
|
|
130
145
|
const scrollY = window.scrollY
|
|
131
146
|
const scrollX = window.scrollX
|
|
132
147
|
const { vpWidth } = getViewportDimensions()
|
|
@@ -146,7 +161,6 @@ export const getPopoverPositionBody = (refEl, popoverEl, options = {}) => {
|
|
|
146
161
|
case 'top-right':
|
|
147
162
|
// top = offsetTop - popoverRect.height - options.offset
|
|
148
163
|
// left = offsetLeft + refRect.width - popoverRect.width
|
|
149
|
-
// console.log(left)
|
|
150
164
|
break
|
|
151
165
|
|
|
152
166
|
case 'top-left':
|
|
@@ -183,18 +197,12 @@ export const getPopoverPositionBody = (refEl, popoverEl, options = {}) => {
|
|
|
183
197
|
left = options.viewportPadding
|
|
184
198
|
} else if (left + popoverRect.width > vpWidth - options.viewportPadding) {
|
|
185
199
|
// too right
|
|
186
|
-
leftCorrection =
|
|
187
|
-
vpWidth - options.viewportPadding - popoverRect.width - left
|
|
200
|
+
leftCorrection = vpWidth - options.viewportPadding - popoverRect.width - left
|
|
188
201
|
left = vpWidth - options.viewportPadding - popoverRect.width
|
|
189
202
|
}
|
|
190
203
|
|
|
191
204
|
// Check top or bottom overflow
|
|
192
205
|
// TODO: Add these corrections
|
|
193
206
|
|
|
194
|
-
return {
|
|
195
|
-
top,
|
|
196
|
-
left,
|
|
197
|
-
leftCorrection,
|
|
198
|
-
topCorrection,
|
|
199
|
-
}
|
|
207
|
+
return { top, left, leftCorrection, topCorrection }
|
|
200
208
|
}
|
|
@@ -1,88 +1,68 @@
|
|
|
1
1
|
<script setup lang="ts">
|
|
2
2
|
import { ref } from 'vue'
|
|
3
3
|
import { getPopoverPositionBody } from './popover'
|
|
4
|
+
import type { PopoverPositionParams } from './popover'
|
|
4
5
|
|
|
5
|
-
interface Props {
|
|
6
|
+
interface Props extends PopoverPositionParams {
|
|
6
7
|
title: string
|
|
7
|
-
placement?:
|
|
8
|
-
| 'top-right'
|
|
9
|
-
| 'top-left'
|
|
10
|
-
| 'top-center'
|
|
11
|
-
| 'bottom-right'
|
|
12
|
-
| 'bottom-left'
|
|
13
|
-
| 'bottom-center'
|
|
14
|
-
offset?: number
|
|
15
|
-
viewportPadding?: number
|
|
16
8
|
}
|
|
17
9
|
|
|
18
|
-
const {
|
|
19
|
-
placement = 'top-center',
|
|
20
|
-
offset = 12,
|
|
21
|
-
viewportPadding = 20,
|
|
22
|
-
} = defineProps<Props>()
|
|
10
|
+
const { placement = 'top-center', offset = 12, viewportPadding = 20 } = defineProps<Props>()
|
|
23
11
|
|
|
24
|
-
const tooltip = ref(null)
|
|
25
|
-
const root = ref(null)
|
|
26
|
-
const pointer = ref(null)
|
|
12
|
+
const tooltip = ref<HTMLDivElement | null>(null)
|
|
13
|
+
const root = ref<HTMLSpanElement | null>(null)
|
|
14
|
+
const pointer = ref<HTMLSpanElement | null>(null)
|
|
15
|
+
const isTooltipVisible = ref<boolean>(false)
|
|
27
16
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
})
|
|
34
|
-
|
|
35
|
-
// Set position
|
|
36
|
-
tooltip.value.style.top = `${popoverPosition.top}px`
|
|
37
|
-
tooltip.value.style.left = `${popoverPosition.left}px`
|
|
38
|
-
|
|
39
|
-
// Fix pointer position if necessary
|
|
40
|
-
if (popoverPosition.leftCorrection) {
|
|
41
|
-
pointer.value.style.transform = `translateX(${popoverPosition.leftCorrection * -1}px)`
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
tooltip.value.ariaHidden = 'false'
|
|
45
|
-
tooltip.value.classList.toggle('tooltip--open', true)
|
|
17
|
+
// On desktop (or non-touch devices) mouse events will be fired before the pointer events.
|
|
18
|
+
// For mobile and touch devices it's th opposite, pointer events will be fired first.
|
|
19
|
+
// This way we can detect on what device we are and handle the behaviour accordingly.
|
|
20
|
+
function hideTooltip() {
|
|
21
|
+
isTooltipVisible.value = false
|
|
46
22
|
}
|
|
47
23
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
tooltip.value.style.top = ''
|
|
53
|
-
tooltip.value.style.left = ''
|
|
54
|
-
pointer.value.style.transform = ''
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
tooltip.value.addEventListener('transitionend', onTransitionend)
|
|
58
|
-
|
|
59
|
-
tooltip.value.ariaHidden = 'true'
|
|
60
|
-
tooltip.value.classList.toggle('tooltip--open', false)
|
|
24
|
+
function showTooltip() {
|
|
25
|
+
updatePositions()
|
|
26
|
+
isTooltipVisible.value = true
|
|
61
27
|
}
|
|
62
28
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
showTooltip()
|
|
29
|
+
function onTransitionend() {
|
|
30
|
+
if (isTooltipVisible.value) return
|
|
31
|
+
updatePositions(true)
|
|
32
|
+
isTooltipVisible.value = false
|
|
68
33
|
}
|
|
69
34
|
|
|
70
|
-
|
|
71
|
-
|
|
35
|
+
function updatePositions(resetPositions = false) {
|
|
36
|
+
const { top, left, leftCorrection } = getPopoverPositionBody(root.value!, tooltip.value!, {
|
|
37
|
+
offset,
|
|
38
|
+
placement,
|
|
39
|
+
viewportPadding,
|
|
40
|
+
})
|
|
41
|
+
|
|
42
|
+
tooltip.value!.style.top = resetPositions ? '' : `${top}px`
|
|
43
|
+
tooltip.value!.style.left = resetPositions ? '' : `${left}px`
|
|
44
|
+
// Fix pointer position if necessary
|
|
45
|
+
pointer.value!.style.transform =
|
|
46
|
+
!resetPositions && leftCorrection ? `translateX(${leftCorrection * -1}px)` : ''
|
|
72
47
|
}
|
|
73
48
|
</script>
|
|
74
49
|
|
|
75
50
|
<template>
|
|
76
|
-
<span
|
|
77
|
-
<slot
|
|
51
|
+
<span ref="root" @mouseenter="showTooltip" @mouseleave="hideTooltip">
|
|
52
|
+
<slot />
|
|
78
53
|
</span>
|
|
79
54
|
|
|
80
55
|
<Teleport to="body">
|
|
81
|
-
<div
|
|
56
|
+
<div
|
|
57
|
+
ref="tooltip"
|
|
58
|
+
:class="['tooltip', { 'tooltip--open': isTooltipVisible }]"
|
|
59
|
+
:aria-hidden="!isTooltipVisible"
|
|
60
|
+
@transitionend="onTransitionend"
|
|
61
|
+
>
|
|
82
62
|
<div class="tooltip__content">
|
|
83
63
|
{{ title }}
|
|
84
64
|
</div>
|
|
85
|
-
<span
|
|
65
|
+
<span ref="pointer" class="tooltip__pointer" />
|
|
86
66
|
</div>
|
|
87
67
|
</Teleport>
|
|
88
68
|
</template>
|