@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,118 @@
1
+ <template lang="pug">
2
+ .card
3
+ .card-header(
4
+ v-if='title !== "" || $slots.header || $slots.title || $slots["title-container"] || desc !== "" || $slots.desc || $slots["header-left"] || $slots["header-right"]'
5
+ )
6
+ slot(name='header')
7
+ .header-left
8
+ slot(name='header-left')
9
+ .card-title(v-if='title !== "" || $slots.title || $slots["title-container"]')
10
+ .card-title-container
11
+ slot(name="title-container")
12
+ slot(name="title") {{ title }}
13
+ SLIcon.title-icon(v-if='titleIcon !== ""', :icon='titleIcon')
14
+ SLInfoTip(
15
+ v-if='info !== "" || !!$slots.info', :info='info'
16
+ data-skip-download
17
+ )
18
+ slot(name="info")
19
+ .card-title-border
20
+ .inline-info(v-if='inlineInfo !== ""') {{ inlineInfo }}
21
+ .header-right
22
+ slot(name='header-right')
23
+ .card-desc(v-if='desc !== "" || $slots.desc')
24
+ slot(name='desc') {{ desc }}
25
+ .card-body
26
+ slot
27
+ .card-footer(v-if="$slots.footer")
28
+ slot(name='footer')
29
+ </template>
30
+
31
+ <script lang="ts">
32
+ export default defineComponent({
33
+ name: "SLCard",
34
+ components: {},
35
+ props: {
36
+ title: {
37
+ type: String,
38
+ default: "",
39
+ },
40
+ titleIcon: {
41
+ type: String,
42
+ default: "",
43
+ },
44
+ info: {
45
+ type: String,
46
+ default: "",
47
+ },
48
+ inlineInfo: {
49
+ type: String,
50
+ default: "",
51
+ },
52
+ desc: {
53
+ type: String,
54
+ default: "",
55
+ },
56
+ },
57
+ setup() {
58
+ return {};
59
+ },
60
+ });
61
+ </script>
62
+
63
+ <style lang="stylus">
64
+ .tabs
65
+ .tabs
66
+ .card
67
+ @apply border-0 rounded-none shadow-none
68
+
69
+ .card
70
+ @apply rounded-lg border-primary-lighter shadow-md
71
+
72
+ .card
73
+ @apply border-0 rounded-none shadow-none
74
+
75
+ .card
76
+ @apply p-4 border border-border rounded bg-white shadow
77
+
78
+ .card-header
79
+ @apply flex justify-between mb-4
80
+
81
+ .card-title
82
+ @apply inline-flex flex-col
83
+
84
+ &-container
85
+ @apply inline-flex items-center mb-2 text-lg text-primary
86
+
87
+ .title-icon
88
+ @apply ml-2
89
+
90
+ &-border
91
+ @apply s('h-0.5') rounded-full bg-secondary
92
+
93
+ .card-desc
94
+ @apply text-base text-gray-2
95
+
96
+ .date-range
97
+ @apply ml-2 text-sm text-gray-3
98
+
99
+ .inline-info
100
+ @apply mt-2 text-sm text-gray-3
101
+
102
+ .card-body
103
+ @apply relative
104
+
105
+ .card-footer
106
+ @apply mt-4 pb-2 border-b s('border-primary-lighter/30')
107
+
108
+ .card
109
+ @apply border-primary-lighter rounded-lg shadow-md
110
+
111
+ .card-header
112
+ .card-title
113
+ &-container
114
+ @apply mb-0 text-base text-gray-2
115
+
116
+ &-border
117
+ @apply hidden
118
+ </style>
@@ -0,0 +1,181 @@
1
+ <template lang="pug">
2
+ .chart
3
+ ECharts.vchart(
4
+ :loading="loading"
5
+ :loading-options="loadingOptions"
6
+ :option="formatedOption"
7
+ :update-options="{ notMerge: true }"
8
+ :autoresize="true"
9
+ @highlight="onHighlighted"
10
+ @click="onClicked"
11
+ @mouseover="onMouseOvered"
12
+ @mouseout="onMouseOuted"
13
+ @mousemove="onMouseMoved"
14
+ ref="chartComp"
15
+ )
16
+ </template>
17
+
18
+ <script lang="ts">
19
+ import type { PropType } from "vue";
20
+ import ECharts from "vue-echarts";
21
+ import { use } from "echarts/core";
22
+ import {
23
+ LineChart,
24
+ BarChart,
25
+ PieChart,
26
+ ScatterChart,
27
+ HeatmapChart,
28
+ SankeyChart,
29
+ CustomChart,
30
+ } from "echarts/charts";
31
+ import "echarts-wordcloud";
32
+ import {
33
+ GridComponent,
34
+ LegendComponent,
35
+ TooltipComponent,
36
+ VisualMapComponent,
37
+ VisualMapContinuousComponent,
38
+ PolarComponent,
39
+ GraphicComponent,
40
+ MarkLineComponent,
41
+ } from "echarts/components";
42
+ import { CanvasRenderer } from "echarts/renderers";
43
+ import type { ECBasicOption } from "echarts/types/dist/shared";
44
+
45
+ use([
46
+ LineChart,
47
+ BarChart,
48
+ PieChart,
49
+ ScatterChart,
50
+ HeatmapChart,
51
+ SankeyChart,
52
+ CustomChart,
53
+ GridComponent,
54
+ LegendComponent,
55
+ TooltipComponent,
56
+ VisualMapComponent,
57
+ VisualMapContinuousComponent,
58
+ PolarComponent,
59
+ GraphicComponent,
60
+ MarkLineComponent,
61
+ CanvasRenderer,
62
+ ]);
63
+
64
+ const DefaultGrid = {
65
+ top: 14,
66
+ right: 0,
67
+ bottom: 0,
68
+ left: 30,
69
+ containLabel: true,
70
+ };
71
+
72
+ type EChartsType = InstanceType<typeof ECharts>;
73
+
74
+ export default defineComponent({
75
+ name: "SLChart",
76
+ components: {
77
+ ECharts,
78
+ },
79
+ props: {
80
+ option: {
81
+ type: Object as PropType<ECBasicOption | null>,
82
+ default: null,
83
+ },
84
+ loading: {
85
+ type: Boolean,
86
+ default: false,
87
+ },
88
+ },
89
+ emits: ["click", "highlight", "mouseover", "mouseout", "mousemove"],
90
+ setup(props, { emit }) {
91
+ const { option } = toRefs(props);
92
+
93
+ const chartComp = shallowRef<EChartsType | null>(null);
94
+
95
+ const loadingOptions = computed(() => ({
96
+ color: "#5A5C85",
97
+ }));
98
+ const formatedOption = computed(() => {
99
+ return {
100
+ xAxis: {
101
+ axisLine: {
102
+ lineStyle: {
103
+ color: "#828282",
104
+ },
105
+ },
106
+ },
107
+ yAxis: {
108
+ axisLine: {
109
+ show: true,
110
+ lineStyle: {
111
+ color: "#828282",
112
+ },
113
+ },
114
+ axisTick: {
115
+ show: true,
116
+ },
117
+ splitLine: {
118
+ lineStyle: {
119
+ color: "#E5E6F4",
120
+ opacity: 0.5,
121
+ },
122
+ },
123
+ },
124
+ tooltip: {
125
+ appendToBody: true,
126
+ padding: 16,
127
+ textStyle: {
128
+ color: "#4F4F4F",
129
+ fontSize: 16,
130
+ lineHeight: 21,
131
+ },
132
+ },
133
+ grid: DefaultGrid,
134
+ ...(option.value || {}),
135
+ };
136
+ });
137
+
138
+ function onClicked(e: any) {
139
+ emit("click", e);
140
+ }
141
+
142
+ function onHighlighted(e: any) {
143
+ emit("highlight", e);
144
+ }
145
+
146
+ function onMouseOvered(e: any) {
147
+ emit("mouseover", e);
148
+ }
149
+
150
+ function onMouseOuted(e: any) {
151
+ emit("mouseout", e);
152
+ }
153
+
154
+ function onMouseMoved(e: any) {
155
+ emit("mousemove", e);
156
+ }
157
+
158
+ function resize() {
159
+ if (!chartComp.value) return;
160
+ chartComp.value.resize();
161
+ }
162
+
163
+ return {
164
+ chartComp,
165
+ loadingOptions,
166
+ formatedOption,
167
+ onClicked,
168
+ onHighlighted,
169
+ onMouseOvered,
170
+ onMouseOuted,
171
+ onMouseMoved,
172
+ resize,
173
+ };
174
+ },
175
+ });
176
+ </script>
177
+
178
+ <style lang="stylus">
179
+ .chart
180
+ @apply flex w-full h-52 min-h-52 overflow-hidden
181
+ </style>
@@ -0,0 +1,179 @@
1
+ <template lang="pug">
2
+ .checkbox(:class='checkboxClass')
3
+ input(
4
+ type='checkbox',
5
+ :id='uid',
6
+ :checked='isChecked',
7
+ :value='value',
8
+ :disabled='disabled',
9
+ @change='onChanged'
10
+ )
11
+ label(:for='uid', :class='{ "is-check-some": isCheckSome }')
12
+ SLIcon.icon-square(:icon='falseIcon')
13
+ SLIcon.icon-check-square(:icon='trueIcon')
14
+ SLIcon.icon-minus-square(icon='minus-square')
15
+ slot
16
+ span(v-if='label !== ""') {{ label }}
17
+ </template>
18
+
19
+ <script lang="ts">
20
+ import { isEqual } from "lodash-es";
21
+ import { getUID } from "~ui/utils";
22
+
23
+ export default defineComponent({
24
+ name: "SLCheckbox",
25
+ components: {},
26
+ props: {
27
+ label: {
28
+ type: String,
29
+ default: "",
30
+ },
31
+ value: {
32
+ type: [Number, String, Boolean, Object],
33
+ default: true,
34
+ },
35
+ modelValue: {
36
+ type: [Number, String, Boolean, Array],
37
+ default: false,
38
+ },
39
+ trueValue: {
40
+ type: [Number, String, Boolean],
41
+ default: true,
42
+ },
43
+ falseValue: {
44
+ type: [Number, String, Boolean],
45
+ default: false,
46
+ },
47
+ trueIcon: {
48
+ type: String,
49
+ default: "check-square",
50
+ },
51
+ falseIcon: {
52
+ type: String,
53
+ default: "square",
54
+ },
55
+ isCheckSome: {
56
+ type: Boolean,
57
+ default: false,
58
+ },
59
+ disabled: {
60
+ type: Boolean,
61
+ default: false,
62
+ },
63
+ size: {
64
+ type: String,
65
+ default: "",
66
+ validator: (value: string) => ["", "sm"].includes(value),
67
+ },
68
+ },
69
+ emits: ["update:modelValue"],
70
+ setup(props, { emit }) {
71
+ const { size, disabled, modelValue, trueValue, falseValue, value } =
72
+ toRefs(props);
73
+ const uid = ref<string | undefined>(undefined);
74
+
75
+ const checkboxClass = computed(() => {
76
+ if (size.value === "") return "";
77
+
78
+ return `checkbox-${size.value}`;
79
+ });
80
+ const isChecked = computed(() => {
81
+ if (Array.isArray(modelValue.value)) {
82
+ let found = false;
83
+ modelValue.value.forEach((v) => {
84
+ if (isEqual(v, value.value)) {
85
+ found = true;
86
+ }
87
+ });
88
+ return found;
89
+ }
90
+ return modelValue.value === trueValue.value;
91
+ });
92
+
93
+ function onChanged(e: Event) {
94
+ if (disabled.value) {
95
+ return;
96
+ }
97
+
98
+ const isChecked = (e.target as HTMLInputElement).checked;
99
+
100
+ if (Array.isArray(modelValue.value)) {
101
+ let newValue = [...modelValue.value];
102
+
103
+ if (isChecked) {
104
+ newValue.push(value.value);
105
+ } else {
106
+ newValue = newValue.filter((v) => v !== value.value);
107
+ }
108
+
109
+ emit("update:modelValue", newValue);
110
+ } else {
111
+ emit(
112
+ "update:modelValue",
113
+ isChecked ? trueValue.value : falseValue.value
114
+ );
115
+ }
116
+ }
117
+
118
+ onMounted(() => {
119
+ uid.value = getUID();
120
+ });
121
+
122
+ return {
123
+ uid,
124
+ checkboxClass,
125
+ isChecked,
126
+ onChanged,
127
+ };
128
+ },
129
+ });
130
+ </script>
131
+
132
+ <style lang="stylus">
133
+ .checkbox
134
+ @apply inline-flex relative text-base align-top overflow-hidden
135
+
136
+ &.checkbox-sm
137
+ @apply text-sm
138
+
139
+ label
140
+ @apply text-sm
141
+
142
+ input
143
+ @apply absolute s("-top-10") s("-left-10")
144
+
145
+ label
146
+ @apply relative text-base text-gray-3 cursor-pointer
147
+
148
+ .icon
149
+ @apply hidden
150
+
151
+ span
152
+ @apply ml-2
153
+
154
+ input:not(:checked) + label:not(.is-check-some)
155
+ .icon-square
156
+ @apply inline-block
157
+
158
+ input:not(:checked) + label.is-check-some
159
+ @apply text-primary
160
+
161
+ .icon-square
162
+ @apply hidden
163
+
164
+ .icon-minus-square
165
+ @apply inline-block
166
+
167
+ input:checked + label, &.is-check-some
168
+ @apply text-primary
169
+
170
+ input:checked + label
171
+ .icon-check-square
172
+ @apply inline-block
173
+
174
+ input:not(:checked):disabled + label
175
+ @apply text-gray-4 cursor-default
176
+
177
+ input:checked:disabled + label
178
+ @apply text-gray-3 cursor-default
179
+ </style>
@@ -0,0 +1,95 @@
1
+ <template lang="pug">
2
+ a.circle-button(
3
+ href="javascript:;"
4
+ :class="btnClass"
5
+ @click="onClicked"
6
+ )
7
+ SLIcon(v-if="icon !== ''" :icon="icon")
8
+ .badge(v-show="showBadge")
9
+ </template>
10
+
11
+ <script lang="ts">
12
+ export default defineComponent({
13
+ name: "SLCircleButton",
14
+ components: {},
15
+ props: {
16
+ size: {
17
+ type: String,
18
+ default: "",
19
+ },
20
+ color: {
21
+ type: String,
22
+ default: "secondary-hover",
23
+ },
24
+ icon: {
25
+ type: String,
26
+ default: "box",
27
+ },
28
+ shadow: {
29
+ type: Boolean,
30
+ default: false,
31
+ },
32
+ showBadge: {
33
+ type: Boolean,
34
+ default: false,
35
+ },
36
+ active: {
37
+ type: Boolean,
38
+ default: false,
39
+ },
40
+ },
41
+ emits: ["click"],
42
+ setup(props, { emit }) {
43
+ const { size, color, shadow, active } = toRefs(props);
44
+
45
+ const btnClass = computed(() => {
46
+ const btnClass = [];
47
+ if (size.value !== "") {
48
+ btnClass.push(`circle-button-${size.value}`);
49
+ }
50
+ if (color.value !== "") {
51
+ btnClass.push(`circle-button-${color.value}`);
52
+ }
53
+ if (shadow.value) {
54
+ btnClass.push("shadow");
55
+ }
56
+ if (active.value) {
57
+ btnClass.push("active");
58
+ }
59
+ return btnClass.join(" ");
60
+ });
61
+
62
+ function onClicked(e: Event) {
63
+ emit("click", e);
64
+ }
65
+
66
+ return {
67
+ btnClass,
68
+ onClicked,
69
+ };
70
+ },
71
+ });
72
+ </script>
73
+
74
+ <style lang="stylus">
75
+ .circle-button
76
+ @apply inline-flex items-center justify-center relative w-10 h-10 rounded-full bg-white text-xl s('leading-5.75') text-white
77
+
78
+ &&-lg
79
+ @apply s('w-13.5') s('h-13.5') s('text-2.67xl')
80
+
81
+ .badge
82
+ @apply s('top-1.75') right-3 w-1 h-1
83
+
84
+ &&-secondary-hover
85
+ @apply bg-secondary-hover
86
+
87
+ &.shadow
88
+ @apply shadow-md
89
+
90
+ &.active
91
+ @apply border border-secondary-line text-secondary-line
92
+
93
+ .badge
94
+ @apply absolute s('top-1.5') right-2 block s('w-0.75') s('h-0.75') rounded-full bg-secondary-line
95
+ </style>
@@ -0,0 +1,116 @@
1
+ <template lang="pug">
2
+ .sl-collapsable-block(:class="{ collapsed: isCollapsed }")
3
+ .sl-collapsable-block-header
4
+ slot(name="header")
5
+ a(
6
+ href="javascript:;"
7
+ @click="onToggleCollapse"
8
+ ).flex.items-center.justify-between
9
+ .text-base.text-gray-1.font-bold {{ blockTitle }}
10
+ .toggle-collapse-button
11
+ .button-title {{ isCollapsed ? '展開' : '收合' }}
12
+ SLIcon(icon="chevron-down")
13
+ .sl-collapsable-block-body(:style="`height: ${bodyHeight}px;`")
14
+ .collapsable-block-wrapper(ref="wrapperEl")
15
+ slot
16
+ </template>
17
+
18
+ <script lang="ts">
19
+ export default defineComponent({
20
+ name: "SLCollapsableBlock",
21
+ components: {},
22
+ props: {
23
+ blockTitle: {
24
+ type: String,
25
+ default: "",
26
+ },
27
+ defaultCollapsed: {
28
+ type: Boolean,
29
+ default: true,
30
+ },
31
+ },
32
+ setup(props) {
33
+ const { defaultCollapsed } = toRefs(props);
34
+
35
+ const wrapperEl = shallowRef<HTMLElement | null>(null);
36
+ const isCollapsed = ref(defaultCollapsed.value);
37
+ const bodyHeight = ref(0);
38
+
39
+ function onToggleCollapse() {
40
+ isCollapsed.value = !isCollapsed.value;
41
+ }
42
+
43
+ function checkImageLoaded() {
44
+ nextTick(() => {
45
+ if (!wrapperEl.value) return;
46
+
47
+ const imageEls = wrapperEl.value.querySelectorAll("img");
48
+ for (let i = 0; i < imageEls.length; i += 1) {
49
+ const imageEl = imageEls[i] as HTMLImageElement;
50
+ imageEl.onload = () => {
51
+ getWrapperHeight();
52
+ };
53
+ }
54
+ });
55
+ }
56
+
57
+ function getWrapperHeight() {
58
+ nextTick(() => {
59
+ if (isCollapsed.value || !wrapperEl.value) {
60
+ bodyHeight.value = 0;
61
+ return;
62
+ }
63
+
64
+ const wrapperElRect = wrapperEl.value.getBoundingClientRect();
65
+ bodyHeight.value = wrapperElRect.height + 16;
66
+ });
67
+ }
68
+
69
+ watch(isCollapsed, () => {
70
+ getWrapperHeight();
71
+ });
72
+
73
+ onMounted(() => {
74
+ getWrapperHeight();
75
+ checkImageLoaded();
76
+ });
77
+
78
+ return {
79
+ wrapperEl,
80
+ isCollapsed,
81
+ bodyHeight,
82
+ onToggleCollapse,
83
+ };
84
+ },
85
+ });
86
+ </script>
87
+
88
+ <style lang="stylus">
89
+ .sl-collapsable-block
90
+ @apply s('p-3.75') border border-secondary-new-300 rounded-lg
91
+
92
+ &.collapsed
93
+ .sl-collapsable-block-header
94
+ .toggle-collapse-button
95
+ .icon
96
+ @apply rotate-0
97
+
98
+ .sl-collapsable-block-body
99
+ @apply pt-0
100
+
101
+ &-header
102
+ .toggle-collapse-button
103
+ @apply inline-flex items-center space-x-2
104
+
105
+ .button-title
106
+ @apply text-sm text-primary
107
+
108
+ .icon
109
+ @apply text-xs leading-none text-primary-hover rotate-180 transition-transform
110
+
111
+ &-body
112
+ @apply relative pt-4 overflow-hidden transition-all
113
+
114
+ .collapsable-block-wrapper
115
+ @apply absolute top-4 left-0 w-full
116
+ </style>