@fy-/fws-vue 2.2.63 → 2.2.65

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.
@@ -219,161 +219,221 @@ onMounted(async () => {
219
219
  <form
220
220
  v-if="!completed"
221
221
  class="fws-login w-full"
222
+ aria-labelledby="login-title"
222
223
  @submit.prevent="userFlow()"
223
224
  >
224
- <!-- <FyLoader id="klblogin" /> -->
225
- <div class="w-full">
225
+ <!-- Message / Header -->
226
+ <div
227
+ v-if="responseMessage"
228
+ class="fws-login__header mb-4"
229
+ >
226
230
  <h2
227
- v-if="responseMessage"
228
- class="text-lg text-fv-neutral-700 dark:text-fv-neutral-300 px-2"
231
+ id="login-title"
232
+ class="text-lg font-medium text-fv-neutral-700 dark:text-fv-neutral-300"
229
233
  >
230
234
  {{ responseMessage }}
231
235
  </h2>
232
- <template v-if="hasOauth && !showEmail">
233
- <div class="flex flex-col gap-2 px-2 justify-center py-2">
234
- <template v-for="field of responseFields" :key="field.id">
235
- <a
236
- v-if="field.type && field.type === 'oauth2' && field.button"
237
- href="javascript:void(0);"
238
- class="flex border border-fv-neutral-300 dark:border-fv-neutral-700 shadow items-center gap-2 justify-start btn neutral defaults w-full mx-auto !font-semibold"
239
- :style="`background: ${
240
- field.button['background-color']
241
- }; color: ${$getContrastingTextColor(
242
- field.button['background-color'],
243
- )}`"
244
- @click="
245
- () => {
246
- if (field.info.Button_Extra?.trigger) {
247
- doTrigger(field);
248
- }
249
- else {
250
- userFlow({ initial: true, oauth: field.id });
251
- }
252
- }
253
- "
254
- >
255
- <img
256
- :key="`${field.label}oauth`"
257
- class="h-12 w-12 block p-2 mr-3"
258
- :alt="field.info.Name"
259
- :src="field.button.logo"
260
- >
261
- <div>
262
- {{
263
- $t("user_flow_signin_with", {
264
- provider: field.name,
265
- })
266
- }}
267
- </div>
268
- </a>
269
- </template>
270
- <button
271
- type="button"
272
- class="flex items-center gap-2 justify-start btn neutral defaults w-full mx-auto !font-semibold"
273
- @click="
274
- () => {
275
- showEmail = true;
236
+ </div>
237
+
238
+ <!-- OAuth providers section -->
239
+ <div v-if="hasOauth && !showEmail" class="fws-login__oauth space-y-3">
240
+ <template v-for="field of responseFields" :key="field.id">
241
+ <a
242
+ v-if="field.type && field.type === 'oauth2' && field.button"
243
+ href="javascript:void(0);"
244
+ class="flex w-full items-center justify-start gap-3 px-4 py-2.5 rounded-lg border border-fv-neutral-200 dark:border-fv-neutral-700
245
+ transition-all duration-200 hover:shadow-sm focus:outline-none focus:ring-2 focus:ring-offset-2 dark:focus:ring-offset-fv-neutral-800
246
+ focus:ring-fv-primary-500 dark:focus:ring-fv-primary-600"
247
+ :style="field.button && field.button['background-color']
248
+ ? `background: ${field.button['background-color']}; color: ${$getContrastingTextColor(field.button['background-color'])}`
249
+ : ''"
250
+ @click="
251
+ () => {
252
+ if (field.info && field.info.Button_Extra && field.info.Button_Extra.trigger) {
253
+ doTrigger(field);
276
254
  }
277
- "
255
+ else {
256
+ userFlow({ initial: true, oauth: field.id });
257
+ }
258
+ }
259
+ "
260
+ >
261
+ <img
262
+ v-if="field.button && field.button.logo"
263
+ :key="`${field.label}oauth`"
264
+ class="h-6 w-6 flex-shrink-0"
265
+ :alt="field.info && field.info.Name ? field.info.Name : ''"
266
+ :src="field.button.logo"
278
267
  >
279
- <EnvelopeIcon class="h-12 w-12 block p-2 mr-3" />
280
- <div>
281
- {{
282
- $t("user_flow_signin_with", {
283
- provider: $t("user_flow_provider_email_cta"),
284
- })
285
- }}
286
- </div>
287
- </button>
288
- </div>
268
+ <span class="text-base font-medium">
269
+ {{ $t("user_flow_signin_with", { provider: field.name }) }}
270
+ </span>
271
+ </a>
289
272
  </template>
290
- <div
291
- v-if="forceAction || (showEmail && initial) || !initial"
292
- class="px-2 py-2"
273
+
274
+ <div class="relative my-6">
275
+ <div class="absolute inset-0 flex items-center">
276
+ <div class="w-full border-t border-fv-neutral-200 dark:border-fv-neutral-700" />
277
+ </div>
278
+ <div class="relative flex justify-center text-sm">
279
+ <span class="px-2 bg-white dark:bg-fv-neutral-800 text-fv-neutral-500 dark:text-fv-neutral-400">
280
+ {{ $t("user_flow_or") }}
281
+ </span>
282
+ </div>
283
+ </div>
284
+
285
+ <button
286
+ type="button"
287
+ class="flex w-full items-center justify-start gap-3 px-4 py-2.5 rounded-lg border border-fv-neutral-200 dark:border-fv-neutral-700
288
+ bg-white dark:bg-fv-neutral-700 text-fv-neutral-800 dark:text-white
289
+ hover:bg-fv-neutral-50 dark:hover:bg-fv-neutral-650 transition-all duration-200
290
+ hover:shadow-sm focus:outline-none focus:ring-2 focus:ring-fv-primary-500 dark:focus:ring-fv-primary-600"
291
+ aria-label="Sign in with email"
292
+ @click="showEmail = true"
293
293
  >
294
- <template v-if="responseFields && responseFields.length > 0">
295
- <template v-for="field of responseFields" :key="field.label">
294
+ <EnvelopeIcon class="h-6 w-6 text-fv-primary-500 dark:text-fv-primary-400 flex-shrink-0" />
295
+ <span class="text-base font-medium">
296
+ {{ $t("user_flow_signin_with", { provider: $t("user_flow_provider_email_cta") }) }}
297
+ </span>
298
+ </button>
299
+ </div>
300
+
301
+ <!-- Form fields section -->
302
+ <div
303
+ v-if="forceAction || (showEmail && initial) || !initial"
304
+ class="fws-login__form space-y-4"
305
+ >
306
+ <template v-if="responseFields && responseFields.length > 0">
307
+ <!-- Labels and text elements -->
308
+ <template v-for="field of responseFields" :key="field.label">
309
+ <div
310
+ v-if="field.type === 'label'"
311
+ class="mb-2"
312
+ >
296
313
  <h3
297
- v-if="field.type === 'label'"
298
- class="pt-2 pb-1 text-sm text-fv-neutral-500 dark:text-fv-neutral-400"
299
- :class="
314
+ class="text-sm"
315
+ :class="[
300
316
  field.style === 'error'
301
- ? 'text-sm my-2 p-1 font-semibold text-red-800 dark:text-red-300'
302
- : ''
303
- "
317
+ ? 'p-3 rounded-lg bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-300 font-medium'
318
+ : 'text-fv-neutral-600 dark:text-fv-neutral-400',
319
+ ]"
304
320
  >
305
- <a v-if="field.link" :href="field.link" class="fws-link mb-3">{{
306
- field.label
307
- }}</a>
308
- <span v-else class="mb-2" v-html="field.label" />
321
+ <a
322
+ v-if="field.link"
323
+ :href="field.link"
324
+ class="text-fv-primary-600 dark:text-fv-primary-400 hover:underline focus:outline-none focus:ring-2 focus:ring-fv-primary-500 dark:focus:ring-fv-primary-600 rounded"
325
+ >
326
+ {{ field.label }}
327
+ </a>
328
+ <span v-else v-html="field.label" />
309
329
  </h3>
330
+ </div>
310
331
 
311
- <template v-if="field.cat === 'input'">
312
- <template
313
- v-if="
314
- field.type === 'text'
315
- || field.type === 'password'
316
- || field.type === 'email'
317
- || field.type === 'mask'
318
- || field.type === 'tel'
319
- || field.type === 'number'
320
- || field.type === 'phone'
321
- "
322
- >
323
- <DefaultInput
324
- v-if="field.name"
325
- :id="field.name"
326
- ref="inputs"
327
- v-model="formData[field.name]"
328
- :label="field.label"
329
- :mask="field.mask"
330
- class="mt-3"
331
- :placeholder="
332
- field.name === 'name' ? 'John Doe' : field.label
333
- "
334
- :error="fieldsError[field.name]"
335
- :type="field.type"
336
- :req="responseReq.includes(field.name)"
337
- :autocomplete="autocompleteValue(field.name)"
338
- />
339
- </template>
340
- </template>
341
- <template v-if="field.type === 'checkbox'">
332
+ <!-- Input fields -->
333
+ <template v-if="field.cat === 'input'">
334
+ <template
335
+ v-if="
336
+ field.type === 'text'
337
+ || field.type === 'password'
338
+ || field.type === 'email'
339
+ || field.type === 'mask'
340
+ || field.type === 'tel'
341
+ || field.type === 'number'
342
+ || field.type === 'phone'
343
+ "
344
+ >
342
345
  <DefaultInput
343
346
  v-if="field.name"
344
347
  :id="field.name"
345
- v-model:checkbox-value="formData[field.name]"
346
- class="mt-3"
348
+ ref="inputs"
349
+ v-model="formData[field.name]"
347
350
  :label="field.label"
351
+ :mask="field.mask"
352
+ :placeholder="
353
+ field.name === 'name' ? 'John Doe' : field.label
354
+ "
348
355
  :error="fieldsError[field.name]"
349
356
  :type="field.type"
350
357
  :req="responseReq.includes(field.name)"
351
- :link-icon="field.link"
358
+ :autocomplete="autocompleteValue(field.name)"
352
359
  />
353
360
  </template>
354
361
  </template>
355
- <div
356
- v-if="responseError && responseError.token"
357
- class="text-sm my-2 p-1 font-semibold text-red-800 dark:text-red-300"
358
- v-html="$t(responseError.token)"
359
- />
360
- <div v-if="responseReq.includes('password') && 0" class="reset-pwd">
361
- <a
362
- href="javascript:void(0)"
363
- @click="
364
- () => {
365
- eventBus.emit('ResetPasswordModal', true);
366
- pwdRecoverMailSent = false;
367
- }
368
- "
369
- >{{ $t("recover_pwd_link") }}</a>
370
- </div>
371
- <button class="btn primary medium mt-4">
372
- {{ $t("cta_login_next") }}
373
- </button>
362
+
363
+ <!-- Checkbox inputs -->
364
+ <template v-if="field.type === 'checkbox'">
365
+ <DefaultInput
366
+ v-if="field.name"
367
+ :id="field.name"
368
+ v-model:checkbox-value="formData[field.name]"
369
+ :label="field.label"
370
+ :error="fieldsError[field.name]"
371
+ :type="field.type"
372
+ :req="responseReq.includes(field.name)"
373
+ :link-icon="field.link"
374
+ />
375
+ </template>
374
376
  </template>
375
- </div>
377
+
378
+ <!-- Error message -->
379
+ <div
380
+ v-if="responseError && responseError.token"
381
+ class="p-3 rounded-lg bg-red-50 dark:bg-red-900/20 text-red-700 dark:text-red-300 text-sm font-medium"
382
+ role="alert"
383
+ v-html="$t(responseError.token)"
384
+ />
385
+
386
+ <!-- Password recovery link -->
387
+ <div
388
+ v-if="responseReq.includes('password') && 0"
389
+ class="text-right my-2"
390
+ >
391
+ <button
392
+ type="button"
393
+ class="text-fv-primary-600 dark:text-fv-primary-400 text-sm hover:underline focus:outline-none focus:ring-2 focus:ring-fv-primary-500 rounded"
394
+ @click="
395
+ () => {
396
+ eventBus.emit('ResetPasswordModal', true);
397
+ pwdRecoverMailSent = false;
398
+ }
399
+ "
400
+ >
401
+ {{ $t("recover_pwd_link") }}
402
+ </button>
403
+ </div>
404
+
405
+ <!-- Submit button -->
406
+ <button
407
+ type="submit"
408
+ class="w-full flex justify-center py-2.5 px-4 border border-transparent rounded-lg shadow-sm
409
+ text-white bg-fv-primary-600 hover:bg-fv-primary-700 dark:bg-fv-primary-700 dark:hover:bg-fv-primary-800
410
+ focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-fv-primary-500 dark:focus:ring-fv-primary-600
411
+ dark:focus:ring-offset-fv-neutral-800 font-medium transition-all duration-200"
412
+ aria-label="Continue"
413
+ >
414
+ {{ $t("cta_login_next") }}
415
+ </button>
416
+ </template>
376
417
  </div>
377
418
  </form>
378
419
  </ClientOnly>
379
420
  </template>
421
+
422
+ <style scoped>
423
+ .fws-login {
424
+ @apply transition-all duration-300;
425
+ }
426
+
427
+ .fws-login__oauth a,
428
+ .fws-login__oauth button,
429
+ .fws-login__form button[type="submit"] {
430
+ @apply transition-all duration-200;
431
+ }
432
+
433
+ @media (max-width: 640px) {
434
+ .fws-login__oauth,
435
+ .fws-login__form {
436
+ @apply px-0;
437
+ }
438
+ }
439
+ </style>
@@ -187,7 +187,7 @@ defineExpose({ focus, blur, getInputRef })
187
187
  :name="id"
188
188
  :class="{
189
189
  'error': checkErrors,
190
- 'bg-fv-neutral-50 border border-fv-neutral-300 text-fv-neutral-900 text-sm rounded-lg block w-full dark:bg-fv-neutral-700 dark:border-fv-neutral-600 dark:placeholder-fv-neutral-400 dark:text-white transition-all duration-200': type !== 'range',
190
+ 'bg-fv-neutral-50 border border-fv-neutral-300 text-fv-neutral-900 text-sm rounded-lg block w-full dark:bg-fv-neutral-700 dark:border-fv-neutral-600 dark:placeholder-fv-neutral-300 dark:text-white transition-all duration-200': type !== 'range',
191
191
  'p-2.5': type !== 'range',
192
192
  'focus:border-fv-primary-500 dark:focus:border-fv-primary-500': !checkErrors,
193
193
  'focus:ring-2 focus:ring-fv-primary-300 dark:focus:ring-fv-primary-800 focus:ring-opacity-50': type !== 'range',
@@ -288,7 +288,7 @@ defineExpose({ focus, blur, getInputRef })
288
288
  :aria-invalid="checkErrors ? 'true' : 'false'"
289
289
  class="block p-2.5 w-full text-sm text-fv-neutral-900 bg-fv-neutral-50 rounded-lg
290
290
  border border-fv-neutral-300 focus:ring-2 focus:ring-fv-primary-300 focus:border-fv-primary-500
291
- dark:bg-fv-neutral-700 dark:border-fv-neutral-600 dark:placeholder-fv-neutral-400
291
+ dark:bg-fv-neutral-700 dark:border-fv-neutral-600 dark:placeholder-fv-neutral-300
292
292
  dark:text-white dark:focus:ring-fv-primary-800 dark:focus:border-fv-primary-500
293
293
  transition-colors duration-200 shadow-sm"
294
294
  @focus="handleFocus"
@@ -332,7 +332,7 @@ defineExpose({ focus, blur, getInputRef })
332
332
  :aria-invalid="checkErrors ? 'true' : 'false'"
333
333
  class="block p-2.5 w-full text-sm text-fv-neutral-900 bg-fv-neutral-50
334
334
  rounded-lg border border-fv-neutral-300 focus:ring-2 focus:ring-fv-primary-300 focus:border-fv-primary-500
335
- dark:bg-fv-neutral-700 dark:border-fv-neutral-600 dark:placeholder-fv-neutral-400
335
+ dark:bg-fv-neutral-700 dark:border-fv-neutral-600 dark:placeholder-fv-neutral-300
336
336
  dark:text-white dark:focus:ring-fv-primary-800 dark:focus:border-fv-primary-500
337
337
  transition-colors duration-200 shadow-sm min-h-[100px]"
338
338
  @focus="handleFocus"
@@ -375,7 +375,7 @@ defineExpose({ focus, blur, getInputRef })
375
375
  class="appearance-none bg-fv-neutral-50 border border-fv-neutral-300 text-fv-neutral-900 text-sm
376
376
  rounded-lg focus:ring-2 focus:ring-fv-primary-300 focus:border-fv-primary-500
377
377
  block w-full p-2.5 dark:bg-fv-neutral-700 dark:border-fv-neutral-600
378
- dark:placeholder-fv-neutral-400 dark:text-white dark:focus:ring-fv-primary-800
378
+ dark:placeholder-fv-neutral-300 dark:text-white dark:focus:ring-fv-primary-800
379
379
  dark:focus:border-fv-primary-500 shadow-sm transition-colors duration-200 pr-10"
380
380
  @focus="handleFocus"
381
381
  @blur="handleBlur"
@@ -577,6 +577,7 @@ input, select, textarea, input[type="range"]::-webkit-slider-thumb, input[type="
577
577
  input::placeholder,
578
578
  textarea::placeholder,
579
579
  select::placeholder {
580
- @apply text-fv-neutral-400 dark:text-fv-neutral-500;
580
+ @apply text-fv-neutral-400 dark:text-fv-neutral-300;
581
+ opacity: 0.8;
581
582
  }
582
583
  </style>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@fy-/fws-vue",
3
- "version": "2.2.63",
3
+ "version": "2.2.65",
4
4
  "author": "Florian 'Fy' Gasquez <m@fy.to>",
5
5
  "license": "MIT",
6
6
  "homepage": "https://github.com/fy-to/FWJS#readme",