embystream 0.0.33

Another Emby streaming application (frontend/backend separation) written in Rust.
Documentation
<script setup lang="ts">
import { UI_LABELS } from "@/constants/app";

const _props = withDefaults(
  defineProps<{
    open: boolean;
    title: string;
    description: string;
    confirmLabel: string;
    cancelLabel: string;
    showClose?: boolean;
    confirmTone?: "primary" | "danger";
    inputLabel?: string;
    inputValue?: string;
    inputPlaceholder?: string;
  }>(),
  {
    showClose: true,
    confirmTone: "primary",
    inputLabel: "",
    inputValue: "",
    inputPlaceholder: "",
  },
);

const _emit = defineEmits<{
  close: [];
  confirm: [];
  "update:inputValue": [value: string];
}>();
</script>

<template>
  <Teleport to="body">
    <Transition name="modal-pop">
      <div
        v-if="open"
        class="action-dialog__overlay"
        role="dialog"
        aria-modal="true"
        @click.self="$emit('close')"
      >
        <div class="action-dialog">
          <div class="action-dialog__head">
            <div>
              <p class="section-label">{{ UI_LABELS.dialogEyebrow }}</p>
              <h3>{{ title }}</h3>
            </div>
          </div>

          <div class="action-dialog__body">
            <p>{{ description }}</p>

            <label v-if="inputLabel" class="action-dialog__field">
              <span>{{ inputLabel }}</span>
              <input
                :placeholder="inputPlaceholder"
                :value="inputValue"
                type="text"
                @input="
                  $emit(
                    'update:inputValue',
                    ($event.target as HTMLInputElement).value,
                  )
                "
              />
            </label>

            <div class="action-dialog__actions">
              <button type="button" @click="$emit('close')">
                {{ cancelLabel }}
              </button>
              <button
                class="action-dialog__confirm"
                :class="`action-dialog__confirm--${confirmTone}`"
                type="button"
                @click="$emit('confirm')"
              >
                {{ confirmLabel }}
              </button>
            </div>
          </div>
        </div>
      </div>
    </Transition>
  </Teleport>
</template>

<style scoped>
.action-dialog__overlay {
  position: fixed;
  inset: 0;
  z-index: 80;
  display: grid;
  place-items: center;
  padding: 1rem;
  background: rgba(20, 20, 19, 0.3);
}

.action-dialog {
  width: min(28rem, 100%);
  padding: 1.25rem;
  border-radius: var(--radius-lg);
  border: 1px solid var(--border-subtle);
  background: var(--bg-surface-strong);
  box-shadow: var(--shadow-medium);
}

.action-dialog__head {
  display: flex;
  justify-content: flex-start;
  gap: 1rem;
  align-items: start;
}

.action-dialog__head h3,
.action-dialog__body p,
.action-dialog__field span {
  margin: 0;
}

.action-dialog__head h3 {
  margin-top: 0.45rem;
  font-size: 1.3rem;
  line-height: 1.14;
  font-weight: 500;
}

.action-dialog__body {
  display: grid;
  gap: 1rem;
  margin-top: 1rem;
}

.action-dialog__body p {
  color: var(--text-muted);
  line-height: 1.7;
}

.action-dialog__field {
  display: grid;
  gap: 0.45rem;
}

.action-dialog__field span {
  color: var(--text-main);
  font-size: 0.88rem;
  font-weight: 600;
}

.action-dialog__actions {
  display: flex;
  justify-content: flex-end;
  gap: 0.75rem;
  flex-wrap: wrap;
}

.action-dialog__confirm {
  min-height: 2.45rem;
  padding-inline: 1rem;
  border-color: transparent;
  color: #fff;
  box-shadow: none;
}

.action-dialog__confirm--primary {
  background: var(--button-primary-bg);
}

.action-dialog__confirm--primary:hover {
  background: var(--button-primary-hover);
}

.action-dialog__confirm--danger {
  background: #b53333;
}

.action-dialog__actions button:not(.action-dialog__confirm) {
  min-width: 6.25rem;
  border-color: var(--border-subtle);
  box-shadow: none;
}

.modal-pop-enter-active,
.modal-pop-leave-active {
  transition: opacity 220ms var(--curve-swift);
}

.modal-pop-enter-from,
.modal-pop-leave-to {
  opacity: 0;
}

.modal-pop-enter-active .action-dialog,
.modal-pop-leave-active .action-dialog {
  transition:
    transform 320ms var(--curve-spring),
    opacity 220ms var(--curve-swift);
}

.modal-pop-enter-from .action-dialog,
.modal-pop-leave-to .action-dialog {
  opacity: 0;
  transform: scale(0.96);
}

</style>