@nexdom/uimed-vue 2.5.0 → 2.6.0
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/dist/components/main/back-to-top.js +5 -0
- package/dist/components/main/back-to-top.vue_vue_type_script_setup_true_lang.js +35 -0
- package/dist/components/main/main.vue_vue_type_script_setup_true_lang.js +17 -4
- package/dist/composables/navigation/use-back-to-top.js +36 -0
- package/dist/consts/icons.d.ts +1 -0
- package/dist/consts/icons.js +2 -1
- package/package.json +1 -1
- package/skills/uimed-vue/SKILL.md +1 -0
- package/skills/uimed-vue/references/api/components/main.md +4 -0
- package/skills/uimed-vue/references/guide/components/main.md +35 -0
- package/skills/uimed-vue/references/guide/icons.md +1 -1
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import back_to_top_vue_vue_type_script_setup_true_lang_default from "./back-to-top.vue_vue_type_script_setup_true_lang.js";
|
|
2
|
+
//#region src/components/main/back-to-top.vue
|
|
3
|
+
var back_to_top_default = back_to_top_vue_vue_type_script_setup_true_lang_default;
|
|
4
|
+
//#endregion
|
|
5
|
+
export { back_to_top_default as default };
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { icons } from "../../consts/icons.js";
|
|
2
|
+
import { createBlock, createCommentVNode, createElementVNode, createVNode, defineComponent, openBlock, unref, withCtx } from "vue";
|
|
3
|
+
import { VFab, VIcon } from "vuetify/components";
|
|
4
|
+
var back_to_top_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
5
|
+
inheritAttrs: false,
|
|
6
|
+
__name: "back-to-top",
|
|
7
|
+
props: { active: {
|
|
8
|
+
type: Boolean,
|
|
9
|
+
required: true
|
|
10
|
+
} },
|
|
11
|
+
emits: ["click"],
|
|
12
|
+
setup(__props, { emit: __emit }) {
|
|
13
|
+
const props = __props;
|
|
14
|
+
const emit = __emit;
|
|
15
|
+
return (_ctx, _cache) => {
|
|
16
|
+
return openBlock(), createBlock(unref(VFab), {
|
|
17
|
+
active: props.active,
|
|
18
|
+
icon: unref(icons).backToTop,
|
|
19
|
+
color: "tertiary",
|
|
20
|
+
location: "bottom end",
|
|
21
|
+
app: "",
|
|
22
|
+
onClick: _cache[0] || (_cache[0] = ($event) => emit("click"))
|
|
23
|
+
}, {
|
|
24
|
+
default: withCtx(() => [
|
|
25
|
+
createVNode(unref(VIcon), { icon: unref(icons).backToTop }, null, 8, ["icon"]),
|
|
26
|
+
createCommentVNode(" The FAB gives its attributes to its wrapper instead of its button, so the button can't take\n an aria-label: its name is a text that only screen readers read, which no prop provides "),
|
|
27
|
+
_cache[1] || (_cache[1] = createElementVNode("span", { class: "d-sr-only" }, "Retornar ao topo", -1))
|
|
28
|
+
]),
|
|
29
|
+
_: 1
|
|
30
|
+
}, 8, ["active", "icon"]);
|
|
31
|
+
};
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
//#endregion
|
|
35
|
+
export { back_to_top_vue_vue_type_script_setup_true_lang_default as default };
|
|
@@ -2,12 +2,15 @@ import app_bar_default from "../app-bar/app-bar.js";
|
|
|
2
2
|
import { useMobileNavigation } from "../../composables/navigation/use-navigation-rail.js";
|
|
3
3
|
import navigation_menu_default from "../navigation-menu/navigation-menu.js";
|
|
4
4
|
import footer_default from "../footer/footer.js";
|
|
5
|
+
import back_to_top_default from "./back-to-top.js";
|
|
5
6
|
import container_default from "../grid/container/container.js";
|
|
6
7
|
import toast_default from "../dialogs/toast.js";
|
|
7
8
|
import dialog_host_default from "../dialogs/dialog-host.js";
|
|
8
9
|
import useVuetifyBackgroundColor from "../../composables/colors/use-vuetify-background-color.js";
|
|
9
|
-
import
|
|
10
|
+
import useBackToTop from "../../composables/navigation/use-back-to-top.js";
|
|
11
|
+
import { computed, createBlock, createCommentVNode, createSlots, createVNode, defineComponent, mergeModels, mergeProps, normalizeClass, normalizeProps, openBlock, ref, renderSlot, unref, useModel, useSlots, useTemplateRef, withCtx, withDirectives } from "vue";
|
|
10
12
|
import { VApp, VMain } from "vuetify/components";
|
|
13
|
+
import { Scroll } from "vuetify/directives";
|
|
11
14
|
var main_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineComponent({
|
|
12
15
|
inheritAttrs: false,
|
|
13
16
|
__name: "main",
|
|
@@ -61,6 +64,7 @@ var main_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCompo
|
|
|
61
64
|
const backgroundClass = useVuetifyBackgroundColor(() => props.background);
|
|
62
65
|
const notificationsOpen = useModel(__props, "notificationsOpen");
|
|
63
66
|
const navigationMenuOpen = ref(false);
|
|
67
|
+
const { visible, onScroll, backToTop } = useBackToTop(useTemplateRef("main"));
|
|
64
68
|
return (_ctx, _cache) => {
|
|
65
69
|
return openBlock(), createBlock(unref(VApp), {
|
|
66
70
|
class: normalizeClass(unref(backgroundClass)),
|
|
@@ -82,7 +86,11 @@ var main_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCompo
|
|
|
82
86
|
"onUpdate:modelValue": _cache[2] || (_cache[2] = ($event) => navigationMenuOpen.value = $event),
|
|
83
87
|
loading: props.loading
|
|
84
88
|
}), null, 16, ["modelValue", "loading"])) : createCommentVNode("v-if", true),
|
|
85
|
-
|
|
89
|
+
createCommentVNode(" Focusable, so the back-to-top button can move the focus to the start of the content "),
|
|
90
|
+
createVNode(unref(VMain), {
|
|
91
|
+
ref: "main",
|
|
92
|
+
tabindex: "-1"
|
|
93
|
+
}, {
|
|
86
94
|
default: withCtx(() => [createCommentVNode(" Full height, so the page's rows and containers with `fullHeight` have a height to fill "), createVNode(container_default, { "full-height": "" }, {
|
|
87
95
|
default: withCtx(() => [
|
|
88
96
|
renderSlot(_ctx.$slots, "default"),
|
|
@@ -92,8 +100,13 @@ var main_vue_vue_type_script_setup_true_lang_default = /*@__PURE__*/ defineCompo
|
|
|
92
100
|
_: 3
|
|
93
101
|
})]),
|
|
94
102
|
_: 3
|
|
95
|
-
}),
|
|
96
|
-
props.footer?.description ? (openBlock(), createBlock(footer_default, normalizeProps(mergeProps({ key: 2 }, props.footer)), null, 16)) : createCommentVNode("v-if", true)
|
|
103
|
+
}, 512),
|
|
104
|
+
props.footer?.description ? (openBlock(), createBlock(footer_default, normalizeProps(mergeProps({ key: 2 }, props.footer)), null, 16)) : createCommentVNode("v-if", true),
|
|
105
|
+
createCommentVNode(" After the footer, so the layout places it above the footer "),
|
|
106
|
+
withDirectives(createVNode(back_to_top_default, {
|
|
107
|
+
active: unref(visible),
|
|
108
|
+
onClick: unref(backToTop)
|
|
109
|
+
}, null, 8, ["active", "onClick"]), [[unref(Scroll), unref(onScroll)]])
|
|
97
110
|
]),
|
|
98
111
|
_: 3
|
|
99
112
|
}, 8, ["class", "data-testid"]);
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { onMounted, ref, toValue } from "vue";
|
|
2
|
+
//#region src/composables/navigation/use-back-to-top.ts
|
|
3
|
+
/**
|
|
4
|
+
* Back-to-top button's state for `content`, the template ref of the component whose element the
|
|
5
|
+
* button brings into view and focuses (which must be focusable, e.g. with `tabindex="-1"`). The
|
|
6
|
+
* button shows once the top of the content is a screen above the viewport, instead of after the page
|
|
7
|
+
* scrolls a screen: in apps both are the same, but on pages with more than one `UMain` (like the
|
|
8
|
+
* docs) only the one scrolled past shows it.
|
|
9
|
+
*/
|
|
10
|
+
function useBackToTop(content) {
|
|
11
|
+
const visible = ref(false);
|
|
12
|
+
function onScroll() {
|
|
13
|
+
const element = toValue(content)?.$el;
|
|
14
|
+
visible.value = element ? -element.getBoundingClientRect().top > globalThis.innerHeight : false;
|
|
15
|
+
}
|
|
16
|
+
function backToTop() {
|
|
17
|
+
const element = toValue(content)?.$el;
|
|
18
|
+
if (!element) return;
|
|
19
|
+
globalThis.scrollTo({
|
|
20
|
+
top: globalThis.scrollY + element.getBoundingClientRect().top,
|
|
21
|
+
behavior: prefersReducedMotion() ? "instant" : "smooth"
|
|
22
|
+
});
|
|
23
|
+
element.focus({ preventScroll: true });
|
|
24
|
+
}
|
|
25
|
+
onMounted(onScroll);
|
|
26
|
+
return {
|
|
27
|
+
visible,
|
|
28
|
+
onScroll,
|
|
29
|
+
backToTop
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
function prefersReducedMotion() {
|
|
33
|
+
return globalThis.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
34
|
+
}
|
|
35
|
+
//#endregion
|
|
36
|
+
export { useBackToTop as default };
|
package/dist/consts/icons.d.ts
CHANGED
package/dist/consts/icons.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { mdiAccount, mdiAccountGroup, mdiAccountGroupOutline, mdiAccountOutline, mdiAlertCircle, mdiBellOutline, mdiCalendar, mdiCalendarOutline, mdiChartBox, mdiChartBoxOutline, mdiCheckCircle, mdiCheckboxBlankOutline, mdiCheckboxMarked, mdiChevronDown, mdiChevronLeft, mdiChevronRight, mdiChevronUp, mdiClipboardText, mdiClipboardTextOutline, mdiClose, mdiCloseCircle, mdiCog, mdiCogOutline, mdiEye, mdiEyeOff, mdiFileDocument, mdiFileDocumentOutline, mdiFlask, mdiFlaskOutline, mdiFolder, mdiFolderOutline, mdiHelpCircleOutline, mdiHome, mdiHomeOutline, mdiHospitalBuilding, mdiInformation, mdiMagnify, mdiMenu, mdiMenuDown, mdiPill, mdiStethoscope, mdiWallet, mdiWalletOutline } from "@mdi/js";
|
|
1
|
+
import { mdiAccount, mdiAccountGroup, mdiAccountGroupOutline, mdiAccountOutline, mdiAlertCircle, mdiArrowUp, mdiBellOutline, mdiCalendar, mdiCalendarOutline, mdiChartBox, mdiChartBoxOutline, mdiCheckCircle, mdiCheckboxBlankOutline, mdiCheckboxMarked, mdiChevronDown, mdiChevronLeft, mdiChevronRight, mdiChevronUp, mdiClipboardText, mdiClipboardTextOutline, mdiClose, mdiCloseCircle, mdiCog, mdiCogOutline, mdiEye, mdiEyeOff, mdiFileDocument, mdiFileDocumentOutline, mdiFlask, mdiFlaskOutline, mdiFolder, mdiFolderOutline, mdiHelpCircleOutline, mdiHome, mdiHomeOutline, mdiHospitalBuilding, mdiInformation, mdiMagnify, mdiMenu, mdiMenuDown, mdiPill, mdiStethoscope, mdiWallet, mdiWalletOutline } from "@mdi/js";
|
|
2
2
|
//#region src/consts/icons.ts
|
|
3
3
|
/**
|
|
4
4
|
* SVG path of each icon, by its name, in the order the "Ícones" guide page lists them. The paths
|
|
@@ -51,6 +51,7 @@ const icons = {
|
|
|
51
51
|
showPassword: mdiEye,
|
|
52
52
|
hidePassword: mdiEyeOff,
|
|
53
53
|
close: mdiClose,
|
|
54
|
+
backToTop: mdiArrowUp,
|
|
54
55
|
clear: mdiCloseCircle,
|
|
55
56
|
dropdown: mdiMenuDown,
|
|
56
57
|
subgroup: mdiMenuDown,
|
package/package.json
CHANGED
|
@@ -83,6 +83,7 @@ createApp(App).use(createUimed()).mount("#app");
|
|
|
83
83
|
type SectionAction = NonNullable<ComponentProps<typeof USection>["actions"]>[number];
|
|
84
84
|
```
|
|
85
85
|
|
|
86
|
+
- `UMain` shows a "back to top" floating button on its own once the page scrolls past a screen, so don't add one to the screens.
|
|
86
87
|
- Icons are passed by name (e.g. `icon="cog"`), from the list in [`references/guide/icons.md`](references/guide/icons.md).
|
|
87
88
|
- The components' internal texts, such as their messages and the names read by screen readers, are in Portuguese (pt-BR).
|
|
88
89
|
|
|
@@ -105,6 +105,10 @@ Item dentro de um [grupo](#navigationmenuparent). Tem as mesmas propriedades do
|
|
|
105
105
|
| `dataTestid` | `string` | | Aplica atributo `data-testid` para testes sobre o rodapé. |
|
|
106
106
|
| `description` | `string` | | Texto exibido no rodapé, por exemplo a versão da aplicação (`"Versão 1.4.2"`). **O rodapé será ocultado quando omitido ou vazio.** |
|
|
107
107
|
|
|
108
|
+
## Retornar ao topo
|
|
109
|
+
|
|
110
|
+
O componente exibe um botão flutuante "Retornar ao topo" no canto inferior direito quando o usuário rola a página por mais de uma tela, sem props para configurá-lo. Veja [Retornar ao topo](../../guide/components/main.md#retornar-ao-topo) no guia.
|
|
111
|
+
|
|
108
112
|
## Eventos
|
|
109
113
|
|
|
110
114
|
| Event | Retorno | Descrição |
|
|
@@ -391,6 +391,41 @@ const appBar: NonNullable<MainProps["appBar"]> = {
|
|
|
391
391
|
</script>
|
|
392
392
|
```
|
|
393
393
|
|
|
394
|
+
## Retornar ao topo
|
|
395
|
+
|
|
396
|
+
Em páginas longas, como listagens e formulários extensos, o `UMain` exibe um botão flutuante no canto inferior direito para voltar ao início do conteúdo. Ele aparece quando o usuário rola a página por mais de uma tela e some ao voltar ao topo, sem nenhuma prop: páginas curtas nunca o exibem.
|
|
397
|
+
|
|
398
|
+
O botão fica acima do rodapé, quando exibido, e abaixo dos diálogos e dos toasts. Ao ser acionado, ele rola a página até o início do conteúdo de forma suave, ou imediata quando o usuário prefere reduzir os movimentos nas configurações do sistema, e move o foco para o início do conteúdo, para que quem navega pelo teclado continue a partir dali. Os leitores de tela o anunciam como "Retornar ao topo".
|
|
399
|
+
|
|
400
|
+
Role a demonstração abaixo para ver o botão:
|
|
401
|
+
|
|
402
|
+
```vue
|
|
403
|
+
<template>
|
|
404
|
+
<u-main :appBar :footer>
|
|
405
|
+
<h2>Manual do usuário</h2>
|
|
406
|
+
<p v-for="step in 12" :key="step">{{ step }}. {{ manualText }}</p>
|
|
407
|
+
</u-main>
|
|
408
|
+
</template>
|
|
409
|
+
|
|
410
|
+
<script lang="ts" setup>
|
|
411
|
+
import type { ComponentProps } from "vue-component-type-helpers";
|
|
412
|
+
import { UMain } from "@nexdom/uimed-vue/components";
|
|
413
|
+
|
|
414
|
+
type MainProps = ComponentProps<typeof UMain>;
|
|
415
|
+
|
|
416
|
+
const manualText =
|
|
417
|
+
"Este manual descreve cada etapa do atendimento, do agendamento da consulta à emissão do relatório final, com as orientações para preencher os formulários, conferir os dados do paciente antes de salvar e resolver as pendências mais comuns. Siga as etapas na ordem em que aparecem e, em caso de dúvida, procure a equipe de suporte da sua unidade.";
|
|
418
|
+
|
|
419
|
+
const appBar: NonNullable<MainProps["appBar"]> = {
|
|
420
|
+
title: "Menu superior",
|
|
421
|
+
};
|
|
422
|
+
|
|
423
|
+
const footer: NonNullable<MainProps["footer"]> = {
|
|
424
|
+
description: "Versão 1.4.2",
|
|
425
|
+
};
|
|
426
|
+
</script>
|
|
427
|
+
```
|
|
428
|
+
|
|
394
429
|
## Eventos
|
|
395
430
|
|
|
396
431
|
### Notificações
|
|
@@ -57,7 +57,7 @@ A lista começa pelos ícones de conteúdo, como `home` e `calendar`, cujos nome
|
|
|
57
57
|
|
|
58
58
|
Em seguida vêm os ícones que os próprios componentes exibem, cujos nomes descrevem a função com que os componentes os usam:
|
|
59
59
|
|
|
60
|
-
- Ações, como `help`, `search`, `showPassword` e `
|
|
60
|
+
- Ações, como `help`, `search`, `showPassword`, `close` e `backToTop`.
|
|
61
61
|
- Expansão de menus e grupos, como `dropdown`, `expand` e `collapse`, e navegação entre páginas, com `prev` e `next`.
|
|
62
62
|
- Marcações da caixa de seleção: `checkboxOn` e `checkboxOff`.
|
|
63
63
|
- Retorno ao usuário, nas cores das mensagens: `success`, `info`, `warning` e `error`.
|