@cloudron/pankow 3.1.8

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 (62) hide show
  1. package/.gitlab-ci.yml +30 -0
  2. package/.jshintrc +8 -0
  3. package/LICENSE +21 -0
  4. package/README.md +20 -0
  5. package/components/BottomBar.vue +22 -0
  6. package/components/Breadcrumb.vue +64 -0
  7. package/components/Button.vue +243 -0
  8. package/components/ButtonGroup.vue +37 -0
  9. package/components/Checkbox.vue +112 -0
  10. package/components/Dialog.vue +178 -0
  11. package/components/DirectoryView.vue +772 -0
  12. package/components/DirectoryViewListItem.vue +412 -0
  13. package/components/EmailInput.vue +22 -0
  14. package/components/FileUploader.vue +204 -0
  15. package/components/FormGroup.vue +26 -0
  16. package/components/Icon.vue +12 -0
  17. package/components/InputDialog.vue +170 -0
  18. package/components/InputGroup.vue +32 -0
  19. package/components/MainLayout.vue +63 -0
  20. package/components/Menu.vue +284 -0
  21. package/components/MenuItem.vue +106 -0
  22. package/components/MenuItemLink.vue +52 -0
  23. package/components/MultiSelect.vue +202 -0
  24. package/components/Notification.vue +163 -0
  25. package/components/NumberInput.vue +31 -0
  26. package/components/OfflineBanner.vue +47 -0
  27. package/components/PasswordInput.vue +86 -0
  28. package/components/Popover.vue +185 -0
  29. package/components/ProgressBar.vue +75 -0
  30. package/components/Radiobutton.vue +128 -0
  31. package/components/SideBar.vue +104 -0
  32. package/components/SingleSelect.vue +190 -0
  33. package/components/Spinner.vue +67 -0
  34. package/components/Switch.vue +94 -0
  35. package/components/TabView.vue +161 -0
  36. package/components/TableView.vue +187 -0
  37. package/components/TagInput.vue +104 -0
  38. package/components/TextInput.vue +58 -0
  39. package/components/TopBar.vue +88 -0
  40. package/fallbackImage.js +29 -0
  41. package/fetcher.js +81 -0
  42. package/gallery/CustomMenuItem.vue +40 -0
  43. package/gallery/DirectoryViewDemo.vue +73 -0
  44. package/gallery/Index.vue +790 -0
  45. package/gallery/folder.svg +151 -0
  46. package/gallery/index.html +25 -0
  47. package/gallery/index.js +10 -0
  48. package/gallery/logo.png +0 -0
  49. package/gallery/vite.config.mjs +9 -0
  50. package/gestures.js +60 -0
  51. package/index.js +86 -0
  52. package/logo.png +0 -0
  53. package/logo.svg +78 -0
  54. package/package.json +26 -0
  55. package/style.css +351 -0
  56. package/tooltip.js +83 -0
  57. package/utils.js +383 -0
  58. package/viewers/GenericViewer.vue +84 -0
  59. package/viewers/ImageViewer.vue +239 -0
  60. package/viewers/PdfViewer.vue +82 -0
  61. package/viewers/TextViewer.vue +221 -0
  62. package/viewers.js +11 -0
@@ -0,0 +1,202 @@
1
+ <script setup>
2
+
3
+ import { ref, useTemplateRef, onMounted, computed, watch } from 'vue';
4
+ import Button from './Button.vue';
5
+ import Menu from './Menu.vue';
6
+ import Icon from './Icon.vue';
7
+
8
+ const props = defineProps({
9
+ placeholder: {
10
+ type: String,
11
+ default: 'Select'
12
+ },
13
+ selectedLabel: {
14
+ type: String,
15
+ default: '%n selected'
16
+ },
17
+ selectAllLabel: {
18
+ type: String,
19
+ default: '',
20
+ },
21
+ optionLabel: {
22
+ type: String,
23
+ default: 'label'
24
+ },
25
+ optionKey: {
26
+ type: String,
27
+ default: ''
28
+ },
29
+ options: {
30
+ type: Array,
31
+ default: []
32
+ },
33
+ disabled: {
34
+ type: Boolean,
35
+ default: false
36
+ },
37
+ searchThreshold: {
38
+ type: Number,
39
+ default: Infinity,
40
+ },
41
+ });
42
+
43
+ const emits = defineEmits(['select']);
44
+
45
+ const model = defineModel();
46
+
47
+ let selected = ref([]);
48
+
49
+ const elem = useTemplateRef('elem');
50
+ const menu = useTemplateRef('menu');
51
+
52
+ watch(model, (newValue, oldValue) => {
53
+ selected.value = newValue;
54
+ });
55
+
56
+ const buttonLabel = computed(() => {
57
+ return props.selectedLabel.replace('%n', selected.value.length);
58
+ });
59
+
60
+ const allSelected = ref(false);
61
+
62
+ const menuModel = computed(() => {
63
+ const ret = props.options.map((item) => {
64
+ if (item.separator) {
65
+ return {
66
+ separator: true,
67
+ label: '',
68
+ };
69
+ } else {
70
+ return {
71
+ label: item[props.optionLabel],
72
+ checkbox: true,
73
+ checked: selected.value.indexOf(props.optionKey ? item[props.optionKey] : item) !== -1,
74
+ action: () => {
75
+ const idx = selected.value.indexOf(props.optionKey ? item[props.optionKey] : item);
76
+
77
+ if (idx >= 0) {
78
+ selected.value.splice(idx, 1);
79
+ allSelected.value = false;
80
+ } else {
81
+ selected.value.push(props.optionKey ? item[props.optionKey] : item);
82
+ }
83
+
84
+ model.value = selected.value;
85
+ emits('select', selected.value);
86
+ }
87
+ };
88
+ }
89
+ });
90
+
91
+ if (props.selectAllLabel) {
92
+ ret.unshift({
93
+ label: props.selectAllLabel || '',
94
+ checkbox: true,
95
+ checked: allSelected.value,
96
+ // checked: props.options.filter((item) => !item.separator).every((item) => item.checked),
97
+ action: () => {
98
+ allSelected.value = !allSelected.value;
99
+
100
+ if (allSelected.value) {
101
+ selected.value = props.options.filter((item) => !item.separator).map((item) => props.optionKey ? item[props.optionKey] : item);
102
+ } else {
103
+ selected.value = [];
104
+ }
105
+
106
+ model.value = selected.value;
107
+ emits('select', selected.value);
108
+ }
109
+ }, {
110
+ separator: true,
111
+ label: '',
112
+ });
113
+ }
114
+
115
+ return ret;
116
+ });
117
+
118
+ function onMenuClosed() {
119
+ elem.value.focus();
120
+ }
121
+
122
+ function onClick(event) {
123
+ if (props.disabled) return;
124
+
125
+ if (menu.value.isOpen) menu.value.close();
126
+ else menu.value.open(event, elem.value);
127
+ }
128
+
129
+ function onOpen(event) {
130
+ if (props.disabled) return;
131
+
132
+ menu.value.open(event, elem.value);
133
+ }
134
+
135
+ function onClose(event) {
136
+ menu.value.close();
137
+ }
138
+
139
+ function onClosed() {
140
+ // restores the focus
141
+ elem.value.focus();
142
+ }
143
+
144
+ onMounted(() => {
145
+ selected.value = model.value;
146
+ });
147
+
148
+ </script>
149
+
150
+ <template>
151
+ <div class="pankow-multiselect" :class="{ 'pankow-multiselect-disabled': disabled }" ref="elem" tabindex="0" @click="onClick" @keydown.enter="onOpen" @keydown.down.stop="onOpen" @keydown.up.stop="onOpen" @keydown.esc.stop="onClose">
152
+ <Menu ref="menu" :model="menuModel" :close-on-activation="false" @close="onMenuClosed" :search-threshold="searchThreshold"></Menu>
153
+ {{ buttonLabel }}
154
+ <Icon icon="fa-solid fa-chevron-down" class="pankow-button-icon-right-with-text" />
155
+ </div>
156
+ </template>
157
+
158
+ <style>
159
+
160
+ .pankow-multiselect {
161
+ display: inline-flex;
162
+ justify-content: space-between;
163
+ align-items: center;
164
+ font-weight: 400;
165
+ font-size: 14px;
166
+ color: var(--pankow-color-dark);
167
+ user-select: none;
168
+ text-align: center;
169
+ border-width: 1px;
170
+ border-style: solid;
171
+ border-color: var(--pankow-input-border-color);
172
+ background-color: var(--pankow-input-background-color);
173
+ padding: 5px 12px;
174
+ border-radius: var(--pankow-border-radius);
175
+ text-decoration: none;
176
+ cursor: pointer;
177
+ transition: background-color 250ms;
178
+ min-width: 100px;
179
+ }
180
+
181
+ @media (prefers-color-scheme: dark) {
182
+ .pankow-multiselect {
183
+ color: var(--pankow-color-light-dark);
184
+ }
185
+ }
186
+
187
+ .pankow-multiselect:focus {
188
+ outline: none;
189
+ border-color: var(--pankow-color-dark) !important;
190
+ }
191
+
192
+ .pankow-multiselect:hover {
193
+ border-color: var(--pankow-color-primary-hover);
194
+ }
195
+
196
+ .pankow-multiselect-disabled,
197
+ .pankow-multiselect-disabled:hover {
198
+ cursor: not-allowed;
199
+ border-color: var(--pankow-input-border-color);
200
+ }
201
+
202
+ </style>
@@ -0,0 +1,163 @@
1
+ <template>
2
+ <teleport to="#app">
3
+ <TransitionGroup name="pankow-notification-fade" :class="`pankow-notification-container ${position}`" tag="div">
4
+ <div v-for="message in messages" key="id">
5
+ <div class="pankow-notification" :[message.type]="''">
6
+ {{ message.text }}
7
+ <Icon @click="onClose(message.id)" icon="fa-solid fa-xmark" class="pankow-notification-close"/>
8
+ </div>
9
+ </div>
10
+ </TransitionGroup>
11
+ </teleport>
12
+ </template>
13
+
14
+ <script>
15
+
16
+ import { uuidv4 } from '../utils.js';
17
+
18
+ import Icon from './Icon.vue';
19
+
20
+ export default {
21
+ name: 'Notification',
22
+ components: {
23
+ Icon
24
+ },
25
+ props: {
26
+ position: {
27
+ type: String,
28
+ default: 'top-center',
29
+ validator(value, props) {
30
+ return [ 'top-left', 'top-center', 'top-right', 'bottom-left', 'bottom-center', 'bottom-right' ].includes(value);
31
+ }
32
+ }
33
+ },
34
+ data() {
35
+ return {
36
+ messages: []
37
+ }
38
+ },
39
+ methods: {
40
+ raise(options) {
41
+ const id = uuidv4();
42
+ const text = typeof options === 'object' && 'text' in options ? options.text : options;
43
+ const persistent = typeof options === 'object' && 'persistent' in options ? options.persistent : false;
44
+ const timeout = typeof options === 'object' && 'timeout' in options ? options.timeout : 2000;
45
+ const type = typeof options === 'object' && 'type' in options ? options.type : null;
46
+
47
+ this.messages.push({ id, text, persistent, type });
48
+
49
+ if (!persistent) {
50
+ setTimeout(() => {
51
+ const index = this.messages.findIndex((value) => value.id === id);
52
+ if (index > -1) this.messages.splice(index, 1);
53
+ }, timeout);
54
+ }
55
+ },
56
+ onClose(id) {
57
+ const index = this.messages.findIndex((value) => value.id === id);
58
+ if (index > -1) this.messages.splice(index, 1);
59
+ }
60
+ },
61
+ mounted() {
62
+ if (!window.pankow) window.pankow = {};
63
+ if (window.pankow.notify) return console.error('Pankow: Notification DOM node already exists! Only ever use a single one.');
64
+
65
+ window.pankow.notify = (options) => { this.raise(options); }
66
+ }
67
+ };
68
+
69
+ </script>
70
+
71
+ <style>
72
+
73
+ .pankow-notification-container {
74
+ position: absolute;
75
+ z-index: 30001;
76
+ }
77
+
78
+ .pankow-notification-container.top-left {
79
+ top: 0;
80
+ left: 10px;
81
+ }
82
+
83
+ .pankow-notification-container.top-center {
84
+ top: 0;
85
+ left: 50%;
86
+ transform: translateX(-50%);
87
+ text-align: center;
88
+ }
89
+
90
+ .pankow-notification-container.top-right {
91
+ top: 0;
92
+ right: 10px;
93
+ text-align: right;
94
+ }
95
+
96
+ .pankow-notification-container.bottom-left {
97
+ left: 10px;
98
+ bottom: 10px;
99
+ }
100
+
101
+ .pankow-notification-container.bottom-center {
102
+ left: 50%;
103
+ transform: translateX(-50%);
104
+ text-align: center;
105
+ bottom: 10px;
106
+ }
107
+
108
+ .pankow-notification-container.bottom-right {
109
+ right: 10px;
110
+ text-align: right;
111
+ bottom: 10px;
112
+ }
113
+
114
+ .pankow-notification {
115
+ display: inline-block;
116
+ background-color: rgba(255,255,255,0.6);
117
+ backdrop-filter: blur(10px);
118
+ opacity: 0.9;
119
+ padding: 10px 15px;
120
+ margin-top: 10px;
121
+ text-align: left;
122
+ color: black;
123
+ box-shadow: var(--pankow-notification-shadow);
124
+ border-radius: var(--pankow-border-radius);
125
+ }
126
+
127
+ .pankow-notification[danger] {
128
+ background-color: var(--pankow-color-danger);
129
+ color: white;
130
+ }
131
+
132
+ .pankow-notification[success] {
133
+ background-color: var(--pankow-color-success);
134
+ color: white;
135
+ }
136
+
137
+ .pankow-notification[secondary] {
138
+ background-color: var(--pankow-color-secondary);
139
+ color: white;
140
+ }
141
+
142
+ .pankow-notification:hover {
143
+ opacity: 1;
144
+ }
145
+
146
+ .pankow-notification-close {
147
+ padding: 5px;
148
+ cursor: pointer;
149
+ }
150
+
151
+ .pankow-notification-fade-move,
152
+ .pankow-notification-fade-enter-active,
153
+ .pankow-notification-fade-leave-active {
154
+ transition: all 0.25s ease-in-out;
155
+ }
156
+
157
+ .pankow-notification-fade-enter-from,
158
+ .pankow-notification-fade-leave-to {
159
+ opacity: 0;
160
+ transform: scale(0.01);
161
+ }
162
+
163
+ </style>
@@ -0,0 +1,31 @@
1
+ <script setup>
2
+
3
+ const model = defineModel();
4
+ const props = defineProps({
5
+ placeholder: String,
6
+ readonly: {
7
+ type: Boolean,
8
+ default: false
9
+ },
10
+ min: {
11
+ default: NaN
12
+ },
13
+ max: {
14
+ default: NaN
15
+ },
16
+ step: {
17
+ default: NaN
18
+ },
19
+ });
20
+
21
+ </script>
22
+
23
+ <template>
24
+ <input class="pankow-text-input" type="number" :step="step" :min="min" :max="max" :placeholder="placeholder" :readonly="readonly" :value="model" @input="$emit('update:modelValue', parseInt($event.target.value))"/>
25
+ </template>
26
+
27
+ <style>
28
+
29
+ /* using TextInput styles */
30
+
31
+ </style>
@@ -0,0 +1,47 @@
1
+ <script setup>
2
+
3
+ const props = defineProps({
4
+ active: {
5
+ type: Boolean,
6
+ default: false
7
+ },
8
+ href: {
9
+ type: String,
10
+ default: ''
11
+ },
12
+ label: {
13
+ type: String,
14
+ default: ''
15
+ },
16
+ });
17
+
18
+ </script>
19
+
20
+ <template>
21
+ <Transition name="pankow-roll-down">
22
+ <a class="pankow-offline-banner" v-show="active" :href="href" target="_blank">
23
+ <i class="fa fa-solid fa-spin fa-circle-notch"/> {{ label }}
24
+ </a>
25
+ </Transition>
26
+ </template>
27
+
28
+ <style>
29
+
30
+ .pankow-offline-banner {
31
+ position: fixed;
32
+ top: 0;
33
+ left: 0;
34
+ z-index: 30000;
35
+ background-color: #ff4c4c;
36
+ width: 100%;
37
+ padding: 2px;
38
+ text-align: center;
39
+ color: white;
40
+ }
41
+
42
+ .pankow-offline-banner:hover, .pankow-offline-banner:focus {
43
+ color: white;
44
+ text-decoration: none;
45
+ }
46
+
47
+ </style>
@@ -0,0 +1,86 @@
1
+ <script setup>
2
+
3
+ import { ref } from 'vue';
4
+
5
+ const svgEye = '<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="eye" class="svg-inline--fa fa-eye fa-w-18" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 576 512"><path fill="currentColor" d="M572.52 241.4C518.29 135.59 410.93 64 288 64S57.68 135.64 3.48 241.41a32.35 32.35 0 0 0 0 29.19C57.71 376.41 165.07 448 288 448s230.32-71.64 284.52-177.41a32.35 32.35 0 0 0 0-29.19zM288 400a144 144 0 1 1 144-144 143.93 143.93 0 0 1-144 144zm0-240a95.31 95.31 0 0 0-25.31 3.79 47.85 47.85 0 0 1-66.9 66.9A95.78 95.78 0 1 0 288 160z"></path></svg>';
6
+ const svgEyeSlash = '<svg aria-hidden="true" focusable="false" data-prefix="fas" data-icon="eye-slash" class="svg-inline--fa fa-eye-slash fa-w-20" role="img" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><path fill="currentColor" d="M320 400c-75.85 0-137.25-58.71-142.9-133.11L72.2 185.82c-13.79 17.3-26.48 35.59-36.72 55.59a32.35 32.35 0 0 0 0 29.19C89.71 376.41 197.07 448 320 448c26.91 0 52.87-4 77.89-10.46L346 397.39a144.13 144.13 0 0 1-26 2.61zm313.82 58.1l-110.55-85.44a331.25 331.25 0 0 0 81.25-102.07 32.35 32.35 0 0 0 0-29.19C550.29 135.59 442.93 64 320 64a308.15 308.15 0 0 0-147.32 37.7L45.46 3.37A16 16 0 0 0 23 6.18L3.37 31.45A16 16 0 0 0 6.18 53.9l588.36 454.73a16 16 0 0 0 22.46-2.81l19.64-25.27a16 16 0 0 0-2.82-22.45zm-183.72-142l-39.3-30.38A94.75 94.75 0 0 0 416 256a94.76 94.76 0 0 0-121.31-92.21A47.65 47.65 0 0 1 304 192a46.64 46.64 0 0 1-1.54 10l-73.61-56.89A142.31 142.31 0 0 1 320 112a143.92 143.92 0 0 1 144 144c0 21.63-5.29 41.79-13.9 60.11z"></path></svg>';
7
+
8
+ const emit = defineEmits([ 'update:modelValue' ]);
9
+ const props = defineProps({
10
+ placeholder: String,
11
+ modelValue: String,
12
+ id: String,
13
+ disabled: {
14
+ type: Boolean,
15
+ default: false
16
+ }
17
+ });
18
+
19
+ const revealed =ref(false);
20
+ const revealIcon = ref(svgEye);
21
+
22
+ function toggleReveal() {
23
+ revealed.value = !revealed.value;
24
+ revealIcon.value = revealed.value ? svgEyeSlash : svgEye;
25
+ }
26
+
27
+ </script>
28
+
29
+ <template>
30
+ <div class="pankow-password">
31
+ <input class="pankow-password-input" :id="id" :type="revealed ? 'text' : 'password'" :disabled="disabled ? true : undefined" :placeholder="placeholder" :autofocus="$attrs['autofocus']" :required="$attrs['required']" :value="modelValue" @input="$emit('update:modelValue', $event.target.value)"/>
32
+ <i class="pankow-password-reveal" v-html="revealIcon" @click="toggleReveal"></i>
33
+ </div>
34
+ </template>
35
+
36
+ <style>
37
+
38
+ .pankow-password {
39
+ display: flex;
40
+ white-space: nowrap;
41
+ position: relative;
42
+ }
43
+
44
+ .pankow-password-input {
45
+ flex-grow: 1;
46
+ font-size: 14px;
47
+ color: var(--pankow-text-color);
48
+ background-color: var(--pankow-input-background-color);
49
+ border: none;
50
+ padding: var(--pankow-input-vertial-padding) var(--pankow-input-horizontal-padding);
51
+ border-radius: var(--pankow-border-radius);
52
+ display: inline-block;
53
+ border: 1px solid var(--pankow-input-border-color);
54
+ transition: border-color 250ms;
55
+ }
56
+
57
+ .pankow-password.has-error > .pankow-password-input {
58
+ border: 1px solid var(--pankow-color-danger) !important;
59
+ }
60
+
61
+ .pankow-password-input:hover {
62
+ border: 1px solid var(--pankow-color-dark);
63
+ }
64
+
65
+ .pankow-password-input:focus {
66
+ border: 1px solid var(--pankow-color-primary-hover);
67
+ outline: none;
68
+ }
69
+
70
+ .pankow-password-input:disabled {
71
+ cursor: not-allowed;
72
+ opacity: 0.5;
73
+ }
74
+
75
+ .pankow-password-reveal {
76
+ display: inline-block;
77
+ position: absolute;
78
+ padding: 6px;
79
+ width: 30px;
80
+ height: 30px;
81
+ right: 5px;
82
+ top: 1px;
83
+ cursor: pointer;
84
+ }
85
+
86
+ </style>
@@ -0,0 +1,185 @@
1
+ <script setup>
2
+
3
+ import { nextTick, ref, useTemplateRef } from 'vue';
4
+
5
+ const container = useTemplateRef('container');
6
+
7
+ function getViewport() {
8
+ var win = window,
9
+ d = document,
10
+ e = d.documentElement,
11
+ g = d.getElementsByTagName('body')[0],
12
+ w = win.innerWidth || e.clientWidth || g.clientWidth,
13
+ h = win.innerHeight || e.clientHeight || g.clientHeight;
14
+
15
+ return {
16
+ width: w,
17
+ height: h
18
+ };
19
+ }
20
+
21
+ function getHiddenElementOuterHeight(element) {
22
+ if (element) {
23
+ element.style.visibility = 'hidden';
24
+ element.style.display = 'block';
25
+ var elementHeight = element.offsetHeight;
26
+ element.style.display = 'none';
27
+ element.style.visibility = 'visible';
28
+ return elementHeight;
29
+ }
30
+ return 0;
31
+ }
32
+
33
+ function getHiddenElementOuterWidth(element) {
34
+ if (element) {
35
+ element.style.visibility = 'hidden';
36
+ element.style.display = 'block';
37
+ var elementWidth = element.offsetWidth;
38
+ element.style.display = 'none';
39
+ element.style.visibility = 'visible';
40
+ return elementWidth;
41
+ }
42
+ return 0;
43
+ }
44
+
45
+ function getActiveElement(children) {
46
+ for (const child of children) {
47
+ if (child === document.activeElement) return child;
48
+ }
49
+ return null;
50
+ }
51
+
52
+ const emits = defineEmits(['close']);
53
+ const props = defineProps({
54
+ width: {
55
+ type: String,
56
+ default: '',
57
+ },
58
+ height: {
59
+ type: String,
60
+ default: '',
61
+ }
62
+ });
63
+
64
+ const openEventTimeStamp = ref(0);
65
+ const forElement = ref(null);
66
+ const isOpen = ref(false);
67
+ const pageX = ref(0);
68
+ const pageY = ref(0);
69
+ const offsetY = ref(0);
70
+ const offsetX = ref(0);
71
+ const rollUp = ref(false);
72
+
73
+ function close() {
74
+ isOpen.value = false;
75
+ emits('close');
76
+ }
77
+
78
+ function onBackdrop(event) {
79
+ close();
80
+ event.preventDefault();
81
+ }
82
+
83
+ function position() {
84
+ let width = container.value.offsetParent ? container.value.offsetWidth : getHiddenElementOuterWidth(container.value);
85
+ let height = container.value.offsetParent ? container.value.offsetHeight : getHiddenElementOuterHeight(container.value);
86
+ let left = pageX.value + offsetX.value/2 - width/2;
87
+ let top = pageY.value + 6;
88
+ let viewport = getViewport();
89
+
90
+ //flip
91
+ if (left + width - document.body.scrollLeft > viewport.width) {
92
+ // if this is like a dropdown right align instead of flip
93
+ if (forElement.value) {
94
+ left = forElement.value.getBoundingClientRect().left + (forElement.value.getBoundingClientRect().width - width);
95
+ } else {
96
+ left -= width;
97
+ }
98
+ }
99
+
100
+ //flip
101
+ if (top + height - document.body.scrollTop > viewport.height) {
102
+ top -= height + offsetY.value + 12;
103
+ rollUp.value = true;
104
+ } else {
105
+ rollUp.value = false;
106
+ }
107
+
108
+ //fit
109
+ if (left < document.body.scrollLeft) {
110
+ left = document.body.scrollLeft;
111
+ }
112
+
113
+ //fit
114
+ if (top < document.body.scrollTop) {
115
+ top = document.body.scrollTop;
116
+ }
117
+
118
+ container.value.style.left = left + 'px';
119
+ container.value.style.top = top + 'px';
120
+ }
121
+
122
+ defineExpose({
123
+ async open(event, forElem = null) {
124
+ isOpen.value = true;
125
+ pageX.value = forElem ? forElem.getBoundingClientRect().left : event.pageX;
126
+ pageY.value = forElem ? forElem.getBoundingClientRect().bottom : event.pageY;
127
+ offsetX.value = forElem ? (forElem.getBoundingClientRect().width + 2) : 0;
128
+ offsetY.value = forElem ? (forElem.getBoundingClientRect().height + 2) : 0; // offset in case we roll up
129
+ forElement.value = forElem;
130
+
131
+ if (forElem) container.value.style.minWidth = forElem.getBoundingClientRect().width + 'px';
132
+
133
+ position();
134
+ openEventTimeStamp.value = event.timeStamp;
135
+
136
+ event.preventDefault();
137
+ }
138
+ });
139
+
140
+ </script>
141
+
142
+ <template>
143
+ <teleport to="#app">
144
+ <div class="pankow-popover-backdrop" @click="onBackdrop($event)" @contextmenu="onBackdrop($event)" v-show="isOpen"></div>
145
+ <Transition :name="rollUp ? 'pankow-animation-pop-up' : 'pankow-animation-pop-down'">
146
+ <div v-show="isOpen" class="pankow-popover" ref="container" :style="{ 'max-width': width || null, 'max-height': height || null }">
147
+ <slot></slot>
148
+ </div>
149
+ </Transition>
150
+ </teleport>
151
+ </template>
152
+
153
+ <style>
154
+
155
+ .pankow-popover-backdrop {
156
+ position: fixed;
157
+ height: 100%;
158
+ width: 100%;
159
+ left: 0px;
160
+ top: 0px;
161
+ z-index: 3001;
162
+ background-color: transparent;
163
+ }
164
+
165
+ .pankow-popover {
166
+ position: fixed;
167
+ box-shadow: var(--pankow-menu-shadow);
168
+ border-radius: var(--pankow-border-radius);
169
+ background-color: rgba(255,255,255,0.5);
170
+ z-index: 3001;
171
+ color: black;
172
+ backdrop-filter: blur(10px);
173
+ overflow: auto;
174
+ outline: none;
175
+ max-width: min(100%, 800px);
176
+ }
177
+
178
+ @media (prefers-color-scheme: dark) {
179
+ .pankow-popover {
180
+ background-color: rgba(50,50,50,0.5);
181
+ color: white;
182
+ }
183
+ }
184
+
185
+ </style>