@fy-/fws-vue 2.2.63 → 2.2.64

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