@energie360/ui-library 0.1.32 → 0.1.34

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 (63) hide show
  1. package/base/_resets.scss +4 -0
  2. package/components/badge/badge.scss +11 -4
  3. package/components/badge/u-badge.vue +3 -13
  4. package/components/button-group/button-group.scss +12 -0
  5. package/components/button-group/u-button-group.vue +15 -0
  6. package/components/card-amount/u-card-amount.vue +1 -1
  7. package/components/card-amount-illustrated/u-card-amount-illustrated.vue +1 -1
  8. package/components/card-contact/u-card-contact.vue +1 -1
  9. package/components/card-highlight/u-card-highlight.vue +2 -2
  10. package/components/card-price-list/u-card-price-list.vue +2 -2
  11. package/components/card-statistic/card-statistic.scss +31 -0
  12. package/components/card-statistic/u-card-statistic.vue +34 -0
  13. package/components/chip/chip.scss +1 -0
  14. package/components/chip/u-chip.vue +1 -0
  15. package/components/data-card/data-card.scss +15 -0
  16. package/components/data-card/u-data-card.vue +6 -1
  17. package/components/download-list-item/u-download-list-item.vue +1 -0
  18. package/components/empty/empty.scss +27 -0
  19. package/components/empty/u-empty.vue +32 -0
  20. package/components/hint/hint.scss +106 -35
  21. package/components/hint/u-hint.vue +35 -4
  22. package/components/index.js +3 -0
  23. package/components/navigation-panel-tile/navigation-panel-tile.scss +0 -18
  24. package/components/navigation-panel-tile/u-navigation-panel-tile.vue +8 -7
  25. package/components/navigation-toolbar-link/u-navigation-toolbar-link.vue +2 -2
  26. package/components/notification-item/notification-item.scss +1 -0
  27. package/components/progress-avatar/u-progress-avatar.vue +1 -1
  28. package/components/slider/u-slider.vue +1 -1
  29. package/components/slider-progress-animation/u-slider-progress-animation.vue +1 -1
  30. package/components/table/table-cell.scss +10 -0
  31. package/components/table/u-cell-icon-group.vue +1 -1
  32. package/components/table/u-cell-progress-bar.vue +1 -1
  33. package/components/table/u-table-cell.vue +3 -0
  34. package/components/text-block/u-text-block.vue +1 -1
  35. package/components/welcome/butterfly-sprite.json +9 -0
  36. package/components/welcome/butterfly-sprite.png +0 -0
  37. package/components/welcome/u-welcome.vue +69 -1
  38. package/components/welcome/welcome.scss +10 -0
  39. package/dist/base-style.css +6 -2
  40. package/dist/base-style.css.map +1 -1
  41. package/dist/elements/form.css +1 -1
  42. package/dist/layout/form-grid.css.map +1 -1
  43. package/elements/button/u-button.vue +2 -1
  44. package/elements/form/form.scss +1 -1
  45. package/i18n/i18n.ts +22 -20
  46. package/layout/form-grid/form-grid.scss +2 -0
  47. package/layout/form-grid/u-form-col.vue +1 -1
  48. package/layout/form-grid/u-form-fieldset.vue +36 -0
  49. package/layout/form-grid/u-form-row.vue +29 -3
  50. package/layout/form-grid/u-form-wrapper.vue +7 -0
  51. package/layout/index.js +2 -0
  52. package/layout/portal-block/portal-block.scss +36 -3
  53. package/layout/portal-block/u-portal-block.vue +9 -3
  54. package/modules/dialog/dialog.scss +34 -24
  55. package/modules/dialog/u-dialog.vue +14 -17
  56. package/modules/footer/footer.scss +30 -4
  57. package/modules/login-animation/login-animation.scss +1 -3
  58. package/modules/login-animation/u-login-animation.vue +11 -12
  59. package/modules/navigation-toolbar-top/u-navigation-toolbar-top.vue +24 -2
  60. package/package.json +9 -9
  61. package/utils/http/url.js +25 -0
  62. package/utils/vue/helpers.ts +27 -0
  63. package/wizard/wizard-outro/u-wizard-outro.vue +3 -0
@@ -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 {
@@ -129,16 +125,30 @@
129
125
  left: 0;
130
126
  width: 100%;
131
127
  height: var(--overflow-gradient-height);
132
- background-image: linear-gradient(0deg, rgb(255 255 255 / 100%) 0%, rgb(255 255 255 / 0%) 100%);
128
+ background-image: linear-gradient(
129
+ 0deg,
130
+ rgb(255 255 255 / 100%) 0%,
131
+ rgb(255 255 255 / 80%) 50%,
132
+ rgb(255 255 255 / 0%) 100%
133
+ );
134
+ }
135
+
136
+ &.cta-inline {
137
+ display: grid;
138
+ grid-template-columns: 1fr 1fr;
133
139
  }
134
140
 
135
141
  @include a.bp(m) {
136
- padding: var(--e-space-8) 0 var(--e-space-7);
142
+ padding: var(--e-space-8) 0 var(--e-space-6);
143
+
144
+ &.cta-inline {
145
+ display: flex;
146
+ }
137
147
  }
138
148
  }
139
149
 
140
150
  // MOBILE "MODAL" Variant
141
- .dialog-container.modal {
151
+ .dialog-container.variant-modal {
142
152
  @include a.bp(m) {
143
153
  padding-left: a.rem(a.$container-edge-m);
144
154
  padding-right: a.rem(a.$container-edge-m);
@@ -151,7 +161,7 @@
151
161
  }
152
162
 
153
163
  // MOBILE "SLIDE-OUT" Variant
154
- .dialog-container.slideout {
164
+ .dialog-container.variant-slideout {
155
165
  @include a.bp(m) {
156
166
  align-items: flex-end;
157
167
 
@@ -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>
@@ -28,9 +28,35 @@
28
28
  }
29
29
 
30
30
  &.portal {
31
+ container-type: inline-size;
32
+
31
33
  .footer__container {
32
34
  @include l.portal-content-container;
33
35
  }
36
+
37
+ @container (width < 820px) {
38
+ // Mobile Layout
39
+ .footer__bottom-row {
40
+ flex-direction: column;
41
+ }
42
+
43
+ .footer__meta-navigation {
44
+ flex-direction: column;
45
+ gap: var(--e-space-4);
46
+ margin-bottom: var(--e-space-8);
47
+ }
48
+
49
+ .footer__copyright {
50
+ position: absolute;
51
+ left: 0;
52
+ bottom: 0;
53
+ }
54
+
55
+ .footer__language-nav {
56
+ position: relative;
57
+ margin-bottom: calc(var(--e-space-8) + #{a.rem(18)});
58
+ }
59
+ }
34
60
  }
35
61
  }
36
62
 
@@ -48,7 +74,7 @@
48
74
  .footer__top-row {
49
75
  display: flex;
50
76
  flex-wrap: wrap;
51
- grid-gap: var(--e-space-12);
77
+ gap: var(--e-space-12);
52
78
  }
53
79
 
54
80
  .footer__addresses-column {
@@ -87,7 +113,7 @@
87
113
 
88
114
  .footer__social-links {
89
115
  display: flex;
90
- grid-gap: var(--e-space-6);
116
+ gap: var(--e-space-6);
91
117
  }
92
118
 
93
119
  .footer__bottom {
@@ -126,7 +152,7 @@
126
152
  @include a.type(100);
127
153
 
128
154
  display: flex;
129
- grid-gap: var(--e-space-6);
155
+ gap: var(--e-space-6);
130
156
  margin-bottom: var(--e-space-3);
131
157
 
132
158
  a {
@@ -139,7 +165,7 @@
139
165
 
140
166
  @include a.bp(m) {
141
167
  flex-direction: column;
142
- grid-gap: var(--e-space-4);
168
+ gap: var(--e-space-4);
143
169
  margin-bottom: var(--e-space-8);
144
170
  }
145
171
  }
@@ -27,6 +27,7 @@ $sprite-height-mobile: 59px;
27
27
  position: relative;
28
28
  max-width: 680px;
29
29
  margin: 0 60px;
30
+ padding: 5%;
30
31
 
31
32
  img {
32
33
  user-select: none;
@@ -59,7 +60,4 @@ $sprite-height-mobile: 59px;
59
60
  width: $sprite-width-mobile;
60
61
  height: $sprite-height-mobile;
61
62
  }
62
-
63
- &.flip {
64
- }
65
63
  }
@@ -15,6 +15,8 @@ const onSpriteReady = () => {
15
15
  }
16
16
 
17
17
  const onMouseleave = () => {
18
+ mouseRightSide.value = Number(gsap.getProperty(butterflyWrapper.value, 'x')) < 0
19
+
18
20
  gsap.to(butterflyWrapper.value, {
19
21
  x: 0,
20
22
  y: 0,
@@ -24,7 +26,7 @@ const onMouseleave = () => {
24
26
  })
25
27
  }
26
28
 
27
- const onMousemove = (e) => {
29
+ const onMousemove = (e: MouseEvent) => {
28
30
  const { left, top, width, height } = container.value.getBoundingClientRect()
29
31
 
30
32
  const halfW = width / 2
@@ -32,30 +34,27 @@ const onMousemove = (e) => {
32
34
  const mouseX = e.x - left
33
35
  const mouseY = e.y - top
34
36
 
35
- mouseRightSide.value = mouseX > halfW
36
-
37
37
  const x = gsap.utils.interpolate(-halfW, halfW, mouseX / width)
38
38
  const y = gsap.utils.interpolate(-halfH, halfH, mouseY / height)
39
39
 
40
40
  gsap.to(butterflyWrapper.value, {
41
41
  x: x,
42
42
  y: y,
43
- duration: 4,
43
+ duration: 3.5,
44
44
  ease: 'power1',
45
45
  overwrite: true,
46
+ onUpdate() {
47
+ mouseRightSide.value =
48
+ Number(gsap.getProperty(butterflyWrapper.value, 'x')) < mouseX - width / 2
49
+ },
46
50
  })
47
51
  }
48
52
  </script>
49
53
 
50
54
  <template>
51
- <div class="login-animation">
52
- <div
53
- ref="container"
54
- class="login-animation__image"
55
- @mousemove="onMousemove"
56
- @mouseleave="onMouseleave"
57
- >
58
- <div ref="butterflyWrapper" :class="['login-animation__sprite', { flip: !mouseRightSide }]">
55
+ <div ref="container" class="login-animation" @mousemove="onMousemove" @mouseleave="onMouseleave">
56
+ <div class="login-animation__image">
57
+ <div ref="butterflyWrapper" class="login-animation__sprite">
59
58
  <USpriteAnimation
60
59
  ref="butterfly"
61
60
  :style="{
@@ -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.34",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -25,19 +25,19 @@
25
25
  "license": "MIT",
26
26
  "devDependencies": {
27
27
  "@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",
28
+ "@types/node": "^25.0.10",
29
+ "@vue/tsconfig": "^0.8.1",
30
+ "autoprefixer": "^10.4.23",
31
+ "chokidar": "^5.0.0",
32
32
  "postcss": "^8.5.6",
33
- "sass": "^1.96.0",
33
+ "sass": "^1.97.3",
34
34
  "typescript": "^5.9.3"
35
35
  },
36
36
  "dependencies": {
37
- "@lottiefiles/dotlottie-vue": "^0.10.10",
38
- "@lottiefiles/dotlottie-web": "^0.58.1",
37
+ "@lottiefiles/dotlottie-vue": "^0.10.13",
38
+ "@lottiefiles/dotlottie-web": "^0.61.0",
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