@xy-planning-network/trees 0.4.0-rc-6 → 0.4.1

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.
Files changed (82) hide show
  1. package/README.md +236 -53
  2. package/dist/trees.es.js +910 -274
  3. package/dist/trees.umd.js +6 -6
  4. package/package.json +9 -6
  5. package/src/lib-components/forms/BaseInput.vue +83 -0
  6. package/src/lib-components/forms/Checkbox.vue +46 -0
  7. package/src/lib-components/forms/DateRangePicker.vue +65 -0
  8. package/src/lib-components/forms/InputHelp.vue +24 -0
  9. package/src/lib-components/forms/InputLabel.vue +23 -0
  10. package/src/lib-components/forms/MultiCheckboxes.vue +55 -0
  11. package/src/lib-components/forms/Radio.vue +58 -0
  12. package/src/lib-components/forms/Select.vue +65 -0
  13. package/src/lib-components/forms/TextArea.vue +50 -0
  14. package/src/lib-components/forms/Toggle.vue +25 -0
  15. package/src/lib-components/forms/YesOrNoRadio.vue +70 -0
  16. package/src/lib-components/layout/DateFilter.vue +54 -0
  17. package/src/lib-components/layout/SidebarLayout.vue +239 -0
  18. package/src/lib-components/layout/StackedLayout.vue +172 -0
  19. package/src/lib-components/lists/Cards.vue +33 -0
  20. package/src/lib-components/lists/DetailList.vue +114 -0
  21. package/src/lib-components/lists/DownloadCell.vue +12 -0
  22. package/src/lib-components/lists/StaticTable.vue +83 -0
  23. package/src/lib-components/lists/Table.vue +291 -0
  24. package/src/lib-components/navigation/ActionsDropdown.vue +78 -0
  25. package/src/lib-components/navigation/Paginator.vue +115 -0
  26. package/src/lib-components/navigation/Steps.vue +83 -0
  27. package/src/lib-components/navigation/Tabs.vue +92 -0
  28. package/src/lib-components/overlays/ContentModal.vue +95 -0
  29. package/src/lib-components/overlays/Flash.vue +131 -0
  30. package/src/lib-components/overlays/Modal.vue +133 -0
  31. package/src/lib-components/overlays/Popover/Popover.vue +109 -0
  32. package/src/lib-components/overlays/Popover/PopoverContent.vue +8 -0
  33. package/src/lib-components/overlays/Slideover.vue +87 -0
  34. package/src/lib-components/overlays/Spinner.vue +149 -0
  35. package/src/lib-components/overlays/Tooltip.vue +31 -0
  36. package/{dist → types}/api/base.d.ts +0 -0
  37. package/types/components.d.ts +12 -0
  38. package/types/composables/date.d.ts +4 -0
  39. package/types/composables/nav.d.ts +13 -0
  40. package/types/composables/overlay.d.ts +4 -0
  41. package/types/composables/table.d.ts +32 -0
  42. package/types/composables/user.d.ts +6 -0
  43. package/{dist → types}/entry.d.ts +0 -0
  44. package/types/global.d.ts +13 -0
  45. package/{dist → types}/helpers/Uniques.d.ts +0 -0
  46. package/{dist → types}/lib-components/forms/BaseInput.vue.d.ts +0 -0
  47. package/{dist → types}/lib-components/forms/Checkbox.vue.d.ts +0 -0
  48. package/{dist → types}/lib-components/forms/DateRangePicker.vue.d.ts +0 -0
  49. package/{dist → types}/lib-components/forms/InputHelp.vue.d.ts +0 -0
  50. package/{dist → types}/lib-components/forms/InputLabel.vue.d.ts +0 -0
  51. package/{dist → types}/lib-components/forms/MultiCheckboxes.vue.d.ts +0 -0
  52. package/{dist → types}/lib-components/forms/Radio.vue.d.ts +0 -0
  53. package/{dist → types}/lib-components/forms/Select.vue.d.ts +2 -2
  54. package/{dist → types}/lib-components/forms/TextArea.vue.d.ts +0 -0
  55. package/{dist → types}/lib-components/forms/Toggle.vue.d.ts +0 -0
  56. package/{dist → types}/lib-components/forms/YesOrNoRadio.vue.d.ts +0 -0
  57. package/{dist → types}/lib-components/index.d.ts +9 -9
  58. package/{dist → types}/lib-components/layout/DateFilter.vue.d.ts +1 -4
  59. package/{dist → types}/lib-components/layout/SidebarLayout.vue.d.ts +1 -1
  60. package/{dist → types}/lib-components/layout/StackedLayout.vue.d.ts +2 -2
  61. package/{dist → types}/lib-components/lists/Cards.vue.d.ts +0 -0
  62. package/{dist → types}/lib-components/lists/DetailList.vue.d.ts +0 -0
  63. package/{dist → types}/lib-components/lists/DownloadCell.vue.d.ts +0 -0
  64. package/{dist → types}/lib-components/lists/StaticTable.vue.d.ts +1 -1
  65. package/{dist → types}/lib-components/lists/Table.vue.d.ts +1 -1
  66. package/{dist → types}/lib-components/navigation/ActionsDropdown.vue.d.ts +2 -2
  67. package/{dist → types}/lib-components/navigation/Paginator.vue.d.ts +1 -6
  68. package/{dist → types}/lib-components/navigation/Steps.vue.d.ts +0 -0
  69. package/{dist → types}/lib-components/navigation/Tabs.vue.d.ts +0 -0
  70. package/{dist → types}/lib-components/overlays/ContentModal.vue.d.ts +0 -0
  71. package/{dist/lib-components/overlays/Spinner.vue.d.ts → types/lib-components/overlays/Flash.vue.d.ts} +0 -0
  72. package/{dist → types}/lib-components/overlays/Modal.vue.d.ts +0 -0
  73. package/types/lib-components/overlays/Popover/Popover.vue.d.ts +15 -0
  74. package/types/lib-components/overlays/Popover/PopoverContent.vue.d.ts +2 -0
  75. package/{dist → types}/lib-components/overlays/Slideover.vue.d.ts +0 -0
  76. package/{dist/lib-components/overlays/Flash.vue.d.ts → types/lib-components/overlays/Spinner.vue.d.ts} +0 -4
  77. package/types/lib-components/overlays/Tooltip.vue.d.ts +16 -0
  78. package/dist/types/components.d.ts +0 -6
  79. package/dist/types/global.d.ts +0 -10
  80. package/dist/types/nav.d.ts +0 -8
  81. package/dist/types/table.d.ts +0 -36
  82. package/dist/types/users.d.ts +0 -10
@@ -0,0 +1,95 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ Dialog,
4
+ DialogOverlay,
5
+ DialogTitle,
6
+ TransitionChild,
7
+ TransitionRoot,
8
+ } from "@headlessui/vue"
9
+
10
+ withDefaults(
11
+ defineProps<{
12
+ modelValue: boolean
13
+ title?: string
14
+ }>(),
15
+ {
16
+ title: "",
17
+ }
18
+ )
19
+
20
+ const emit = defineEmits<{
21
+ (e: "update:modelValue", val: boolean): void
22
+ }>()
23
+
24
+ const updateModelValue = (value: boolean) => {
25
+ emit("update:modelValue", value)
26
+ }
27
+ </script>
28
+ <template>
29
+ <TransitionRoot as="template" :show="modelValue">
30
+ <Dialog
31
+ as="div"
32
+ static
33
+ class="fixed z-10 inset-0 overflow-y-auto"
34
+ @close="updateModelValue(false)"
35
+ :open="modelValue"
36
+ >
37
+ <div
38
+ class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
39
+ >
40
+ <TransitionChild
41
+ as="template"
42
+ enter="ease-out duration-300"
43
+ enter-from="opacity-0"
44
+ enter-to="opacity-100"
45
+ leave="ease-in duration-200"
46
+ leave-from="opacity-100"
47
+ leave-to="opacity-0"
48
+ >
49
+ <DialogOverlay
50
+ class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
51
+ />
52
+ </TransitionChild>
53
+
54
+ <!-- This element is to trick the browser into centering the modal contents. -->
55
+ <span
56
+ class="hidden sm:inline-block sm:align-middle sm:h-screen"
57
+ aria-hidden="true"
58
+ >&#8203;</span
59
+ >
60
+ <TransitionChild
61
+ as="template"
62
+ enter="ease-out duration-300"
63
+ enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
64
+ enter-to="opacity-100 translate-y-0 sm:scale-100"
65
+ leave="ease-in duration-200"
66
+ leave-from="opacity-100 translate-y-0 sm:scale-100"
67
+ leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
68
+ >
69
+ <div
70
+ class="inline-block align-bottom bg-white rounded-lg px-4 pt-5 pb-4 text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-sm sm:w-full sm:p-6"
71
+ >
72
+ <div>
73
+ <slot name="icon"></slot>
74
+ <div class="mt-3 text-center sm:mt-5">
75
+ <DialogTitle as="h3" v-text="title"></DialogTitle>
76
+ <div class="mt-2">
77
+ <slot></slot>
78
+ </div>
79
+ </div>
80
+ </div>
81
+ <div class="mt-5 sm:mt-6">
82
+ <button
83
+ type="button"
84
+ class="inline-flex justify-center w-full xy-btn"
85
+ @click="updateModelValue(false)"
86
+ >
87
+ Go back
88
+ </button>
89
+ </div>
90
+ </div>
91
+ </TransitionChild>
92
+ </div>
93
+ </Dialog>
94
+ </TransitionRoot>
95
+ </template>
@@ -0,0 +1,131 @@
1
+ <script setup lang="ts">
2
+ import { Flash } from "@/composables/overlay"
3
+ import { onMounted, ref } from "vue"
4
+
5
+ // TODO: spk this might benefit from the composition api to avoid race conditions where a flash is requested before the component is mounted.
6
+
7
+ const flashes = ref<Flash[]>([])
8
+ const flashTypeBorderClass = {
9
+ warning: "border-orange-500",
10
+ error: "border-red-500",
11
+ info: "border-blue-500",
12
+ success: "border-green-500",
13
+ }
14
+
15
+ const getFlashClass = (flash: Flash): string => {
16
+ const availableTypes = Object.keys(flashTypeBorderClass)
17
+ // default the flash type to "info" if no flash type or an unsupported flash.type is found
18
+ const type =
19
+ flash?.type && availableTypes.includes(flash.type)
20
+ ? (flash.type as "warning" | "error" | "info" | "success")
21
+ : "info"
22
+ return flashTypeBorderClass[type]
23
+ }
24
+
25
+ const remove = (flash: Flash): void => {
26
+ let index = 0
27
+ for (const f of flashes.value) {
28
+ if (flash.message === f.message) {
29
+ flashes.value.splice(index, 1)
30
+ return
31
+ }
32
+ index++
33
+ }
34
+ }
35
+ const renderFlash = (flash: Flash): void => {
36
+ flashes.value.push(flash)
37
+ // Super simple flash implementation. This could get "smarter" by adding an
38
+ // id to the flash object, and then searching for the specific flash in the
39
+ // array and splicing, instead of simply doing a pop().
40
+ setTimeout(
41
+ (flashes: Flash[]) => {
42
+ flashes.pop()
43
+ },
44
+ 10000,
45
+ flashes.value
46
+ )
47
+ }
48
+
49
+ const renderGenericError = (email: string): void => {
50
+ renderFlash({
51
+ type: "error",
52
+ message:
53
+ "Whoops! Something went wrong, please reach out to " +
54
+ `<a class="underline text-xy-blue" href="mailto:${email}">${email}</a>` +
55
+ " if the issue persists.",
56
+ })
57
+ }
58
+
59
+ onMounted(() => {
60
+ window.VueBus.on("Flash-show-message", (flash) => {
61
+ renderFlash(flash)
62
+ })
63
+
64
+ window.VueBus.on("Flash-show-generic-error", (email) => {
65
+ renderGenericError(email)
66
+ })
67
+
68
+ if (window.Flashes) {
69
+ for (const flash of window.Flashes) {
70
+ if (typeof flash.type === "undefined") {
71
+ const values: string[] = flash.message.split(": ")
72
+ renderFlash({ type: values[0], message: values[1] })
73
+ return
74
+ }
75
+ renderFlash({ type: flash.type, message: flash.message })
76
+ }
77
+ }
78
+ })
79
+ </script>
80
+ <template>
81
+ <div
82
+ class="fixed inset-0 flex flex-col items-end justify-end px-4 py-6 pointer-events-none sm:p-6 z-40"
83
+ >
84
+ <transition-group
85
+ tag="div"
86
+ class="max-w-sm w-full"
87
+ enter-active-class="ease-out duration-300"
88
+ enter-from-class="translate-y-2 opacity-0 sm:translate-y-0 sm:translate-x-2"
89
+ enter-to-class="translate-y-0 opacity-100 sm:translate-x-0"
90
+ leave-active-class="ease-in duration-100"
91
+ leave-from-class="opacity-100"
92
+ leave-to-class="opacity-0"
93
+ >
94
+ <div
95
+ v-for="(flash, idx) in flashes"
96
+ :key="flash.message"
97
+ class="bg-white shadow-lg rounded-lg pointer-events-auto border-t-4 transform"
98
+ :class="[{ 'mt-2': idx > 0 }, getFlashClass(flash)]"
99
+ >
100
+ <div
101
+ class="rounded-lg ring-1 ring-black ring-opacity-5 overflow-hidden"
102
+ >
103
+ <div class="p-4">
104
+ <div class="flex items-center">
105
+ <div class="w-0 flex-1 flex justify-between">
106
+ <p
107
+ class="w-0 flex-1 text-sm leading-5 font-medium text-gray-900"
108
+ v-html="flash.message"
109
+ ></p>
110
+ </div>
111
+ <div class="ml-4 flex-shrink-0 flex">
112
+ <button
113
+ @click="remove(flash)"
114
+ class="inline-flex text-gray-400 focus:outline-none focus:text-gray-500 transition ease-in-out duration-150"
115
+ >
116
+ <svg class="h-5 w-5" viewBox="0 0 20 20" fill="currentColor">
117
+ <path
118
+ fill-rule="evenodd"
119
+ d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z"
120
+ clip-rule="evenodd"
121
+ />
122
+ </svg>
123
+ </button>
124
+ </div>
125
+ </div>
126
+ </div>
127
+ </div>
128
+ </div>
129
+ </transition-group>
130
+ </div>
131
+ </template>
@@ -0,0 +1,133 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ Dialog,
4
+ DialogOverlay,
5
+ DialogTitle,
6
+ TransitionChild,
7
+ TransitionRoot,
8
+ } from "@headlessui/vue"
9
+ import { XIcon } from "@heroicons/vue/outline"
10
+
11
+ withDefaults(
12
+ defineProps<{
13
+ destructive?: boolean
14
+ disabled?: boolean
15
+ modelValue: boolean
16
+ submitText?: string
17
+ title?: string
18
+ }>(),
19
+ {
20
+ destructive: false,
21
+ disabled: false,
22
+ submitText: "",
23
+ title: "",
24
+ }
25
+ )
26
+
27
+ const emit = defineEmits<{
28
+ (e: "submit"): void
29
+ (e: "update:modelValue", val: boolean): void
30
+ }>()
31
+
32
+ const submit = () => {
33
+ emit("submit")
34
+ }
35
+
36
+ const updateModelValue = (value: boolean) => {
37
+ emit("update:modelValue", value)
38
+ }
39
+ </script>
40
+ <template>
41
+ <TransitionRoot as="template" :show="modelValue">
42
+ <Dialog
43
+ as="div"
44
+ static
45
+ class="fixed z-10 inset-0 overflow-y-auto"
46
+ @close="updateModelValue(false)"
47
+ :open="modelValue"
48
+ >
49
+ <div
50
+ class="flex items-end justify-center min-h-screen pt-4 px-4 pb-20 text-center sm:block sm:p-0"
51
+ >
52
+ <TransitionChild
53
+ as="template"
54
+ enter="ease-out duration-300"
55
+ enter-from="opacity-0"
56
+ enter-to="opacity-100"
57
+ leave="ease-in duration-200"
58
+ leave-from="opacity-100"
59
+ leave-to="opacity-0"
60
+ >
61
+ <DialogOverlay
62
+ class="fixed inset-0 bg-gray-500 bg-opacity-75 transition-opacity"
63
+ />
64
+ </TransitionChild>
65
+
66
+ <!-- This element is to trick the browser into centering the modal contents. -->
67
+ <span
68
+ class="hidden sm:inline-block sm:align-middle sm:h-screen"
69
+ aria-hidden="true"
70
+ >&#8203;</span
71
+ >
72
+ <TransitionChild
73
+ as="template"
74
+ enter="ease-out duration-300"
75
+ enter-from="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
76
+ enter-to="opacity-100 translate-y-0 sm:scale-100"
77
+ leave="ease-in duration-200"
78
+ leave-from="opacity-100 translate-y-0 sm:scale-100"
79
+ leave-to="opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"
80
+ >
81
+ <div
82
+ class="inline-block align-bottom bg-white rounded-lg text-left overflow-hidden shadow-xl transform transition-all sm:my-8 sm:align-middle sm:max-w-2xl w-full"
83
+ >
84
+ <div class="block absolute top-0 right-0 pt-4 pr-4">
85
+ <button
86
+ type="button"
87
+ class="bg-white rounded-md text-gray-400 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500"
88
+ @click="updateModelValue(false)"
89
+ >
90
+ <span class="sr-only">Close</span>
91
+ <XIcon class="h-6 w-6" aria-hidden="true" />
92
+ </button>
93
+ </div>
94
+ <div class="bg-white px-4 pt-5 pb-4 sm:p-6 sm:pb-4">
95
+ <div class="mt-3 sm:mt-0 sm:text-left">
96
+ <DialogTitle
97
+ as="h3"
98
+ class="text-center text-lg leading-6 font-medium text-gray-900"
99
+ v-text="title"
100
+ ></DialogTitle>
101
+ <div class="mt-2">
102
+ <slot></slot>
103
+ </div>
104
+ </div>
105
+ </div>
106
+ <div
107
+ class="bg-gray-50 px-4 py-3 sm:px-6 sm:flex sm:flex-row-reverse"
108
+ v-if="submitText"
109
+ >
110
+ <button
111
+ type="button"
112
+ class="xy-btn w-full sm:ml-3 sm:w-auto sm:text-sm"
113
+ @click="submit()"
114
+ v-text="submitText"
115
+ :class="[destructive ? 'xy-btn-red' : 'xy-btn']"
116
+ :disabled="disabled"
117
+ ></button>
118
+ <button
119
+ type="button"
120
+ class="xy-btn-white mt-3 w-full sm:mt-0 sm:ml-3 sm:w-auto sm:text-sm"
121
+ @click="updateModelValue(false)"
122
+ ref="cancelButtonRef"
123
+ >
124
+ Cancel
125
+ </button>
126
+ </div>
127
+ <slot name="buttons"></slot>
128
+ </div>
129
+ </TransitionChild>
130
+ </div>
131
+ </Dialog>
132
+ </TransitionRoot>
133
+ </template>
@@ -0,0 +1,109 @@
1
+ <script lang="ts">
2
+ export type PopoverPosition =
3
+ | "top-left"
4
+ | "top-center"
5
+ | "top-right"
6
+ | "bottom-left"
7
+ | "bottom-center"
8
+ | "bottom-right"
9
+ | "left"
10
+ | "right"
11
+ </script>
12
+ <script lang="ts" setup>
13
+ import {
14
+ Popover as HeadlessPopover,
15
+ PopoverButton as HeadlessPopoverButton,
16
+ PopoverPanel as HeadlessPopoverPanel,
17
+ } from "@headlessui/vue"
18
+ import { computed } from "vue"
19
+
20
+ const props = withDefaults(
21
+ defineProps<{
22
+ position?: PopoverPosition
23
+ }>(),
24
+ {
25
+ position: "top-center",
26
+ }
27
+ )
28
+
29
+ const positionClasses = computed(() => {
30
+ // NOTE: by always pushing the screen width wrapper classes to the left we can avoid overflow scrolling
31
+
32
+ let wrapperClasses = ""
33
+ let contentClasses = ""
34
+
35
+ switch (props.position) {
36
+ case "top-left":
37
+ wrapperClasses =
38
+ "top-0 left-0 -translate-y-full -translate-x-full justify-end"
39
+ break
40
+ case "top-center":
41
+ wrapperClasses =
42
+ "top-0 -translate-y-full -translate-x-full left-1/2 justify-end"
43
+ contentClasses = "translate-x-1/2"
44
+ break
45
+ case "top-right":
46
+ wrapperClasses = "top-0 -translate-y-full right-0 justify-end"
47
+ contentClasses = "translate-x-full"
48
+ break
49
+ case "bottom-left":
50
+ wrapperClasses = "top-full left-0 -translate-x-full justify-end"
51
+ break
52
+ case "bottom-center":
53
+ wrapperClasses = "top-full -translate-x-full left-1/2 justify-end"
54
+ contentClasses = "translate-x-1/2"
55
+ break
56
+ case "bottom-right":
57
+ wrapperClasses = "top-full right-0 justify-end"
58
+ contentClasses = "translate-x-full"
59
+ break
60
+ case "left":
61
+ wrapperClasses =
62
+ "top-1/2 left-0 -translate-y-1/2 -translate-x-full justify-end"
63
+ break
64
+ case "right":
65
+ wrapperClasses = "top-1/2 -translate-y-1/2 right-0 justify-end"
66
+ contentClasses = "translate-x-full"
67
+ break
68
+ }
69
+
70
+ return {
71
+ wrapper: wrapperClasses,
72
+ content: contentClasses,
73
+ }
74
+ })
75
+
76
+ // TODO: maybe auto positioning - dynamic based on button location and closed overflow hidden container?
77
+ </script>
78
+
79
+ <template>
80
+ <div class="flex">
81
+ <HeadlessPopover v-slot="{ open, close }" class="relative leading-none">
82
+ <HeadlessPopoverButton>
83
+ <slot name="button" :open="open" :close="close"></slot>
84
+ </HeadlessPopoverButton>
85
+
86
+ <transition
87
+ enter-active-class="transition-opacity transition-faster ease-out-quad"
88
+ leave-active-class="transition-opacity transition-faster ease-in-quad"
89
+ enter-from-class="opacity-0"
90
+ enter-to-class="opacity-100"
91
+ leave-from-class="opacity-100"
92
+ leave-to-class="opacity-0"
93
+ >
94
+ <!--NOTE: use prop "static" for dev work to keep the tooptip visible-->
95
+ <HeadlessPopoverPanel>
96
+ <!--positioning wrappers-->
97
+ <div
98
+ class="absolute z-10 transform w-screen flex"
99
+ :class="positionClasses.wrapper"
100
+ >
101
+ <div :class="positionClasses.content">
102
+ <slot :open="open" :close="close"></slot>
103
+ </div>
104
+ </div>
105
+ </HeadlessPopoverPanel>
106
+ </transition>
107
+ </HeadlessPopover>
108
+ </div>
109
+ </template>
@@ -0,0 +1,8 @@
1
+ <template>
2
+ <!--styling wrapper - top level element will merge attrs for class overrides -->
3
+ <div
4
+ class="max-w-xs bg-white rounded-md p-2 border border-gray-100 shadow-md"
5
+ >
6
+ <slot></slot>
7
+ </div>
8
+ </template>
@@ -0,0 +1,87 @@
1
+ <script setup lang="ts">
2
+ import {
3
+ Dialog,
4
+ DialogOverlay,
5
+ DialogTitle,
6
+ TransitionChild,
7
+ TransitionRoot,
8
+ } from "@headlessui/vue"
9
+ import { XIcon } from "@heroicons/vue/outline"
10
+ import { ref } from "vue"
11
+
12
+ const props = defineProps<{
13
+ header: string
14
+ description: string
15
+ modelValue: boolean
16
+ }>()
17
+
18
+ const open = ref(props.modelValue)
19
+
20
+ const emit = defineEmits<{
21
+ (e: "close", val: boolean): void
22
+ (e: "update:modelValue", val: boolean): void
23
+ }>()
24
+
25
+ const close = () => {
26
+ open.value = false
27
+ emit("close", open.value)
28
+ emit("update:modelValue", open.value)
29
+ }
30
+ </script>
31
+ <template>
32
+ <TransitionRoot as="template" :show="modelValue">
33
+ <Dialog
34
+ as="div"
35
+ static
36
+ class="fixed inset-0 z-40 overflow-hidden bg-black bg-opacity-50"
37
+ @close="close()"
38
+ :open="modelValue"
39
+ >
40
+ <div class="absolute inset-0 overflow-hidden">
41
+ <DialogOverlay class="absolute inset-0" />
42
+
43
+ <div class="fixed inset-y-0 right-0 pl-10 max-w-full flex">
44
+ <TransitionChild
45
+ as="template"
46
+ enter="transform transition ease-in-out duration-500 sm:duration-700"
47
+ enter-from="translate-x-full"
48
+ enter-to="translate-x-0"
49
+ leave="transform transition ease-in-out duration-500 sm:duration-700"
50
+ leave-from="translate-x-0"
51
+ leave-to="translate-x-full"
52
+ >
53
+ <div class="w-screen max-w-md">
54
+ <div
55
+ class="h-full flex flex-col bg-white shadow-xl overflow-y-scroll"
56
+ >
57
+ <div class="py-6 px-4 bg-blue-700 sm:px-6">
58
+ <div class="flex items-center justify-between">
59
+ <DialogTitle as="h3" class="text-white" v-text="header">
60
+ </DialogTitle>
61
+ <div class="ml-3 h-7 flex items-center">
62
+ <button
63
+ class="bg-blue-700 rounded-md text-blue-200 hover:text-white focus:outline-none focus:ring-2 focus:ring-white"
64
+ @click="close()"
65
+ >
66
+ <span class="sr-only">Close panel</span>
67
+ <XIcon class="h-6 w-6" aria-hidden="true" />
68
+ </button>
69
+ </div>
70
+ </div>
71
+ <div class="mt-1">
72
+ <p class="text-blue-300" v-text="description"></p>
73
+ </div>
74
+ </div>
75
+ <div class="relative flex-1 py-6 px-4 sm:px-6">
76
+ <slot></slot>
77
+ </div>
78
+
79
+ <slot name="footer"></slot>
80
+ </div>
81
+ </div>
82
+ </TransitionChild>
83
+ </div>
84
+ </div>
85
+ </Dialog>
86
+ </TransitionRoot>
87
+ </template>