@bagelink/vue 1.7.94 → 1.7.98

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@bagelink/vue",
3
3
  "type": "module",
4
- "version": "1.7.94",
4
+ "version": "1.7.98",
5
5
  "description": "Bagel core sdk packages",
6
6
  "author": {
7
7
  "name": "Bagel Studio",
@@ -57,17 +57,17 @@
57
57
  "bin/experimentalGenTypedRoutes.ts"
58
58
  ],
59
59
  "devDependencies": {
60
- "swiper": "^12.0.3",
61
60
  "@types/leaflet": "^1.9.18",
62
61
  "@types/signature_pad": "^4.0.0",
62
+ "swiper": "^12.0.3",
63
63
  "vue": "^3.5.16",
64
64
  "vue-component-type-helpers": "^2.2.10"
65
65
  },
66
66
  "peerDependencies": {
67
67
  "@bagelink/sdk": "*",
68
68
  "type-fest": "^4",
69
- "vue-component-type-helpers": "^2.2.10",
70
69
  "vue": "*",
70
+ "vue-component-type-helpers": "^2.2.10",
71
71
  "vue-draggable-next": "^2.2.1",
72
72
  "vue-router": "*"
73
73
  },
@@ -81,6 +81,7 @@
81
81
  },
82
82
  "dependencies": {
83
83
  "@vueuse/core": "^13.3.0",
84
+ "@zxcvbn-ts/core": "^3.0.4",
84
85
  "axios": "^1.9.0",
85
86
  "floating-vue": "^5.2.2",
86
87
  "libphonenumber-js": "1.12.9",
@@ -87,7 +87,6 @@ const color = {
87
87
  display: none;
88
88
  position: absolute;
89
89
  inset-inline-end: 1.5rem;
90
- top: 2rem;
91
90
  }
92
91
 
93
92
  .alert[dismissable="true"] .alert_close {
@@ -1,6 +1,7 @@
1
1
  <script setup lang="ts">
2
2
  import type { IconType } from '@bagelink/vue'
3
3
  import { Btn, TextInput } from '@bagelink/vue'
4
+ import { zxcvbn } from '@zxcvbn-ts/core'
4
5
  import { computed } from 'vue'
5
6
 
6
7
  export interface TextInputProps {
@@ -27,12 +28,14 @@ export interface TextInputProps {
27
28
  debounceDelay?: number
28
29
  autocomplete?: AutoFillField
29
30
  onFocusout?: (e: FocusEvent) => void
31
+ strengthMeter?: boolean
30
32
  }
31
33
  const props = withDefaults(
32
34
  defineProps<TextInputProps>(),
33
35
  {
34
36
  autocomplete: 'current-password',
35
- label: ''
37
+ label: '',
38
+ strengthMeter: false
36
39
  }
37
40
  )
38
41
 
@@ -41,25 +44,75 @@ const showPwd = defineModel<boolean>('showPwd', { default: false })
41
44
 
42
45
  const toggleShowPwdIcon = computed(() => showPwd.value ? 'visibility_off' : 'visibility')
43
46
  const inputType = computed(() => (showPwd.value ? 'text' : 'password'))
47
+
48
+ const passwordStrength = computed(() => {
49
+ const pwd = password.value
50
+ if (!props.strengthMeter || pwd == null || pwd.length === 0) {
51
+ return null
52
+ }
53
+ return zxcvbn(pwd)
54
+ })
55
+
56
+ const strengthScore = computed(() => {
57
+ if (password.value == null || password.value.length === 0) {
58
+ return null
59
+ }
60
+ return passwordStrength.value?.score ?? 0
61
+ })
62
+
63
+ const strengthColor = computed(() => {
64
+ const score = strengthScore.value
65
+ if (score == null) return 'var(--input-bg)' // gray - no password
66
+ if (score <= 1) return 'var(--bgl-red)' // red
67
+ if (score === 2) return 'var(--bgl-orange)' // orange
68
+ if (score === 3) return 'var(--bgl-yellow)' // amber
69
+ return 'var(--bgl-green)' // green
70
+ })
71
+
72
+ const strengthLabel = computed(() => {
73
+ const score = strengthScore.value
74
+ if (score == null) return ''
75
+ if (score === 0) return 'Very Weak'
76
+ if (score === 1) return 'Weak'
77
+ if (score === 2) return 'Fair'
78
+ if (score === 3) return 'Good'
79
+ return 'Strong'
80
+ })
81
+
82
+ const strengthPercentage = computed(() => {
83
+ const score = strengthScore.value
84
+ if (score == null) return 0
85
+ return ((score + 1) / 5) * 100
86
+ })
87
+
88
+ const feedbackMessage = computed(() => {
89
+ const strength = passwordStrength.value
90
+ if (!strength)
91
+ return ''
92
+ const { feedback: { warning, suggestions } } = strength
93
+ const warnings = (warning != null && warning !== '') ? [warning] : []
94
+ return [...warnings, ...suggestions].join(' ')
95
+ })
44
96
  </script>
45
97
 
46
98
  <template>
47
- <div class="relative passwordInput">
48
- <TextInput
49
- v-model="password"
50
- v-bind="props"
51
- :type="inputType"
52
- class="mb-0"
53
- :name="props.name"
54
- />
99
+ <div class="relative passwordInput" :class="{ 'relative mb-2': props.strengthMeter }">
100
+ <TextInput v-model="password" v-bind="props" :type="inputType" class="mb-0" :name="props.name" />
55
101
  <div class="m-password position-bottom-end flex column justify-content-center">
56
- <Btn
57
- flat
58
- thin
59
- class="mx-05"
60
- :icon="toggleShowPwdIcon"
61
- @click="showPwd = !showPwd"
62
- />
102
+ <Btn flat thin class="mx-05" :icon="toggleShowPwdIcon" @click="showPwd = !showPwd" />
103
+ </div>
104
+ <div v-if="props.strengthMeter" class="password-strength-container absolute start end -bottom-1-5">
105
+ <div v-tooltip="feedbackMessage || `Password strength: ${strengthLabel}`" class="password-strength-bar pointer overflow-hidden round">
106
+ <div
107
+ class="password-strength-fill h-100p round" :style="{
108
+ width: `${strengthPercentage}%`,
109
+ backgroundColor: strengthColor,
110
+ }"
111
+ />
112
+ </div>
113
+ <span class="password-strength-label txt10 light white-space " :style="{ color: strengthColor }">
114
+ {{ strengthLabel }}
115
+ </span>
63
116
  </div>
64
117
  </div>
65
118
  </template>
@@ -68,8 +121,27 @@ const inputType = computed(() => (showPwd.value ? 'text' : 'password'))
68
121
  .m-password {
69
122
  height: var(--input-height) !important;
70
123
  }
124
+
71
125
  [dir='rtl'] .passwordInput input {
72
- direction: ltr;
73
- text-align: right;
126
+ direction: ltr;
127
+ text-align: right;
128
+ }
129
+
130
+ .password-strength-container {
131
+ height: 1.25rem;
132
+ }
133
+
134
+ .password-strength-bar {
135
+ flex: 1;
136
+ height: 6px;
137
+ background-color: var(--input-bg);
138
+ }
139
+
140
+ .password-strength-fill {
141
+ transition: width 0.3s ease, background-color 0.3s ease;
142
+ }
143
+
144
+ .password-strength-label {
145
+ transition: color 0.3s ease;
74
146
  }
75
147
  </style>