@appscode/design-system 1.0.3-alpha.8 → 1.0.43-alpha.101

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 (114) hide show
  1. package/base/utilities/_default.scss +285 -23
  2. package/base/utilities/_derived-variables.scss +2 -15
  3. package/base/utilities/_initial-variables.scss +99 -64
  4. package/base/utilities/_mixin.scss +90 -10
  5. package/base/utilities/_typography.scss +23 -7
  6. package/base/utilities/dark-theme.scss +25 -0
  7. package/components/_ac-accordion.scss +1 -0
  8. package/components/_ac-alert-box.scss +47 -11
  9. package/components/_ac-card.scss +55 -20
  10. package/components/_ac-code-highlight.scss +7 -1
  11. package/components/_ac-content-layout.scss +4 -4
  12. package/components/_ac-drag.scss +6 -6
  13. package/components/_ac-input.scss +140 -38
  14. package/components/_ac-modal.scss +5 -4
  15. package/components/_ac-multi-select.scss +220 -18
  16. package/components/_ac-options.scss +31 -16
  17. package/components/_ac-select-box.scss +15 -5
  18. package/components/_ac-table.scss +88 -47
  19. package/components/_ac-tabs.scss +72 -23
  20. package/components/_ac-tags.scss +2 -2
  21. package/components/_ac-terminal.scss +272 -0
  22. package/components/_app-drawer.scss +6 -6
  23. package/components/_breadcumb.scss +8 -3
  24. package/components/_buttons.scss +86 -33
  25. package/components/_card-body-wrapper.scss +3 -3
  26. package/components/_dashboard-header.scss +1 -1
  27. package/components/_direct-deploy.scss +69 -0
  28. package/components/_go-to-top.scss +1 -1
  29. package/components/_graph.scss +45 -0
  30. package/components/_image-upload.scss +6 -4
  31. package/components/_left-sidebar-menu.scss +206 -46
  32. package/components/_monaco-editor.scss +1 -1
  33. package/components/_navbar.scss +104 -27
  34. package/components/_overview-info.scss +4 -4
  35. package/components/_overview-page.scss +1 -2
  36. package/components/_pagination.scss +45 -7
  37. package/components/_payment-card.scss +28 -12
  38. package/components/_preloader.scss +1 -1
  39. package/components/_preview-modal.scss +8 -8
  40. package/components/_pricing-table.scss +1 -1
  41. package/components/_progress-bar.scss +5 -5
  42. package/components/_subscription-card.scss +15 -8
  43. package/components/_table-of-content.scss +1 -1
  44. package/components/_tfa.scss +69 -0
  45. package/components/_widget-menu.scss +9 -9
  46. package/components/_wizard.scss +32 -20
  47. package/components/ac-toaster/_ac-toasted.scss +40 -8
  48. package/components/bbum/_card-team.scss +18 -10
  49. package/components/bbum/_information-center.scss +19 -5
  50. package/components/bbum/_mobile-desktop.scss +6 -6
  51. package/components/bbum/_post.scss +5 -4
  52. package/components/bbum/_sign-up-notification.scss +6 -6
  53. package/components/bbum/_single-post-preview.scss +9 -9
  54. package/components/bbum/_user-profile.scss +97 -90
  55. package/components/ui-builder/_ui-builder.scss +31 -12
  56. package/components/ui-builder/_vue-open-api.scss +98 -0
  57. package/layouts/_404.scss +2 -1
  58. package/layouts/_code-preview.scss +14 -6
  59. package/main.scss +4 -0
  60. package/package.json +2 -7
  61. package/plugins/theme.js +142 -0
  62. package/plugins/vue-toaster.js +7 -6
  63. package/vue-components/v2/breadcrumbs/Breadcrumb.vue +95 -0
  64. package/vue-components/v2/card/CardContent.vue +5 -0
  65. package/vue-components/v2/card/CardHeader.vue +12 -0
  66. package/vue-components/v2/card/OverviewCard.vue +10 -0
  67. package/vue-components/v2/card/OverviewCards.vue +5 -0
  68. package/vue-components/v2/card/PaymentCards.vue +16 -10
  69. package/vue-components/v2/content/ContentHeader.vue +1 -1
  70. package/vue-components/v2/editor/Editor.vue +37 -17
  71. package/vue-components/v2/editor/ResourceKeyValueEditor.vue +232 -0
  72. package/vue-components/v2/header/Header.vue +0 -1
  73. package/vue-components/v2/modal/Modal.vue +32 -14
  74. package/vue-components/v2/modals/JsonShowModal.vue +0 -1
  75. package/vue-components/v2/navbar/Appdrawer.vue +9 -6
  76. package/vue-components/v2/navbar/ThemeMode.vue +120 -0
  77. package/vue-components/v2/pagination/Pagination.vue +8 -1
  78. package/vue-components/v2/preloader/Preloader.vue +5 -5
  79. package/vue-components/v2/sidebar/ClusterSwitcher.vue +126 -0
  80. package/vue-components/v2/sidebar/SidebarItem.vue +24 -2
  81. package/vue-components/v2/table/InfoTable.vue +13 -3
  82. package/vue-components/v2/table/Table.vue +75 -5
  83. package/vue-components/v2/table/TableRow.vue +17 -8
  84. package/vue-components/v2/table/table-cell/CellValue.vue +10 -1
  85. package/vue-components/v2/tabs/EditorTabs.vue +1 -1
  86. package/vue-components/v3/button/Button.vue +73 -0
  87. package/vue-components/v3/content/ContentHeader.vue +54 -0
  88. package/vue-components/v3/content/ContentTable.vue +65 -0
  89. package/vue-components/v3/dropdown/DropdownDivider.vue +3 -0
  90. package/vue-components/v3/dropdown/DropdownItem.vue +5 -0
  91. package/vue-components/v3/dropdown/DropdownMenu.vue +111 -0
  92. package/vue-components/v3/editor/Editor.vue +157 -0
  93. package/vue-components/v3/form-fields/Input.vue +21 -0
  94. package/vue-components/v3/header/Header.vue +45 -0
  95. package/vue-components/v3/modal/Modal.vue +135 -0
  96. package/vue-components/v3/modals/JsonShowModal.vue +87 -0
  97. package/vue-components/v3/navbar/Appdrawer.vue +63 -0
  98. package/vue-components/v3/navbar/ThemeMode.vue +128 -0
  99. package/vue-components/v3/navbar/User.vue +64 -0
  100. package/vue-components/v3/pagination/Pagination.vue +159 -0
  101. package/vue-components/v3/searchbars/SearchBar.vue +47 -0
  102. package/vue-components/v3/sidebar/ClusterSwitcher.vue +133 -0
  103. package/vue-components/v3/tab/TabItem.vue +17 -0
  104. package/vue-components/v3/table/FakeTableCell.vue +39 -0
  105. package/vue-components/v3/table/InfoTable.vue +105 -0
  106. package/vue-components/v3/table/Table.vue +238 -0
  107. package/vue-components/v3/table/TableCell.vue +28 -0
  108. package/vue-components/v3/table/TableRow.vue +60 -0
  109. package/vue-components/v3/table/table-cell/ArrayCell.vue +111 -0
  110. package/vue-components/v3/table/table-cell/CellValue.vue +117 -0
  111. package/vue-components/v3/table/table-cell/ObjectCell.vue +105 -0
  112. package/vue-components/v3/table/table-cell/ValueWithModal.vue +43 -0
  113. package/vue-components/v3/tabs/EditorTabs.vue +36 -0
  114. package/vue-components/v3/tag/Tag.vue +17 -0
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <li>
2
+ <li ref="sidebarItem">
3
3
  <router-link
4
4
  :id="id"
5
5
  :title="title"
@@ -34,9 +34,31 @@ export default {
34
34
  default: false,
35
35
  },
36
36
  icon: {
37
- type: String,
37
+ type: null,
38
38
  default: "@/assets/images/icons/basic.svg",
39
39
  },
40
40
  },
41
+ watch: {
42
+ isActive: {
43
+ immediate: true,
44
+ handler(n) {
45
+ if (n) {
46
+ this.$nextTick(() => {
47
+ setTimeout(() => {
48
+ const top = (this.$refs.sidebarItem && this.$refs.sidebarItem.getBoundingClientRect().top) || 0;
49
+ // preventing scroll to options that are already visible
50
+ if (top > window.innerHeight - 200) {
51
+ // scroll to selected option item
52
+ this.$refs.sidebarItem.scrollIntoView({
53
+ behavior: 'smooth',
54
+ block: "center"
55
+ });
56
+ }
57
+ }, 500);
58
+ });
59
+ }
60
+ }
61
+ }
62
+ }
41
63
  };
42
64
  </script>
@@ -1,6 +1,9 @@
1
1
  <template>
2
2
  <table-container>
3
- <table class="table ac-info-table is-fullwidth">
3
+ <table
4
+ class="table ac-info-table is-fullwidth"
5
+ :class="{ 'pl-0 pr-0': removeContentPadding }"
6
+ >
4
7
  <tbody v-if="isFullTableLoaderActive">
5
8
  <!-- table loader -->
6
9
  <table-row v-for="i in loaderCols" :key="i">
@@ -15,7 +18,10 @@
15
18
  </tbody>
16
19
  <tbody
17
20
  v-else
18
- :class="isTableEmpty ? 'no-data-available has-text-centered p-10' : ''"
21
+ :class="{
22
+ 'no-data-available has-text-centered p-10': isTableEmpty,
23
+ 'pl-0 pr-0': removeContentPadding
24
+ }"
19
25
  >
20
26
  <template v-if="!isTableEmpty">
21
27
  <!-- table rows -->
@@ -60,7 +66,11 @@ export default {
60
66
  },
61
67
  tableHeaders: {
62
68
  type: Array,
63
- default: () => [],
69
+ default: () => []
70
+ },
71
+ removeContentPadding: {
72
+ type: Boolean,
73
+ default: false,
64
74
  },
65
75
  },
66
76
 
@@ -18,8 +18,17 @@
18
18
  </th>
19
19
  </table-row>
20
20
  <table-row v-else>
21
- <th v-for="tableHeader in tableHeaders" :key="tableHeader">
22
- {{ tableHeader }}
21
+ <th
22
+ v-for="(tableHeader, idx) in tableHeaders"
23
+ :key="idx"
24
+ :class="{
25
+ sorting: headerSortables[idx] && headerSortables[idx].enabled,
26
+ 'sorting-desc': headerSortables[idx] && headerSortables[idx].mode === 'desc',
27
+ 'sorting-asc': headerSortables[idx] && headerSortables[idx].mode === 'asc',
28
+ }"
29
+ @click.prevent="headerSortables[idx] && headerSortables[idx].enabled && emitSortEvent(idx)"
30
+ >
31
+ {{ headerLabels[idx] }}
23
32
  </th>
24
33
  <th
25
34
  ref="action-section"
@@ -41,8 +50,14 @@
41
50
  </th>
42
51
  </table-row>
43
52
  <table-row v-else>
44
- <table-cell v-for="tableHeader in tableHeaders" :key="tableHeader">
45
- <cell-value :is-loader-active="true" :cell-title="tableHeader" />
53
+ <table-cell
54
+ v-for="(tableHeader, idx) in tableHeaders"
55
+ :key="headerLabels[idx]"
56
+ >
57
+ <cell-value
58
+ :is-loader-active="true"
59
+ :cell-title="headerLabels[idx]"
60
+ />
46
61
  </table-cell>
47
62
  </table-row>
48
63
  </tbody>
@@ -112,12 +127,13 @@ export default {
112
127
  data() {
113
128
  return {
114
129
  fakeCellWidth: 0,
130
+ headerSortables: [],
115
131
  };
116
132
  },
117
133
 
118
134
  computed: {
119
135
  loaderCols() {
120
- if (this.isFullLoaderActive) {
136
+ if (this.isFullTableLoaderActive) {
121
137
  return 5;
122
138
  } else if (this.isLoaderActive) {
123
139
  return Math.max(this.tableHeaders.length + 1, 5);
@@ -128,6 +144,41 @@ export default {
128
144
  isFullTableLoaderActive() {
129
145
  return !this.tableHeaders.length;
130
146
  },
147
+ headerLabels() {
148
+ return this.tableHeaders.map((th) =>
149
+ typeof th === "string" ? th : th.label || "Label"
150
+ );
151
+ },
152
+ },
153
+
154
+ watch: {
155
+ tableHeaders: {
156
+ immediate: true,
157
+ handler(n) {
158
+ if (this.headerSortables.length === n.length) {
159
+ n.forEach((th, idx) => {
160
+ if (this.headerSortables[idx].enabled !== !!th.sortable) {
161
+ this.headerSortables[idx].enabled = !!th.sortable;
162
+ this.headerSortables[idx].mode = "";
163
+ }
164
+ });
165
+ } else {
166
+ this.headerSortables = n.map((th) => {
167
+ if (typeof th === "string") {
168
+ return {
169
+ enabled: false,
170
+ mode: "",
171
+ };
172
+ } else {
173
+ return {
174
+ enabled: !!th.sortable,
175
+ mode: "",
176
+ };
177
+ }
178
+ });
179
+ }
180
+ },
181
+ },
131
182
  },
132
183
 
133
184
  updated() {
@@ -144,6 +195,25 @@ export default {
144
195
  this.fakeCellWidth = d;
145
196
  }
146
197
  },
198
+ emitSortEvent(index) {
199
+ const emitValue = {
200
+ index,
201
+ label: this.tableHeaders[index].label,
202
+ mode: "",
203
+ };
204
+
205
+ this.headerSortables.forEach((hs, idx) => {
206
+ if (idx !== index) hs.mode = "";
207
+ else {
208
+ if (hs.mode === "asc") hs.mode = "desc";
209
+ else hs.mode = "asc";
210
+
211
+ emitValue.mode = hs.mode;
212
+ }
213
+ });
214
+
215
+ this.$emit("sort", emitValue);
216
+ },
147
217
  },
148
218
  };
149
219
  </script>
@@ -6,7 +6,7 @@
6
6
  custom
7
7
  v-slot="{ navigate }"
8
8
  >
9
- <tr @click="navigate">
9
+ <tr class="is-link" @click="navigate">
10
10
  <slot />
11
11
  <fake-table-cell
12
12
  v-if="fakeCellWidth > 0"
@@ -14,9 +14,14 @@
14
14
  />
15
15
  </tr>
16
16
  </router-link>
17
+
17
18
  <tr
18
19
  v-else
19
- :class="{ 'is-selected': isSelected, 'is-hoverless': !isSelected }"
20
+ :class="{
21
+ 'is-selected': isSelected,
22
+ 'is-hoverless': !isSelected,
23
+ 'is-disabled': isDisabled
24
+ }"
20
25
  @click.prevent="$emit('rowselect', true)"
21
26
  >
22
27
  <slot />
@@ -32,21 +37,25 @@ export default {
32
37
  props: {
33
38
  link: {
34
39
  type: String,
35
- default: "",
40
+ default: ""
36
41
  },
37
42
  isSelected: {
38
43
  type: Boolean,
39
- default: false,
44
+ default: false
45
+ },
46
+ isDisabled: {
47
+ type: Boolean,
48
+ default: false
40
49
  },
41
50
  fakeCellWidth: {
42
51
  type: Number,
43
- default: 0,
44
- },
52
+ default: 0
53
+ }
45
54
  },
46
55
 
47
56
  components: {
48
57
  TableCell: () => import("./TableCell.vue"),
49
- FakeTableCell: () => import("./FakeTableCell.vue"),
50
- },
58
+ FakeTableCell: () => import("./FakeTableCell.vue")
59
+ }
51
60
  };
52
61
  </script>
@@ -1,6 +1,7 @@
1
1
  <template>
2
2
  <div v-if="isLoaderActive" ref="cellLoaderDiv" :style="{ maxWidth: '300px' }">
3
- <content-loader :height="10" :width="computedCellWidth || 300" />
3
+ <content-loader :height="10" :width="computedCellWidth || 300" :primaryColor="primaryColor"
4
+ :secondaryColor="secondaryColor" />
4
5
  </div>
5
6
  <div v-else class="haha" ref="cellDiv">
6
7
  <object-cell
@@ -24,6 +25,8 @@
24
25
  </template>
25
26
 
26
27
  <script>
28
+ import {loaderLightThemePrimaryColor, loaderDarkThemePrimaryColor, loaderLightThemeSecondaryColor, loaderDarkThemeSecondaryColor} from "@appscode/design-system/plugins/theme";
29
+
27
30
  export default {
28
31
  props: {
29
32
  isLoaderActive: {
@@ -56,6 +59,12 @@ export default {
56
59
  maxCharacterLength() {
57
60
  return Math.ceil(this.computedCellWidth / 8);
58
61
  },
62
+ primaryColor() {
63
+ return document.documentElement.classList.contains("is-dark-theme") ? loaderDarkThemePrimaryColor : loaderLightThemePrimaryColor;
64
+ },
65
+ secondaryColor() {
66
+ return document.documentElement.classList.contains("is-dark-theme") ? loaderDarkThemeSecondaryColor : loaderLightThemeSecondaryColor;
67
+ }
59
68
  },
60
69
 
61
70
  data() {
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <tabs-body class="mt-20">
2
+ <tabs-body>
3
3
  <tabs class="is-line">
4
4
  <tab-item :is-active="activeTab === 'edit'">
5
5
  <a @click.prevent="$emit('tabchange', 'edit')">Edit</a>
@@ -0,0 +1,73 @@
1
+ <template>
2
+ <button
3
+ class="button ac-button is-extra-small"
4
+ :class="`${modifierClasses}${isLoaderActive ? ' is-loading' : ''}`"
5
+ :disabled="disabled ? true : null"
6
+ @click="handleClick"
7
+ >
8
+ <span v-if="iconClass || iconImage" class="icon is-small">
9
+ <img
10
+ v-if="iconImage"
11
+ :src="iconImage"
12
+ alt="close-icon"
13
+ :width="iconImageWidth"
14
+ />
15
+ <i v-else :class="`fa fa-${iconClass}`" aria-hidden="true" />
16
+ </span>
17
+ <span v-if="title" :class="titleModifierClass">{{ title }}</span>
18
+ <slot />
19
+ </button>
20
+ </template>
21
+
22
+ <script>
23
+ import { defineComponent } from "vue";
24
+ export default defineComponent({
25
+ props: {
26
+ // button title
27
+ title: {
28
+ type: String,
29
+ default: "",
30
+ },
31
+ // for loader
32
+ isLoaderActive: {
33
+ type: Boolean,
34
+ default: false,
35
+ },
36
+ // for disability
37
+ disabled: {
38
+ type: Boolean,
39
+ default: false,
40
+ },
41
+ // for icon
42
+ iconClass: {
43
+ type: String,
44
+ default: "",
45
+ },
46
+ iconImage: {
47
+ type: String,
48
+ default: "",
49
+ },
50
+ iconImageWidth: {
51
+ type: Number,
52
+ default: undefined,
53
+ },
54
+ // modifier class
55
+ modifierClasses: {
56
+ type: String,
57
+ default: "is-primary",
58
+ },
59
+ titleModifierClass: {
60
+ type: String,
61
+ default: "",
62
+ },
63
+ },
64
+
65
+ emits: ["click"],
66
+
67
+ methods: {
68
+ handleClick(e) {
69
+ if (!this.disabled) this.$emit("click", e);
70
+ },
71
+ },
72
+ });
73
+ </script>
@@ -0,0 +1,54 @@
1
+ <template>
2
+ <div class="ac-content-header" :class="{ 'b-b-1': !removeBorderBottom }">
3
+ <!-- header left start -->
4
+ <div class="ac-cheader-left">
5
+ <!-- title -->
6
+ <div class="ac-content-title">
7
+ <h6>
8
+ <slot name="header-icon" />
9
+ {{ headerTitle }}
10
+ </h6>
11
+ <p v-if="headerSubTitle">{{ headerSubTitle }}</p>
12
+ </div>
13
+ <!-- title -->
14
+ </div>
15
+ <!-- header left end -->
16
+
17
+ <!-- header right start -->
18
+ <div class="ac-cheader-right">
19
+ <!-- your feedom content start here -->
20
+ <header-items>
21
+ <slot />
22
+ </header-items>
23
+ <!-- your feedom content end here -->
24
+ </div>
25
+ <!-- header right end -->
26
+ </div>
27
+ </template>
28
+
29
+ <script>
30
+ import { defineComponent, defineAsyncComponent } from "vue";
31
+
32
+ export default defineComponent({
33
+ props: {
34
+ headerTitle: {
35
+ type: String,
36
+ default: "Content Header",
37
+ },
38
+ headerSubTitle: {
39
+ type: String,
40
+ default: "",
41
+ },
42
+ removeBorderBottom: {
43
+ type: Boolean,
44
+ default: false,
45
+ },
46
+ },
47
+
48
+ components: {
49
+ HeaderItems: defineAsyncComponent(() =>
50
+ import("../../v2/header/HeaderItems.vue").then((module) => module.default)
51
+ ),
52
+ },
53
+ });
54
+ </script>
@@ -0,0 +1,65 @@
1
+ <template>
2
+ <content-layout>
3
+ <template #content-header>
4
+ <content-header
5
+ :header-title="tableTitle"
6
+ :header-sub-title="tableSubTitle"
7
+ :class="{ 'pl-0 pr-0': removeTableHeaderPadding }"
8
+ >
9
+ <slot name="content-left-controls" />
10
+ <header-item>
11
+ <search-bar v-if="searchable" @search="searchText = $event" />
12
+ </header-item>
13
+ <slot name="content-right-controls" />
14
+ </content-header>
15
+ </template>
16
+ <slot name="content" :search-text="searchText" />
17
+ </content-layout>
18
+ </template>
19
+
20
+ <script>
21
+ import { defineComponent, defineAsyncComponent } from "vue";
22
+
23
+ export default defineComponent({
24
+ props: {
25
+ removeTableHeaderPadding: {
26
+ type: Boolean,
27
+ default: false,
28
+ },
29
+ tableTitle: {
30
+ type: String,
31
+ default: "Table",
32
+ },
33
+ tableSubTitle: {
34
+ type: String,
35
+ default: "",
36
+ },
37
+ searchable: {
38
+ type: Boolean,
39
+ default: true,
40
+ },
41
+ },
42
+ components: {
43
+ ContentLayout: defineAsyncComponent(() =>
44
+ import("../../v2/content/ContentLayout.vue").then(
45
+ (module) => module.default
46
+ )
47
+ ),
48
+ ContentHeader: defineAsyncComponent(() =>
49
+ import("./ContentHeader.vue").then((module) => module.default)
50
+ ),
51
+ HeaderItem: defineAsyncComponent(() =>
52
+ import("../../v2/header/HeaderItem.vue").then((module) => module.default)
53
+ ),
54
+ SearchBar: defineAsyncComponent(() =>
55
+ import("../searchbars/SearchBar.vue").then((module) => module.default)
56
+ ),
57
+ },
58
+
59
+ data() {
60
+ return {
61
+ searchText: "",
62
+ };
63
+ },
64
+ });
65
+ </script>
@@ -0,0 +1,3 @@
1
+ <template>
2
+ <hr class="dropdown-divider" />
3
+ </template>
@@ -0,0 +1,5 @@
1
+ <template>
2
+ <div class="dropdown-item">
3
+ <slot />
4
+ </div>
5
+ </template>
@@ -0,0 +1,111 @@
1
+ <template>
2
+ <div
3
+ ref="dropdown"
4
+ class="dropdown"
5
+ :class="{
6
+ 'is-active': isDropdownActive,
7
+ 'dropdown-show-animation': dropdownShowAnimation,
8
+ 'dropdown-hide-animation': dropdownHideAnimation,
9
+ }"
10
+ >
11
+ <div class="dropdown-trigger">
12
+ <slot name="dropdown-trigger" />
13
+ </div>
14
+ <div id="dropdown-menu" class="dropdown-menu" role="menu">
15
+ <div class="dropdown-content">
16
+ <slot />
17
+ </div>
18
+ </div>
19
+ </div>
20
+ </template>
21
+
22
+ <script lang="ts">
23
+ //@ts-ignore
24
+ import { defineComponent, onUnmounted, Ref, ref, toRefs, watch } from "vue";
25
+
26
+ export default defineComponent({
27
+ props: {
28
+ isActive: {
29
+ type: Boolean,
30
+ default: false,
31
+ },
32
+ },
33
+ emits: ["update:is-active"],
34
+
35
+ setup(props, { emit }) {
36
+ const { isActive } = toRefs(props);
37
+ watch(isActive, (n) => {
38
+ if (ignoreClick.value) {
39
+ // ignore new value
40
+ // emit old value
41
+ emit("update:is-active", isDropdownActive.value);
42
+ } else {
43
+ if (n) {
44
+ animateDropdown("show");
45
+ isDropdownActive.value = true;
46
+ } else {
47
+ animateDropdown("hide");
48
+ setTimeout(() => {
49
+ isDropdownActive.value = false;
50
+ }, 200);
51
+ }
52
+ }
53
+ });
54
+
55
+ const dropdown: Ref<HTMLElement | null> = ref(null);
56
+ const isDropdownActive = ref(isActive.value);
57
+
58
+ // attach click event listener on window, and close the dropdown
59
+ function deactivateDropdown(e: Event) {
60
+ const { target } = e;
61
+ if (
62
+ isDropdownActive.value &&
63
+ dropdown.value &&
64
+ dropdown.value !== target &&
65
+ !dropdown.value.contains(target as Node)
66
+ ) {
67
+ if (!ignoreClick.value) {
68
+ animateDropdown("hide");
69
+ setTimeout(() => {
70
+ isDropdownActive.value = false;
71
+ emit("update:is-active", false);
72
+ }, 200);
73
+ }
74
+ }
75
+ }
76
+ window.addEventListener("click", deactivateDropdown);
77
+ onUnmounted(() => {
78
+ window.removeEventListener("click", deactivateDropdown);
79
+ });
80
+
81
+ // for animation
82
+ const dropdownShowAnimation = ref(false);
83
+ const dropdownHideAnimation = ref(false);
84
+ const ignoreClick = ref(false);
85
+ function animateDropdown(type: string) {
86
+ ignoreClick.value = true;
87
+ if (type === "show") {
88
+ dropdownShowAnimation.value = true;
89
+ // remove class after 200 miliseconds
90
+ setTimeout(() => {
91
+ dropdownShowAnimation.value = false;
92
+ ignoreClick.value = false;
93
+ }, 200);
94
+ } else {
95
+ dropdownHideAnimation.value = true;
96
+ setTimeout(() => {
97
+ dropdownHideAnimation.value = false;
98
+ ignoreClick.value = false;
99
+ }, 200);
100
+ }
101
+ }
102
+
103
+ return {
104
+ dropdown,
105
+ isDropdownActive,
106
+ dropdownShowAnimation,
107
+ dropdownHideAnimation,
108
+ };
109
+ },
110
+ });
111
+ </script>