@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,211 @@
1
+ <template lang="pug">
2
+ .hour-range-input(
3
+ :class="{ inline, disabled, readonly: beReadonly }"
4
+ )
5
+ label(v-if="label")
6
+ span {{ label }}
7
+ SLElementWithTitle(title='起始時間請輸入0-23,結束時間請輸入1-24')
8
+ SLIcon(icon='info-circle')
9
+ .form-control(
10
+ ref="toggle"
11
+ )
12
+ .input-group(:class='{ invalid: isStartHourInvalid }')
13
+ input(
14
+ type='text'
15
+ :value='startHour'
16
+ placeholder='0'
17
+ :disabled='disabled'
18
+ @input='onInputedStart'
19
+ )
20
+ span.minutes-label : 00
21
+ .input-group-splitter
22
+ .input-group(:class='{ invalid: isEndHourInvalid }')
23
+ input(
24
+ type='text'
25
+ :value='endHour'
26
+ placeholder='24'
27
+ :disabled='disabled'
28
+ @input='onInputedEnd'
29
+ )
30
+ span.minutes-label : 00
31
+ .invalid-feedback(v-if='isStartHourInvalid || isEndHourInvalid') 起始時間請輸入0-23,結束時間請輸入1-24
32
+ </template>
33
+
34
+ <script lang="ts">
35
+ import type { PropType } from "vue";
36
+ import type IHourRange from "~ui/interface/IHourRange";
37
+
38
+ export default defineComponent({
39
+ name: "SLHourRangeInput",
40
+ components: {},
41
+ props: {
42
+ label: {
43
+ type: String,
44
+ default: undefined,
45
+ },
46
+ inline: {
47
+ type: Boolean,
48
+ default: false,
49
+ },
50
+ modelValue: {
51
+ type: Object as PropType<IHourRange | null>,
52
+ default: null,
53
+ },
54
+ step: {
55
+ type: Number,
56
+ default: 1,
57
+ },
58
+ disabled: {
59
+ type: Boolean,
60
+ default: false,
61
+ },
62
+ beReadonly: {
63
+ type: Boolean,
64
+ default: false,
65
+ },
66
+ },
67
+ emits: ["update:modelValue"],
68
+ setup(props, { emit }) {
69
+ const { modelValue } = toRefs(props);
70
+
71
+ const toggle = ref(null);
72
+ const startHour = ref(
73
+ modelValue.value && modelValue.value.start !== null
74
+ ? modelValue.value.start
75
+ : ""
76
+ );
77
+ const endHour = ref(
78
+ modelValue.value && modelValue.value.end !== null
79
+ ? modelValue.value.end
80
+ : ""
81
+ );
82
+ const isStartHourInvalid = ref(false);
83
+ const isEndHourInvalid = ref(false);
84
+
85
+ function onInputedStart(e: Event) {
86
+ startHour.value = (e.target as HTMLInputElement).value;
87
+ isStartHourInvalid.value = false;
88
+
89
+ let start =
90
+ (e.target as HTMLInputElement).value === ""
91
+ ? null
92
+ : Number((e.target as HTMLInputElement).value);
93
+
94
+ if (start !== null && (Number.isNaN(start) || start < 0 || start > 24)) {
95
+ start = null;
96
+ }
97
+
98
+ if (start !== null && start === 24) {
99
+ isStartHourInvalid.value = true;
100
+ return;
101
+ }
102
+
103
+ emit("update:modelValue", {
104
+ start,
105
+ end: endHour.value === "" ? null : endHour.value,
106
+ });
107
+ }
108
+
109
+ function onInputedEnd(e: Event) {
110
+ endHour.value = (e.target as HTMLInputElement).value;
111
+ isEndHourInvalid.value = false;
112
+
113
+ let end =
114
+ (e.target as HTMLInputElement).value === ""
115
+ ? null
116
+ : Number((e.target as HTMLInputElement).value);
117
+
118
+ if (end !== null && (Number.isNaN(end) || end < 0 || end > 24)) {
119
+ end = null;
120
+ }
121
+
122
+ if (end !== null && end === 0) {
123
+ isEndHourInvalid.value = true;
124
+ return;
125
+ }
126
+
127
+ emit("update:modelValue", {
128
+ start: startHour.value === "" ? null : startHour.value,
129
+ end,
130
+ });
131
+ }
132
+
133
+ watch(modelValue, () => {
134
+ startHour.value = "";
135
+ endHour.value = "";
136
+
137
+ if (
138
+ modelValue.value &&
139
+ modelValue.value.start !== null &&
140
+ modelValue.value?.start !== undefined &&
141
+ !Number.isNaN(modelValue.value.start)
142
+ ) {
143
+ startHour.value = modelValue.value.start;
144
+ }
145
+
146
+ if (
147
+ modelValue.value &&
148
+ modelValue.value.end !== null &&
149
+ modelValue.value?.end !== undefined &&
150
+ !Number.isNaN(modelValue.value.end)
151
+ ) {
152
+ endHour.value = modelValue.value.end;
153
+ }
154
+ });
155
+
156
+ return {
157
+ toggle,
158
+ startHour,
159
+ endHour,
160
+ isStartHourInvalid,
161
+ isEndHourInvalid,
162
+ onInputedStart,
163
+ onInputedEnd,
164
+ };
165
+ },
166
+ });
167
+ </script>
168
+
169
+ <style lang="stylus">
170
+ .hour-range-input
171
+ &.inline
172
+ @apply flex
173
+
174
+ & > label
175
+ @apply flex-grow-0 flex-shrink-0 mb-0 mr-2 py-2 border-t border-b border-transparent
176
+
177
+ .form-control
178
+ @apply flex-grow
179
+
180
+ &.disabled, &.readonly
181
+ .input-group
182
+ @apply border-primary-light bg-bg
183
+
184
+
185
+ & > label
186
+ @apply flex items-center mb-2 space-x-2 text-base leading-normal text-gray-2
187
+
188
+ .icon
189
+ @apply text-xs leading-none
190
+
191
+ .form-control
192
+ @apply flex items-center space-x-2
193
+
194
+ .input-group
195
+ @apply flex items-center flex-nowrap p-2 bg-white border border-primary-light rounded text-base space-x-1
196
+
197
+ &.invalid
198
+ @apply border-danger
199
+
200
+ input[type=text]
201
+ @apply flex-grow-0 flex-shrink-0 s('w-4.75') border-0 bg-transparent text-base text-center text-gray-2 outline-none
202
+
203
+ .minutes-label
204
+ @apply flex-grow-0 flex-shrink-0
205
+
206
+ .input-group-splitter
207
+ @apply w-4 h-px bg-primary
208
+
209
+ .invalid-feedback
210
+ @apply mt-1 text-danger text-xs
211
+ </style>
@@ -0,0 +1,235 @@
1
+ <template lang="pug">
2
+ .ios-switch(
3
+ :class="[`${size === '' ? '' : `ios-switch-${size}`}`, colorClass]"
4
+ ref="el"
5
+ )
6
+ input(
7
+ type="checkbox"
8
+ :id="uid"
9
+ v-model="modelProxy"
10
+ :true-value="trueValue",
11
+ :false-value="falseValue",
12
+ )
13
+ label(:for="uid" :style="`width: ${labelWidth}px;`" ref="labelEl")
14
+ span(ref="trueLabelEl").true-label {{ size === '' ? trueLabel : minTrueLabel }}
15
+ span(ref="falseLabelEl").false-label {{ size === '' ? falseLabel : minFalseLabel }}
16
+ </template>
17
+
18
+ <script lang="ts">
19
+ import { getUID } from "~ui/utils";
20
+
21
+ export default defineComponent({
22
+ name: "SLIOSSwitch",
23
+ props: {
24
+ modelValue: {
25
+ type: [Number, String, Boolean],
26
+ default: false,
27
+ },
28
+ trueLabel: {
29
+ type: String,
30
+ default: "",
31
+ },
32
+ minTrueLabel: {
33
+ type: String,
34
+ default: "",
35
+ },
36
+ trueValue: {
37
+ type: [Number, String, Boolean],
38
+ default: true,
39
+ },
40
+ falseLabel: {
41
+ type: String,
42
+ default: "",
43
+ },
44
+ minFalseLabel: {
45
+ type: String,
46
+ default: "",
47
+ },
48
+ falseValue: {
49
+ type: [Number, String, Boolean],
50
+ default: false,
51
+ },
52
+ size: {
53
+ type: String,
54
+ default: "",
55
+ validator: (val: string) => ["", "min", "sm"].includes(val),
56
+ },
57
+ color: {
58
+ type: String,
59
+ default: "",
60
+ validator: (val: string) => ["", "primary-hover"].includes(val),
61
+ },
62
+ },
63
+ emits: ["update:modelValue"],
64
+ setup(props, { emit }) {
65
+ const { modelValue, size, color } = toRefs(props);
66
+
67
+ const el = shallowRef<HTMLElement | null>(null);
68
+ const trueLabelEl = shallowRef<HTMLElement | null>(null);
69
+ const falseLabelEl = shallowRef<HTMLElement | null>(null);
70
+ const labelEl = shallowRef<HTMLElement | null>(null);
71
+ const uid = ref<string | undefined>(undefined);
72
+ const modelProxy = ref(modelValue.value);
73
+ const labelWidth = ref(23);
74
+
75
+ let timeoutId: any = null;
76
+ let trueLabelObserver: MutationObserver | null = null;
77
+ let falseLabelObserver: MutationObserver | null = null;
78
+
79
+ const colorClass = computed(() => {
80
+ if (color.value !== "") {
81
+ return `ios-switch-${color.value}`;
82
+ }
83
+
84
+ return "";
85
+ });
86
+
87
+ async function caculateLabelWidth() {
88
+ await nextTick();
89
+
90
+ // 有時候會抓不到 width,所以 setTimeout 才抓取
91
+ timeoutId = setTimeout(() => {
92
+ let width = 23;
93
+ const elRect = el.value?.getBoundingClientRect() || null;
94
+ const trueLabelRect =
95
+ trueLabelEl.value?.getBoundingClientRect() || null;
96
+ const falseLabelRect =
97
+ falseLabelEl.value?.getBoundingClientRect() || null;
98
+
99
+ if (
100
+ elRect &&
101
+ trueLabelRect &&
102
+ falseLabelRect &&
103
+ labelEl.value &&
104
+ size.value !== "min"
105
+ ) {
106
+ width = Math.ceil(
107
+ Math.max(trueLabelRect.width, falseLabelRect.width, elRect.width)
108
+ );
109
+ }
110
+
111
+ labelWidth.value = width;
112
+ }, 10);
113
+ }
114
+
115
+ watch(modelValue, () => {
116
+ modelProxy.value = modelValue.value;
117
+ });
118
+ watch(modelProxy, () => {
119
+ emit("update:modelValue", modelProxy.value);
120
+ });
121
+ watch(size, caculateLabelWidth);
122
+
123
+ onMounted(() => {
124
+ uid.value = getUID();
125
+
126
+ if (trueLabelEl.value) {
127
+ trueLabelObserver = new MutationObserver(caculateLabelWidth);
128
+ trueLabelObserver.observe(trueLabelEl.value, {
129
+ characterData: true,
130
+ });
131
+ }
132
+ if (falseLabelEl.value) {
133
+ falseLabelObserver = new MutationObserver(caculateLabelWidth);
134
+ falseLabelObserver.observe(falseLabelEl.value, {
135
+ characterData: true,
136
+ });
137
+ }
138
+
139
+ caculateLabelWidth();
140
+ });
141
+
142
+ onBeforeUnmount(() => {
143
+ if (trueLabelObserver) {
144
+ trueLabelObserver.disconnect();
145
+ }
146
+ if (falseLabelObserver) {
147
+ falseLabelObserver.disconnect();
148
+ }
149
+ if (timeoutId !== null) {
150
+ clearTimeout(timeoutId);
151
+ }
152
+
153
+ trueLabelObserver = null;
154
+ falseLabelObserver = null;
155
+ timeoutId = null;
156
+ });
157
+
158
+ return {
159
+ el,
160
+ trueLabelEl,
161
+ falseLabelEl,
162
+ labelEl,
163
+ uid,
164
+ modelProxy,
165
+ labelWidth,
166
+ colorClass,
167
+ };
168
+ },
169
+ });
170
+ </script>
171
+
172
+ <style lang="stylus">
173
+ .ios-switch
174
+ @apply inline-block relative overflow-hidden
175
+
176
+ &.ios-switch-min
177
+ label
178
+ @apply min-w-0 border-2 text-sm
179
+ width 23px
180
+ height 23px
181
+
182
+ &:before
183
+ @apply hidden
184
+
185
+ .true-label, .false-label
186
+ @apply p-0
187
+
188
+ &.ios-switch-sm
189
+ label
190
+ @apply s('min-w-[52px]') s('h-[28px]') text-sm
191
+
192
+ &:before
193
+ @apply s('w-[20px]')
194
+
195
+ .true-label
196
+ @apply s('pr-[31px]')
197
+
198
+ .false-label
199
+ @apply s('pl-[31px]')
200
+
201
+ &.ios-switch-primary-hover
202
+ input:checked + label
203
+ @apply bg-primary-hover border-primary
204
+
205
+ input
206
+ @apply absolute s('left-1/2') s('top-1/2') transform s('-translate-x-1/2') s('-translate-y-1/2')
207
+
208
+ label
209
+ @apply relative min-w-full s('h-[2.3125rem]') bg-bg border border-line rounded-full text-base leading-normal align-top cursor-pointer transition-colors
210
+
211
+ &:before
212
+ content ''
213
+ @apply block absolute s('left-0.75') s('inset-y-0.75') s('w-7.25') bg-white rounded-full shadow
214
+
215
+ span
216
+ @apply flex items-center justify-center absolute top-0 left-0 min-w-full h-full whitespace-nowrap
217
+
218
+ .true-label
219
+ @apply pl-2 s('pr-[35px]') text-white opacity-0
220
+
221
+ .false-label
222
+ @apply s('pl-[35px]') pr-2 text-primary-hover opacity-100
223
+
224
+ input:checked + label
225
+ @apply bg-primary border-primary-heavy
226
+
227
+ &:before
228
+ @apply left-auto s('right-0.75')
229
+
230
+ .true-label
231
+ @apply opacity-100
232
+
233
+ .false-label
234
+ @apply opacity-0
235
+ </style>
@@ -0,0 +1,35 @@
1
+ <template lang="pug">
2
+ i(:class="iconClass").icon
3
+ </template>
4
+
5
+ <script lang="ts">
6
+ export default defineComponent({
7
+ name: "SLIcon",
8
+ props: {
9
+ icon: {
10
+ type: String,
11
+ default: "",
12
+ },
13
+ },
14
+ setup(props) {
15
+ const { icon } = toRefs(props);
16
+
17
+ const iconClass = computed(() => {
18
+ if (icon.value && icon.value !== "") {
19
+ return `icon-${icon.value}`;
20
+ }
21
+
22
+ return "";
23
+ });
24
+
25
+ return {
26
+ iconClass,
27
+ };
28
+ },
29
+ });
30
+ </script>
31
+
32
+ <style lang="stylus">
33
+ i.icon
34
+ @apply inline-block leading-none
35
+ </style>
@@ -0,0 +1,130 @@
1
+ <template lang="pug">
2
+ a.icon-btn(
3
+ href='javascript:;'
4
+ :class='btnClass'
5
+ :style='`width: ${btnWidth}px`'
6
+ @click.prevent='onClicked'
7
+ @mouseenter.prevent='onMouseEnter'
8
+ @mouseleave.prevent='onMouseLeave'
9
+ )
10
+ SLIcon(:icon='icon')
11
+ .icon-btn-title(ref='titleEl') {{ title }}
12
+ </template>
13
+
14
+ <script lang="ts">
15
+ export default defineComponent({
16
+ name: "SLIconButton",
17
+ components: {},
18
+ props: {
19
+ size: {
20
+ type: String,
21
+ default: "",
22
+ validator: (val: string) => [""].includes(val),
23
+ },
24
+ color: {
25
+ type: String,
26
+ default: "",
27
+ validator: (val: string) =>
28
+ ["", "primary", "secondary-new"].includes(val),
29
+ },
30
+ variant: {
31
+ type: String,
32
+ default: "",
33
+ validator: (val: string) => ["", "outline"].includes(val),
34
+ },
35
+ icon: {
36
+ type: String,
37
+ required: true,
38
+ },
39
+ title: {
40
+ type: String,
41
+ required: true,
42
+ },
43
+ },
44
+ emits: ["click"],
45
+ setup(props, { emit }) {
46
+ const { size, color, variant } = toRefs(props);
47
+
48
+ const titleEl = shallowRef<HTMLDivElement | null>(null);
49
+ const btnWidth = ref(0);
50
+
51
+ const btnClass = computed(() => {
52
+ const btnClass = [];
53
+ if (size.value !== "") {
54
+ btnClass.push(`icon-btn--${size.value}`);
55
+ }
56
+ if (color.value !== "") {
57
+ btnClass.push(`icon-btn--${color.value}`);
58
+ }
59
+ if (variant.value !== "") {
60
+ btnClass.push(`icon-btn--${variant.value}`);
61
+ }
62
+
63
+ return btnClass.join(" ");
64
+ });
65
+
66
+ function initBtnWidth() {
67
+ nextTick(() => {
68
+ btnWidth.value = 10.5 * 2 + 16;
69
+ });
70
+ }
71
+
72
+ function onClicked() {
73
+ emit("click");
74
+ }
75
+
76
+ function onMouseEnter() {
77
+ if (!titleEl.value) return;
78
+
79
+ const titleRect = titleEl.value.getBoundingClientRect();
80
+ btnWidth.value = 10.5 * 2 + 16 + titleRect.width;
81
+ }
82
+
83
+ function onMouseLeave() {
84
+ initBtnWidth();
85
+ }
86
+
87
+ onMounted(() => {
88
+ initBtnWidth();
89
+ });
90
+
91
+ return {
92
+ titleEl,
93
+ btnWidth,
94
+ btnClass,
95
+ onClicked,
96
+ onMouseEnter,
97
+ onMouseLeave,
98
+ };
99
+ },
100
+ });
101
+ </script>
102
+
103
+ <style lang="stylus" scoped>
104
+ .icon-btn
105
+ @apply inline-flex flex-nowrap justify-start items-center s('p-[0.59375rem]') border rounded-full text-base text-white overflow-hidden transition-width duration-300
106
+ &:hover
107
+ .icon-btn-title
108
+ @apply opacity-100
109
+
110
+ &.icon-btn--primary
111
+ @apply bg-primary border-primary
112
+
113
+ &.icon-btn--outline
114
+ @apply text-primary
115
+
116
+ &.icon-btn--secondary-new
117
+ @apply bg-secondary-new border-secondary-new
118
+
119
+ &.icon-btn--outline
120
+ @apply text-secondary-new
121
+
122
+ &.icon-btn--outline
123
+ @apply bg-transparent
124
+
125
+ .icon
126
+ @apply flex-grow-0 flex-shrink-0
127
+
128
+ &-title
129
+ @apply flex-grow-0 flex-shrink-0 pl-2 opacity-0 transition-opacity duration-300
130
+ </style>