@energie360/ui-library 0.1.6 → 0.1.7

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 (34) hide show
  1. package/components/collapsible/u-collapsible.vue +0 -1
  2. package/components/inline-edit/index.js +1 -0
  3. package/components/inline-edit/inline-edit.scss +54 -0
  4. package/components/inline-edit/u-inline-edit.vue +98 -0
  5. package/elements/button/_button-plain-spaceless.scss +8 -0
  6. package/elements/button/button.scss +5 -0
  7. package/elements/button/index.js +1 -0
  8. package/elements/button-chip/index.js +1 -0
  9. package/elements/icon/index.js +1 -0
  10. package/elements/icon-button/index.js +1 -0
  11. package/elements/image/index.js +1 -0
  12. package/elements/loader/index.js +1 -0
  13. package/elements/numeric-stepper/index.js +1 -0
  14. package/elements/password-progress/index.js +1 -0
  15. package/elements/password-progress/password-progress.scss +36 -0
  16. package/elements/password-progress/u-password-progress.vue +36 -0
  17. package/elements/select/index.js +1 -0
  18. package/elements/select-chip/index.js +1 -0
  19. package/elements/select-chips/index.js +1 -0
  20. package/elements/spectro/index.js +1 -0
  21. package/elements/text-field/index.js +1 -0
  22. package/elements/text-field/text-field.scss +2 -1
  23. package/elements/text-field/u-text-field.vue +3 -3
  24. package/elements/textarea/index.js +1 -0
  25. package/elements/textarea/textarea.scss +14 -0
  26. package/elements/textarea/u-textarea.vue +129 -0
  27. package/elements/toggle-switch/index.js +1 -0
  28. package/i18n/i18n.ts +12 -1
  29. package/modules/feedback/u-feedback-close.vue +7 -0
  30. package/modules/feedback/u-feedback-finish-view.vue +4 -0
  31. package/modules/inline-edit-group/index.js +1 -0
  32. package/modules/inline-edit-group/inline-edit-group.scss +0 -0
  33. package/modules/inline-edit-group/u-inline-edit-group.vue +22 -0
  34. package/package.json +2 -2
@@ -65,7 +65,6 @@ defineExpose({
65
65
  <section
66
66
  v-if="!panelHidden"
67
67
  :id="panelId"
68
- class="collapsible__panel"
69
68
  :aria-labelledby="headerId"
70
69
  >
71
70
  <slot name="panel">
@@ -0,0 +1 @@
1
+ export { default as UInlineEdit } from './u-inline-edit.vue'
@@ -0,0 +1,54 @@
1
+ @use '../../base/abstracts/' as a;
2
+
3
+ .inline-edit {
4
+ border-bottom: 1px solid var(--e-c-mono-200);
5
+ padding-top: var(--e-space-8);
6
+ padding-bottom: var(--e-space-8);
7
+
8
+ &.expanded {
9
+ .inline-edit__summary {
10
+ display: none;
11
+ }
12
+
13
+ .inline-edit__content,
14
+ .inline-edit__description {
15
+ display: block;
16
+ }
17
+ }
18
+
19
+ &.disabled {
20
+ .inline-edit__title {
21
+ color: var(--e-c-mono-500);
22
+ }
23
+ }
24
+ }
25
+
26
+ .inline-edit__heading {
27
+ display: flex;
28
+ column-gap: var(--e-space-4);
29
+ }
30
+
31
+ .inline-edit__title {
32
+ @include a.type(300, strong);
33
+ }
34
+
35
+ .inline-edit__description,
36
+ .inline-edit__summary {
37
+ @include a.type(300);
38
+
39
+ color: var(--e-c-mono-700);
40
+ }
41
+
42
+ .inline-edit__description {
43
+ display: none;
44
+ }
45
+
46
+ .inline-edit__heading-cta-col {
47
+ margin-left: auto;
48
+ text-align: right;
49
+ }
50
+
51
+ .inline-edit__content {
52
+ display: none;
53
+ margin-top: var(--e-space-6);
54
+ }
@@ -0,0 +1,98 @@
1
+ <script setup lang="ts">
2
+ import { ref, inject, useId, watch, useTemplateRef } from 'vue'
3
+ import UButton from '../../elements/button/u-button.vue'
4
+ import { getTranslation } from '../../utils/translations/translate'
5
+
6
+ interface Props {
7
+ title: string
8
+ summary: string
9
+ description: string
10
+ }
11
+
12
+ defineProps<Props>()
13
+
14
+ const emit = defineEmits(['expanded', 'collapsed'])
15
+ const rootEl = useTemplateRef('root')
16
+ const editId = useId()
17
+ const headingId = `heading-${editId}`
18
+ const panelId = `panel-${editId}`
19
+ const expanded = ref(false)
20
+ const disabled = ref(false)
21
+ const { update, activeEdit } = inject('inline-edit-group', {})
22
+
23
+ const onEditToggle = () => {
24
+ expanded.value = !expanded.value
25
+
26
+ emit(expanded.value ? 'expanded' : 'collapsed')
27
+
28
+ if (update && expanded.value) {
29
+ update(editId)
30
+
31
+ setTimeout(() => {
32
+ rootEl.value.scrollIntoView()
33
+ }, 80)
34
+
35
+ return
36
+ }
37
+
38
+ if (update && !expanded.value) {
39
+ // Active inline-edit is collapsed.
40
+ // Restore state of other items.
41
+ update('')
42
+ }
43
+ }
44
+
45
+ if (activeEdit) {
46
+ watch(activeEdit, (newV) => {
47
+ if (newV === '') {
48
+ expanded.value = false
49
+ disabled.value = false
50
+ return
51
+ }
52
+
53
+ if (newV !== editId) {
54
+ expanded.value = false
55
+ disabled.value = true
56
+ }
57
+ })
58
+ }
59
+
60
+ const collapse = () => {
61
+ expanded.value = false
62
+
63
+ if (update) {
64
+ update('')
65
+ }
66
+ }
67
+
68
+ defineExpose({ collapse })
69
+ </script>
70
+
71
+ <template>
72
+ <div ref="root" class="inline-edit" :class="{ expanded, disabled }">
73
+ <div class="inline-edit__heading">
74
+ <div class="inline-edit__heading-col">
75
+ <p :id="headingId" class="inline-edit__title">{{ title }}</p>
76
+
77
+ <p class="inline-edit__summary" v-html="summary"></p>
78
+ <p class="inline-edit__description" v-html="description"></p>
79
+ </div>
80
+ <div class="inline-edit__heading-cta-col">
81
+ <UButton
82
+ variant="plain-spaceless"
83
+ :disabled
84
+ :aria-controls="panelId"
85
+ :aria-expanded="expanded"
86
+ @click="onEditToggle"
87
+ >{{ getTranslation(expanded ? 'cancel' : 'edit') }}</UButton
88
+ >
89
+ </div>
90
+ </div>
91
+
92
+ <section :id="panelId" :aria-labelledby="headingId" class="inline-edit__content">
93
+ <slot></slot>
94
+ </section>
95
+ </div>
96
+ </template>
97
+
98
+ <style lang="scss" src="./inline-edit.scss"></style>
@@ -0,0 +1,8 @@
1
+ @use './button-plain' as *;
2
+
3
+ @mixin button-plain-spaceless {
4
+ @include button-plain;
5
+
6
+ padding: 0;
7
+ border-radius: var(--e-brd-radius-1); // For nicer focus outline.
8
+ }
@@ -2,6 +2,7 @@
2
2
  @use 'button-filled' as *;
3
3
  @use 'button-outlined' as *;
4
4
  @use 'button-plain' as *;
5
+ @use 'button-plain-spaceless' as *;
5
6
  @use 'button-plain-small' as *;
6
7
  @use 'button-filled-inverted' as *;
7
8
  @use 'button-outlined-inverted' as *;
@@ -32,6 +33,10 @@
32
33
  @include button-plain;
33
34
  }
34
35
 
36
+ &.plain-spaceless {
37
+ @include button-plain-spaceless;
38
+ }
39
+
35
40
  &.plain-small {
36
41
  @include button-plain-small;
37
42
  }
@@ -0,0 +1 @@
1
+ export { default as UButton } from './u-button.vue'
@@ -0,0 +1 @@
1
+ export { default as UButtonChip } from './u-button-chip.vue'
@@ -0,0 +1 @@
1
+ export { default as UIcon } from './u-icon.vue'
@@ -0,0 +1 @@
1
+ export { default as UIconButton } from './u-icon-button.vue'
@@ -0,0 +1 @@
1
+ export { default as UImage } from './u-image.vue'
@@ -0,0 +1 @@
1
+ export { default as ULoader } from './u-loader.vue'
@@ -0,0 +1 @@
1
+ export { default as UNumericStepper } from './u-numeric-stepper.vue'
@@ -0,0 +1 @@
1
+ export { default as UPasswordProgress } from './u-password-progress.vue'
@@ -0,0 +1,36 @@
1
+ @use '../../base/abstracts/' as a;
2
+
3
+ .password-progress__caption {
4
+ margin-bottom: var(--e-space-2);
5
+
6
+ @include a.type(100, strong);
7
+ }
8
+
9
+ .password-progress__rules {
10
+ display: flex;
11
+ flex-direction: column;
12
+ row-gap: var(--e-space-2);
13
+ }
14
+
15
+ .password-progress__rule {
16
+ display: flex;
17
+ align-items: center;
18
+ column-gap: var(--e-space-1);
19
+
20
+ @include a.type(100);
21
+
22
+ .icon {
23
+ color: var(--e-c-mono-500);
24
+ transition: color a.$trs-default;
25
+ }
26
+
27
+ &.met {
28
+ .icon {
29
+ color: var(--e-c-signal-01-500);
30
+ }
31
+ }
32
+ }
33
+
34
+ .text-field + .password-progress {
35
+ margin-top: var(--e-space-6);
36
+ }
@@ -0,0 +1,36 @@
1
+ <script setup lang="ts">
2
+ import UIcon from '../icon/u-icon.vue'
3
+ import { getTranslation } from '../../utils/translations/translate'
4
+
5
+ interface PasswordRule {
6
+ label: string
7
+ met: boolean
8
+ }
9
+
10
+ interface Props {
11
+ rules: PasswordRule[]
12
+ }
13
+
14
+ defineProps<Props>()
15
+ </script>
16
+
17
+ <template>
18
+ <div class="password-progress">
19
+ <p class="password-progress__caption">
20
+ {{ getTranslation('passwordRequirements') }}
21
+ </p>
22
+ <ul class="password-progress__rules">
23
+ <li
24
+ v-for="(rule, idx) in rules"
25
+ :key="idx"
26
+ class="password-progress__rule"
27
+ :class="{ met: rule.met }"
28
+ >
29
+ <UIcon :name="rule.met ? 'check-circle' : 'close-circle'" />
30
+ {{ rule.label }}
31
+ </li>
32
+ </ul>
33
+ </div>
34
+ </template>
35
+
36
+ <style lang="scss" src="./password-progress.scss"></style>
@@ -0,0 +1 @@
1
+ export { default as USelect } from './u-select.vue'
@@ -0,0 +1 @@
1
+ export { default as USelectChip } from './u-select-chip.vue'
@@ -0,0 +1 @@
1
+ export { default as USelectChips } from './u-select-chips.vue'
@@ -0,0 +1 @@
1
+ export { default as USpectro } from './u-spectro.vue'
@@ -0,0 +1 @@
1
+ export { default as UTextField } from './u-text-field.vue'
@@ -16,7 +16,8 @@
16
16
  .password-toggle {
17
17
  display: block;
18
18
  border-radius: var(--e-brd-radius-1);
19
- color: var(--e-c-primary-01-500);
19
+ color: var(--e-c-primary-01-700);
20
+ transition: color a.$trs-default;
20
21
  cursor: pointer;
21
22
 
22
23
  &:hover {
@@ -68,7 +68,7 @@ const onHoverOut = () => {
68
68
  }
69
69
 
70
70
  const hasHelpText = computed(() => {
71
- return !!helpText || !!slots.helpText || required
71
+ return !!helpText || !!slots.helpText || !required
72
72
  })
73
73
 
74
74
  const needsHelpTextSpacer = computed(() => {
@@ -81,7 +81,7 @@ const onPasswordToggle = () => {
81
81
  }
82
82
 
83
83
  const passwordIcon = computed(() => {
84
- return _inputType.value === TextFieldTypes.password ? 'password' : 'password-show'
84
+ return _inputType.value === TextFieldTypes.password ? 'visibility' : 'visibility-off'
85
85
  })
86
86
 
87
87
  const passwordA11yText = computed(() => {
@@ -113,7 +113,7 @@ watch(
113
113
  'has-value': hasValue,
114
114
  'float-label': isFocused || hasValue || placeholder || forceFloatLabel,
115
115
  'has-error': error,
116
- 'show-help-text': !required || hasHelpText,
116
+ 'show-help-text': hasHelpText,
117
117
  },
118
118
  ]"
119
119
  @mouseenter="onHover"
@@ -0,0 +1 @@
1
+ export { default as UTextarea } from './u-textarea.vue'
@@ -0,0 +1,14 @@
1
+ @use '../../base/abstracts' as a;
2
+ @use '../form-field' as f;
3
+
4
+ // Maybe use this trick for textarea https://css-tricks.com/the-cleanest-trick-for-autogrowing-textareas/
5
+
6
+ .textarea {
7
+ @include f.form-field-base(textarea);
8
+ @include f.form-field-states-all(textarea);
9
+ @include f.form-field-error;
10
+
11
+ .label {
12
+ top: a.rem(24);
13
+ }
14
+ }
@@ -0,0 +1,129 @@
1
+ <script setup lang="ts">
2
+ import { useId, ref, useTemplateRef, useSlots, computed } from 'vue'
3
+ import { FormFieldBase } from '../form-field/form-field.types'
4
+ import { getTranslation } from '../../utils/translations/translate'
5
+
6
+ interface Props extends FormFieldBase {
7
+ name: string
8
+ readonly?: boolean
9
+ placeholder?: string
10
+ }
11
+
12
+ const {
13
+ required = false,
14
+ disabled = false,
15
+ readonly = false,
16
+ helpText = '',
17
+ errorMessage = '',
18
+ } = defineProps<Props>()
19
+
20
+ const slots = useSlots()
21
+ const model = defineModel<string>()
22
+
23
+ const input = useTemplateRef('input')
24
+
25
+ const inputId = useId()
26
+ const isFocused = ref(false)
27
+ const isHovering = ref(false)
28
+ const hasValue = ref(false)
29
+
30
+ const spacer = '.&nbsp;'
31
+
32
+ const hasHelpText = computed(() => {
33
+ return !!helpText || !!slots.helpText || !required
34
+ })
35
+
36
+ const needsHelpTextSpacer = computed(() => {
37
+ return !!helpText || !!slots.helpText
38
+ })
39
+
40
+ const onHover = () => {
41
+ if (readonly || disabled) {
42
+ return
43
+ }
44
+
45
+ isHovering.value = true
46
+ }
47
+
48
+ const onHoverOut = () => {
49
+ isHovering.value = false
50
+ }
51
+
52
+ const onFocus = () => {
53
+ if (readonly || disabled) {
54
+ return
55
+ }
56
+
57
+ isFocused.value = true
58
+ }
59
+
60
+ const onBlur = () => {
61
+ isFocused.value = false
62
+ }
63
+
64
+ const onInput = () => {
65
+ hasValue.value = !!input.value && input.value.value !== ''
66
+ }
67
+ </script>
68
+
69
+ <template>
70
+ <div
71
+ :class="[
72
+ 'textarea',
73
+ {
74
+ required,
75
+ disabled,
76
+ readonly,
77
+ 'float-label': isFocused || placeholder || hasValue,
78
+ focus: isFocused,
79
+ hover: isHovering,
80
+ 'has-error': error,
81
+ 'show-help-text': hasHelpText,
82
+ },
83
+ ]"
84
+ @mouseenter="onHover"
85
+ @mouseleave="onHoverOut"
86
+ >
87
+ <div class="wrapper">
88
+ <div class="label">
89
+ <slot name="label"
90
+ ><label :for="inputId">{{ label }}</label></slot
91
+ >
92
+ </div>
93
+
94
+ <div class="control">
95
+ <slot>
96
+ <textarea
97
+ :id="inputId"
98
+ ref="input"
99
+ v-model="model"
100
+ :name
101
+ :disabled
102
+ :readonly
103
+ :required
104
+ :placeholder
105
+ @focus="onFocus"
106
+ @blur="onBlur"
107
+ @input="onInput"
108
+ />
109
+ </slot>
110
+ <div class="control-border"></div>
111
+ </div>
112
+ </div>
113
+
114
+ <div class="help-text">
115
+ <span class="optional-text"
116
+ >{{ getTranslation('optional')
117
+ }}<span v-if="needsHelpTextSpacer" v-html="spacer"></span></span
118
+ ><slot name="helpText">{{ helpText }}</slot>
119
+ </div>
120
+
121
+ <div class="error-messages-container">
122
+ <slot name="error-message"
123
+ ><span>{{ errorMessage }}</span></slot
124
+ >
125
+ </div>
126
+ </div>
127
+ </template>
128
+
129
+ <style lang="scss" scoped src="./textarea.scss"></style>
@@ -0,0 +1 @@
1
+ export { default as UToggleSwitch } from './u-toggle-switch.vue'
package/i18n/i18n.ts CHANGED
@@ -1,4 +1,3 @@
1
- // TODO: translate this, dude!!
2
1
  const translations = {
3
2
  DE: {
4
3
  yes: 'Ja',
@@ -9,6 +8,9 @@ const translations = {
9
8
  showPassword: 'Passwort anzeigen',
10
9
  hidePassword: 'Passwort verbergen',
11
10
  close: 'Schliessen',
11
+ passwordRequirements: 'Anforderungen an das Passwort',
12
+ edit: 'Bearbeiten',
13
+ cancel: 'Abbrechen',
12
14
  },
13
15
  FR: {
14
16
  yes: 'Ja',
@@ -19,6 +21,9 @@ const translations = {
19
21
  showPassword: 'Passwort anzeigen',
20
22
  hidePassword: 'Passwort verbergen',
21
23
  close: 'Fermer',
24
+ passwordRequirements: 'Exigences relatives au mot de passe',
25
+ edit: 'Modifier',
26
+ cancel: 'Annuler',
22
27
  },
23
28
  IT: {
24
29
  yes: 'Ja',
@@ -29,6 +34,9 @@ const translations = {
29
34
  showPassword: 'Passwort anzeigen',
30
35
  hidePassword: 'Passwort verbergen',
31
36
  close: 'Chiudere',
37
+ passwordRequirements: 'Requisiti per la password',
38
+ edit: 'modificare',
39
+ cancel: 'cancellare',
32
40
  },
33
41
  EN: {
34
42
  yes: 'Ja',
@@ -39,6 +47,9 @@ const translations = {
39
47
  showPassword: 'Passwort anzeigen',
40
48
  hidePassword: 'Passwort verbergen',
41
49
  close: 'Close',
50
+ passwordRequirements: 'Password requirements',
51
+ edit: 'Edit',
52
+ cancel: 'Cancel',
42
53
  },
43
54
  }
44
55
 
@@ -18,12 +18,19 @@ const closeColor = computed(() => `var(--e-c-${color})`)
18
18
  </template>
19
19
 
20
20
  <style lang="scss" scoped>
21
+ @use '../../base/abstracts' as a;
22
+
21
23
  .feedback-close-wrapper {
22
24
  position: absolute;
23
25
  padding: var(--e-space-2);
24
26
  right: var(--e-space-2);
25
27
  top: var(--e-space-2);
26
28
 
29
+ @include a.bp(xs) {
30
+ right: 0;
31
+ top: 0;
32
+ }
33
+
27
34
  button {
28
35
  cursor: pointer;
29
36
  color: v-bind('closeColor');
@@ -23,6 +23,10 @@ defineEmits<{ close: [] }>()
23
23
  background-color: var(--e-c-primary-01-700);
24
24
  color: var(--e-c-mono-00);
25
25
  --fill-color: var(--e-c-mono-00);
26
+
27
+ @include a.bp(xs) {
28
+ padding: var(--e-space-6) var(--e-space-7) var(--e-space-6) var(--e-space-4);
29
+ }
26
30
  }
27
31
 
28
32
  .finish-text-title {
@@ -0,0 +1 @@
1
+ export { default as UInlineEditGroup } from './u-inline-edit-group.vue'
@@ -0,0 +1,22 @@
1
+ <script setup lang="ts">
2
+ import { provide, ref } from 'vue'
3
+
4
+ const update = (id) => {
5
+ activeEdit.value = id
6
+ }
7
+
8
+ const activeEdit = ref('')
9
+
10
+ provide('inline-edit-group', {
11
+ update,
12
+ activeEdit,
13
+ })
14
+ </script>
15
+
16
+ <template>
17
+ <div class="inline-edit-group">
18
+ <slot></slot>
19
+ </div>
20
+ </template>
21
+
22
+ <style lang="scss" src="./inline-edit-group.scss"></style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@energie360/ui-library",
3
- "version": "0.1.6",
3
+ "version": "0.1.7",
4
4
  "description": "",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -9,7 +9,7 @@
9
9
  "./package.json": "./package.json",
10
10
  "./base-style.css": "./dist/base-style.css",
11
11
  "./elements": "./elements/index.js",
12
- "./elements/*": "./elements/*",
12
+ "./elements/*": "./elements/*/index.js",
13
13
  "./components": "./components/index.js",
14
14
  "./components/*": "./components/*",
15
15
  "./modules": "./modules/index.js",