@mptw/skylens-ui 1.0.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 (143) hide show
  1. package/.editorconfig +12 -0
  2. package/.eslintrc.cjs +4 -0
  3. package/.nuxtrc +1 -0
  4. package/.playground/app.config.ts +5 -0
  5. package/.playground/nuxt.config.ts +3 -0
  6. package/README.md +73 -0
  7. package/app.config.ts +5 -0
  8. package/app.vue +3 -0
  9. package/assets/css/fonts/MEShPlus-font-icon.eot +0 -0
  10. package/assets/css/fonts/MEShPlus-font-icon.svg +186 -0
  11. package/assets/css/fonts/MEShPlus-font-icon.ttf +0 -0
  12. package/assets/css/fonts/MEShPlus-font-icon.woff +0 -0
  13. package/assets/css/icons.css +555 -0
  14. package/assets/css/layout.styl +98 -0
  15. package/assets/css/tailwind.css +3 -0
  16. package/assets/images/commons/app-logo-white.png +0 -0
  17. package/assets/images/commons/app-logo.png +0 -0
  18. package/assets/images/commons/empty-box.png +0 -0
  19. package/assets/images/commons/watermark.svg +15 -0
  20. package/components/SLAlertModal.vue +99 -0
  21. package/components/SLBreadcrumbs.vue +42 -0
  22. package/components/SLButton.vue +301 -0
  23. package/components/SLCalendarButton.vue +259 -0
  24. package/components/SLCalendarPopup.vue +832 -0
  25. package/components/SLCard.vue +118 -0
  26. package/components/SLChart.vue +181 -0
  27. package/components/SLCheckbox.vue +179 -0
  28. package/components/SLCircleButton.vue +95 -0
  29. package/components/SLCollapsableBlock.vue +116 -0
  30. package/components/SLCollapsableMulitPillSelect.vue +92 -0
  31. package/components/SLConfirmModal.vue +122 -0
  32. package/components/SLDateInput.vue +460 -0
  33. package/components/SLDatePicker.vue +143 -0
  34. package/components/SLDistributor.vue +30 -0
  35. package/components/SLDownloadButton.vue +410 -0
  36. package/components/SLDraggable.vue +151 -0
  37. package/components/SLDropdown.vue +277 -0
  38. package/components/SLDropdownItem.vue +31 -0
  39. package/components/SLElementWithTitle.vue +181 -0
  40. package/components/SLEyeCheckbox.vue +162 -0
  41. package/components/SLFileInput.vue +101 -0
  42. package/components/SLGenderAgeSelect.vue +279 -0
  43. package/components/SLHeatMap.vue +151 -0
  44. package/components/SLHourRangeInput.vue +211 -0
  45. package/components/SLIOSSwitch.vue +235 -0
  46. package/components/SLIcon.vue +35 -0
  47. package/components/SLIconButton.vue +130 -0
  48. package/components/SLInfoTip.vue +221 -0
  49. package/components/SLInsightSitePage.vue +27 -0
  50. package/components/SLLegend.vue +76 -0
  51. package/components/SLLinearProgressBar.vue +35 -0
  52. package/components/SLLink.vue +162 -0
  53. package/components/SLLoading.vue +127 -0
  54. package/components/SLMapChart.vue +151 -0
  55. package/components/SLModal.vue +377 -0
  56. package/components/SLMonthCalendarButton.vue +378 -0
  57. package/components/SLMonthPicker.vue +151 -0
  58. package/components/SLMultiSelect.vue +121 -0
  59. package/components/SLNoData.vue +43 -0
  60. package/components/SLPageModal.vue +64 -0
  61. package/components/SLPagination.vue +192 -0
  62. package/components/SLPillCheckbox.vue +207 -0
  63. package/components/SLPillLabel.vue +101 -0
  64. package/components/SLPopupMenuButton.vue +158 -0
  65. package/components/SLProfileButton.vue +186 -0
  66. package/components/SLRadioButton.vue +97 -0
  67. package/components/SLRadioButtonGroup.vue +113 -0
  68. package/components/SLRadioGroup.vue +201 -0
  69. package/components/SLRangeInput.vue +209 -0
  70. package/components/SLRegionsMapChart.vue +151 -0
  71. package/components/SLRightSider.vue +242 -0
  72. package/components/SLSearchInput.vue +87 -0
  73. package/components/SLSelect.vue +545 -0
  74. package/components/SLSelectAllToggle.vue +118 -0
  75. package/components/SLSiderbarNav.vue +394 -0
  76. package/components/SLSiderbarNavItem.vue +204 -0
  77. package/components/SLSitePage.vue +96 -0
  78. package/components/SLSkyInsightSitePage.vue +27 -0
  79. package/components/SLSortByDropdown.vue +189 -0
  80. package/components/SLSpinIcon.vue +24 -0
  81. package/components/SLStayRangeInput.vue +306 -0
  82. package/components/SLTab.vue +159 -0
  83. package/components/SLTable.vue +409 -0
  84. package/components/SLTableTooltip.vue +111 -0
  85. package/components/SLTabs.vue +388 -0
  86. package/components/SLTextInput.vue +362 -0
  87. package/components/SLTextarea.vue +134 -0
  88. package/components/SLToast.vue +71 -0
  89. package/components/SLToggleButton.vue +78 -0
  90. package/components/SLTwoLayerMultiSelect.vue +428 -0
  91. package/components/SLTwoLayerSelect.vue +265 -0
  92. package/components/SLWarnModal.vue +75 -0
  93. package/interface/IDateRange.ts +5 -0
  94. package/interface/IHourRange.ts +4 -0
  95. package/interface/IInputOption.ts +6 -0
  96. package/interface/ILegendItem.ts +7 -0
  97. package/interface/IPopupMenuButton.ts +5 -0
  98. package/interface/IStayRange.ts +15 -0
  99. package/interface/ITableField.ts +19 -0
  100. package/interface/IWeb.ts +5 -0
  101. package/interface/NavConfigType.ts +14 -0
  102. package/interface/commons.ts +7 -0
  103. package/models/DateRange.ts +21 -0
  104. package/nuxt.config.ts +22 -0
  105. package/package.json +52 -0
  106. package/plugins/googlemaps.ts +17 -0
  107. package/plugins/v-calendar.ts +5 -0
  108. package/public/assets/files/taiwan-regions.json +7 -0
  109. package/public/assets/files/taiwan.json +24 -0
  110. package/public/assets/images/commons/default-favico.png +0 -0
  111. package/public/assets/images/dashboard/closure-button-icon.png +0 -0
  112. package/public/assets/images/dashboard/dashboard-widget-bar-chart-icon-disabled.png +0 -0
  113. package/public/assets/images/dashboard/dashboard-widget-bar-chart-icon-poi.png +0 -0
  114. package/public/assets/images/dashboard/dashboard-widget-bar-chart-icon-web.png +0 -0
  115. package/public/assets/images/dashboard/dashboard-widget-country-heatmap-chart-icon-disabled.png +0 -0
  116. package/public/assets/images/dashboard/dashboard-widget-country-heatmap-chart-icon.png +0 -0
  117. package/public/assets/images/dashboard/dashboard-widget-geo-list-icon-disabled.png +0 -0
  118. package/public/assets/images/dashboard/dashboard-widget-geo-list-icon-poi.png +0 -0
  119. package/public/assets/images/dashboard/dashboard-widget-geo-list-icon-web.png +0 -0
  120. package/public/assets/images/dashboard/dashboard-widget-heatmap-chart-icon-disabled.png +0 -0
  121. package/public/assets/images/dashboard/dashboard-widget-heatmap-chart-icon-web.png +0 -0
  122. package/public/assets/images/dashboard/dashboard-widget-line-chart-icon-disabled.png +0 -0
  123. package/public/assets/images/dashboard/dashboard-widget-line-chart-icon-poi.png +0 -0
  124. package/public/assets/images/dashboard/dashboard-widget-list-icon-disabled.png +0 -0
  125. package/public/assets/images/dashboard/dashboard-widget-list-icon-poi.png +0 -0
  126. package/public/assets/images/dashboard/dashboard-widget-list-icon-web.png +0 -0
  127. package/public/assets/images/dashboard/dashboard-widget-pie-chart-icon-disabled.png +0 -0
  128. package/public/assets/images/dashboard/dashboard-widget-pie-chart-icon-poi.png +0 -0
  129. package/public/assets/images/dashboard/dashboard-widget-pie-chart-icon-web.png +0 -0
  130. package/public/assets/images/dashboard/drag-hint-icon.svg +3 -0
  131. package/public/assets/images/dashboard/extend-button-icon.png +0 -0
  132. package/public/assets/images/dashboard/map-list-chart-icon.svg +17 -0
  133. package/public/assets/images/dashboard/map-marker-chart-icon.svg +32 -0
  134. package/public/assets/images/dashboard/pie-chart-icon.svg +4 -0
  135. package/public/assets/images/dashboard/poi-gender-age-chart-icon.svg +20 -0
  136. package/public/assets/images/dashboard/poi-nav-icon.png +0 -0
  137. package/public/assets/images/dashboard/web-gender-age-chart-icon.svg +20 -0
  138. package/public/assets/images/dashboard/web-nav-icon.png +0 -0
  139. package/tailwind.config.js +300 -0
  140. package/tsconfig.json +8 -0
  141. package/utils/constants.ts +160 -0
  142. package/utils/generateSheet.ts +36 -0
  143. package/utils/index.ts +539 -0
@@ -0,0 +1,221 @@
1
+ <template lang="pug">
2
+ .info-tip(:class='{ "info-tip--xs": size === "xs" }')
3
+ a(
4
+ href="javascript:;"
5
+ @mouseenter="onInfoMouseEntered"
6
+ @mouseleave="onInfoMouseLeaved"
7
+ ref="toggleEl"
8
+ ).info-tip-toggle
9
+ slot(name='toggle')
10
+ SLIcon(icon="info-circle")
11
+ .info(
12
+ :class="{ pinned: isPinned }"
13
+ ref="popupEl"
14
+ )
15
+ .info-content(
16
+ :style="{ 'max-width': `${maxWidth}px`, 'min-width': `${minWidth}px` }"
17
+ )
18
+ slot {{ info }}
19
+ a.pin-toggle(
20
+ v-if='pinnable'
21
+ href="javascript:;"
22
+ @click="togglePin"
23
+ )
24
+ SLIcon(icon="pin-line")
25
+ </template>
26
+
27
+ <script lang="ts">
28
+ import { fromEvent, interval } from "rxjs";
29
+ import { debounce, startWith } from "rxjs/operators";
30
+ import { createPopper } from "@popperjs/core";
31
+ import type { Placement } from "@popperjs/core";
32
+
33
+ export default defineComponent({
34
+ name: "SLInfoTip",
35
+ components: {},
36
+ props: {
37
+ info: {
38
+ type: String,
39
+ default: "",
40
+ },
41
+ placement: {
42
+ type: String as PropType<Placement>,
43
+ default: "right-start",
44
+ },
45
+ size: {
46
+ type: String as PropType<"" | "xs">,
47
+ default: "",
48
+ },
49
+ pinnable: {
50
+ type: Boolean,
51
+ default: true,
52
+ },
53
+ minWidth: {
54
+ type: Number,
55
+ default: 0,
56
+ },
57
+ },
58
+ emits: ["infoTip:mouseenter"],
59
+ setup(props, { slots, emit }) {
60
+ const { info, placement } = toRefs(props);
61
+
62
+ const toggleEl = shallowRef<HTMLLIElement | null>(null);
63
+ const popupEl = shallowRef<HTMLLIElement | null>(null);
64
+ const maxWidth = ref(480);
65
+ const isPinned = ref(false);
66
+
67
+ let popperInstance: any = null;
68
+ let resizeSubscriber: any = null;
69
+
70
+ function subscribeResize() {
71
+ resizeSubscriber = fromEvent(window, "resize")
72
+ .pipe(debounce(() => interval(500)))
73
+ .pipe(startWith(0))
74
+ .subscribe(() => {
75
+ const toggleRect = toggleEl.value?.getBoundingClientRect() || null;
76
+ const windowWidth = window.innerWidth;
77
+
78
+ if (!toggleRect) return;
79
+
80
+ maxWidth.value = Math.min(
81
+ 365,
82
+ windowWidth - toggleRect.left - toggleRect.width - 11
83
+ );
84
+ });
85
+ }
86
+
87
+ function unsubscribeResize() {
88
+ if (resizeSubscriber) {
89
+ resizeSubscriber.unsubscribe();
90
+ }
91
+ resizeSubscriber = null;
92
+ }
93
+
94
+ function show() {
95
+ if (!popupEl.value) return;
96
+
97
+ popupEl.value.setAttribute("data-show", "");
98
+
99
+ popperInstance.setOptions({
100
+ modifiers: [
101
+ {
102
+ name: "offset",
103
+ options: {
104
+ offset: [0, 0],
105
+ },
106
+ },
107
+ { name: "eventListeners", enabled: true },
108
+ ],
109
+ });
110
+
111
+ popperInstance.update();
112
+ }
113
+
114
+ function hide() {
115
+ if (!popupEl.value || popupEl.value.getAttribute("data-show") === null)
116
+ return;
117
+ popupEl.value.removeAttribute("data-show");
118
+ popperInstance.setOptions({
119
+ modifiers: [{ name: "eventListeners", enabled: false }],
120
+ });
121
+ }
122
+
123
+ function onInfoMouseEntered() {
124
+ emit("infoTip:mouseenter");
125
+ show();
126
+ }
127
+
128
+ function onInfoMouseLeaved() {
129
+ hide();
130
+ }
131
+
132
+ function togglePin() {
133
+ isPinned.value = !isPinned.value;
134
+ }
135
+
136
+ onMounted(() => {
137
+ if (info.value === "" && !slots.default) {
138
+ return;
139
+ }
140
+
141
+ if (toggleEl.value && popupEl.value) {
142
+ popperInstance = createPopper(toggleEl.value, popupEl.value, {
143
+ placement: placement.value,
144
+ modifiers: [{ name: "eventListeners", enabled: false }],
145
+ });
146
+ }
147
+
148
+ subscribeResize();
149
+ });
150
+
151
+ onBeforeUnmount(() => {
152
+ if (popperInstance) {
153
+ popperInstance.destroy();
154
+ }
155
+ popperInstance = null;
156
+
157
+ unsubscribeResize();
158
+ });
159
+
160
+ return {
161
+ toggleEl,
162
+ popupEl,
163
+ maxWidth,
164
+ isPinned,
165
+ onInfoMouseEntered,
166
+ onInfoMouseLeaved,
167
+ togglePin,
168
+ };
169
+ },
170
+ });
171
+ </script>
172
+
173
+ <style lang="stylus">
174
+ .info-tip
175
+ @apply inline-flex ml-2 text-base s('leading-4.5') text-primary s('hover:text-primary-hover') cursor-default
176
+
177
+ &--xs
178
+ @apply ml-1 text-xs
179
+
180
+ .info-tip-toggle
181
+ @apply inline-flex cursor-default
182
+
183
+ .info
184
+ @apply z-10 absolute invisible
185
+
186
+ &[data-popper-placement^='bottom']
187
+ @apply pt-1
188
+
189
+ &[data-popper-placement^='right']
190
+ @apply pl-3
191
+
192
+ &[data-show], &.pinned
193
+ @apply visible
194
+
195
+ &.pinned
196
+ .info-content
197
+ .pin-toggle
198
+ &:hover
199
+ .icon
200
+ &:before
201
+ content '\e943'
202
+
203
+ .icon
204
+ &:before
205
+ content '\e943'
206
+
207
+ &-content
208
+ @apply relative flex py-5 pl-4 pr-6 border s('border-border/50') rounded-lg bg-white text-sm text-gray-2 font-normal shadow-popup-sm
209
+
210
+ .pin-toggle
211
+ @apply absolute top-0 right-0 mt-2 mr-2 text-base text-primary-hover
212
+
213
+ &:hover
214
+ .icon
215
+ &:before
216
+ content '\e967'
217
+
218
+ .icon
219
+ &:before
220
+ content '\e980'
221
+ </style>
@@ -0,0 +1,27 @@
1
+ <template lang="pug">
2
+ .insight-site-page
3
+ .insight-site-page-header(v-if="$slots['site-page-header']")
4
+ slot(name="site-page-header")
5
+ .insight-site-page-content
6
+ slot
7
+ </template>
8
+
9
+ <script>
10
+ export default {
11
+ name: "SLInsightSitePage",
12
+ props: {},
13
+ computed: {},
14
+ methods: {},
15
+ };
16
+ </script>
17
+
18
+ <style lang="stylus">
19
+ .insight-site-page
20
+ @apply flex-grow flex flex-col
21
+
22
+ &-header
23
+ @apply s('-mx-4')
24
+
25
+ &-content
26
+ @apply flex flex-col flex-grow w-full p-4 rounded bg-bg-middle
27
+ </style>
@@ -0,0 +1,76 @@
1
+ <template lang="pug">
2
+ .chart-legend
3
+ a.chart-legend-item(
4
+ v-for='(item, index) in items',
5
+ href='javascript:;',
6
+ :class='{ unactive: !item.active }',
7
+ :key='item.key',
8
+ @click='onClickedItem(item, index, $event)',
9
+ @mouseenter='onMouseEnterItem(item, index, $event)',
10
+ @mouseleave='onMouseLeaveItem(item, index, $event)'
11
+ )
12
+ SLIcon.item-icon(
13
+ v-if='item.icon',
14
+ :icon='item.icon',
15
+ :style='{ color: item.active ? item.color : "#E0E0E0" }',
16
+ )
17
+ span.item-marker(
18
+ v-else
19
+ :style='{ background: item.active ? item.color : "#E0E0E0" }'
20
+ )
21
+ span {{ item.name }}
22
+ slot(name="custom-item")
23
+ </template>
24
+
25
+ <script lang="ts">
26
+ import type { PropType } from "vue";
27
+ import type ILegendItem from "~ui/interface/ILegendItem";
28
+
29
+ export default defineComponent({
30
+ name: "SLLegend",
31
+ components: {},
32
+ props: {
33
+ items: {
34
+ type: Array as PropType<Array<ILegendItem>>,
35
+ default: () => [],
36
+ },
37
+ },
38
+ emits: ["toggle", "enter", "leave"],
39
+ setup(_, { emit }) {
40
+ function onClickedItem(item: ILegendItem, index: number, e: Event) {
41
+ emit("toggle", item, index, e);
42
+ }
43
+
44
+ function onMouseEnterItem(item: ILegendItem, index: number, e: Event) {
45
+ emit("enter", item, index, e);
46
+ }
47
+
48
+ function onMouseLeaveItem(item: ILegendItem, index: number, e: Event) {
49
+ emit("leave", item, index, e);
50
+ }
51
+
52
+ return {
53
+ onClickedItem,
54
+ onMouseEnterItem,
55
+ onMouseLeaveItem,
56
+ };
57
+ },
58
+ });
59
+ </script>
60
+
61
+ <style lang="stylus">
62
+ .chart-legend
63
+ @apply text-center
64
+
65
+ &-item
66
+ @apply inline-flex items-center mx-2 text-base text-gray-2
67
+
68
+ &.unactive
69
+ @apply text-cancel
70
+
71
+ .item-icon
72
+ @apply mr-1
73
+
74
+ .item-marker
75
+ @apply inline-block w-3 h-3 mr-1 rounded-full
76
+ </style>
@@ -0,0 +1,35 @@
1
+ <template lang="pug">
2
+ .linear-progress-bar
3
+ .linear-progress-bar-value
4
+ </template>
5
+
6
+ <script lang="ts">
7
+ export default defineComponent({
8
+ name: "SLLinearProgressBar",
9
+ });
10
+ </script>
11
+
12
+ <style lang="stylus" scoped>
13
+ .linear-progress-bar
14
+ @apply w-full h-2 overflow-hidden
15
+ background-color #D3D3D3
16
+ border-radius 1px
17
+
18
+ &-value
19
+ @apply w-full h-full
20
+ background linear-gradient(270deg, rgba(211, 211, 211, 0.62) 0.08%, #E1E1E1 46.69%, rgba(211, 211, 211, 0.62) 100%)
21
+ animation indeterminateAnimation 1s infinite linear
22
+ transform-origin 0% 50%
23
+
24
+ @keyframes indeterminateAnimation {
25
+ 0% {
26
+ transform translateX(0) scaleX(0)
27
+ }
28
+ 40% {
29
+ transform translateX(0) scaleX(0.4)
30
+ }
31
+ 100% {
32
+ transform translateX(100%) scaleX(0.5)
33
+ }
34
+ }
35
+ </style>
@@ -0,0 +1,162 @@
1
+ <template lang="pug">
2
+ a.sl-link(
3
+ href="javascript:;"
4
+ @mouseenter="onMouseEnter"
5
+ @mouseleave="onMouseLeave"
6
+ @mousemove="onMouseMove"
7
+ )
8
+ slot
9
+ .sl-link-tooltip(
10
+ v-if="title !== ''"
11
+ ref="tooltipEl"
12
+ ) {{ title }}
13
+ </template>
14
+
15
+ <script lang="ts">
16
+ import type { PropType } from "vue";
17
+ import { createPopper } from "@popperjs/core";
18
+ import type {
19
+ VirtualElement,
20
+ Placement,
21
+ Instance as PopperInstance,
22
+ } from "@popperjs/core";
23
+
24
+ export default defineComponent({
25
+ name: "SLLink",
26
+ components: {},
27
+ props: {
28
+ title: {
29
+ type: String,
30
+ default: "",
31
+ },
32
+ placement: {
33
+ type: String as PropType<Placement>,
34
+ default: "bottom",
35
+ },
36
+ },
37
+ setup(props) {
38
+ const { placement } = toRefs(props);
39
+
40
+ const tooltipEl = shallowRef<HTMLElement | null>(null);
41
+ const mouseX = ref(0);
42
+ const mouseY = ref(0);
43
+
44
+ let popperInstance: PopperInstance | null = null;
45
+
46
+ const virtualElement = computed(() => {
47
+ return {
48
+ getBoundingClientRect: () => {
49
+ return {
50
+ width: 0,
51
+ height: 0,
52
+ top: mouseY.value,
53
+ right: mouseX.value,
54
+ bottom: mouseY.value,
55
+ left: mouseX.value,
56
+ };
57
+ },
58
+ } as VirtualElement;
59
+ });
60
+
61
+ function createTipPopper() {
62
+ destroyPopper();
63
+ if (!virtualElement.value || !tooltipEl.value) return;
64
+
65
+ popperInstance = createPopper(virtualElement.value, tooltipEl.value, {
66
+ placement: placement.value,
67
+ strategy: "fixed",
68
+ modifiers: [{ name: "eventListeners", enabled: false }],
69
+ });
70
+ }
71
+
72
+ function destroyPopper() {
73
+ if (popperInstance) {
74
+ popperInstance.destroy();
75
+ }
76
+ popperInstance = null;
77
+ }
78
+
79
+ function show() {
80
+ if (!tooltipEl.value || !popperInstance) return;
81
+ tooltipEl.value.setAttribute("data-show", "");
82
+ popperInstance.setOptions({
83
+ modifiers: [
84
+ {
85
+ name: "offset",
86
+ options: {
87
+ offset: placement.value === "bottom" ? [0, 18] : [0, 0],
88
+ },
89
+ },
90
+ // {
91
+ // name: 'flip',
92
+ // options: {
93
+ // boundary: boundaryTarget,
94
+ // },
95
+ // },
96
+ { name: "eventListeners", enabled: true },
97
+ ],
98
+ });
99
+ popperInstance.update();
100
+ }
101
+
102
+ function hide() {
103
+ if (
104
+ !tooltipEl.value ||
105
+ tooltipEl.value.getAttribute("data-show") === null ||
106
+ !popperInstance
107
+ )
108
+ return;
109
+ tooltipEl.value.removeAttribute("data-show");
110
+ popperInstance.setOptions({
111
+ modifiers: [{ name: "eventListeners", enabled: false }],
112
+ });
113
+ }
114
+
115
+ function onMouseEnter(e: MouseEvent) {
116
+ show();
117
+ }
118
+
119
+ function onMouseLeave(e: MouseEvent) {
120
+ hide();
121
+ }
122
+
123
+ function onMouseMove(e: MouseEvent) {
124
+ mouseX.value = e.clientX;
125
+ mouseY.value = e.clientY;
126
+
127
+ nextTick(() => {
128
+ if (popperInstance) {
129
+ popperInstance.update();
130
+ }
131
+ });
132
+ }
133
+
134
+ onMounted(() => {
135
+ createTipPopper();
136
+ });
137
+
138
+ onBeforeUnmount(() => {
139
+ destroyPopper();
140
+ });
141
+
142
+ return {
143
+ tooltipEl,
144
+ onMouseEnter,
145
+ onMouseLeave,
146
+ onMouseMove,
147
+ };
148
+ },
149
+ });
150
+ </script>
151
+
152
+ <style lang="stylus">
153
+ .sl-link
154
+ *
155
+ @apply pointer-events-none
156
+
157
+ &-tooltip
158
+ @apply p-1 bg-gray-2 text-xs text-white whitespace-nowrap shadow invisible
159
+
160
+ &[data-show]
161
+ @apply visible
162
+ </style>
@@ -0,0 +1,127 @@
1
+ <template lang="pug">
2
+ .loading-modal(:style="{ position: 'absolute', top: containerCoords.top, right: containerCoords.right, bottom: containerCoords.bottom, left: containerCoords.left }" :class="modalClass" ref='el')
3
+ div(:style="{ position: 'absolute', top: '50%', left: '50%', transform: 'translate(-50%, -50%)' }")
4
+ .loading
5
+ div
6
+ div
7
+ div
8
+ div
9
+ </template>
10
+
11
+ <script lang="ts">
12
+ import type { PropType } from "vue";
13
+
14
+ export default defineComponent({
15
+ name: "SLLoading",
16
+ components: {},
17
+ props: {
18
+ boundaries: {
19
+ type: Array as PropType<
20
+ Array<{
21
+ sides: ("top" | "right" | "bottom" | "left")[];
22
+ query: string;
23
+ }>
24
+ >,
25
+ default: () => [],
26
+ },
27
+ color: {
28
+ type: String,
29
+ default: "primary",
30
+ validator: (value: string) => ["primary", "secondary"].includes(value),
31
+ },
32
+ bgAlpha: {
33
+ type: Number,
34
+ default: 0.2,
35
+ },
36
+ },
37
+ setup(props) {
38
+ const { boundaries, color } = toRefs(props);
39
+
40
+ const el = shallowRef<HTMLElement | null>(null);
41
+ const containerCoords = reactive({
42
+ top: "0px",
43
+ bottom: "0px",
44
+ left: "0px",
45
+ right: "0px",
46
+ });
47
+ const parentCoords = ref<DOMRect | null>(null);
48
+
49
+ const modalClass = computed(() => `loading-modal-${color.value}`);
50
+
51
+ function setOffsets() {
52
+ const parent = el.value?.parentElement || null;
53
+ if (!parent) return;
54
+
55
+ parentCoords.value = parent.getBoundingClientRect();
56
+ boundaries.value.forEach(({ sides, query }) => {
57
+ const element = parent.querySelector(query);
58
+ if (!element || !sides) {
59
+ return;
60
+ }
61
+ const coords = element.getBoundingClientRect();
62
+ sides.forEach((side) => {
63
+ if (!parentCoords.value) return;
64
+
65
+ const sideMargin = Math.abs(coords[side] - parentCoords.value[side]);
66
+ containerCoords[side] = sideMargin + "px";
67
+ });
68
+ });
69
+ }
70
+
71
+ onMounted(() => {
72
+ if (boundaries.value) {
73
+ setOffsets();
74
+ }
75
+ });
76
+
77
+ return {
78
+ el,
79
+ containerCoords,
80
+ parentCoords,
81
+ modalClass,
82
+ };
83
+ },
84
+ });
85
+ </script>
86
+
87
+ <style lang="stylus">
88
+ .loading-modal
89
+ @apply z-10
90
+
91
+ &-primary
92
+ @apply s('bg-primary-lighter/20')
93
+
94
+ .loading
95
+ div
96
+ border-color theme('colors.primary.DEFAULT') transparent transparent transparent
97
+
98
+ &-secondary
99
+ @apply s('bg-secondary/20')
100
+
101
+ .loading
102
+ div
103
+ border-color theme('colors.secondary.DEFAULT') transparent transparent transparent
104
+
105
+ .loading
106
+ @apply relative inline-block w-12 h-12
107
+
108
+ div
109
+ @apply absolute block w-8 h-8 m-2 border-4 rounded-full
110
+ animation loading 1.2s cubic-bezier(0.5, 0, 0.5, 1) infinite
111
+
112
+ &:nth-child(1)
113
+ animation-delay -0.45s
114
+
115
+ &:nth-child(2)
116
+ animation-delay -0.3s
117
+
118
+ &:nth-child(3)
119
+ animation-delay -0.15s
120
+
121
+ @keyframes loading
122
+ 0%
123
+ transform rotate(0deg)
124
+
125
+ 100%
126
+ transform rotate(360deg)
127
+ </style>