<template>
<Transition name="prompt-slide">
<div
v-if="open"
class="notification-prompt"
role="region"
aria-live="polite"
:aria-label="title"
>
<slot name="icon">
<i :class="`ph ${icon}`" />
</slot>
<div class="notification-prompt-body">
<div class="notification-prompt-title">{{ title }}</div>
<div class="notification-prompt-text">
<slot>{{ text }}</slot>
</div>
</div>
<slot name="actions">
<div class="notification-prompt-actions">
<GnButton
variant="secondary"
size="sm"
:disabled="busy"
:loading="busy"
@click="emit('accept')"
>
{{ acceptText }}
</GnButton>
<GnButton
variant="ghost"
size="sm"
:disabled="busy"
@click="emit('dismiss')"
>
{{ dismissText }}
</GnButton>
</div>
</slot>
</div>
</Transition>
</template>
<script setup>
/**
* A bar that asks for a browser permission, with the click that grants it.
*
* Presentation only: it renders what it is told to and reports the two
* answers. It holds no policy — when to show it, what to remember about the
* answer and which storage to keep that in all belong to the caller. That is
* what makes it portable: see the record in
* gnexus-ui-kit/docs/tasks/gn-permission-banner.md.
*
* The accept button reports synchronously, straight from the click: the
* gesture must reach `Notification.requestPermission()` unharmed.
*/
import { GnButton } from 'gnexus-ui-kit/vue'
defineProps({
open: { type: Boolean, default: false },
title: { type: String, default: '' },
text: { type: String, default: '' },
acceptText: { type: String, default: '' },
dismissText: { type: String, default: '' },
busy: { type: Boolean, default: false },
icon: { type: String, default: 'ph-bell' },
})
const emit = defineEmits(['accept', 'dismiss'])
</script>
<style lang="scss" scoped>
@use 'kit-deps' as *;
.notification-prompt {
display: flex;
align-items: center;
flex-wrap: wrap;
gap: $space-sm $space-md;
flex-shrink: 0;
padding: $space-3 $space-md;
background: rgba($color-secondary, 0.12);
border-bottom: 1px solid rgba($color-secondary, 0.3);
i {
font-size: 18px;
color: $color-secondary;
flex-shrink: 0;
}
}
.notification-prompt-body {
flex: 1 1 240px;
min-width: 0;
}
.notification-prompt-title {
font-size: 12px;
font-weight: 700;
text-transform: uppercase;
letter-spacing: 0.03em;
color: $color-secondary;
}
.notification-prompt-text {
font-size: 12px;
color: $color-text-medium;
margin-top: 2px;
}
.notification-prompt-actions {
display: flex;
align-items: center;
gap: $space-sm;
flex-shrink: 0;
}
.prompt-slide-enter-active,
.prompt-slide-leave-active {
transition: max-height $motion-slow $motion-ease, opacity $motion-slow $motion-ease;
overflow: hidden;
}
.prompt-slide-enter-from,
.prompt-slide-leave-to {
max-height: 0;
opacity: 0;
}
.prompt-slide-enter-to,
.prompt-slide-leave-from {
// Generous: the bar wraps to three rows on a phone, and a snug cap would
// clip it on the way in.
max-height: 200px;
opacity: 1;
}
</style>