@nexdom/uimed-vue 2.0.0-beta.40 → 2.0.0-beta.42

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 (2) hide show
  1. package/dist/components.js +55 -30
  2. package/package.json +1 -1
@@ -488,25 +488,41 @@ function useTextFieldType(type, isPasswordVisible) {
488
488
  });
489
489
  }
490
490
  /**
491
- * Controls the button that shows or hides a password field's value. The
492
- * password starts hidden and hides again whenever the type stops being
493
- * `password`.
491
+ * Controls the icon at the end of the field: a decorative magnifier on
492
+ * `search`, and the toggle that shows or hides the value on `password`. The
493
+ * toggle works with click, `Enter` and `Space`, does nothing while disabled,
494
+ * and the password hides again whenever the type stops being `password`.
494
495
  */
495
- function useTextFieldPasswordToggle(type) {
496
+ function useTextFieldAppendIcon(type, disabled) {
496
497
  const isPasswordField = computed(() => toValue(type) === "password");
497
498
  const isPasswordVisible = ref(false);
498
- const icon = computed(() => isPasswordVisible.value ? "mdi-eye-off" : "mdi-eye");
499
499
  watch(isPasswordField, () => {
500
500
  isPasswordVisible.value = false;
501
501
  });
502
502
  function toggle() {
503
+ if (toValue(disabled)) return;
503
504
  isPasswordVisible.value = !isPasswordVisible.value;
504
505
  }
506
+ function onKeydown(event) {
507
+ if (event.key !== "Enter" && event.key !== " ") return;
508
+ event.preventDefault();
509
+ event.stopPropagation();
510
+ toggle();
511
+ }
512
+ const iconProps = computed(() => {
513
+ if (toValue(type) === "search") return { icon: "mdi-magnify" };
514
+ if (!isPasswordField.value) return void 0;
515
+ return {
516
+ icon: isPasswordVisible.value ? "mdi-eye-off" : "mdi-eye",
517
+ "aria-label": "Mostrar senha",
518
+ "aria-pressed": isPasswordVisible.value,
519
+ onClick: toggle,
520
+ onKeydown
521
+ };
522
+ });
505
523
  return {
506
- isPasswordField,
507
524
  isPasswordVisible: readonly(isPasswordVisible),
508
- icon,
509
- toggle
525
+ iconProps
510
526
  };
511
527
  }
512
528
  function useTextFieldRules(props) {
@@ -614,12 +630,10 @@ var text_field_default = /* @__PURE__ */ defineComponent({
614
630
  const modelValue = useModel(__props, "modelValue");
615
631
  const props = __props;
616
632
  const vuetifyVariant = useTextFieldVariant(() => props.variant);
617
- const { isPasswordField, isPasswordVisible, icon: passwordToggleIcon, toggle: togglePassword } = useTextFieldPasswordToggle(() => props.type);
633
+ const { isPasswordVisible, iconProps: appendIconProps } = useTextFieldAppendIcon(() => props.type, () => props.disabled);
618
634
  const vuetifyType = useTextFieldType(() => props.type, isPasswordVisible);
619
635
  const rules = useTextFieldRules(props);
620
- const isSearchField = computed(() => props.type === "search");
621
- const computedClearable = computed(() => props.clearable ?? isSearchField.value);
622
- const appendIcon = computed(() => isSearchField.value ? "mdi-magnify" : void 0);
636
+ const computedClearable = computed(() => props.clearable ?? props.type === "search");
623
637
  function clear() {
624
638
  modelValue.value = "";
625
639
  }
@@ -629,7 +643,6 @@ var text_field_default = /* @__PURE__ */ defineComponent({
629
643
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => modelValue.value = $event),
630
644
  variant: unref(vuetifyVariant),
631
645
  type: unref(vuetifyType),
632
- "append-inner-icon": appendIcon.value,
633
646
  rules: unref(rules),
634
647
  "data-testid": props.dataTestid,
635
648
  disabled: props.disabled,
@@ -644,28 +657,14 @@ var text_field_default = /* @__PURE__ */ defineComponent({
644
657
  }, createSlots({
645
658
  loader: withCtx((loader) => [createVNode(field_loader_default, normalizeProps(guardReactiveProps(loader)), null, 16)]),
646
659
  _: 2
647
- }, [unref(isPasswordField) ? {
660
+ }, [unref(appendIconProps) ? {
648
661
  name: "append-inner",
649
- fn: withCtx(() => [createVNode(unref(VBtn), {
650
- icon: unref(passwordToggleIcon),
651
- "aria-label": "Mostrar senha",
652
- "aria-pressed": unref(isPasswordVisible),
653
- disabled: props.disabled,
654
- size: "small",
655
- variant: "text",
656
- onClick: unref(togglePassword)
657
- }, null, 8, [
658
- "icon",
659
- "aria-pressed",
660
- "disabled",
661
- "onClick"
662
- ])]),
662
+ fn: withCtx(() => [createVNode(unref(VIcon), mergeProps(unref(appendIconProps), { disabled: props.disabled }), null, 16, ["disabled"])]),
663
663
  key: "0"
664
664
  } : void 0]), 1032, [
665
665
  "modelValue",
666
666
  "variant",
667
667
  "type",
668
- "append-inner-icon",
669
668
  "rules",
670
669
  "data-testid",
671
670
  "disabled",
@@ -859,15 +858,41 @@ var container_default = /* @__PURE__ */ defineComponent({
859
858
  }
860
859
  });
861
860
  //#endregion
861
+ //#region src/composables/dialogs/use-decorative-timer.ts
862
+ /**
863
+ * Content props that hide a snackbar's timer bar from assistive technologies,
864
+ * keeping it only as a visual countdown, since the message itself is already
865
+ * announced by the snackbar's live region.
866
+ *
867
+ * Workaround until Vuetify offers an option for it: the timer bar is a
868
+ * `VProgressLinear` that always renders `role="progressbar"` with
869
+ * `aria-hidden="false"` and no accessible name, and neither `VSnackbar` nor
870
+ * Vuetify's defaults can set attributes on it. So these vnode hooks mark the
871
+ * bar after every render of the snackbar content, which also covers the bar
872
+ * recreated when the pointer leaves the snackbar. Vue doesn't patch the
873
+ * attribute back, since Vuetify keeps rendering the same `"false"` value.
874
+ */
875
+ function useDecorativeTimer() {
876
+ return {
877
+ onVnodeMounted: hideTimer,
878
+ onVnodeUpdated: hideTimer
879
+ };
880
+ }
881
+ function hideTimer({ el }) {
882
+ el?.querySelector(".v-snackbar__timer [role='progressbar']")?.setAttribute("aria-hidden", "true");
883
+ }
884
+ //#endregion
862
885
  //#region src/components/dialogs/toast.vue
863
886
  var toast_default = /* @__PURE__ */ defineComponent({
864
887
  inheritAttrs: false,
865
888
  __name: "toast",
866
889
  setup(__props) {
890
+ const decorativeTimer = useDecorativeTimer();
867
891
  return (_ctx, _cache) => {
868
892
  return openBlock(), createBlock(unref(VSnackbarQueue), {
869
893
  modelValue: unref(messages),
870
894
  "onUpdate:modelValue": _cache[0] || (_cache[0] = ($event) => isRef(messages) ? messages.value = $event : null),
895
+ "content-props": unref(decorativeTimer),
871
896
  "display-strategy": "overflow",
872
897
  location: "top end",
873
898
  "total-visible": "3",
@@ -882,7 +907,7 @@ var toast_default = /* @__PURE__ */ defineComponent({
882
907
  variant: "text"
883
908
  }), null, 16)]),
884
909
  _: 1
885
- }, 8, ["modelValue"]);
910
+ }, 8, ["modelValue", "content-props"]);
886
911
  };
887
912
  }
888
913
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nexdom/uimed-vue",
3
- "version": "2.0.0-beta.40",
3
+ "version": "2.0.0-beta.42",
4
4
  "description": "A ui framework for NEXDOM applications.",
5
5
  "keywords": [
6
6
  "lib",