1<template>
2 <Dialog v-model:open="show" :title="title">
3 <div class="flex flex-col gap-4">
4 <div class="flex flex-col gap-2">
5 <Select
6 v-model="access"
7 :aria-label="__('General access')"
8 class="w-full"
9 size="md"
10 :options="accessOptions"
11 :placeholder="__('Mixed')"
12 />
13 <!-- A new option's note can differ in length: the box eases to the
14 new height while the text rises in, as in Move To. -->
15 <div
16 class="overflow-hidden transition-[height] duration-300 ease-[cubic-bezier(0.2,0.8,0.2,1)] motion-reduce:transition-none"
17 :style="noteHeight ? { height: `${noteHeight}px` } : undefined"
18 aria-live="polite"
19 >
20 <div ref="note">
21 <Transition
22 enter-from-class="translate-y-1 opacity-0 blur-[2px]"
23 enter-active-class="transition-[opacity,transform,filter] duration-300 delay-100 ease-out motion-reduce:transition-none"
24 >
25 <div :key="access ?? ''">
26 <slot :access="access" />
27 </div>
28 </Transition>
29 </div>
30 </div>
31 </div>
32
33 <div class="flex justify-end">
34 <Button
35 variant="solid"
36 :label="actionLabel"
37 :disabled="!access || (requireChange && access === visibility)"
38 @click="publish"
39 />
40 </div>
41 </div>
42 </Dialog>
43</template>
44
45<script setup lang="ts">
46import { Button, Dialog, Select } from "frappe-ui";
47import { computed, ref, watch } from "vue";
48import { useElementSize } from "@vueuse/core";
49import { categoryAccess } from "@opendesk/shared/settings/categoryAccess.ts";
50import { __ } from "@/translation";
51
52// null: the category's articles differ in access.
53const props = defineProps<{
54 title: string;
55 visibility: string | null;
56 actionLabel: string;
57 requireChange?: boolean;
58}>();
59const emit = defineEmits<{ publish: [visibility: string] }>();
60const show = defineModel<boolean>({ default: false });
61
62// Same labels as the access column.
63const accessOptions = computed(() => {
64 const meta = categoryAccess();
65 return ["Public", "Private", "Customers"].map((value) => ({
66 value,
67 label: meta[value].label,
68 icon: meta[value].icon,
69 }));
70});
71
72const access = ref(props.visibility);
73const note = ref<HTMLElement | null>(null);
74// 0 until measured: the box opens at its natural height, not from zero.
75const { height: noteHeight } = useElementSize(note);
76
77watch(show, (open) => {
78 if (open) access.value = props.visibility;
79});
80
81function publish() {
82 emit("publish", access.value!);
83 show.value = false;
84}
85</script>
86