@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,545 @@
1
+ <template lang="pug">
2
+ .custom-select(:class='customSelectClass', ref='select')
3
+ label.input-label(v-if='label', :for='uid') {{ label }}
4
+ .form-control(
5
+ :class='{ "is-invalid": isInvalid !== null && isInvalid, "is-valid": isInvalid !== null && !isInvalid }'
6
+ )
7
+ label.toggle-button(
8
+ :for='uid',
9
+ :class='{ selected: isSelected }',
10
+ @click='onClickedToggle',
11
+ ref='toggleButton'
12
+ )
13
+ span.button-title {{ buttonTitle }}
14
+ SLIcon(icon='caret-down')
15
+ .invalid-feedback
16
+ slot(name='invalid-feedback') {{ invalidFeedback }}
17
+ ClientOnly
18
+ Teleport(to="body")
19
+ .select-options(ref='optionList', :style="`min-width: ${popupMaxWidth}px`")
20
+ .options-header(v-if='withSearch')
21
+ SLSearchInput(
22
+ v-model='search',
23
+ placeholder='搜尋',
24
+ :lazy='false',
25
+ :isSearch='isSearch',
26
+ @clear='onClearedSearch'
27
+ )
28
+ .options-wrapper.custom-scrollbar-y(ref='listWrapper')
29
+ .option-list
30
+ a.option-button(
31
+ v-for='option in selectOptions',
32
+ :key='option.label',
33
+ href='javascript:;',
34
+ :class='{ disabled: option.disabled, selected: isOptionSelected(option) }',
35
+ @click.prevent='(e) => onClickedOption(e, option.value, option.disabled)'
36
+ )
37
+ SLIcon.icon-square(v-if='multiple', icon='square')
38
+ SLIcon.icon-check-square(v-if='multiple', icon='check-square')
39
+ span {{ option.label }}
40
+ </template>
41
+
42
+ <script lang="ts">
43
+ import type { PropType } from "vue";
44
+ import { isEqual } from "lodash-es";
45
+ import { fromEvent } from "rxjs";
46
+ import { createPopper } from "@popperjs/core";
47
+ import type { Placement } from "@popperjs/core";
48
+ import { gsap } from "gsap";
49
+ import { ScrollToPlugin } from "gsap/ScrollToPlugin";
50
+ import { getUID } from "~ui/utils";
51
+ import type IInputOption from "~ui/interface/IInputOption";
52
+
53
+ gsap.registerPlugin(ScrollToPlugin);
54
+
55
+ export default defineComponent({
56
+ name: "SLSelect",
57
+ components: {},
58
+ props: {
59
+ size: {
60
+ type: String,
61
+ default: "",
62
+ validator: (val: string) => ["", "sm"].includes(val),
63
+ },
64
+ modelValue: {
65
+ type: [Number, String, Object, Array] as PropType<any>,
66
+ default: () => null,
67
+ },
68
+ label: {
69
+ type: String,
70
+ default: "",
71
+ },
72
+ options: {
73
+ type: Array as PropType<Array<IInputOption>>,
74
+ default: () => [],
75
+ },
76
+ placeholder: {
77
+ type: String,
78
+ default: "請選擇",
79
+ },
80
+ isInvalid: {
81
+ type: Boolean,
82
+ default: false,
83
+ },
84
+ invalidFeedback: {
85
+ type: String,
86
+ default: "",
87
+ },
88
+ withSearch: {
89
+ type: Boolean,
90
+ default: false,
91
+ },
92
+ inline: {
93
+ type: Boolean,
94
+ default: false,
95
+ },
96
+ disabled: {
97
+ type: Boolean,
98
+ default: false,
99
+ },
100
+ multiple: {
101
+ type: Boolean,
102
+ default: false,
103
+ },
104
+ maxSelection: {
105
+ type: Number,
106
+ default: 0,
107
+ },
108
+ minSelection: {
109
+ type: Number,
110
+ default: 0,
111
+ },
112
+ placement: {
113
+ type: String,
114
+ default: "bottom-start",
115
+ },
116
+ beReadonly: {
117
+ type: Boolean,
118
+ default: false,
119
+ },
120
+ lazyUpdate: {
121
+ type: Boolean,
122
+ default: true,
123
+ },
124
+ },
125
+ emits: [
126
+ "update:modelValue",
127
+ "update:selecting",
128
+ "search",
129
+ "show:inputPopup",
130
+ "hide:inputPopup",
131
+ ],
132
+ setup(props, { emit }) {
133
+ const {
134
+ modelValue,
135
+ options,
136
+ placeholder,
137
+ disabled,
138
+ multiple,
139
+ maxSelection,
140
+ minSelection,
141
+ placement,
142
+ inline,
143
+ size,
144
+ beReadonly,
145
+ lazyUpdate,
146
+ } = toRefs(props);
147
+
148
+ const toggleButton = shallowRef<HTMLElement | null>(null);
149
+ const optionList = shallowRef<HTMLElement | null>(null);
150
+ const listWrapper = shallowRef<HTMLElement | null>(null);
151
+ const select = shallowRef<HTMLElement | null>(null);
152
+ const uid = ref<string | undefined>(undefined);
153
+ const modelProxy = ref(
154
+ Array.isArray(modelValue.value) ? [...modelValue.value] : modelValue.value
155
+ );
156
+ const search = ref("");
157
+ const isSelecting = ref(false);
158
+ const popupMaxWidth = ref(200);
159
+
160
+ let clickSubscriber: any = null;
161
+ let popperInstance: any = null;
162
+
163
+ const customSelectClass = computed(() => {
164
+ const classes: Array<Object | string> = [
165
+ {
166
+ inline: inline.value,
167
+ disabled: disabled.value,
168
+ readonly: beReadonly.value,
169
+ },
170
+ ];
171
+
172
+ if (size.value !== "") {
173
+ classes.push(`custom-select-${size.value}`);
174
+ }
175
+
176
+ return classes;
177
+ });
178
+ const selectOptions = computed(() => {
179
+ if (placeholder.value === "" || multiple.value) {
180
+ return options.value;
181
+ }
182
+
183
+ return [
184
+ { label: placeholder.value, value: null, disabled: false },
185
+ ...options.value,
186
+ ];
187
+ });
188
+ const selectedOption = computed(() => {
189
+ const selectedOption = selectOptions.value.find((option) => {
190
+ return isEqual(option.value, modelValue.value);
191
+ });
192
+
193
+ return selectedOption;
194
+ });
195
+ const buttonTitle = computed(() => {
196
+ if (multiple.value) {
197
+ return placeholder.value;
198
+ }
199
+
200
+ return selectedOption.value?.label || placeholder.value;
201
+ });
202
+ const isSearch = computed(() => {
203
+ return search.value !== "";
204
+ });
205
+ const isSelected = computed(() => {
206
+ if (
207
+ multiple.value &&
208
+ Array.isArray(modelProxy.value) &&
209
+ modelProxy.value.length > 0
210
+ ) {
211
+ return true;
212
+ } else if (!multiple.value) {
213
+ return (
214
+ (placeholder.value !== "" &&
215
+ !!selectedOption.value &&
216
+ modelProxy.value) ||
217
+ (placeholder.value === "" && !!selectedOption.value)
218
+ );
219
+ }
220
+
221
+ return false;
222
+ });
223
+
224
+ function subscribeClick() {
225
+ clickSubscriber = fromEvent(window, "click").subscribe((e) => {
226
+ const { target } = e;
227
+ if (select.value && !select.value.contains(target as HTMLElement)) {
228
+ hide();
229
+ }
230
+ });
231
+ }
232
+
233
+ function unsubscribeClick() {
234
+ if (clickSubscriber) {
235
+ clickSubscriber.unsubscribe();
236
+ }
237
+ clickSubscriber = null;
238
+ }
239
+
240
+ function refreshScroll() {
241
+ nextTick(() => {
242
+ gsap.to(listWrapper.value, { duration: 0.5, scrollTo: 0 });
243
+ });
244
+ }
245
+
246
+ function show() {
247
+ isSelecting.value = true;
248
+
249
+ const body = document.querySelector("body");
250
+ if (body) {
251
+ body.classList.add("popup");
252
+ }
253
+
254
+ popupMaxWidth.value = select.value?.getBoundingClientRect().width || 200;
255
+ emit("show:inputPopup");
256
+ subscribeClick();
257
+ if (optionList.value) {
258
+ optionList.value.setAttribute("data-show", "");
259
+ }
260
+ popperInstance.setOptions({
261
+ modifiers: [
262
+ {
263
+ name: "offset",
264
+ options: {
265
+ offset: [0, 4],
266
+ },
267
+ },
268
+ { name: "eventListeners", enabled: true },
269
+ ],
270
+ });
271
+ refreshScroll();
272
+ }
273
+
274
+ function hide() {
275
+ const body = document.querySelector("body");
276
+ if (body) {
277
+ body.classList.remove("popup");
278
+ }
279
+
280
+ if (
281
+ optionList.value &&
282
+ optionList.value.getAttribute("data-show") === null
283
+ )
284
+ return;
285
+
286
+ emit("hide:inputPopup");
287
+ unsubscribeClick();
288
+ if (optionList.value) {
289
+ optionList.value.removeAttribute("data-show");
290
+ }
291
+
292
+ if (popperInstance) {
293
+ popperInstance.setOptions({
294
+ modifiers: [{ name: "eventListeners", enabled: false }],
295
+ });
296
+ }
297
+
298
+ if (multiple.value && lazyUpdate.value) {
299
+ emit("update:modelValue", modelProxy.value);
300
+ }
301
+
302
+ setupModelProxy();
303
+ isSelecting.value = false;
304
+ }
305
+
306
+ function onClickedToggle() {
307
+ if (disabled.value || beReadonly.value) return;
308
+
309
+ if (
310
+ !optionList.value ||
311
+ optionList.value.getAttribute("data-show") !== null
312
+ ) {
313
+ hide();
314
+ return;
315
+ }
316
+
317
+ show();
318
+ }
319
+
320
+ function onClickedOption(_: Event, newValue: any, disabled?: boolean) {
321
+ if (disabled) {
322
+ return;
323
+ }
324
+
325
+ if (!multiple.value) {
326
+ emit("update:modelValue", newValue);
327
+ hide();
328
+ return;
329
+ }
330
+
331
+ if (Array.isArray(modelProxy.value)) {
332
+ if (modelProxy.value.includes(newValue)) {
333
+ if (
334
+ minSelection.value > 0 &&
335
+ modelProxy.value.length <= minSelection.value
336
+ )
337
+ return;
338
+ modelProxy.value = modelProxy.value.filter(
339
+ (v: any) => v !== newValue
340
+ );
341
+ } else {
342
+ if (
343
+ maxSelection.value > 0 &&
344
+ modelProxy.value.length >= maxSelection.value
345
+ )
346
+ return;
347
+ modelProxy.value.push(newValue);
348
+ }
349
+ }
350
+
351
+ if (!lazyUpdate.value) {
352
+ emit("update:modelValue", modelProxy.value);
353
+ }
354
+ }
355
+
356
+ function onClearedSearch() {
357
+ search.value = "";
358
+ }
359
+
360
+ function onFocusedSelect() {
361
+ show();
362
+ }
363
+
364
+ function isOptionSelected(option: any) {
365
+ if (multiple.value && Array.isArray(modelProxy.value)) {
366
+ return !!modelProxy.value.find((v: any) => isEqual(v, option.value));
367
+ } else if (!multiple.value) {
368
+ return option.value === modelProxy.value;
369
+ }
370
+
371
+ return false;
372
+ }
373
+
374
+ function setupModelProxy() {
375
+ if (Array.isArray(modelValue.value)) {
376
+ modelProxy.value = [...modelValue.value];
377
+ return;
378
+ }
379
+
380
+ modelProxy.value = modelValue.value;
381
+ }
382
+
383
+ watch(modelValue, () => {
384
+ setupModelProxy();
385
+ });
386
+ watch(selectOptions, () => {
387
+ refreshScroll();
388
+ });
389
+ watch(isSelecting, () => {
390
+ emit("update:selecting", isSelecting.value);
391
+ });
392
+ watch(search, () => {
393
+ emit("search", search.value);
394
+ });
395
+
396
+ onMounted(() => {
397
+ uid.value = getUID();
398
+
399
+ nextTick(() => {
400
+ if (toggleButton.value && optionList.value) {
401
+ popperInstance = createPopper(toggleButton.value, optionList.value, {
402
+ placement: placement.value as Placement,
403
+ modifiers: [{ name: "eventListeners", enabled: false }],
404
+ });
405
+ }
406
+ });
407
+ });
408
+
409
+ onBeforeUnmount(() => {
410
+ const body = document.querySelector("body");
411
+ if (body) {
412
+ body.classList.remove("popup");
413
+ }
414
+
415
+ if (popperInstance) {
416
+ popperInstance.destroy();
417
+ }
418
+ popperInstance = null;
419
+ });
420
+
421
+ return {
422
+ toggleButton,
423
+ optionList,
424
+ listWrapper,
425
+ select,
426
+ uid,
427
+ modelProxy,
428
+ search,
429
+ isSelecting,
430
+ popupMaxWidth,
431
+ customSelectClass,
432
+ selectOptions,
433
+ selectedOption,
434
+ buttonTitle,
435
+ isSearch,
436
+ isSelected,
437
+ onClickedToggle,
438
+ onClickedOption,
439
+ onClearedSearch,
440
+ onFocusedSelect,
441
+ isOptionSelected,
442
+ hide,
443
+ };
444
+ },
445
+ });
446
+ </script>
447
+
448
+ <style lang="stylus" scoped>
449
+ .custom-select
450
+ @apply relative
451
+
452
+ &.inline
453
+ @apply flex
454
+
455
+ .input-label
456
+ @apply flex-grow-0 flex-shrink-0 mb-0 mr-2 py-2 border-t border-b border-transparent
457
+
458
+ .form-control
459
+ @apply flex-grow
460
+
461
+ &.custom-select-sm
462
+ .form-control
463
+ label
464
+ @apply s("py-0.75") text-sm
465
+
466
+ .select-options
467
+ .option-list
468
+ .option-button
469
+ @apply py-1 text-sm
470
+
471
+ &.disabled, &.readonly
472
+ .form-control
473
+ label
474
+ @apply border-primary-light bg-bg text-gray-4 cursor-default
475
+
476
+ .icon
477
+ @apply text-gray-4
478
+
479
+ .input-label
480
+ @apply mb-2 text-base leading-normal text-gray-2
481
+
482
+ .form-control
483
+ @apply relative block overflow-hidden
484
+
485
+ &.is-invalid
486
+ .toggle-button
487
+ @apply border-danger-hover
488
+
489
+ .invalid-feedback
490
+ @apply block
491
+
492
+ select
493
+ @apply absolute outline-none
494
+
495
+ label
496
+ @apply relative flex items-center s("p-1.75") bg-white border border-primary-light rounded text-base leading-normal text-gray-4 space-x-2 cursor-pointer
497
+
498
+ &.selected
499
+ @apply text-gray-2
500
+
501
+ .button-title
502
+ @apply flex-grow truncate
503
+
504
+ .icon
505
+ @apply flex-grow-0 flex-shrink-0 text-xs text-gray-2
506
+
507
+ .invalid-feedback
508
+ @apply hidden text-danger text-xs
509
+
510
+ .select-options
511
+ @apply flex flex-col z-60 absolute invisible border border-primary-light rounded bg-white overflow-hidden pointer-events-none
512
+
513
+ &[data-show]
514
+ @apply visible pointer-events-auto
515
+
516
+ .options-header
517
+ @apply p-1
518
+
519
+ .options-wrapper
520
+ @apply relative w-full s("max-h-[11.6875rem]") rounded overflow-auto
521
+
522
+ .option-list
523
+ @apply flex flex-col
524
+
525
+ .option-button
526
+ @apply block s("min-w-[10.125rem]") s('p-1.75') bg-white text-base leading-normal text-gray-3 s("hover:bg-bg") s("hover:text-primary")
527
+
528
+ &.selected
529
+ @apply bg-bg text-primary
530
+
531
+ .icon-square
532
+ @apply hidden
533
+
534
+ .icon-check-square
535
+ @apply inline-block
536
+
537
+ &.disabled
538
+ @apply cursor-default s("hover:bg-white") s("hover:text-gray-3")
539
+
540
+ .icon
541
+ @apply mr-2
542
+
543
+ &-check-square
544
+ @apply hidden
545
+ </style>
@@ -0,0 +1,118 @@
1
+ <template lang="pug">
2
+ .select-all-toggle(:class="[sizeClass, colorClass]")
3
+ a(
4
+ href="javascript:;"
5
+ :class="{ active: status === 'all' }"
6
+ @click="onClickedSelectAll"
7
+ ).select-all-option.select-all-option-all 全選
8
+ .select-all-divider(:class="{ active: status !== '' }")
9
+ a(
10
+ href="javascript:;"
11
+ :class="{ active: status === 'none' }"
12
+ @click="onClickedSelectNone"
13
+ ).select-all-option.select-all-option-none 全不選
14
+ </template>
15
+
16
+ <script lang="ts">
17
+ export default defineComponent({
18
+ name: "SLSelectAllToggle",
19
+ components: {},
20
+ props: {
21
+ status: {
22
+ type: String,
23
+ default: "",
24
+ },
25
+ size: {
26
+ type: String,
27
+ default: "",
28
+ validator: (val: string) => ["", "sm", "xs"].includes(val),
29
+ },
30
+ color: {
31
+ type: String,
32
+ default: "primary-light",
33
+ validator: (val: string) =>
34
+ ["primary-light", "secondary-new"].includes(val),
35
+ },
36
+ },
37
+ emits: ["select:all", "select:none"],
38
+ setup(props, { emit }) {
39
+ const { size, color } = toRefs(props);
40
+
41
+ const sizeClass = computed(() => {
42
+ if (size.value === "") return "";
43
+
44
+ return `select-all-toggle-${size.value}`;
45
+ });
46
+ const colorClass = computed(() => {
47
+ return `select-all-toggle-${color.value}`;
48
+ });
49
+
50
+ function onClickedSelectAll() {
51
+ emit("select:all");
52
+ }
53
+
54
+ function onClickedSelectNone() {
55
+ emit("select:none");
56
+ }
57
+
58
+ return {
59
+ sizeClass,
60
+ colorClass,
61
+ onClickedSelectAll,
62
+ onClickedSelectNone,
63
+ };
64
+ },
65
+ });
66
+ </script>
67
+
68
+ <style lang="stylus">
69
+ .select-all-toggle
70
+ @apply inline-flex items-stretch border rounded-sm bg-white overflow-hidden
71
+
72
+ &.select-all-toggle-xs
73
+ .select-all-option
74
+ @apply text-xs
75
+
76
+ &.select-all-toggle-sm
77
+ .select-all-option
78
+ @apply s('py-1.25') text-xs
79
+
80
+ &.select-all-toggle-primary-light
81
+ @apply border-border
82
+
83
+ .select-all-option
84
+ @apply text-primary-light
85
+
86
+ &.active
87
+ @apply bg-primary
88
+
89
+ .select-all-divider
90
+ @apply bg-primary-light
91
+
92
+ &.select-all-toggle-secondary-new
93
+ @apply border-secondary-new-300
94
+
95
+ .select-all-option
96
+ @apply text-secondary-new-300
97
+
98
+ &.active
99
+ @apply bg-secondary-new
100
+
101
+ .select-all-divider
102
+ @apply bg-primary-light
103
+
104
+ .select-all-option
105
+ @apply block py-1 bg-white text-sm
106
+
107
+ &.active
108
+ @apply text-white
109
+
110
+ &-all
111
+ @apply px-2
112
+
113
+ &-none
114
+ @apply pr-2 s('pl-1.75')
115
+
116
+ .select-all-divider
117
+ @apply s('mt-0.5') s('mb-0.5') w-px
118
+ </style>