@inriver/inri 1.14.0 → 1.16.0

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 (120) hide show
  1. package/.changeset/README.md +8 -0
  2. package/.changeset/config.json +11 -0
  3. package/.eslintrc.js +5 -5
  4. package/azure/pipelines/single-build-release.yml +20 -0
  5. package/azure/pipelines/templates/deployFramework.yml +30 -0
  6. package/azure/pipelines/templates/publishFramwork.yml +39 -0
  7. package/componentProperties.d.ts +8 -8
  8. package/configs/base-eslint-config.js +36 -36
  9. package/configs/base-tailwind-config.js +149 -149
  10. package/i18n.ts +11 -11
  11. package/index.ts +29 -29
  12. package/package.json +7 -2
  13. package/src/components/CAutoBreadcrumbs.vue +72 -72
  14. package/src/components/CBtn.vue +98 -98
  15. package/src/components/CColorPicker.vue +55 -55
  16. package/src/components/CContextMenu.vue +29 -29
  17. package/src/components/CDatePicker.vue +51 -51
  18. package/src/components/CDeleteTileBtn.vue +52 -52
  19. package/src/components/CDialog.vue +253 -253
  20. package/src/components/CDialogCloseBtn.vue +9 -9
  21. package/src/components/CDirtyCheck.vue +68 -68
  22. package/src/components/CDrawer.vue +44 -44
  23. package/src/components/CDrawerButton.vue +91 -91
  24. package/src/components/CExpansionItem.vue +57 -57
  25. package/src/components/CExpansionItemWithCheckbox.vue +41 -41
  26. package/src/components/CFileDropzone.vue +114 -114
  27. package/src/components/CGrid.vue +101 -101
  28. package/src/components/CHeader.vue +10 -10
  29. package/src/components/CHeaderLinks.vue +61 -61
  30. package/src/components/CHeaderMenuLink.vue +51 -51
  31. package/src/components/CHeaderSearch.vue +142 -142
  32. package/src/components/CIconBtn.vue +70 -70
  33. package/src/components/CMenuItem.vue +66 -66
  34. package/src/components/CPageContainer.vue +11 -11
  35. package/src/components/CPageError.vue +24 -24
  36. package/src/components/CPageSpinner.vue +18 -18
  37. package/src/components/CSearch.vue +45 -45
  38. package/src/components/CSection.vue +29 -29
  39. package/src/components/CSectionTitle.vue +8 -8
  40. package/src/components/CSelect.vue +208 -208
  41. package/src/components/CSlideEffect.vue +62 -62
  42. package/src/components/CSpinner.vue +107 -107
  43. package/src/components/CTableActions.vue +16 -16
  44. package/src/components/CTileBtn.vue +178 -178
  45. package/src/components/CTimePicker.vue +42 -42
  46. package/src/components/CToggleGridView.vue +26 -26
  47. package/src/components/CToggleSidebarBtn.vue +19 -19
  48. package/src/components/CTooltip.vue +44 -44
  49. package/src/components/CTreeNode.vue +39 -39
  50. package/src/components/cards/CBannerCard.vue +103 -103
  51. package/src/components/cards/CSmallCard.vue +149 -149
  52. package/src/components/internal-components/ConfirmDialog.vue +78 -78
  53. package/src/composables/useDialog.ts +46 -46
  54. package/src/composables/useDirtyCheck.ts +25 -25
  55. package/src/composables/useDoubleClickListener.ts +28 -28
  56. package/src/composables/useSelectable.ts +62 -62
  57. package/src/composables/useTruncateChips.ts +46 -46
  58. package/src/i18n/de/index.ts +17 -17
  59. package/src/i18n/en/index.ts +84 -84
  60. package/src/i18n/es/index.ts +7 -7
  61. package/src/i18n/fr/index.ts +7 -7
  62. package/src/init/initInriFramework.ts +37 -37
  63. package/src/init/useInriApp.ts +55 -55
  64. package/src/layouts/CLayoutBlank.vue +7 -7
  65. package/src/layouts/CLayoutWithBackButton.vue +50 -50
  66. package/src/layouts/CLayoutWithSidebars.vue +46 -46
  67. package/src/layouts/layout-fragments/CPageAutoBreadcrumbs.vue +6 -6
  68. package/src/layouts/layout-fragments/CPageBackButton.vue +18 -18
  69. package/src/layouts/layout-fragments/CPageBlank.vue +20 -20
  70. package/src/layouts/layout-fragments/CPageFlexCol.vue +5 -5
  71. package/src/layouts/layout-fragments/CPageLeftSidebar.vue +5 -5
  72. package/src/layouts/layout-fragments/CPageMainContent.vue +5 -5
  73. package/src/layouts/layout-fragments/CPageOuterWrapper.vue +5 -5
  74. package/src/layouts/layout-fragments/CPageRightSidebar.vue +5 -5
  75. package/src/layouts/layout-fragments/CPageTitle.vue +34 -34
  76. package/src/plugins/dialog.ts +45 -45
  77. package/src/plugins/errors/ErrorFactories.ts +59 -59
  78. package/src/plugins/errors/Errors.ts +156 -156
  79. package/src/plugins/errors/Exception.ts +6 -6
  80. package/src/plugins/errors/globalErrorHandler.ts +21 -21
  81. package/src/plugins/errors/handleError.ts +34 -34
  82. package/src/plugins/errors/logger.ts +16 -16
  83. package/src/plugins/errors/vueErrorHandler.ts +26 -26
  84. package/src/plugins/globalAppConfig.ts +25 -25
  85. package/src/plugins/i18n.ts +46 -46
  86. package/src/plugins/inriIcons.ts +58 -58
  87. package/src/plugins/inriverQuasarComponentConfigurations.ts +67 -67
  88. package/src/plugins/library.ts +119 -119
  89. package/src/plugins/notify.ts +153 -153
  90. package/src/plugins/quasar-user-options.ts +19 -19
  91. package/src/plugins/validationRules.ts +292 -292
  92. package/src/shims-vue.d.ts +6 -6
  93. package/src/stores/inriStore.ts +54 -54
  94. package/src/stores/interfaceLanguageStore.ts +27 -27
  95. package/src/styles/_css-variables.scss +101 -101
  96. package/src/styles/_reset.scss +67 -67
  97. package/src/styles/_transitions.scss +9 -9
  98. package/src/styles/_utils.scss +15 -15
  99. package/src/styles/components/_breadcrumbs.scss +3 -3
  100. package/src/styles/components/_checkboxes.scss +17 -17
  101. package/src/styles/components/_chips.scss +17 -17
  102. package/src/styles/components/_forms.scss +203 -203
  103. package/src/styles/components/_menu.scss +12 -12
  104. package/src/styles/components/_notifications.scss +24 -24
  105. package/src/styles/components/_q-editor.scss +14 -14
  106. package/src/styles/components/_radio-buttons.scss +17 -17
  107. package/src/styles/components/_tables.scss +92 -92
  108. package/src/styles/components/_tooltips.scss +5 -5
  109. package/src/styles/components/_tree.scss +86 -86
  110. package/src/styles/components/_truncated-chips.scss +39 -39
  111. package/src/styles/main.scss +45 -45
  112. package/src/styles/quasar.variables.scss +50 -50
  113. package/src/styles/tailwind/index.css +2 -2
  114. package/src/typings.d.ts +70 -70
  115. package/src/utils/colorUtils.ts +73 -73
  116. package/src/utils/errorUtils.ts +37 -37
  117. package/src/utils/fileUtils.ts +57 -57
  118. package/src/utils/helpers.ts +6 -6
  119. package/src/utils/routeUtils.ts +23 -23
  120. package/tsconfig.json +34 -34
@@ -1,178 +1,178 @@
1
- <template>
2
- <q-btn
3
- ref="button"
4
- class="c-tile-btn group"
5
- :class="{
6
- 'c-tile-btn--small': small,
7
- 'pointer-events-none': loading
8
- }"
9
- unelevated
10
- :color="color"
11
- :to="to"
12
- :text-color="computedTextColor"
13
- :disable="disable"
14
- :aria-label="tooltip || tooltipLeft || tooltipRight"
15
- >
16
- <c-spinner
17
- v-if="loading"
18
- :parent-color="color"
19
- :size="small ? 24 : 60"
20
- absolute
21
- gap="0"
22
- class="-z-1"
23
- />
24
-
25
- <c-slide-effect
26
- v-if="!disable"
27
- :parent-color="color"
28
- :slide-effect-color="slideEffectColor"
29
- slide-right
30
- class="-z-1"
31
- />
32
-
33
- <!-- MDI or inri icon -->
34
- <template v-if="icon">
35
- <q-icon
36
- :name="icon"
37
- :style="`font-size: ${iconSize}px`"
38
- :class="iconClass"
39
- />
40
- </template>
41
-
42
- <!-- Else SVG icon (or any other content)-->
43
- <slot />
44
-
45
- <c-tooltip
46
- v-if="tooltip"
47
- :data-id="tooltipDataId"
48
- >
49
- {{ tooltip }}
50
- </c-tooltip>
51
-
52
- <c-tooltip
53
- v-if="tooltipLeft"
54
- :data-id="tooltipLeftDataId"
55
- left
56
- >
57
- {{ tooltipLeft }}
58
- </c-tooltip>
59
-
60
- <c-tooltip
61
- v-if="tooltipRight"
62
- :data-id="tooltipRightDataId"
63
- right
64
- >
65
- {{ tooltipRight }}
66
- </c-tooltip>
67
- </q-btn>
68
- </template>
69
-
70
- <script setup lang="ts">
71
- import { QBtn } from 'quasar'
72
- import { ref, computed } from 'vue'
73
- import { getRelatedColors } from '../utils/colorUtils'
74
-
75
- const props = defineProps({
76
- color: {
77
- type: String,
78
- default: 'secondary',
79
- },
80
- textColor: {
81
- type: String,
82
- default: undefined,
83
- },
84
- slideEffectColor: {
85
- type: String,
86
- default: undefined,
87
- },
88
- to: {
89
- type: [String, Object],
90
- default: undefined,
91
- },
92
- icon: {
93
- type: String,
94
- default: undefined,
95
- },
96
- iconSize: {
97
- type: [String, Number],
98
- default: undefined,
99
- },
100
- iconClass: {
101
- type: String,
102
- default: undefined,
103
- },
104
- tooltip: {
105
- type: String,
106
- default: undefined,
107
- },
108
- tooltipLeft: {
109
- type: String,
110
- default: undefined,
111
- },
112
- tooltipRight: {
113
- type: String,
114
- default: undefined,
115
- },
116
- small: Boolean,
117
- disable: Boolean,
118
- loading: Boolean,
119
- })
120
-
121
- const button = ref<InstanceType<typeof QBtn> | null>(null)
122
-
123
- const iconSize = computed(() => {
124
- return props.iconSize || (props.small ? 18 : 24)
125
- })
126
-
127
- const computedTextColor = computed(() => {
128
- if (props.textColor) {
129
- return props.textColor
130
- }
131
-
132
- const color = props.color
133
- const relatedColors = getRelatedColors(color)
134
-
135
- return relatedColors ? relatedColors.onColor : 'grey-darkest'
136
- })
137
-
138
- const buttonDataId = computed(() => {
139
- return button.value?.$el.dataset?.id
140
- })
141
-
142
- const tooltipDataId = computed(() => {
143
- return buttonDataId.value ? `${buttonDataId.value}-tooltip` : 'tile-btn-tooltip'
144
- })
145
-
146
- const tooltipLeftDataId = computed(() => {
147
- return buttonDataId.value ? `${buttonDataId.value}-tooltip-left` : 'tile-btn-tooltip-left'
148
- })
149
-
150
- const tooltipRightDataId = computed(() => {
151
- return buttonDataId.value ? `${buttonDataId.value}-tooltip-right` : 'tile-btn-tooltip-right'
152
- })
153
-
154
- </script>
155
-
156
- <style lang="scss">
157
- .c-tile-btn {
158
- padding: 0;
159
- width: 60px;
160
- height: 60px;
161
- border-radius: 0;
162
- vertical-align: top;
163
-
164
- .q-focus-helper {
165
- display: none;
166
- }
167
-
168
- &.disabled {
169
- opacity: 0.3 !important;
170
- }
171
- }
172
-
173
- .c-tile-btn--small {
174
- min-height: 24px;
175
- width: 24px;
176
- height: 24px;
177
- }
178
- </style>
1
+ <template>
2
+ <q-btn
3
+ ref="button"
4
+ class="c-tile-btn group"
5
+ :class="{
6
+ 'c-tile-btn--small': small,
7
+ 'pointer-events-none': loading
8
+ }"
9
+ unelevated
10
+ :color="color"
11
+ :to="to"
12
+ :text-color="computedTextColor"
13
+ :disable="disable"
14
+ :aria-label="tooltip || tooltipLeft || tooltipRight"
15
+ >
16
+ <c-spinner
17
+ v-if="loading"
18
+ :parent-color="color"
19
+ :size="small ? 24 : 60"
20
+ absolute
21
+ gap="0"
22
+ class="-z-1"
23
+ />
24
+
25
+ <c-slide-effect
26
+ v-if="!disable"
27
+ :parent-color="color"
28
+ :slide-effect-color="slideEffectColor"
29
+ slide-right
30
+ class="-z-1"
31
+ />
32
+
33
+ <!-- MDI or inri icon -->
34
+ <template v-if="icon">
35
+ <q-icon
36
+ :name="icon"
37
+ :style="`font-size: ${iconSize}px`"
38
+ :class="iconClass"
39
+ />
40
+ </template>
41
+
42
+ <!-- Else SVG icon (or any other content)-->
43
+ <slot />
44
+
45
+ <c-tooltip
46
+ v-if="tooltip"
47
+ :data-id="tooltipDataId"
48
+ >
49
+ {{ tooltip }}
50
+ </c-tooltip>
51
+
52
+ <c-tooltip
53
+ v-if="tooltipLeft"
54
+ :data-id="tooltipLeftDataId"
55
+ left
56
+ >
57
+ {{ tooltipLeft }}
58
+ </c-tooltip>
59
+
60
+ <c-tooltip
61
+ v-if="tooltipRight"
62
+ :data-id="tooltipRightDataId"
63
+ right
64
+ >
65
+ {{ tooltipRight }}
66
+ </c-tooltip>
67
+ </q-btn>
68
+ </template>
69
+
70
+ <script setup lang="ts">
71
+ import { QBtn } from 'quasar'
72
+ import { ref, computed } from 'vue'
73
+ import { getRelatedColors } from '../utils/colorUtils'
74
+
75
+ const props = defineProps({
76
+ color: {
77
+ type: String,
78
+ default: 'secondary',
79
+ },
80
+ textColor: {
81
+ type: String,
82
+ default: undefined,
83
+ },
84
+ slideEffectColor: {
85
+ type: String,
86
+ default: undefined,
87
+ },
88
+ to: {
89
+ type: [String, Object],
90
+ default: undefined,
91
+ },
92
+ icon: {
93
+ type: String,
94
+ default: undefined,
95
+ },
96
+ iconSize: {
97
+ type: [String, Number],
98
+ default: undefined,
99
+ },
100
+ iconClass: {
101
+ type: String,
102
+ default: undefined,
103
+ },
104
+ tooltip: {
105
+ type: String,
106
+ default: undefined,
107
+ },
108
+ tooltipLeft: {
109
+ type: String,
110
+ default: undefined,
111
+ },
112
+ tooltipRight: {
113
+ type: String,
114
+ default: undefined,
115
+ },
116
+ small: Boolean,
117
+ disable: Boolean,
118
+ loading: Boolean,
119
+ })
120
+
121
+ const button = ref<InstanceType<typeof QBtn> | null>(null)
122
+
123
+ const iconSize = computed(() => {
124
+ return props.iconSize || (props.small ? 18 : 24)
125
+ })
126
+
127
+ const computedTextColor = computed(() => {
128
+ if (props.textColor) {
129
+ return props.textColor
130
+ }
131
+
132
+ const color = props.color
133
+ const relatedColors = getRelatedColors(color)
134
+
135
+ return relatedColors ? relatedColors.onColor : 'grey-darkest'
136
+ })
137
+
138
+ const buttonDataId = computed(() => {
139
+ return button.value?.$el.dataset?.id
140
+ })
141
+
142
+ const tooltipDataId = computed(() => {
143
+ return buttonDataId.value ? `${buttonDataId.value}-tooltip` : 'tile-btn-tooltip'
144
+ })
145
+
146
+ const tooltipLeftDataId = computed(() => {
147
+ return buttonDataId.value ? `${buttonDataId.value}-tooltip-left` : 'tile-btn-tooltip-left'
148
+ })
149
+
150
+ const tooltipRightDataId = computed(() => {
151
+ return buttonDataId.value ? `${buttonDataId.value}-tooltip-right` : 'tile-btn-tooltip-right'
152
+ })
153
+
154
+ </script>
155
+
156
+ <style lang="scss">
157
+ .c-tile-btn {
158
+ padding: 0;
159
+ width: 60px;
160
+ height: 60px;
161
+ border-radius: 0;
162
+ vertical-align: top;
163
+
164
+ .q-focus-helper {
165
+ display: none;
166
+ }
167
+
168
+ &.disabled {
169
+ opacity: 0.3 !important;
170
+ }
171
+ }
172
+
173
+ .c-tile-btn--small {
174
+ min-height: 24px;
175
+ width: 24px;
176
+ height: 24px;
177
+ }
178
+ </style>
@@ -1,42 +1,42 @@
1
- <template>
2
- <q-input
3
- v-bind="dense ? $inri.inputDense : $inri.input"
4
- :model-value="modelValue"
5
- :label="label"
6
- mask="time"
7
- :rules="modelValue ? ['time'] : undefined"
8
- @update:model-value="$emit('update:model-value', $event)"
9
- >
10
- <template #append>
11
- <q-icon
12
- name="mdi-clock-outline"
13
- class="cursor-pointer"
14
- color="grey-dark"
15
- size="19px"
16
- />
17
- </template>
18
- <q-menu>
19
- <q-time
20
- :model-value="modelValue"
21
- format24h
22
- @update:model-value="$emit('update:model-value', $event)"
23
- />
24
- </q-menu>
25
- </q-input>
26
- </template>
27
-
28
- <script setup lang="ts">
29
- defineEmits(['update:model-value'])
30
-
31
- defineProps({
32
- modelValue: {
33
- type: String,
34
- default: undefined,
35
- },
36
- label: {
37
- type: String,
38
- default: undefined,
39
- },
40
- dense: Boolean,
41
- })
42
- </script>
1
+ <template>
2
+ <q-input
3
+ v-bind="dense ? $inri.inputDense : $inri.input"
4
+ :model-value="modelValue"
5
+ :label="label"
6
+ mask="time"
7
+ :rules="modelValue ? ['time'] : undefined"
8
+ @update:model-value="$emit('update:model-value', $event)"
9
+ >
10
+ <template #append>
11
+ <q-icon
12
+ name="mdi-clock-outline"
13
+ class="cursor-pointer"
14
+ color="grey-dark"
15
+ size="19px"
16
+ />
17
+ </template>
18
+ <q-menu>
19
+ <q-time
20
+ :model-value="modelValue"
21
+ format24h
22
+ @update:model-value="$emit('update:model-value', $event)"
23
+ />
24
+ </q-menu>
25
+ </q-input>
26
+ </template>
27
+
28
+ <script setup lang="ts">
29
+ defineEmits(['update:model-value'])
30
+
31
+ defineProps({
32
+ modelValue: {
33
+ type: String,
34
+ default: undefined,
35
+ },
36
+ label: {
37
+ type: String,
38
+ default: undefined,
39
+ },
40
+ dense: Boolean,
41
+ })
42
+ </script>
@@ -1,26 +1,26 @@
1
- <template>
2
- <c-tile-btn
3
- data-id="toggle-grid-view-btn"
4
- :icon="icon"
5
- :tooltip-left="modelValue ? $t('inri.toggle_actions.table_view') : $t('inri.toggle_actions.grid_view')"
6
- @click="toggleGridView"
7
- />
8
- </template>
9
-
10
- <script setup lang="ts">
11
- import { computed } from 'vue'
12
-
13
- const props = defineProps({
14
- modelValue: Boolean,
15
- })
16
-
17
- const emit = defineEmits(['update:modelValue'])
18
-
19
- const icon = computed(() => {
20
- return props.modelValue ? 'inri-table-view' : 'inri-grid-view'
21
- })
22
-
23
- function toggleGridView () {
24
- emit('update:modelValue', !props.modelValue)
25
- }
26
- </script>
1
+ <template>
2
+ <c-tile-btn
3
+ data-id="toggle-grid-view-btn"
4
+ :icon="icon"
5
+ :tooltip-left="modelValue ? $t('inri.toggle_actions.table_view') : $t('inri.toggle_actions.grid_view')"
6
+ @click="toggleGridView"
7
+ />
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { computed } from 'vue'
12
+
13
+ const props = defineProps({
14
+ modelValue: Boolean,
15
+ })
16
+
17
+ const emit = defineEmits(['update:modelValue'])
18
+
19
+ const icon = computed(() => {
20
+ return props.modelValue ? 'inri-table-view' : 'inri-grid-view'
21
+ })
22
+
23
+ function toggleGridView () {
24
+ emit('update:modelValue', !props.modelValue)
25
+ }
26
+ </script>
@@ -1,19 +1,19 @@
1
- <template>
2
- <c-tile-btn
3
- data-id="toggle-sidebar-btn"
4
- :icon="icon"
5
- :tooltip-left="$t('inri.toggle_actions.sidebar')"
6
- @click="inriStore.toggleSidebar"
7
- />
8
- </template>
9
-
10
- <script setup lang="ts">
11
- import { computed } from 'vue'
12
- import { useInriStore } from '../stores/inriStore'
13
-
14
- const inriStore = useInriStore()
15
-
16
- const icon = computed(() => {
17
- return inriStore.state.showSidebar ? 'mdi-fullscreen' : 'mdi-fullscreen-exit'
18
- })
19
- </script>
1
+ <template>
2
+ <c-tile-btn
3
+ data-id="toggle-sidebar-btn"
4
+ :icon="icon"
5
+ :tooltip-left="$t('inri.toggle_actions.sidebar')"
6
+ @click="inriStore.toggleSidebar"
7
+ />
8
+ </template>
9
+
10
+ <script setup lang="ts">
11
+ import { computed } from 'vue'
12
+ import { useInriStore } from '../stores/inriStore'
13
+
14
+ const inriStore = useInriStore()
15
+
16
+ const icon = computed(() => {
17
+ return inriStore.state.showSidebar ? 'mdi-fullscreen' : 'mdi-fullscreen-exit'
18
+ })
19
+ </script>
@@ -1,44 +1,44 @@
1
- <template>
2
- <q-tooltip
3
- v-bind="position"
4
- >
5
- <slot />
6
- </q-tooltip>
7
- </template>
8
-
9
- <script setup lang="ts">
10
- import { computed } from 'vue'
11
-
12
- const props = defineProps({
13
- left: Boolean,
14
- right: Boolean,
15
- top: Boolean,
16
- bottom: Boolean,
17
- })
18
-
19
- const position = computed(() => {
20
- if (props.left) {
21
- return {
22
- anchor: 'center left' as const,
23
- self: 'center right' as const,
24
- }
25
- } else if (props.right) {
26
- return {
27
- anchor: 'center right' as const,
28
- self: 'center left' as const,
29
- }
30
- } else if (props.top) {
31
- return {
32
- anchor: 'top middle' as const,
33
- self: 'bottom middle' as const,
34
- }
35
- } else if (props.bottom) {
36
- return {
37
- anchor: 'bottom middle' as const,
38
- self: 'top middle' as const,
39
- }
40
- }
41
-
42
- return null
43
- })
44
- </script>
1
+ <template>
2
+ <q-tooltip
3
+ v-bind="position"
4
+ >
5
+ <slot />
6
+ </q-tooltip>
7
+ </template>
8
+
9
+ <script setup lang="ts">
10
+ import { computed } from 'vue'
11
+
12
+ const props = defineProps({
13
+ left: Boolean,
14
+ right: Boolean,
15
+ top: Boolean,
16
+ bottom: Boolean,
17
+ })
18
+
19
+ const position = computed(() => {
20
+ if (props.left) {
21
+ return {
22
+ anchor: 'center left' as const,
23
+ self: 'center right' as const,
24
+ }
25
+ } else if (props.right) {
26
+ return {
27
+ anchor: 'center right' as const,
28
+ self: 'center left' as const,
29
+ }
30
+ } else if (props.top) {
31
+ return {
32
+ anchor: 'top middle' as const,
33
+ self: 'bottom middle' as const,
34
+ }
35
+ } else if (props.bottom) {
36
+ return {
37
+ anchor: 'bottom middle' as const,
38
+ self: 'top middle' as const,
39
+ }
40
+ }
41
+
42
+ return null
43
+ })
44
+ </script>