@energie360/ui-library 0.1.32 → 0.1.33

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 (40) hide show
  1. package/components/badge/badge.scss +11 -4
  2. package/components/badge/u-badge.vue +3 -13
  3. package/components/card-amount/u-card-amount.vue +1 -1
  4. package/components/card-amount-illustrated/u-card-amount-illustrated.vue +1 -1
  5. package/components/card-contact/u-card-contact.vue +1 -1
  6. package/components/card-highlight/u-card-highlight.vue +2 -2
  7. package/components/card-price-list/u-card-price-list.vue +2 -2
  8. package/components/card-statistic/card-statistic.scss +31 -0
  9. package/components/card-statistic/u-card-statistic.vue +34 -0
  10. package/components/chip/chip.scss +1 -0
  11. package/components/chip/u-chip.vue +1 -0
  12. package/components/download-list-item/u-download-list-item.vue +1 -0
  13. package/components/empty/empty.scss +27 -0
  14. package/components/empty/u-empty.vue +32 -0
  15. package/components/hint/hint.scss +106 -35
  16. package/components/hint/u-hint.vue +35 -4
  17. package/components/index.js +2 -0
  18. package/components/navigation-panel-tile/navigation-panel-tile.scss +0 -18
  19. package/components/navigation-panel-tile/u-navigation-panel-tile.vue +8 -7
  20. package/components/navigation-toolbar-link/u-navigation-toolbar-link.vue +2 -2
  21. package/components/progress-avatar/u-progress-avatar.vue +1 -1
  22. package/components/slider/u-slider.vue +1 -1
  23. package/components/slider-progress-animation/u-slider-progress-animation.vue +1 -1
  24. package/components/table/table-cell.scss +9 -0
  25. package/components/table/u-cell-icon-group.vue +1 -1
  26. package/components/table/u-cell-progress-bar.vue +1 -1
  27. package/components/table/u-table-cell.vue +3 -0
  28. package/components/text-block/u-text-block.vue +1 -1
  29. package/dist/base-style.css +2 -2
  30. package/elements/button/u-button.vue +2 -1
  31. package/layout/portal-block/portal-block.scss +36 -3
  32. package/layout/portal-block/u-portal-block.vue +9 -3
  33. package/modules/dialog/dialog.scss +28 -23
  34. package/modules/dialog/u-dialog.vue +14 -17
  35. package/modules/login-animation/u-login-animation.vue +2 -7
  36. package/modules/navigation-toolbar-top/u-navigation-toolbar-top.vue +24 -2
  37. package/package.json +9 -9
  38. package/utils/http/url.js +25 -0
  39. package/utils/vue/helpers.ts +27 -0
  40. package/wizard/wizard-outro/u-wizard-outro.vue +3 -0
@@ -31,11 +31,18 @@ $ease-out-bounce: cubic-bezier(0.674, 1.901, 0.651, 0.744);
31
31
  }
32
32
 
33
33
  .badge__sup.number {
34
- width: 16px;
34
+ min-width: 16px;
35
35
  height: 16px;
36
36
  font-size: 10px;
37
37
  font-weight: bold;
38
- line-height: 1;
38
+ line-height: 10px;
39
+ letter-spacing: 0;
40
+ border-radius: 24px;
41
+ text-align: center;
42
+ padding: 0 2px;
43
+ top: var(--top, -5px);
44
+ right: var(--right, 3px);
45
+ transform: translateX(50%);
39
46
  }
40
47
 
41
48
  .badge__sup.state {
@@ -48,8 +55,8 @@ $ease-out-bounce: cubic-bezier(0.674, 1.901, 0.651, 0.744);
48
55
  }
49
56
 
50
57
  .badge__sup.dot {
51
- top: var(--top, -3px);
52
- right: var(--right, -3px);
58
+ top: var(--top, -1px);
59
+ right: var(--right, -1px);
53
60
  width: 8px;
54
61
  height: 8px;
55
62
  border-radius: 100%;
@@ -2,11 +2,9 @@
2
2
  export interface Badge {
3
3
  color?: string
4
4
  dot?: boolean
5
- type?: 'default' | 'success' | 'error' | 'warning' | 'info'
6
5
  value?: number
7
6
  state?: '' | 'inactive'
8
7
  show?: boolean
9
- border?: boolean
10
8
  top?: number
11
9
  right?: number
12
10
  }
@@ -17,7 +15,6 @@ const {
17
15
  top = 0,
18
16
  right = 0,
19
17
  color = 'var(--e-c-mono-900)',
20
- type = 'default',
21
18
  show = true,
22
19
  } = defineProps<Badge>()
23
20
  </script>
@@ -32,23 +29,16 @@ const {
32
29
  >
33
30
  <slot></slot>
34
31
 
35
- <sup
36
- v-if="dot"
37
- class="badge__sup dot"
38
- :class="[`type-${type}`, { border }]"
39
- :style="{ backgroundColor: color }"
40
- ></sup>
32
+ <sup v-if="dot" class="badge__sup dot" :style="{ backgroundColor: color }"></sup>
41
33
 
42
34
  <sup
43
35
  v-else-if="state"
44
36
  class="badge__sup state"
45
- :class="[state, { border }]"
37
+ :class="[state]"
46
38
  :style="{ backgroundColor: color }"
47
39
  ></sup>
48
40
 
49
- <sup v-else class="badge__sup number" :class="{ border }" :style="{ backgroundColor: color }">{{
50
- value
51
- }}</sup>
41
+ <sup v-else class="badge__sup number" :style="{ backgroundColor: color }">{{ value }}</sup>
52
42
  </span>
53
43
  </template>
54
44
 
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { UProgressBar } from '../'
2
+ import UProgressBar from '../progress-bar/u-progress-bar.vue'
3
3
 
4
4
  interface Props {
5
5
  progress: number
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { UProgressBar } from '../'
2
+ import UProgressBar from '../progress-bar/u-progress-bar.vue'
3
3
  import { useTemplateRef, inject, watch, ref, onMounted, onUnmounted } from 'vue'
4
4
  import { Image } from '../../elements/types'
5
5
  import '@lottiefiles/lottie-player'
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  import { Image } from '../../elements/types'
3
- import { URichtext } from '../'
3
+ import URichtext from '../richtext/u-richtext.vue'
4
4
 
5
5
  interface Props {
6
6
  name?: string
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import { Chip } from '../chip/u-chip.vue'
3
3
  import { Image } from '../../elements/types'
4
- import { UIcon } from '../../elements'
5
- import { UChip } from '../'
4
+ import UIcon from '../../elements/icon/u-icon.vue'
5
+ import UChip from '../chip/u-chip.vue'
6
6
 
7
7
  interface Props {
8
8
  title?: string
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { UIcon } from '../../elements'
3
- import { UTooltip } from '../'
2
+ import UIcon from '../../elements/icon/u-icon.vue'
3
+ import UTooltip from '../tooltip/u-tooltip.vue'
4
4
 
5
5
  interface PriceItem {
6
6
  name: string
@@ -0,0 +1,31 @@
1
+ @use '../../base/abstracts' as a;
2
+
3
+ .card-statistic {
4
+ display: flex;
5
+ justify-content: space-between;
6
+ column-gap: var(--e-space-2);
7
+ }
8
+
9
+ .card-statistic__image {
10
+ width: 80px;
11
+ height: 80px;
12
+ flex: 0 0 auto;
13
+ }
14
+
15
+ .card-statistic__content {
16
+ display: flex;
17
+ flex-direction: column;
18
+ row-gap: var(--e-space-2);
19
+ }
20
+
21
+ .card-statistic__label {
22
+ @include a.type(200);
23
+
24
+ order: 2;
25
+ }
26
+
27
+ .card-statistic__value {
28
+ @include a.type(700, strong);
29
+
30
+ order: 1;
31
+ }
@@ -0,0 +1,34 @@
1
+ <script setup lang="ts">
2
+ import { Image } from '../../elements/types'
3
+ import { hasSlotContent } from '../../utils/vue/helpers'
4
+
5
+ interface Props {
6
+ label?: string
7
+ value?: string
8
+ image?: Image
9
+ }
10
+
11
+ defineProps<Props>()
12
+ </script>
13
+
14
+ <template>
15
+ <div class="card-statistic">
16
+ <dl class="card-statistic__content">
17
+ <dt class="card-statistic__label">
18
+ <slot name="label">{{ label }}</slot>
19
+ </dt>
20
+
21
+ <dd class="card-statistic__value">
22
+ <slot name="value">{{ value }}</slot>
23
+ </dd>
24
+ </dl>
25
+
26
+ <div v-if="image || hasSlotContent($slots.image)" class="card-statistic__image">
27
+ <slot name="image">
28
+ <img v-bind="image" />
29
+ </slot>
30
+ </div>
31
+ </div>
32
+ </template>
33
+
34
+ <style scoped lang="scss" src="./card-statistic.scss"></style>
@@ -36,6 +36,7 @@
36
36
  }
37
37
 
38
38
  .chip__icon {
39
+ margin-left: calc(var(--e-space-1) * -1);
39
40
  color: var(--chip-icon-color);
40
41
  }
41
42
 
@@ -1,4 +1,5 @@
1
1
  <script setup lang="ts">
2
+ // TODO: Rename this component. It's not really a 'chip'.
2
3
  import { UIcon } from '../../elements'
3
4
 
4
5
  export interface Chip {
@@ -66,6 +66,7 @@ defineExpose({ click })
66
66
  <td class="download-list-item__cell cta">
67
67
  <component
68
68
  :is="url ? 'a' : 'button'"
69
+ v-bind="$attrs"
69
70
  ref="link"
70
71
  class="download-list-item__link"
71
72
  :href="url"
@@ -0,0 +1,27 @@
1
+ @use '../../base/abstracts' as a;
2
+
3
+ .empty {
4
+ text-align: center;
5
+ }
6
+
7
+ .empty__image {
8
+ margin: 0 auto var(--e-space-6);
9
+ width: a.rem(120);
10
+ height: a.rem(120);
11
+
12
+ > img {
13
+ object-fit: cover;
14
+ width: 100%;
15
+ height: 100%;
16
+ }
17
+ }
18
+
19
+ .empty__title {
20
+ @include a.type(600);
21
+
22
+ margin-bottom: var(--e-space-6);
23
+ }
24
+
25
+ .empty__text {
26
+ @include a.type(300, strong);
27
+ }
@@ -0,0 +1,32 @@
1
+ <script setup lang="ts">
2
+ import { hasSlotContent } from '../../utils/vue/helpers'
3
+
4
+ interface Props {
5
+ title?: string
6
+ text?: string
7
+ }
8
+
9
+ defineProps<Props>()
10
+ </script>
11
+
12
+ <template>
13
+ <div class="empty">
14
+ <div class="empty__image">
15
+ <slot name="image">
16
+ <img src="/static/ui-assets/images/search.svg" alt="" />
17
+ </slot>
18
+ </div>
19
+
20
+ <p v-if="title || hasSlotContent($slots.title)" class="empty__title">
21
+ <slot name="title">
22
+ {{ title }}
23
+ </slot>
24
+ </p>
25
+
26
+ <p v-if="text || hasSlotContent($slots.text)" class="empty__text">
27
+ <slot name="text"> {{ text }} </slot>
28
+ </p>
29
+ </div>
30
+ </template>
31
+
32
+ <style scoped lang="scss" src="./empty.scss"></style>
@@ -7,10 +7,6 @@
7
7
  background-color: var(--e-c-secondary-05-100);
8
8
  color: var(--e-c-secondary-05-900);
9
9
 
10
- &::before {
11
- background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2Zm0 15c-.55 0-1-.45-1-1v-4c0-.55.45-1 1-1s1 .45 1 1v4c0 .55-.45 1-1 1Zm1-8h-2V7h2v2Z' fill='%230096DC'/%3E%3C/svg%3E");
12
- }
13
-
14
10
  .hint__richtext {
15
11
  margin-left: var(--e-space-9);
16
12
 
@@ -19,6 +15,10 @@
19
15
  }
20
16
  }
21
17
 
18
+ .hint__icon {
19
+ color: var(--e-c-secondary-05-500);
20
+ }
21
+
22
22
  // override some richtext styles.
23
23
  .richtext {
24
24
  a {
@@ -37,10 +37,6 @@
37
37
  background-color: var(--e-c-signal-02-100);
38
38
  color: var(--e-c-signal-02-900);
39
39
 
40
- &::before {
41
- background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M1 21 12 2l11 19H1Zm11-3c.283 0 .52-.096.713-.288A.968.968 0 0 0 13 17a.968.968 0 0 0-.287-.712A.968.968 0 0 0 12 16a.968.968 0 0 0-.713.288A.968.968 0 0 0 11 17c0 .283.096.52.287.712.192.192.43.288.713.288Zm-1-3h2v-5h-2v5Z' fill='%23FF9800'/%3E%3C/svg%3E");
42
- }
43
-
44
40
  .hint__richtext {
45
41
  margin-left: var(--e-space-9);
46
42
 
@@ -49,6 +45,10 @@
49
45
  }
50
46
  }
51
47
 
48
+ .hint__icon {
49
+ color: var(--e-c-signal-02-500);
50
+ }
51
+
52
52
  // override some richtext styles.
53
53
  .richtext {
54
54
  a {
@@ -76,17 +76,18 @@
76
76
  border-left-color: var(--e-c-mono-700);
77
77
  color: var(--e-c-mono-900);
78
78
 
79
- &::before {
80
- background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath fill-rule='evenodd' clip-rule='evenodd' d='m12.86 3.404-.81.81 3.352 3.352.81-.81-3.353-3.352Zm.707-1.697a1 1 0 0 0-1.415 0l-1.8 1.8a1 1 0 0 0-.01 1.404L6.952 8.303a1 1 0 0 0-1.404.01l-1.84 1.84a1 1 0 0 0 0 1.414l4.342 4.342a1 1 0 0 0 1.414 0l1.84-1.84a1 1 0 0 0 0-1.414l-.092-.092.93-.93L20.6 20l1.4-1.4-8.46-8.366 1.063-1.062.091.091a1 1 0 0 0 1.415 0l1.8-1.8a1 1 0 0 0 0-1.414l-4.342-4.342ZM6.253 10.01l-.85.85 3.352 3.352.85-.85-3.352-3.352Zm1.975-.581 3.235-3.235L13.32 8.05l-3.235 3.235L8.23 9.429ZM16 21H4v-2h12v2Z' fill='%236B6B6B'/%3E%3C/svg%3E");
81
- top: 0;
82
- left: calc(
83
- -24px - 12px - 4px
84
- ); // 24px: icon width, 12px: distance to box, 4px: additonal border-width
79
+ .hint__label,
80
+ .hint__richtext {
81
+ margin-left: 0;
85
82
  }
86
83
 
87
- > p,
88
- .richtext {
89
- margin-left: 0;
84
+ &.no-label .hint__label {
85
+ height: 0;
86
+ min-height: 0;
87
+
88
+ @include a.bp(lg) {
89
+ margin-top: -8px;
90
+ }
90
91
  }
91
92
 
92
93
  .hint__link {
@@ -96,13 +97,22 @@
96
97
  }
97
98
  }
98
99
 
100
+ .hint__icon {
101
+ top: 0;
102
+ left: calc(
103
+ -24px - 12px - 4px
104
+ ); // 24px: icon width, 12px: distance to box, 4px: additonal border-width
105
+
106
+ color: var(--e-c-mono-700);
107
+ }
108
+
99
109
  @include a.bp(lg) {
100
110
  margin-top: calc(4px + 4px + 24px);
101
111
  border: 1px solid var(--e-c-mono-500);
102
112
  border-top-width: 4px;
103
113
  border-top-color: var(--e-c-mono-700);
104
114
 
105
- &::before {
115
+ .hint__icon {
106
116
  left: 0;
107
117
  top: calc(
108
118
  -24px - 4px - 4px
@@ -115,10 +125,42 @@
115
125
  background-color: var(--e-c-signal-03-100);
116
126
  color: var(--e-c-signal-03-900);
117
127
 
118
- &::before {
119
- background-image: url("data:image/svg+xml,%3Csvg width='24' height='24' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M15.1396 0C15.4048 0 15.6592 0.105542 15.8467 0.292969L21.707 6.15332C21.8945 6.34084 22 6.59522 22 6.86035V15.1396C22 15.4048 21.8945 15.6592 21.707 15.8467L15.8467 21.707C15.6592 21.8945 15.4048 22 15.1396 22H6.86035C6.59522 22 6.34084 21.8945 6.15332 21.707L0.292969 15.8467C0.105542 15.6592 0 15.4048 0 15.1396V6.86035C0 6.59522 0.105542 6.34084 0.292969 6.15332L6.15332 0.292969C6.34084 0.105542 6.59522 0 6.86035 0H15.1396ZM11 14C10.4477 14 10 14.4477 10 15C10 15.5523 10.4477 16 11 16H11.0098C11.5621 16 12.0098 15.5523 12.0098 15C12.0098 14.4477 11.5621 14 11.0098 14H11ZM11 6C10.4477 6 10 6.44772 10 7V11C10 11.5523 10.4477 12 11 12C11.5523 12 12 11.5523 12 11V7C12 6.44772 11.5523 6 11 6Z' fill='%23FF0C3E'/%3E%3C/svg%3E");
128
+ .hint__richtext {
129
+ margin-left: var(--e-space-9);
130
+
131
+ @include a.bp(lg) {
132
+ margin-left: 0;
133
+ }
120
134
  }
121
135
 
136
+ // override some richtext styles.
137
+ .richtext {
138
+ a {
139
+ color: inherit;
140
+
141
+ &:active,
142
+ &:hover {
143
+ text-decoration-color: var(--e-c-signal-03-100);
144
+ }
145
+ }
146
+ }
147
+
148
+ .hint__link {
149
+ &:active,
150
+ &:hover {
151
+ text-decoration-color: var(--e-c-signal-03-100);
152
+ }
153
+ }
154
+
155
+ .hint__icon {
156
+ color: var(--e-c-signal-03-500);
157
+ }
158
+ }
159
+
160
+ @mixin type-task {
161
+ background-color: var(--e-c-secondary-03-100);
162
+ color: var(--e-c-signal-03-900);
163
+
122
164
  .hint__richtext {
123
165
  margin-left: var(--e-space-9);
124
166
 
@@ -127,6 +169,10 @@
127
169
  }
128
170
  }
129
171
 
172
+ .hint__icon {
173
+ color: var(--e-c-secondary-03-500);
174
+ }
175
+
130
176
  // override some richtext styles.
131
177
  .richtext {
132
178
  a {
@@ -160,7 +206,6 @@
160
206
 
161
207
  display: inline-block;
162
208
  color: inherit;
163
- margin-left: var(--e-space-2);
164
209
 
165
210
  &:active,
166
211
  &:hover {
@@ -169,11 +214,19 @@
169
214
 
170
215
  @include a.bp(lg) {
171
216
  display: block;
172
- margin-left: 0;
173
217
  }
174
218
  }
175
219
 
176
220
  .hint__label {
221
+ @include a.type(200, strong);
222
+
223
+ margin-left: var(--e-space-9);
224
+
225
+ @include a.bp(lg) {
226
+ margin-left: var(--e-space-7);
227
+ min-height: 24px;
228
+ }
229
+
177
230
  + .hint__richtext {
178
231
  margin-top: var(--e-space-1);
179
232
 
@@ -183,29 +236,35 @@
183
236
  }
184
237
  }
185
238
 
186
- &::before {
187
- content: '';
188
- position: absolute;
189
- top: var(--e-space-5);
190
- left: var(--e-space-4);
191
- width: a.rem(24);
192
- height: a.rem(24);
193
- }
194
-
195
- > p {
196
- @include a.type(200, strong);
239
+ .hint__label-text + .hint__link {
240
+ margin-left: var(--e-space-2);
197
241
 
198
- margin-left: var(--e-space-9);
242
+ @include a.bp(lg) {
243
+ margin-left: 0;
244
+ }
199
245
  }
200
246
 
201
247
  @include a.bp(lg) {
202
248
  padding: var(--e-space-4);
203
249
 
204
- &::before {
250
+ .hint__icon {
205
251
  top: var(--e-space-4);
206
252
  }
207
253
  }
208
254
 
255
+ // States
256
+ &.no-label {
257
+ .hint__label {
258
+ // This gives same layout as when `.hint__label` was removed from DOM.
259
+ // We can't remove it because wew need this element for mobile layout.
260
+ margin-top: -4px;
261
+
262
+ @include a.bp(lg) {
263
+ margin-top: 0;
264
+ }
265
+ }
266
+ }
267
+
209
268
  // Modifiers
210
269
  &.hint--warning {
211
270
  @include type-warning;
@@ -218,4 +277,16 @@
218
277
  &.hint--error {
219
278
  @include type-error;
220
279
  }
280
+
281
+ &.hint--task {
282
+ @include type-task;
283
+ }
284
+ }
285
+
286
+ .hint__icon {
287
+ position: absolute;
288
+ top: var(--e-space-5);
289
+ left: var(--e-space-4);
290
+ width: a.rem(24);
291
+ height: a.rem(24);
221
292
  }
@@ -1,21 +1,43 @@
1
1
  <script setup lang="ts">
2
- import { URichtext } from '../'
2
+ import URichtext from '../richtext/u-richtext.vue'
3
3
  import { Cta } from '../../elements/types'
4
+ import { hasSlotContent } from '../../utils/vue/helpers'
5
+ import UIcon from '../../elements/icon/u-icon.vue'
4
6
 
5
7
  interface Props {
6
- type?: 'neutral' | 'warning' | 'legal' | 'error'
8
+ type?: 'neutral' | 'warning' | 'legal' | 'error' | 'task'
7
9
  label?: string
8
10
  link?: Cta
9
11
  text?: string
10
12
  }
11
13
 
12
14
  const { label = '', link = undefined, text = '', type = 'neutral' } = defineProps<Props>()
15
+
16
+ // default mapping 'type' -> icon
17
+ const iconMap = {
18
+ neutral: 'info-circle-filled',
19
+ warning: 'alert-triangle-filled',
20
+ legal: 'legal',
21
+ error: 'alert-octagon-filled',
22
+ task: 'check-circle-filled',
23
+ }
13
24
  </script>
14
25
 
15
26
  <template>
16
- <div :class="['hint', `hint--${type}`]">
27
+ <div
28
+ :class="[
29
+ 'hint',
30
+ `hint--${type}`,
31
+ { 'no-label': !label && !hasSlotContent($slots.label) && !link },
32
+ ]"
33
+ >
34
+ <span class="hint__icon">
35
+ <slot name="icon"> <UIcon :name="iconMap[type]" /> </slot>
36
+ </span>
17
37
  <p class="hint__label">
18
- <slot name="label">{{ label }}</slot>
38
+ <span v-if="label || hasSlotContent($slots.label)" class="hint__label-text">
39
+ <slot name="label">{{ label }}</slot>
40
+ </span>
19
41
 
20
42
  <a v-if="link" :href="link.href" :target="link.target" class="hint__link">
21
43
  {{ link.label }}
@@ -68,4 +90,13 @@ const { label = '', link = undefined, text = '', type = 'neutral' } = defineProp
68
90
  }
69
91
  }
70
92
  }
93
+
94
+ .hint--error {
95
+ .hint__richtext a {
96
+ &:active,
97
+ &:hover {
98
+ text-decoration-color: var(--e-c-secondary-03-100);
99
+ }
100
+ }
101
+ }
71
102
  </style>
@@ -72,3 +72,5 @@ export { default as UNotificationItem } from './notification-item/u-notification
72
72
  export { default as UNotificationList } from './notification-list/u-notification-list.vue'
73
73
  export { default as UDefinitionList } from './definition-list/u-definition-list.vue'
74
74
  export { default as UDefinitionListItem } from './definition-list-item/u-definition-list-item.vue'
75
+ export { default as UCardStatistic } from './card-statistic/u-card-statistic.vue'
76
+ export { default as UEmpty } from './empty/u-empty.vue'
@@ -82,24 +82,6 @@
82
82
  }
83
83
  }
84
84
 
85
- .badge {
86
- position: absolute;
87
- top: -2px;
88
- right: -2px;
89
- width: a.rem(13);
90
- height: a.rem(13);
91
- background-color: var(--e-c-mono-900);
92
- border-radius: 100%;
93
-
94
- &.inactive {
95
- background-image: url("data:image/svg+xml,%3Csvg width='13' height='13' fill='none' xmlns='http://www.w3.org/2000/svg'%3E%3Ccircle cx='6.5' cy='6.5' r='6.5' fill='%23333'/%3E%3Cpath d='m4.414 3 2.12 2.121L8.658 3l1.414 1.414-2.122 2.122 2.122 2.121-1.414 1.414-2.122-2.12-2.12 2.12L3 8.656l2.12-2.12L3 4.412 4.414 3Z' fill='%23fff'/%3E%3C/svg%3E");
96
- }
97
- }
98
-
99
85
  .navigation-panel-tile__text-column {
100
86
  flex: 1 1 auto;
101
87
  }
102
-
103
- .badge + .icon {
104
- color: var(--e-c-mono-500);
105
- }
@@ -1,8 +1,8 @@
1
1
  <script setup lang="ts">
2
2
  import { computed } from 'vue'
3
3
  import { UIcon } from '../../elements'
4
-
5
- type IconBadge = 'inactive'
4
+ import UBadge from '../../components/badge/u-badge.vue'
5
+ import type { Badge } from '../badge/u-badge.vue'
6
6
 
7
7
  interface Props {
8
8
  active?: boolean
@@ -11,7 +11,7 @@ interface Props {
11
11
  greyed?: boolean
12
12
  href?: string
13
13
  icon?: string
14
- iconBadge?: IconBadge
14
+ badge?: Badge
15
15
  target?: string
16
16
  title?: string
17
17
  }
@@ -19,7 +19,7 @@ interface Props {
19
19
  const {
20
20
  description = '',
21
21
  icon = '',
22
- iconBadge = undefined,
22
+ badge = undefined,
23
23
  title = '',
24
24
  href = '',
25
25
  target = '_self',
@@ -53,9 +53,10 @@ const tag = computed(() => (href ? 'a' : 'div'))
53
53
 
54
54
  <div class="navigation-panel-tile__icon-column">
55
55
  <div v-if="icon" class="navigation-panel-tile__icon-wrapper">
56
- <span v-if="iconBadge" :class="['badge', iconBadge]" />
57
-
58
- <UIcon :name="icon" />
56
+ <UBadge v-if="badge" v-bind="badge">
57
+ <UIcon :name="icon" />
58
+ </UBadge>
59
+ <UIcon v-else :name="icon" />
59
60
  </div>
60
61
  </div>
61
62
  </component>
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { ref, watch, computed, useTemplateRef } from 'vue'
3
- import { UIcon } from '../../elements'
4
- import { UBadge } from '../../components'
3
+ import UIcon from '../../elements/icon/u-icon.vue'
4
+ import UBadge from '../../components/badge/u-badge.vue'
5
5
  import { Badge } from '../badge/u-badge.vue'
6
6
 
7
7
  // TODO: Label animation when collapsed is a mess. Refactor it as soon as possbile!
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { onMounted, useTemplateRef, ref } from 'vue'
3
3
  import { Image } from '../../elements/types'
4
- import { UCircularProgress } from '../'
4
+ import UCircularProgress from '../circular-progress/u-circular-progress.vue'
5
5
 
6
6
  interface Props {
7
7
  image: Image
@@ -2,7 +2,7 @@
2
2
  import { watch, useId, useTemplateRef, onMounted, ref } from 'vue'
3
3
  import { scaleValue } from '../../utils/math/scale-value'
4
4
  import { clamp } from '../../utils/math/clamp'
5
- import { UTooltip } from '../'
5
+ import UTooltip from '../tooltip/u-tooltip.vue'
6
6
 
7
7
  interface SliderDot {
8
8
  value: number
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import { onMounted, onUnmounted, ref, useTemplateRef } from 'vue'
3
3
  import '@lottiefiles/lottie-player'
4
- import { USlider } from '../'
4
+ import USlider from '../slider/u-slider.vue'
5
5
  import { Slider } from '../slider/u-slider.vue'
6
6
  import { Image } from '../../elements/types'
7
7
 
@@ -27,3 +27,12 @@
27
27
  vertical-align: middle;
28
28
  color: var(--e-c-secondary-05-500);
29
29
  }
30
+
31
+ .table-cell__dot {
32
+ align-self: center;
33
+ display: block;
34
+ width: 8px;
35
+ height: 8px;
36
+ border-radius: 100%;
37
+ background-color: transparent;
38
+ }
@@ -1,7 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import UIcon from '../../elements/icon/u-icon.vue'
3
3
  import UTooltip from '../tooltip/u-tooltip.vue'
4
- import { UBadge } from '../'
4
+ import UBadge from '../badge/u-badge.vue'
5
5
  import { Badge } from '../badge/u-badge.vue'
6
6
  import { TableCellIconColor } from './table.type'
7
7
 
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { UProgressBar } from '../'
2
+ import UProgressBar from '../progress-bar/u-progress-bar.vue'
3
3
 
4
4
  interface Props {
5
5
  label?: string
@@ -7,6 +7,7 @@ interface Props extends TableCellBase {
7
7
  infoText?: string
8
8
  textStyle?: TableCellTextStyle
9
9
  nowrap?: boolean
10
+ dotColor?: string
10
11
  }
11
12
 
12
13
  const {
@@ -15,6 +16,7 @@ const {
15
16
  hAlign = TableCellHAlign.left,
16
17
  vAlign = TableCellVAlign.center,
17
18
  nowrap = false,
19
+ dotColor = '',
18
20
  } = defineProps<Props>()
19
21
  </script>
20
22
 
@@ -24,6 +26,7 @@ const {
24
26
  :class="['table-cell', `h-align-${hAlign}`, `v-align-${vAlign}`, { 'has-tooltip': infoText }]"
25
27
  >
26
28
  <div :class="['cell-content', `text-${textStyle}`, { nowrap }]">
29
+ <span v-if="dotColor" class="table-cell__dot" :style="{ backgroundColor: dotColor }"></span>
27
30
  <slot><span v-html="text" /></slot>
28
31
  </div>
29
32
 
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { URichtext } from '../'
2
+ import URichtext from '../richtext/u-richtext.vue'
3
3
 
4
4
  interface Props {
5
5
  variant: 'lead' | 'sub'
@@ -317,8 +317,8 @@ body {
317
317
  --e-c-signal-02-700-rgb: 204, 122, 0;
318
318
  --e-c-signal-02-900: #703600;
319
319
  --e-c-signal-02-900-rgb: 112, 54, 0;
320
- --e-c-signal-03-100: #ffb5c4;
321
- --e-c-signal-03-100-rgb: 255, 181, 196;
320
+ --e-c-signal-03-100: #ffccd7;
321
+ --e-c-signal-03-100-rgb: 255, 204, 215;
322
322
  --e-c-signal-03-500: #ff0c3e;
323
323
  --e-c-signal-03-500-rgb: 255, 12, 62;
324
324
  --e-c-signal-03-700: #b90d31;
@@ -1,5 +1,6 @@
1
1
  <script setup lang="ts">
2
- import { UIcon, ULoader } from '../'
2
+ import ULoader from '../loader/u-loader.vue'
3
+ import UIcon from '../icon/u-icon.vue'
3
4
  import { computed } from 'vue'
4
5
 
5
6
  // TODO: use enums for variant prop.
@@ -1,8 +1,41 @@
1
1
  .portal-block {
2
- // default 'small'
3
- margin-bottom: var(--e-space-6);
2
+ &.bottom-space-0 {
3
+ margin-bottom: 0;
4
+ }
5
+
6
+ &.bottom-space-100 {
7
+ margin-bottom: var(--e-space-6);
8
+ }
4
9
 
5
- &.big {
10
+ &.bottom-space-300 {
6
11
  margin-bottom: var(--e-space-10);
7
12
  }
13
+
14
+ &.bottom-space-500 {
15
+ margin-bottom: var(--e-space-16);
16
+ }
17
+
18
+ &.bottom-space-700 {
19
+ margin-bottom: var(--e-space-28);
20
+ }
21
+
22
+ &.top-space-0 {
23
+ margin-top: 0;
24
+ }
25
+
26
+ &.top-space-100 {
27
+ margin-top: var(--e-space-6);
28
+ }
29
+
30
+ &.top-space-300 {
31
+ margin-top: var(--e-space-10);
32
+ }
33
+
34
+ &.top-space-500 {
35
+ margin-top: var(--e-space-16);
36
+ }
37
+
38
+ &.top-space-700 {
39
+ margin-top: var(--e-space-28);
40
+ }
8
41
  }
@@ -1,14 +1,20 @@
1
1
  <script setup lang="ts">
2
+ /*
3
+ Preferably only use `bottom-space`.
4
+ `top-space` can make sense for last portal-block for example.
5
+ This works because parent container is `display: block`, meaning that the vertical margins will collapse.
6
+ */
2
7
  interface Props {
3
8
  big?: boolean
4
- space?: 'small' | 'big'
9
+ bottomSpace?: '0' | '100' | '300' | '500' | '700'
10
+ topSpace?: '0' | '100' | '300' | '500' | '700'
5
11
  }
6
12
 
7
- const { space = 'small' } = defineProps<Props>()
13
+ const { bottomSpace = '100', topSpace = '0' } = defineProps<Props>()
8
14
  </script>
9
15
 
10
16
  <template>
11
- <div :class="['portal-block', space]">
17
+ <div :class="['portal-block', `bottom-space-${bottomSpace}`, `top-space-${topSpace}`]">
12
18
  <slot />
13
19
  </div>
14
20
  </template>
@@ -4,6 +4,7 @@
4
4
 
5
5
  .dialog-container {
6
6
  --overflow-gradient-height: var(--e-space-10);
7
+ --dialog-max-width: #{a.rem(540)};
7
8
 
8
9
  padding: 0;
9
10
  border: 0;
@@ -26,18 +27,6 @@
26
27
  opacity: 0.4;
27
28
  animation: fade-in-backdrop var(--e-trs-duration-faster) var(--e-trs-easing-default);
28
29
  }
29
-
30
- &.has-header-image {
31
- .dialog__header-image-container {
32
- display: flex;
33
- }
34
- }
35
-
36
- &.has-content-image {
37
- .dialog__content-image {
38
- display: block;
39
- }
40
- }
41
30
  }
42
31
 
43
32
  .dialog {
@@ -47,14 +36,22 @@
47
36
  overflow: auto;
48
37
  scrollbar-width: none;
49
38
  border-radius: var(--e-brd-radius-3);
50
- padding: var(--e-space-7) var(--e-space-7) 0;
39
+ padding: var(--e-space-8) var(--e-space-8) 0;
51
40
  height: a.rem(670);
52
41
  max-height: 80vh;
53
- max-width: a.rem(540);
42
+ max-width: var(--dialog-max-width);
54
43
  min-width: a.rem(480);
55
44
 
56
- &.auto-height {
45
+ &.fit-height {
57
46
  height: auto;
47
+
48
+ .cta-container {
49
+ margin-top: 0;
50
+
51
+ &::after {
52
+ content: none;
53
+ }
54
+ }
58
55
  }
59
56
 
60
57
  * + .dialog__content-image {
@@ -67,13 +64,14 @@
67
64
  min-width: 0;
68
65
  height: auto;
69
66
  min-height: a.rem(440);
67
+ padding-top: var(--e-space-6);
70
68
  padding-left: var(--e-space-5);
71
69
  padding-right: var(--e-space-5);
72
70
  }
73
71
  }
74
72
 
75
73
  .dialog__header-image-container {
76
- display: none;
74
+ display: flex;
77
75
  justify-content: center;
78
76
  height: a.rem(184);
79
77
  margin-bottom: var(--e-space-8);
@@ -105,8 +103,6 @@
105
103
  }
106
104
 
107
105
  .dialog__content-image {
108
- display: none;
109
-
110
106
  img {
111
107
  width: 100%;
112
108
  }
@@ -116,10 +112,10 @@
116
112
  position: sticky;
117
113
  display: flex;
118
114
  flex-direction: column;
119
- row-gap: var(--e-space-4);
115
+ gap: var(--e-space-4) var(--e-space-3);
120
116
  bottom: 0;
121
117
  margin-top: var(--overflow-gradient-height);
122
- padding: var(--e-space-8) var(--e-space-1) var(--e-space-7);
118
+ padding: var(--e-space-8) var(--e-space-1) var(--e-space-8);
123
119
  background-color: var(--e-c-mono-00);
124
120
 
125
121
  &::after {
@@ -132,13 +128,22 @@
132
128
  background-image: linear-gradient(0deg, rgb(255 255 255 / 100%) 0%, rgb(255 255 255 / 0%) 100%);
133
129
  }
134
130
 
131
+ &.cta-inline {
132
+ display: grid;
133
+ grid-template-columns: 1fr 1fr;
134
+ }
135
+
135
136
  @include a.bp(m) {
136
- padding: var(--e-space-8) 0 var(--e-space-7);
137
+ padding: var(--e-space-8) 0 var(--e-space-6);
138
+
139
+ &.cta-inline {
140
+ display: flex;
141
+ }
137
142
  }
138
143
  }
139
144
 
140
145
  // MOBILE "MODAL" Variant
141
- .dialog-container.modal {
146
+ .dialog-container.variant-modal {
142
147
  @include a.bp(m) {
143
148
  padding-left: a.rem(a.$container-edge-m);
144
149
  padding-right: a.rem(a.$container-edge-m);
@@ -151,7 +156,7 @@
151
156
  }
152
157
 
153
158
  // MOBILE "SLIDE-OUT" Variant
154
- .dialog-container.slideout {
159
+ .dialog-container.variant-slideout {
155
160
  @include a.bp(m) {
156
161
  align-items: flex-end;
157
162
 
@@ -11,9 +11,10 @@ interface Props {
11
11
  headerImage?: Image
12
12
  contentImage?: Image
13
13
  closeBtnLabel?: string
14
- autoHeight?: boolean
15
14
  modal?: boolean
16
15
  mobileDialogStyle?: 'modal' | 'slideout'
16
+ ctaInline?: boolean
17
+ fitHeight?: boolean
17
18
  }
18
19
 
19
20
  const {
@@ -24,6 +25,8 @@ const {
24
25
  contentImage = undefined,
25
26
  mobileDialogStyle = 'modal',
26
27
  closeBtnLabel = getTranslation('close'),
28
+ ctaInline = false,
29
+ fitHeight = false,
27
30
  } = defineProps<Props>()
28
31
 
29
32
  const visible = defineModel<boolean>('visible')
@@ -84,18 +87,11 @@ watch(visible, (newV) => {
84
87
  <dialog
85
88
  ref="dialog"
86
89
  closedby="none"
87
- :class="[
88
- 'dialog-container',
89
- mobileDialogStyle,
90
- {
91
- 'has-header-image': !!slots['header-image'] || (headerImage?.src && headerImage?.alt),
92
- 'has-content-image': !!slots['content-image'] || contentImage,
93
- },
94
- ]"
90
+ :class="['dialog-container', `variant-${mobileDialogStyle}`]"
95
91
  >
96
- <div :class="['dialog', { 'auto-height': autoHeight }]">
97
- <div class="dialog__header-image-container">
98
- <slot name="header-image">
92
+ <div :class="['dialog', { 'fit-height': fitHeight }]">
93
+ <div v-if="!!slots.headerImage || headerImage" class="dialog__header-image-container">
94
+ <slot name="headerImage">
99
95
  <img v-bind="headerImage" />
100
96
  </slot>
101
97
  </div>
@@ -119,16 +115,17 @@ watch(visible, (newV) => {
119
115
  <slot name="custom"></slot>
120
116
  </div>
121
117
 
122
- <div class="dialog__content-image">
123
- <slot name="content-image">
118
+ <div v-if="!!slots.contentImage || contentImage" class="dialog__content-image">
119
+ <slot name="contentImage">
124
120
  <img v-bind="contentImage" />
125
121
  </slot>
126
122
  </div>
127
123
  </div>
128
124
 
129
- <div class="cta-container">
130
- <slot name="cta"></slot>
131
- <UButton @click="visible = false">{{ closeBtnLabel || getTranslation('close') }}</UButton>
125
+ <div :class="['cta-container', { 'cta-inline': ctaInline }]">
126
+ <slot name="cta">
127
+ <UButton @click="visible = false">{{ closeBtnLabel || getTranslation('close') }}</UButton>
128
+ </slot>
132
129
  </div>
133
130
  </div>
134
131
  </dialog>
@@ -48,13 +48,8 @@ const onMousemove = (e) => {
48
48
  </script>
49
49
 
50
50
  <template>
51
- <div class="login-animation">
52
- <div
53
- ref="container"
54
- class="login-animation__image"
55
- @mousemove="onMousemove"
56
- @mouseleave="onMouseleave"
57
- >
51
+ <div ref="container" class="login-animation" @mousemove="onMousemove" @mouseleave="onMouseleave">
52
+ <div class="login-animation__image">
58
53
  <div ref="butterflyWrapper" :class="['login-animation__sprite', { flip: !mouseRightSide }]">
59
54
  <USpriteAnimation
60
55
  ref="butterfly"
@@ -23,24 +23,38 @@ interface Props {
23
23
 
24
24
  defineProps<Props>()
25
25
 
26
+ const model = defineModel<boolean>({ default: false })
27
+
26
28
  const mobilePanelEl = useTemplateRef('mobile-panel')
27
29
  const mobileOpen = ref(false)
28
30
  const isMobilePanelOpening = ref(false)
29
31
  const isMobilePanelClosing = ref(false)
30
32
 
31
- const onToggleMenu = () => {
33
+ const openPanel = () => {
32
34
  mobilePanelEl.value?.addEventListener('transitionend', onMobilePanelTransitionEnd)
33
35
 
34
36
  document.documentElement.classList.toggle('navigation-toolbar-mobile-open', true)
35
37
  isMobilePanelOpening.value = true
36
38
  }
37
39
 
38
- const onNavClose = () => {
40
+ const closePanel = () => {
39
41
  mobilePanelEl.value?.addEventListener('transitionend', onMobilePanelTransitionEnd)
40
42
 
41
43
  isMobilePanelClosing.value = true
42
44
  }
43
45
 
46
+ const onToggleMenu = () => {
47
+ openPanel()
48
+
49
+ model.value = true
50
+ }
51
+
52
+ const onNavClose = () => {
53
+ closePanel()
54
+
55
+ model.value = false
56
+ }
57
+
44
58
  const onMobilePanelTransitionEnd = (e) => {
45
59
  if (e.target !== mobilePanelEl.value) {
46
60
  return
@@ -58,6 +72,14 @@ const onMobilePanelTransitionEnd = (e) => {
58
72
  watch(mobileOpen, (newV) => {
59
73
  document.documentElement.classList.toggle('navigation-toolbar-mobile-open', newV)
60
74
  })
75
+
76
+ watch(model, (newV) => {
77
+ if (newV === true) {
78
+ openPanel()
79
+ } else {
80
+ closePanel()
81
+ }
82
+ })
61
83
  </script>
62
84
 
63
85
  <template>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@energie360/ui-library",
3
- "version": "0.1.32",
3
+ "version": "0.1.33",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -24,20 +24,20 @@
24
24
  "author": "",
25
25
  "license": "MIT",
26
26
  "devDependencies": {
27
+ "@lottiefiles/dotlottie-web": "^0.60.0",
27
28
  "@tsconfig/node22": "^22.0.5",
28
- "@types/node": "^22.19.2",
29
- "@vue/tsconfig": "^0.7.0",
30
- "autoprefixer": "^10.4.22",
31
- "chokidar": "^4.0.3",
29
+ "@types/node": "^25.0.3",
30
+ "@vue/tsconfig": "^0.8.1",
31
+ "autoprefixer": "^10.4.23",
32
+ "chokidar": "^5.0.0",
32
33
  "postcss": "^8.5.6",
33
- "sass": "^1.96.0",
34
+ "sass": "^1.97.1",
34
35
  "typescript": "^5.9.3"
35
36
  },
36
37
  "dependencies": {
37
- "@lottiefiles/dotlottie-vue": "^0.10.10",
38
- "@lottiefiles/dotlottie-web": "^0.58.1",
38
+ "@lottiefiles/dotlottie-vue": "^0.10.12",
39
39
  "@lottiefiles/lottie-player": "^2.0.12",
40
- "gsap": "^3.14.1",
40
+ "gsap": "^3.14.2",
41
41
  "@energie360/design-tokens": "^1.3.0"
42
42
  },
43
43
  "peerDependencies": {
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Create query parameter for an url with an object or an array
3
+ * INFO: Use an array as parameter if you have multiple values for a key
4
+ *
5
+ * @param url {string} - url to append the query parameters
6
+ * @param params {(object | Array)} - object with key, value as query parameter
7
+ * e.q object => random/testing, data = {'test': 1, 'hello': 'world'} => random/testing?test=1&hello=world
8
+ * e.q array => random/testing, data = [['test', 2], ['hello', 'earth']] => random/testing?test=2&hello=earth
9
+ *
10
+ * @returns {string}
11
+ */
12
+ export function addQueryParameter(url, params) {
13
+ const queryParams = []
14
+
15
+ if (Array.isArray(params)) {
16
+ queryParams.push(params.map((param) => param.map(encodeURIComponent)))
17
+ } else {
18
+ queryParams.push(Object.keys(params).map((key) => [key, params[key]].map(encodeURIComponent)))
19
+ }
20
+
21
+ const queryString = queryParams.map((param) =>
22
+ param.map((keyValue) => keyValue.join('=')).join('&'),
23
+ )
24
+ return `${url}?${queryString}`
25
+ }
@@ -0,0 +1,27 @@
1
+ // From https://github.com/vuejs/core/issues/4733
2
+
3
+ import { Comment, Text, Fragment, type Slot, type VNode } from 'vue'
4
+
5
+ export function hasSlotContent(slot: Slot | undefined | null, props: any = {}) {
6
+ return !isSlotEmpty(slot, props)
7
+ }
8
+
9
+ export function isSlotEmpty(slot: Slot | undefined | null, props: any = {}) {
10
+ return isVNodeEmpty(slot?.(props))
11
+ }
12
+
13
+ export function isVNodeEmpty(vnode: VNode | VNode[] | undefined | null) {
14
+ return (
15
+ !vnode ||
16
+ asArray(vnode).every(
17
+ (vnode) =>
18
+ vnode.type === Comment ||
19
+ (vnode.type === Text && !vnode.children?.length) ||
20
+ (vnode.type === Fragment && !vnode.children?.length),
21
+ )
22
+ )
23
+ }
24
+
25
+ export function asArray<T>(arg: T | T[] | null) {
26
+ return Array.isArray(arg) ? arg : arg !== null ? [arg] : []
27
+ }
@@ -7,13 +7,16 @@ interface Props {
7
7
  title?: string
8
8
  text?: string
9
9
  image?: Image
10
+
10
11
  cta?: Cta
11
12
  }
12
13
 
13
14
  const { text = '', image = undefined, cta = undefined, title = '' } = defineProps<Props>()
14
15
 
15
16
  const slots = useSlots()
17
+
16
18
  const hasCta = computed(() => !!slots.cta || cta)
19
+
17
20
  const hasText = computed(() => !!slots.text || text)
18
21
  </script>
19
22