Dialog Loader para Quasar
🇺🇸 English | 🇪🇸 Versión en español
Extensão Quasar para diálogo modal de carregamento global em apps Vue 3 + Quasar, com API imperativa baseada em composable.
Índice
- Em 30 segundos
- O que é e o que inclui
- Requisitos e compatibilidade
- Instalação na app Quasar
- O que a extensão adiciona ao host
- Defaults globais (boot)
- Idioma (i18n)
- Uso rápido
- TypeScript no seu IDE
- API pública
- Variantes visuais
- Indicadores de carregamento (spinner)
- Anti-piscar e operações concorrentes
- Tipos TypeScript exportados
- Entrypoints públicos
- Troubleshooting
Em 30 segundos
Fluxo base recomendado:
- Instalar a extensão na sua app Quasar (
quasar ext add). - Garantir que o boot do Pinia do host roda antes do boot desta extensão.
- Importar
useDialogLoader()onde executar pedidos async. - Envolver a tarefa com
await loader.run({ task: () => api.save() })— ou{ label, task }para override pontual.
A extensão regista boots e CSS no projeto host automaticamente após quasar ext add.
O que é e o que inclui
Diálogo modal global controlado por useDialogLoader() — não é um componente embebível.
run({ task })ourun(task)— caso principal; usa o label do boot se não passarlabel.run({ label, task })— override pontual do texto sem alterar o boot.show()/hide()— controlo manual com contador interno de operações pendentes.- Três variantes de apresentação:
glass,primary,minimal. - Spinners configuráveis (subset curado do Quasar; default
ios). - Anti-piscar: delays de abertura/fecho e tempo mínimo visível.
- Defaults globais no boot do host (
gap,font, variant, spinner…); label por chamada opcional. - i18n built-in
en,es,pt— segue$q.langdo Quasar; overrides no boot (locale,messages). - Controlos em
task:pause,resume,cancelesignal(AbortSignal).
Requisitos e compatibilidade
| Item | Valor |
|---|---|
| Node | >= 20.0.0 |
| Quasar | ^2.6.0 |
| Vue | ^3.4.18 |
| Pinia | ^2.0.11 | ^3.0.0 |
| Formato do pacote | ES modules |
| CLI recomendado | @quasar/app-vite ^2.x ou ^3.x |
O boot do Pinia do host deve executar-se antes do boot desta extensão.
Instalação na app Quasar
quasar ext add @benjaminor-dev/dialog-loader
Remover:
quasar ext remove @benjaminor-dev/dialog-loader
Após quasar ext add, a extensão regista boots e CSS na sua app automaticamente. Não precisa de passos adicionais numa instalação normal via npm.
O que a extensão adiciona ao host
Recursos instalados ou registados pela extensão:
| Recurso | Rota npm |
|---|---|
| Boot do diálogo | ~@benjaminor-dev/quasar-app-extension-dialog-loader/boot/dialog |
| Estilos | ~@benjaminor-dev/quasar-app-extension-dialog-loader/main.css |
| Defaults boot (host) | src/boot/bor/bor-dialog-loader-defaults.ts — criado no install; Skip/Overwrite se já existir |
Install (quasar ext add / invoke): cria src/boot/bor/bor-dialog-loader-defaults.ts (ou .js) e regista-o em quasar.config → boot: [] automaticamente. Não precisa de editar manualmente numa instalação normal. O boot de pacote (boot/dialog) e main.css são injetados em cada dev/build (não aparecem como entradas curtas em quasar.config).
Remove (quasar ext remove): remove o defaults boot e a respetiva entrada em quasar.config.
Ordem mínima de boots:
1. Pinia (host)
2. Dialog Loader — boot/dialog
3. bor/bor-dialog-loader-defaults (host)
Em quasar.config só verá bor/bor-dialog-loader-defaults; o boot boot/dialog é adicionado pelo runner ao compilar.
O boot monta o diálogo no documento e liga a store Pinia do host. Não precisa importar DialogLoader em App.vue.
Defaults globais (boot)
Evite repetir variant, spinner, gap, font, delays e zIndex em cada chamada. Prioridade do label: run({ label }) / show() → boot label → i18n (locale / $q.lang / en).
Após quasar ext add, reveja src/boot/bor/bor-dialog-loader-defaults.ts — plantilla com todas as opções boot (// recomendado: …) e instruções de idioma no comentário do ficheiro.
// src/boot/bor/bor-dialog-loader-defaults.ts
import { configureDialogLoaderDefaults } from "@benjaminor-dev/quasar-app-extension-dialog-loader";
configureDialogLoaderDefaults({
// locale: "pt", // descomente para fixar um idioma específico no Dialog Loader
variant: "glass",
spinner: "ios",
openDelayMs: 120,
hideDelayMs: 450,
minVisibleMs: 600,
backdropBlur: "blur(10px)",
zIndex: 6000,
gap: "2rem",
font: {
family: "Arial, sans-serif",
size: "1.35rem",
},
spinnerColor: {
light: "primary",
dark: "white",
},
spinnerTrackColor: {
light: "grey-4",
dark: "grey-7",
},
});
Idioma (i18n)
Built-in en, es e pt. Por defeito o label segue o idioma do Quasar ($q.lang.isoName); se o Quasar não definir idioma, cai em en ("Loading...").
Resolução de locale: boot locale → prefixo de $q.lang.isoName (es* → es, pt* → pt) → en.
Resolução do label: run({ label }) / show(label) → boot label → boot messages.loadingLabel → catálogo built-in → en.
Chaves i18n built-in: loadingLabel (texto padrão do loader), operationCancelled (DialogLoaderRunCancelledError quando cancel() aborta a tarefa).
| Opção | Quando usar |
|---|---|
framework.lang em quasar.config.ts |
Recomendado — alinha Dialog Loader com o resto do Quasar |
locale: 'pt' no boot |
Fixar um idioma específico (en | es | pt); ignora mudanças de $q.lang |
messages: { loadingLabel: '…' } |
Só alterar um texto sem tocar no locale |
label: '…' no boot |
Label fixo sempre (ignora i18n) |
Exemplo Quasar config (português em toda a app):
// quasar.config.ts
framework: {
lang: "pt-BR",
},
configureDialogLoader é alias de configureDialogLoaderDefaults.
| Opção boot | Default built-in | Descrição |
|---|---|---|
locale |
(segue $q.lang) |
Fixa en | es | pt para textos da extensão |
messages |
(por locale) | Override parcial (loadingLabel, operationCancelled) |
label |
"Loading..." (i18n) |
Texto fixo quando não passa label em show() / run() |
variant |
"glass" |
Apresentação do diálogo |
spinner |
"ios" |
Indicador de carregamento (ver spinners) |
openDelayMs |
120 |
Atraso antes de mostrar (evita flashes em pedidos muito rápidos) |
hideDelayMs |
450 |
Atraso extra ao fechar (alinha com animação de saída) |
minVisibleMs |
600 |
Tempo mínimo visível ainda que a operação termine antes |
backdropBlur |
"blur(10px)" |
Valor CSS backdrop-filter do overlay |
zIndex |
6000 |
Abaixo do Quasar Notify (~9500) para toasts continuarem visíveis |
gap |
"2rem" |
Espaço label↔spinner |
font.family |
"Arial, sans-serif" |
CSS font-family do label |
font.size |
"1.35rem" |
CSS font-size do label |
spinnerColor |
(por variant) | Cor Quasar do spinner — plano ou { light, dark } |
spinnerTrackColor |
(por variant) | Cor Quasar do track em spinners circulares — plano ou { light, dark } |
Se omitir spinnerColor / spinnerTrackColor, o fallback depende de variant: em primary o spinner é branco; em glass e minimal usa primary e grey-4. Configure { light, dark } no boot para adaptar o spinner ao tema do host ($q.dark).
Uso rápido
<script setup lang="ts">
import { useDialogLoader } from "@benjaminor-dev/quasar-app-extension-dialog-loader";
const loader = useDialogLoader();
async function onSave() {
try {
await loader.run({ task: () => api.save(formData) });
// notify.success('Guardado');
} catch (error) {
// notify.error('Erro ao guardar');
}
}
</script>
<template>
<q-btn label="Guardar" @click="onSave" />
</template>
Sem label explícito — usa i18n conforme $q.lang (p. ex. "Carregando..." com lang: 'pt-BR'):
await loader.run(() => fetchUsers());
// equivalente:
await loader.run({ task: () => fetchUsers() });
Override pontual do texto:
await loader.run({ label: "A guardar...", task: () => api.save(formData) });
Controlos opcionais dentro de task:
await loader.run({
task: async ({ pause, resume, cancel, signal }) => {
await api.prepare();
pause(); // oculta overlay; a tarefa continua
await api.finishInBackground();
resume(); // volta a mostrar o loader
await fetch("/api/finalize", { signal }); // cancel() aborta este fetch
},
});
| Controlo | O que faz |
|---|---|
pause() |
Oculta o loader; não cancela a tarefa |
resume() |
Reabre o loader após um pause() |
cancel() |
Aborta signal, fecha o loader e rejeita run() com DialogLoaderRunCancelledError |
signal |
Passe a fetch / axios para cancelação cooperativa |
pause(), resume() e cancel() são idempotentes e só afetam essa operação de run().
import { DialogLoaderRunCancelledError } from "@benjaminor-dev/quasar-app-extension-dialog-loader";
try {
await loader.run({ task: ({ signal }) => fetch(url, { signal }) });
} catch (error) {
if (error instanceof DialogLoaderRunCancelledError) return;
throw error;
}
Controlo manual (menos habitual):
loader.show("A processar...");
try {
await longTask();
} finally {
loader.hide();
}
TypeScript no seu IDE
Não memorize a API: deixe o autocompletado guiá-lo.
useDialogLoader()devolveDialogLoaderApi— métodos e refs aparecem ao escreverloader..- Em
configureDialogLoaderDefaults({ ... }), Ctrl+Space (Windows/Linux) ou Cmd+Space (macOS) listavariant,spinner,spinnerColor,spinnerTrackColor,gap,font,openDelayMs, etc. variantaceita"glass" \| "primary" \| "minimal";spinneraceita os valores deDIALOG_LOADER_SPINNER_TYPES(p. ex."ios","dots","circular").run()— overloads:run(task)ourun({ task, label? }); emtask, desestruture{ pause, resume, cancel, signal }com tipos inferidos.- No boot,
localeaceita"en" \| "es" \| "pt";messagesaceitaDialogLoaderMessagesparcial (loadingLabel,operationCancelled). - Passe o cursor sobre um método: o tooltip mostra parâmetros e JSDoc.
Se algo não aparecer no autocompletado, costuma ser uma prop inexistente ou import incorreto — reveja o entrypoint principal antes de procurar no README.
API pública
Ponto de entrada: useDialogLoader().
import { useDialogLoader } from "@benjaminor-dev/quasar-app-extension-dialog-loader";
const loader = useDialogLoader();
Métodos
| Método | Descrição |
|---|---|
run(task) |
Executa a tarefa com loader; label do boot. Fecha em finally. |
run({ task }) |
Igual ao acima (forma objeto). |
run({ label, task }) |
Igual com label pontual para essa operação. |
show(label?) |
Incrementa o contador interno e agenda abertura do diálogo. |
hide() |
Decrementa o contador; fecha a UI só quando não restam operações pendentes. |
Estado reativo (somente leitura)
| Ref | Descrição |
|---|---|
visible |
Se o diálogo está visível. |
label |
Texto mostrado no diálogo. |
Quando usar run vs show / hide
| Abordagem | Quando |
|---|---|
run() |
Pedidos async — preferir run({ task }) ou run(task); label só se precisar override. Recomendado. |
show() / hide() |
Fluxos manuais ou várias fases com o mesmo label. |
Variantes visuais
Configure variant no boot do host (ou built-in).
| Valor | Apresentação |
|---|---|
glass |
Glassmorphism — card semitransparente com blur (default) |
primary |
Card sólida com cor --q-primary; texto e spinner brancos |
minimal |
Pill horizontal compacto, menos intrusivo |
Indicadores de carregamento (spinner)
Configure spinner no boot. Subset curado de spinners Quasar:
| Valor | Estilo |
|---|---|
ios |
Barras radiais estilo iOS (default) |
circular |
Anel com track (QCircularProgress) |
dots |
Três pontos animados |
oval |
Óvalo a rodar |
tail |
Cauda rotativa |
puff |
Círculo pulsante |
rings |
Anéis concêntricos |
bars |
Barras verticais |
hourglass |
Ampulheta |
infinity |
Símbolo ∞ |
Constante exportada: DIALOG_LOADER_SPINNER_TYPES — útil para validar valores em runtime.
Anti-piscar e operações concorrentes
A store mantém um pendingCount: cada show() / início de run() incrementa; cada hide() / fim de run() decrementa. A UI só fecha quando o contador chega a zero.
Delays configuráveis no boot:
openDelayMs— não mostra o diálogo se todas as operações terminarem antes.minVisibleMs— evita flash de fecho imediato após abrir.hideDelayMs— alinha o fecho com a animação fade do overlay.
Várias chamadas concorrentes a run() ou show() partilham o mesmo diálogo até todas terminarem.
Tipos TypeScript exportados
Desde o entrypoint principal:
import {
useDialogLoader,
DialogLoaderRunCancelledError,
resolveDialogLoaderMessages,
resolveExtensionLocale,
type DialogLoaderApi,
type DialogLoaderRunControls,
type DialogLoaderRunOptions,
type DialogLoaderRunTask,
type DialogLoaderDefaults,
type DialogLoaderFontDefaults,
type DialogLoaderMessages,
type DialogLoaderThemeColor,
type DialogLoaderThemeColorValue,
type DialogLoaderThemeMode,
type DialogLoaderVariant,
type DialogLoaderSpinnerType,
type ExtensionLocale,
type ResolvedDialogLoaderDefaults,
} from "@benjaminor-dev/quasar-app-extension-dialog-loader";
| Tipo | Uso |
|---|---|
DialogLoaderApi |
Retorno de useDialogLoader() |
ExtensionLocale |
"en" | "es" | "pt" — boot locale |
DialogLoaderMessages |
Forma de boot messages (loadingLabel, operationCancelled) |
DialogLoaderRunOptions |
Argumento objeto de run({ label?, task }) |
DialogLoaderRunControls |
{ pause, resume, cancel, signal } recebido em task |
DialogLoaderRunTask |
Tipo do callback task |
DialogLoaderRunPause |
Oculta o loader sem cancelar |
DialogLoaderRunResume |
Reabre o loader após pause() |
DialogLoaderRunCancel |
Cancela a operação |
DialogLoaderRunCancelledError |
Erro ao cancelar com cancel() |
DialogLoaderDefaults |
Opções admitidas em configureDialogLoaderDefaults |
DialogLoaderFontDefaults |
{ family?, size? } dentro de DialogLoaderDefaults.font |
DialogLoaderThemeColorValue |
Cor plana ou { light?, dark? } para spinner |
DialogLoaderThemeColor |
{ light?, dark? } — parte de DialogLoaderThemeColorValue |
DialogLoaderThemeMode |
"light" | "dark" — resolvido com $q.dark.isActive |
DialogLoaderVariant |
"glass" | "primary" | "minimal" |
DialogLoaderSpinnerType |
União de spinners suportados |
ResolvedDialogLoaderDefaults |
Defaults já mergeados (interno / referência) |
Também se exportam BUILTIN_DIALOG_LOADER_DEFAULTS, DIALOG_LOADER_SPINNER_TYPES, resolveDialogLoaderMessages, resolveExtensionLocale, configureDialogLoader (alias) e DIALOG_LOADER_API_KEY (chave de provide do boot para integrações avançadas).
Entrypoints públicos
O que importa no seu código:
| Entrypoint | Propósito |
|---|---|
@benjaminor-dev/quasar-app-extension-dialog-loader |
useDialogLoader, configureDialogLoaderDefaults, helpers i18n (resolveDialogLoaderMessages, resolveExtensionLocale), tipos e DIALOG_LOADER_SPINNER_TYPES |
Boot (boot/dialog) e estilos (main.css) são registados pela extensão em quasar.config ao instalar — não precisa importá-los manualmente.
Troubleshooting
Vejo "Loading..." e quero português (ou espanhol)
O default sem configurar Quasar é inglês. Escolha uma linha:
// quasar.config.ts — recomendado (toda a app Quasar)
framework: {
lang: "pt-BR",
},
// src/boot/bor/bor-dialog-loader-defaults.ts — fixar só o locale do Dialog Loader
configureDialogLoaderDefaults({ locale: "pt" });
Override de um só texto:
configureDialogLoaderDefaults({ messages: { loadingLabel: "Carregando..." } });
O label não muda se o idioma for alterado durante um run()
Se o usuário mudar o idioma da app enquanto o loader está visível, o texto na tela permanece o de quando run() / show() começou. Ao fechar o loader, o label em repouso segue o novo locale. Para fixar um idioma em toda a extensão, use locale no boot.
Boots ou estilos não aparecem em quasar.config
Em condições normais o install regista o defaults boot em boot: [] e o runner injeta boot/dialog + CSS ao compilar. Se faltar o defaults boot:
npx quasar ext invoke @benjaminor-dev/dialog-loader
Erro: Pinia não inicializada
- Verifique um boot de Pinia no host (
app.use(pinia)ou equivalente). - Em
quasar.config, o boot de Pinia deve ir antes do bootdialogdesta extensão.
useDialogLoader() fora de setup
Chame dentro de setup, outro composable ou <script setup>. O boot deve ter sido executado (app montada).
O loader pisca em pedidos rápidos
Ajuste no boot: suba openDelayMs, minVisibleMs ou ambos.
O loader não fecha após show()
Cada show() requer um hide() correspondente (ou use run(), que equilibra automaticamente).
O diálogo fica atrás de outro overlay
Revise zIndex no boot (default 6000).
Mudei spinner no boot e não se reflete
Reinicie o dev server. Confirme que edita src/boot/bor/bor-dialog-loader-defaults.ts do host.
MIT © Benjamín Olvera R.