@energie360/ui-library 0.1.16 → 0.1.18

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 (102) hide show
  1. package/components/badge/badge.scss +56 -0
  2. package/components/badge/u-badge.vue +47 -0
  3. package/components/card-contact/card-contact.scss +39 -0
  4. package/components/card-contact/u-card-contact.vue +44 -0
  5. package/components/card-cta-bar/card-cta-bar.scss +4 -0
  6. package/components/card-cta-bar/u-card-cta-bar.vue +24 -0
  7. package/components/card-cta-header/u-card-cta-header.vue +10 -7
  8. package/components/card-footer/u-card-footer.vue +5 -3
  9. package/components/card-group/u-card-group.vue +1 -1
  10. package/components/card-header/card-header.scss +29 -4
  11. package/components/card-header/u-card-header.vue +22 -3
  12. package/components/card-highlight/card-highlight.scss +70 -0
  13. package/components/card-highlight/u-card-highlight.vue +41 -0
  14. package/components/card-info/card-info.scss +40 -0
  15. package/components/card-info/u-card-info.vue +35 -0
  16. package/components/card-price-list/card-price-list.scss +39 -0
  17. package/components/card-price-list/u-card-price-list.vue +37 -0
  18. package/components/card-section/card-section.scss +21 -1
  19. package/components/card-section/u-card-section.vue +9 -1
  20. package/components/data-card/data-card.scss +34 -0
  21. package/components/data-card/u-data-card.vue +49 -0
  22. package/components/data-card-group/data-card-group.scss +12 -0
  23. package/components/data-card-group/u-data-card-group.vue +7 -0
  24. package/components/download-list/download-list.scss +58 -0
  25. package/components/download-list/u-download-list.vue +44 -0
  26. package/components/download-list-item/download-list-item.scss +267 -0
  27. package/components/download-list-item/u-download-list-item.vue +65 -0
  28. package/components/file-upload/file-list.scss +68 -0
  29. package/components/file-upload/file-upload.scss +119 -0
  30. package/components/file-upload/u-file-list.vue +55 -0
  31. package/components/file-upload/u-file-upload.vue +220 -0
  32. package/components/hint/hint.scss +67 -6
  33. package/components/hint/u-hint.vue +11 -1
  34. package/components/index.js +13 -0
  35. package/components/inline-edit/inline-edit.scss +5 -1
  36. package/components/inline-edit/u-inline-edit.vue +21 -12
  37. package/components/progress-avatar/u-progress-avatar.vue +27 -3
  38. package/components/richtext/richtext.scss +9 -2
  39. package/components/richtext/u-richtext.vue +3 -1
  40. package/components/search-group/search-group.scss +59 -0
  41. package/components/search-group/u-search-group.vue +32 -0
  42. package/components/skeleton-loader/skeleton-loader.scss +39 -0
  43. package/components/skeleton-loader/u-skeleton-loader.vue +28 -0
  44. package/components/table/cell-ctas.scss +1 -7
  45. package/components/table/cell-icon-text.scss +15 -4
  46. package/components/table/table-cell.mixins.scss +3 -2
  47. package/components/table/table-cell.scss +5 -0
  48. package/components/table/table-heading.scss +7 -0
  49. package/components/table/u-cell-ctas.vue +15 -6
  50. package/components/table/u-cell-icon-text.vue +13 -5
  51. package/components/table/u-table-cell.vue +3 -1
  52. package/components/table/u-table-heading.vue +2 -1
  53. package/components/tabs/tabs.scss +10 -1
  54. package/components/tabs/u-tabs.vue +64 -25
  55. package/dist/elements/form.css +170 -0
  56. package/dist/elements/form.css.map +1 -0
  57. package/dist/layout/form-grid.css +184 -0
  58. package/dist/layout/form-grid.css.map +1 -0
  59. package/dist/layout/split.css.map +1 -1
  60. package/elements/button/_button-plain-small-spaceless.scss +10 -0
  61. package/elements/button/button.scss +32 -0
  62. package/elements/button/u-button.vue +47 -4
  63. package/elements/form/form.scss +1 -1
  64. package/elements/form-field/form-field-base.scss +4 -0
  65. package/elements/form-field/form-field-prefix-suffix.scss +5 -0
  66. package/elements/select/u-select.vue +6 -6
  67. package/elements/text-field/text-field.scss +15 -0
  68. package/elements/text-field/text-field.types.ts +1 -0
  69. package/elements/text-field/u-text-field.vue +44 -8
  70. package/elements/toggle-switch/toggle-switch-small.scss +10 -0
  71. package/elements/toggle-switch/toggle-switch.scss +25 -21
  72. package/elements/toggle-switch/u-toggle-switch.vue +22 -12
  73. package/i18n/i18n.ts +32 -0
  74. package/layout/container/container.scss +18 -0
  75. package/layout/index.js +4 -0
  76. package/layout/portal/portal.scss +63 -0
  77. package/layout/portal/u-portal.vue +51 -0
  78. package/layout/settings/settings.scss +33 -0
  79. package/layout/settings/u-settings-layout.vue +19 -0
  80. package/layout/tile-grid/tile-grid.scss +13 -0
  81. package/layout/tile-grid/tile-item.scss +31 -0
  82. package/layout/tile-grid/u-tile-grid.vue +7 -0
  83. package/layout/tile-grid/u-tile-item.vue +15 -0
  84. package/modules/content-title/content-title.scss +43 -0
  85. package/modules/content-title/u-content-title.vue +19 -0
  86. package/modules/dialog/_animations.scss +49 -0
  87. package/modules/dialog/dialog.scss +172 -0
  88. package/modules/dialog/u-dialog.vue +139 -0
  89. package/modules/footer/footer.scss +8 -1
  90. package/modules/footer/u-footer.vue +1 -1
  91. package/modules/index.js +3 -0
  92. package/modules/inline-edit-group/u-inline-edit-group.vue +2 -0
  93. package/modules/search-filter/search-filter.scss +106 -0
  94. package/modules/search-filter/u-search-filter.vue +54 -0
  95. package/package.json +3 -1
  96. package/utility/elements/form.scss +1 -0
  97. package/utility/layout/form-grid.scss +1 -0
  98. package/utils/array/intersect.js +7 -0
  99. package/utils/functions/breakpoint.js +4 -9
  100. package/utils/functions/format-bytes.js +17 -0
  101. package/utils/global/mime-types.js +8 -0
  102. package/utils/translations/translate.js +10 -2
@@ -0,0 +1,172 @@
1
+ // TODO: Styles have been copied from 'news'. Refactor when possible.
2
+ @use '../../base/abstracts' as a;
3
+ @use './animations';
4
+
5
+ .dialog-container {
6
+ --overflow-gradient-height: var(--e-space-10);
7
+
8
+ padding: 0;
9
+ border: 0;
10
+ max-height: 100vh;
11
+ max-width: 100vw;
12
+ width: 100%;
13
+ height: 100%;
14
+ background-color: transparent;
15
+ justify-content: center;
16
+ align-items: center;
17
+ overflow: hidden;
18
+
19
+ &[open] {
20
+ display: flex;
21
+ animation: fade-in var(--e-trs-duration-faster) var(--e-trs-easing-default);
22
+ }
23
+
24
+ &[open]::backdrop {
25
+ background-color: var(--e-c-mono-900);
26
+ opacity: 0.4;
27
+ animation: fade-in-backdrop var(--e-trs-duration-faster) var(--e-trs-easing-default);
28
+ }
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
+ }
42
+
43
+ .dialog {
44
+ display: flex;
45
+ flex-direction: column;
46
+ background-color: var(--e-c-mono-00);
47
+ overflow: auto;
48
+ scrollbar-width: none;
49
+ border-radius: var(--e-brd-radius-3);
50
+ padding: var(--e-space-7) var(--e-space-7) 0;
51
+ height: a.rem(670);
52
+ max-height: 80vh;
53
+ max-width: a.rem(540);
54
+ min-width: a.rem(480);
55
+
56
+ * + .dialog__content-image {
57
+ margin-top: var(--e-space-5);
58
+ }
59
+
60
+ @include a.bp(m) {
61
+ width: 100%;
62
+ max-width: none;
63
+ min-width: 0;
64
+ height: auto;
65
+ min-height: a.rem(440);
66
+ padding-left: var(--e-space-5);
67
+ padding-right: var(--e-space-5);
68
+ }
69
+ }
70
+
71
+ .dialog__header-image-container {
72
+ display: none;
73
+ justify-content: center;
74
+ height: a.rem(184);
75
+ margin-bottom: var(--e-space-8);
76
+
77
+ img {
78
+ object-fit: contain;
79
+ }
80
+ }
81
+
82
+ .dialog__content-container {
83
+ margin-bottom: auto;
84
+ }
85
+
86
+ .content-title {
87
+ @include a.type(500, strong);
88
+
89
+ @include a.bp(m) {
90
+ @include a.type(300, strong);
91
+ }
92
+ }
93
+
94
+ .content-title + .content-text,
95
+ .content-title + .content-custom {
96
+ margin-top: var(--e-space-2);
97
+
98
+ @include a.bp(m) {
99
+ margin-top: var(--e-space-1);
100
+ }
101
+ }
102
+
103
+ .dialog__content-image {
104
+ display: none;
105
+
106
+ img {
107
+ width: 100%;
108
+ }
109
+ }
110
+
111
+ .cta-container {
112
+ position: sticky;
113
+ display: flex;
114
+ flex-direction: column;
115
+ row-gap: var(--e-space-4);
116
+ bottom: 0;
117
+ margin-top: var(--overflow-gradient-height);
118
+ padding: var(--e-space-8) var(--e-space-1) var(--e-space-7);
119
+ background-color: var(--e-c-mono-00);
120
+
121
+ &::after {
122
+ content: '';
123
+ position: absolute;
124
+ top: calc(var(--overflow-gradient-height) * -1);
125
+ left: 0;
126
+ width: 100%;
127
+ height: var(--overflow-gradient-height);
128
+ background-image: linear-gradient(0deg, rgb(255 255 255 / 100%) 0%, rgb(255 255 255 / 0%) 100%);
129
+ }
130
+
131
+ @include a.bp(m) {
132
+ padding: var(--e-space-8) 0 var(--e-space-7);
133
+ }
134
+ }
135
+
136
+ // MOBILE "MODAL" Variant
137
+ .dialog-container.modal {
138
+ @include a.bp(m) {
139
+ padding-left: a.rem(a.$container-edge-m);
140
+ padding-right: a.rem(a.$container-edge-m);
141
+ }
142
+
143
+ @include a.bp(s) {
144
+ padding-left: a.rem(a.$container-edge-s);
145
+ padding-right: a.rem(a.$container-edge-s);
146
+ }
147
+ }
148
+
149
+ // MOBILE "SLIDE-OUT" Variant
150
+ .dialog-container.slideout {
151
+ @include a.bp(m) {
152
+ align-items: flex-end;
153
+
154
+ // Closing animation
155
+ animation: slide-out var(--e-trs-duration-faster) var(--e-trs-easing-default);
156
+
157
+ &[open] {
158
+ display: flex;
159
+ animation: slide-in var(--e-trs-duration-fast) var(--e-trs-easing-default);
160
+ }
161
+
162
+ .dialog {
163
+ border-bottom-left-radius: 0;
164
+ border-bottom-right-radius: 0;
165
+ max-height: 90vh;
166
+ }
167
+ }
168
+ }
169
+
170
+ body:has(.dialog[open]) {
171
+ overflow: hidden;
172
+ }
@@ -0,0 +1,139 @@
1
+ <script setup lang="ts">
2
+ import { Image } from '../../elements/types'
3
+ import { useTemplateRef, watch, useSlots } from 'vue'
4
+ import { UButton } from '../../elements'
5
+ import { getTranslation } from '../../utils/translations/translate'
6
+ import { focusTrap } from '../../utils/a11y/focus-trap'
7
+
8
+ interface Props {
9
+ title?: string
10
+ text?: string
11
+ headerImage?: Image
12
+ contentImage?: Image
13
+ closeBtnLabel?: string
14
+ mobileDialogStyle?: 'modal' | 'slideout'
15
+ }
16
+
17
+ const { mobileDialogStyle = 'modal', closeBtnLabel = getTranslation('close') } =
18
+ defineProps<Props>()
19
+
20
+ const visible = defineModel<boolean>('visible')
21
+ const dialogEl = useTemplateRef('dialog')
22
+ const slots = useSlots()
23
+ let focusTrapInstance
24
+
25
+ // TODO: Key codes should be defined globally
26
+ const ESC = 'Escape'
27
+
28
+ const onKeydown = (e: KeyboardEvent) => {
29
+ if (e.code === ESC) {
30
+ visible.value = false
31
+ }
32
+ }
33
+
34
+ const onDocumentClick = (e: Event) => {
35
+ if (e.target.closest('.dialog')) {
36
+ return
37
+ }
38
+
39
+ e.stopPropagation()
40
+ e.preventDefault()
41
+
42
+ visible.value = false
43
+ }
44
+ watch(visible, (newV) => {
45
+ if (newV) {
46
+ // Open Dialog
47
+ dialogEl.value.showModal()
48
+
49
+ focusTrapInstance = focusTrap(dialogEl.value, {
50
+ focusFirstElement: true,
51
+ allowArrowUpDown: true,
52
+ allowArrowLeftRight: true,
53
+ })
54
+
55
+ requestAnimationFrame(() => {
56
+ window.addEventListener('keydown', onKeydown)
57
+ document.addEventListener('click', onDocumentClick)
58
+ })
59
+ } else {
60
+ focusTrapInstance.release()
61
+
62
+ // Close Dialog
63
+ // The trigger element automatically gets the focus when the dialog is closed. yay native :)
64
+ dialogEl.value.close()
65
+
66
+ window.removeEventListener('keydown', onKeydown)
67
+ document.removeEventListener('click', onDocumentClick)
68
+ }
69
+ })
70
+ </script>
71
+
72
+ <template>
73
+ <dialog
74
+ ref="dialog"
75
+ closedby="none"
76
+ :class="[
77
+ 'dialog-container',
78
+ mobileDialogStyle,
79
+ {
80
+ 'has-header-image': !!slots['header-image'] || headerImage,
81
+ 'has-content-image': !!slots['content-image'] || contentImage,
82
+ },
83
+ ]"
84
+ >
85
+ <div class="dialog">
86
+ <div class="dialog__header-image-container">
87
+ <slot name="header-image">
88
+ <img v-bind="headerImage" />
89
+ </slot>
90
+ </div>
91
+
92
+ <div class="dialog__content-container">
93
+ <div class="content-title">
94
+ <h3>
95
+ <slot name="title">{{ title }}</slot>
96
+ </h3>
97
+ </div>
98
+
99
+ <div v-if="$slots.text || text" class="content-text">
100
+ <div class="richtext">
101
+ <slot name="text">
102
+ <div v-html="text"></div>
103
+ </slot>
104
+ </div>
105
+ </div>
106
+
107
+ <div v-if="$slots.custom" class="content-custom">
108
+ <slot name="custom"></slot>
109
+ </div>
110
+
111
+ <div class="dialog__content-image">
112
+ <slot name="content-image">
113
+ <img v-bind="contentImage" />
114
+ </slot>
115
+ </div>
116
+ </div>
117
+
118
+ <div class="cta-container">
119
+ <slot name="cta"></slot>
120
+ <UButton @click="visible = false">{{ closeBtnLabel }}</UButton>
121
+ </div>
122
+ </div>
123
+ </dialog>
124
+ </template>
125
+
126
+ <style scoped lang="scss" src="./dialog.scss"></style>
127
+ <style scoped lang="scss">
128
+ .dialog__header-image-container {
129
+ :slotted(img) {
130
+ object-fit: contain;
131
+ }
132
+ }
133
+
134
+ .dialog__content-image {
135
+ :slotted(img) {
136
+ width: 100%;
137
+ }
138
+ }
139
+ </style>
@@ -1,8 +1,9 @@
1
- @use '../../base/abstracts/' as a;
1
+ @use '../../base/abstracts' as a;
2
2
  @use '../../elements/text-link/text-link.scss' as t;
3
3
  @use '../../layout/container/container.scss' as l;
4
4
 
5
5
  .footer {
6
+ &.portal,
6
7
  &.reduced {
7
8
  .footer__top {
8
9
  display: none;
@@ -25,6 +26,12 @@
25
26
  position: static;
26
27
  }
27
28
  }
29
+
30
+ &.portal {
31
+ .footer__container {
32
+ @include l.portal-content-container;
33
+ }
34
+ }
28
35
  }
29
36
 
30
37
  .footer__top {
@@ -1,6 +1,6 @@
1
1
  <script setup lang="ts">
2
2
  interface Props {
3
- variant?: 'normal' | 'reduced'
3
+ variant?: 'normal' | 'reduced' | 'portal'
4
4
  }
5
5
 
6
6
  const { variant = 'normal' } = defineProps<Props>()
package/modules/index.js CHANGED
@@ -7,3 +7,6 @@ export { default as UNavigationPanel } from './navigation-panel/u-navigation-pan
7
7
  export { default as UProgressIndicator } from './progress-indicator/u-progress-indicator.vue'
8
8
  export { default as UToast } from './toast/u-toast.vue'
9
9
  export { toast } from './toast/useToast.ts'
10
+ export { default as UDialog } from './dialog/u-dialog.vue'
11
+ export { default as USearchFilter } from './search-filter/u-search-filter.vue'
12
+ export { default as UContentTitle } from './content-title/u-content-title.vue'
@@ -7,6 +7,8 @@ const update = (id) => {
7
7
 
8
8
  const activeEdit = ref('')
9
9
 
10
+ defineExpose({ update })
11
+
10
12
  provide('inline-edit-group', {
11
13
  update,
12
14
  activeEdit,
@@ -0,0 +1,106 @@
1
+ @use '../../base/abstracts' as a;
2
+
3
+ @mixin collapsed-desktop {
4
+ grid-template-columns: 100%;
5
+
6
+ .search-filter__search {
7
+ display: flex;
8
+ column-gap: var(--e-space-4);
9
+ flex-direction: row-reverse;
10
+ grid-area: auto;
11
+ }
12
+
13
+ .search-filter__filter-toggle {
14
+ display: block;
15
+ }
16
+
17
+ .search-filter__filter {
18
+ display: none;
19
+ grid-area: auto;
20
+ }
21
+
22
+ &.show-filters {
23
+ .search-filter__filter {
24
+ display: grid;
25
+ grid-template-columns: repeat(calc(var(--field-count) - 1), 1fr);
26
+ }
27
+ }
28
+ }
29
+
30
+ @mixin mobile-filters {
31
+ @include a.bp(m) {
32
+ grid-template-columns: 100%;
33
+
34
+ .search-filter__search {
35
+ display: grid;
36
+ column-gap: var(--e-space-4);
37
+ grid-template-columns: auto min-content;
38
+ grid-area: auto;
39
+ }
40
+
41
+ .search-filter__filter {
42
+ display: none;
43
+ grid-area: auto;
44
+ grid-template-columns: 1fr;
45
+ }
46
+
47
+ .search-filter__filter-toggle {
48
+ flex: 0 0 auto;
49
+ display: block;
50
+ }
51
+
52
+ &.show-filters {
53
+ .search-filter__filter {
54
+ display: grid;
55
+ grid-template-columns: 1fr;
56
+ }
57
+ }
58
+ }
59
+ }
60
+
61
+ .search-filter {
62
+ --field-count: 1;
63
+
64
+ display: grid;
65
+ grid-template-columns: repeat(var(--field-count), minmax(0, 240px));
66
+ gap: var(--e-space-6) var(--e-space-4);
67
+
68
+ @include a.bp(xl) {
69
+ grid-template-columns: repeat(var(--field-count), 1fr);
70
+ }
71
+
72
+ .search-filter__search {
73
+ grid-area: 1 / var(--field-count) / 1 / calc(var(--field-count) + 1);
74
+ }
75
+
76
+ .search-filter__filter {
77
+ display: grid;
78
+ grid-template-columns: repeat(calc(var(--field-count) - 1), minmax(0, #{a.rem(240)}));
79
+ gap: var(--e-space-6) var(--e-space-4);
80
+ grid-area: 1 / 1 / 1 / var(--field-count);
81
+
82
+ @include a.bp(xl) {
83
+ grid-template-columns: repeat(calc(var(--field-count) - 1), 1fr);
84
+ }
85
+ }
86
+
87
+ .search-filter__filter-toggle {
88
+ display: none;
89
+ }
90
+
91
+ &.show-filters {
92
+ .search-filter__filter-toggle .icon-button {
93
+ &::before {
94
+ background-color: var(--e-c-primary-01-100);
95
+ border-color: var(--e-c-primary-01-100);
96
+ }
97
+ }
98
+ }
99
+
100
+ @include mobile-filters;
101
+ }
102
+
103
+ .search-filter.collapsed-desktop {
104
+ @include collapsed-desktop;
105
+ @include mobile-filters;
106
+ }
@@ -0,0 +1,54 @@
1
+ <script setup lang="ts">
2
+ import { ref, useTemplateRef, onMounted } from 'vue'
3
+ import { UIconButton } from '../../elements'
4
+ import { UBadge } from '../../components'
5
+
6
+ interface Props {
7
+ activeFilter?: boolean
8
+ }
9
+
10
+ defineProps<Props>()
11
+
12
+ const showFilters = ref(false)
13
+ const filtersEl = useTemplateRef('filters')
14
+ const filterCount = ref(0)
15
+
16
+ onMounted(() => {
17
+ filterCount.value = filtersEl.value.children.length
18
+ })
19
+ </script>
20
+
21
+ <template>
22
+ <div
23
+ :class="[
24
+ 'search-filter',
25
+ { 'show-filters': showFilters, 'collapsed-desktop': filterCount + 1 >= 4 },
26
+ ]"
27
+ :style="{
28
+ '--field-count': filterCount + 1,
29
+ }"
30
+ >
31
+ <div class="search-filter__search">
32
+ <slot name="search" />
33
+
34
+ <div class="search-filter__filter-toggle">
35
+ <UBadge
36
+ dot
37
+ border
38
+ :show="activeFilter && !showFilters"
39
+ color="var(--e-c-secondary-05-500)"
40
+ :top="11"
41
+ :right="11"
42
+ >
43
+ <UIconButton icon="filter" variant="plain" @click="showFilters = !showFilters" />
44
+ </UBadge>
45
+ </div>
46
+ </div>
47
+
48
+ <div ref="filters" class="search-filter__filter">
49
+ <slot name="filters" />
50
+ </div>
51
+ </div>
52
+ </template>
53
+
54
+ <style scoped lang="scss" src="./search-filter.scss"></style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@energie360/ui-library",
3
- "version": "0.1.16",
3
+ "version": "0.1.18",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -11,9 +11,11 @@
11
11
  "./elements": "./elements/index.js",
12
12
  "./components": "./components/index.js",
13
13
  "./modules": "./modules/index.js",
14
+ "./layout": "./layout/index.js",
14
15
  "./wizard": "./wizard/index.js",
15
16
  "./utility/elements/*": "./dist/elements/*",
16
17
  "./utility/layout/*": "./dist/layout/*",
18
+ "./utils/*": "./utils/*",
17
19
  "./base/abstracts": "./base/abstracts/index.scss",
18
20
  "./styles/form": "./elements/form/form.scss",
19
21
  "./styles/form-grid": "./layout/form-grid/form-grid.scss"
@@ -0,0 +1 @@
1
+ @use '../../elements/form/form';
@@ -0,0 +1 @@
1
+ @use '../../layout/form-grid/form-grid';
@@ -0,0 +1,7 @@
1
+ /**
2
+ *
3
+ * @param {Array} a
4
+ * @param {Array} b
5
+ * @returns {Array}
6
+ */
7
+ export const intersect = (a, b) => a.filter((x) => b.includes(x))
@@ -1,11 +1,6 @@
1
1
  import { mediaquery } from '@energie360/design-tokens/design-tokens.json'
2
2
 
3
- // TODO: return .matches value
4
-
5
- export const isSmall = () => window.matchMedia(`(max-width: ${mediaquery.breakpoint.s})`)
6
-
7
- export const isMedium = () => window.matchMedia(`(max-width: ${mediaquery.breakpoint.m})`)
8
-
9
- export const isLarge = () => window.matchMedia(`(max-width: ${mediaquery.breakpoint.lg})`)
10
-
11
- export const isXLarge = () => window.matchMedia(`(max-width: ${mediaquery.breakpoint.xl})`)
3
+ export const isSmall = () => window.matchMedia(`(max-width: ${mediaquery.breakpoint.s})`).matches
4
+ export const isMedium = () => window.matchMedia(`(max-width: ${mediaquery.breakpoint.m})`).matches
5
+ export const isLarge = () => window.matchMedia(`(max-width: ${mediaquery.breakpoint.lg})`).matches
6
+ export const isXLarge = () => window.matchMedia(`(max-width: ${mediaquery.breakpoint.xl})`).matches
@@ -0,0 +1,17 @@
1
+ /**
2
+ *
3
+ * @param {number} bytes
4
+ * @param decimals
5
+ * @returns {string}
6
+ */
7
+ export const formatBytes = (bytes, decimals = 2) => {
8
+ if (!+bytes) return '0 Bytes'
9
+
10
+ const k = 1024
11
+ const dm = decimals < 0 ? 0 : decimals
12
+ const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
13
+
14
+ const i = Math.floor(Math.log(bytes) / Math.log(k))
15
+
16
+ return `${parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} ${sizes[i]}`
17
+ }
@@ -0,0 +1,8 @@
1
+ // TODO: Add more possible types.
2
+ export const mimeTypes = {
3
+ 'image/png': ['png'],
4
+ 'application/pdf': ['pdf'],
5
+ 'image/jpeg': ['jpeg', 'jpg'],
6
+ 'image/svg+xml': ['svg'],
7
+ 'image/gif': ['gif'],
8
+ }
@@ -8,6 +8,14 @@ const getLanguage = () => {
8
8
  return langAttr === '' ? DEFAULT_LANGUAGE : langAttr
9
9
  }
10
10
 
11
- export const getTranslation = (key) => {
12
- return translations[getLanguage()][key]
11
+ export const getTranslation = (key, replace) => {
12
+ let translation = translations[getLanguage()][key]
13
+
14
+ if (replace) {
15
+ for (key in replace) {
16
+ translation = translation.replace(key, replace[key])
17
+ }
18
+ }
19
+
20
+ return translation
13
21
  }