@appscode/design-system 2.6.38 → 2.7.2

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 (29) hide show
  1. package/main.scss +2 -0
  2. package/package.json +1 -1
  3. package/vue-components/images/banners/banner-background.png +0 -0
  4. package/vue-components/images/banners/banner-overlay-lg.png +0 -0
  5. package/vue-components/images/banners/banner-overlay.png +0 -0
  6. package/vue-components/styles/base/utilities/_spacing.scss +6 -1
  7. package/vue-components/styles/components/_all.scss +2 -0
  8. package/vue-components/styles/components/_avatar.scss +11 -0
  9. package/vue-components/styles/components/_button.scss +15 -0
  10. package/vue-components/styles/components/_platform-design.scss +206 -0
  11. package/vue-components/styles/components/form-fields/_file-upload.scss +1 -1
  12. package/vue-components/styles/components/form-fields/_input.scss +0 -51
  13. package/vue-components/v3/banner/ProfileBanner.vue +93 -0
  14. package/vue-components/v3/cards/Cluster.vue +5 -3
  15. package/vue-components/v3/cards/LinkableThumb.vue +169 -0
  16. package/vue-components/v3/form-fields/Searchbar.vue +8 -2
  17. package/vue-components/v3/form-fields/SimpleSelect.vue +3 -1
  18. package/vue-components/v3/icons/ArrowIcon.vue +0 -1
  19. package/vue-components/v3/icons/BuildingIcon.vue +13 -0
  20. package/vue-components/v3/icons/CogIcon.vue +11 -0
  21. package/vue-components/v3/icons/LinkIcon.vue +13 -0
  22. package/vue-components/v3/icons/MapIcon.vue +9 -0
  23. package/vue-components/v3/icons/PlusIcon.vue +13 -0
  24. package/vue-components/v3/searchbars/FilterableSearchBar.vue +43 -0
  25. package/vue-components/v3/section/SectionContent.vue +63 -0
  26. package/vue-components/v3/sidebar/basic-sidebar/DropDownItems.vue +47 -0
  27. package/vue-components/v3/sidebar/basic-sidebar/Items.vue +22 -0
  28. package/vue-components/v3/sidebar/basic-sidebar/Sidebar.vue +58 -0
  29. package/vue-components/v3/table/basic-table/Table.vue +58 -0
package/main.scss CHANGED
@@ -16,6 +16,7 @@
16
16
  // COMPONENTS
17
17
  // @import "@/components/vue-components/styles/components/all";
18
18
  @import "@/components/vue-components/styles/components/button";
19
+ @import "@/components/vue-components/styles/components/table";
19
20
  @import "@/components/vue-components/styles/components/avatar";
20
21
  @import "@/components/vue-components/styles/components/terminal";
21
22
  @import "@/components/vue-components/styles/components/steps";
@@ -28,5 +29,6 @@
28
29
  @import "@/components/vue-components/styles/components/ui-builder/ui-builder";
29
30
  @import "@/components/vue-components/styles/components/dropdown";
30
31
  @import "@/components/vue-components/styles/components/badge-tags";
32
+ @import "@/components/vue-components/styles/components/platform-design";
31
33
 
32
34
  // @import "@/components/vue-components/styles/theme/appscode.scss";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@appscode/design-system",
3
- "version": "2.6.38",
3
+ "version": "2.7.2",
4
4
  "description": "A design system for Appscode websites and dashboards made using Bulma",
5
5
  "main": "main.scss",
6
6
  "scripts": {
@@ -99,7 +99,7 @@ $spacing-values: (
99
99
  }
100
100
  }
101
101
 
102
- /* Deafult height & widht */
102
+ /* Default height & width */
103
103
  @for $i from 0 through 300 {
104
104
  .height-#{$i} {
105
105
  height: #{$i}px !important;
@@ -108,6 +108,11 @@ $spacing-values: (
108
108
  .width-#{$i} {
109
109
  width: #{$i}px !important;
110
110
  }
111
+
112
+ .dimension-#{$i} {
113
+ height: #{$i}px !important;
114
+ width: #{$i}px !important;
115
+ }
111
116
  }
112
117
 
113
118
  .height-auto {
@@ -32,10 +32,12 @@
32
32
  @import "tfa";
33
33
  @import "transitions";
34
34
  @import "steps";
35
+ @import "platform-design";
35
36
 
36
37
  // @import "pricing-table";
37
38
  // @import "overview-info";
38
39
  // @import "overview-page"
39
40
  // @import "nested-list";
41
+
40
42
  @import "ui-builder/ui-builder";
41
43
  @import "ui-builder/vue-open-api";
@@ -34,4 +34,15 @@
34
34
  border-radius: 50%;
35
35
  }
36
36
  }
37
+ img {
38
+ background-color: #ddd;
39
+ height: 100%;
40
+ width: 100%;
41
+ border: 1px solid $color-border;
42
+ }
43
+ }
44
+ .avatar-group {
45
+ .avatar:not(:first-child) {
46
+ margin-left: -8px;
47
+ }
37
48
  }
@@ -44,6 +44,9 @@
44
44
  border-color: transparent transparent $ac-primary $ac-primary !important;
45
45
  }
46
46
  }
47
+ &.is-soft:not(:hover) {
48
+ border-color: hsl(var(--primary-hue), 30%, 80%);
49
+ }
47
50
  }
48
51
  }
49
52
  &.is-warning {
@@ -56,6 +59,9 @@
56
59
  border-color: transparent;
57
60
  }
58
61
  }
62
+ &.is-soft:not(:hover) {
63
+ border-color: hsl($yellow-hue, 30%, 80%);
64
+ }
59
65
  }
60
66
  &.is-info {
61
67
  &.is-light {
@@ -67,6 +73,9 @@
67
73
  border-color: transparent;
68
74
  }
69
75
  }
76
+ &.is-soft:not(:hover) {
77
+ border-color: hsl($blue-hue, 30%, 80%);
78
+ }
70
79
  }
71
80
  &.is-danger {
72
81
  &.is-light {
@@ -78,6 +87,9 @@
78
87
  border-color: transparent;
79
88
  }
80
89
  }
90
+ &.is-soft:not(:hover) {
91
+ border-color: hsl($red-hue, 30%, 80%);
92
+ }
81
93
  }
82
94
 
83
95
  &.is-success {
@@ -90,6 +102,9 @@
90
102
  border-color: transparent;
91
103
  }
92
104
  }
105
+ &.is-soft:not(:hover) {
106
+ border-color: hsl($green-hue, 30%, 80%);
107
+ }
93
108
  }
94
109
  &.is-light {
95
110
  &.is-loading {
@@ -0,0 +1,206 @@
1
+ .profile-banner {
2
+ background-color: #e3f0ea;
3
+ background-image: url("../../images/banners/banner-background.png");
4
+ padding-top: 16px;
5
+ min-height: 120px;
6
+ border-bottom: 1px solid $color-border;
7
+ overflow: hidden;
8
+ background-color: #eff9f3;
9
+ position: sticky;
10
+ top: 50px;
11
+ z-index: 999;
12
+
13
+ &::after {
14
+ position: absolute;
15
+ content: "";
16
+ left: 0;
17
+ top: 0;
18
+ width: 100%;
19
+ height: 100%;
20
+ background-color: #e7f4ee;
21
+ background-image: url("../../images/banners/banner-overlay.png");
22
+ background-position: 0% 0%;
23
+ backdrop-filter: blur(142px);
24
+ z-index: -1;
25
+ background-repeat: round;
26
+ }
27
+ }
28
+
29
+ .solid-tab {
30
+ ul {
31
+ border-color: transparent !important;
32
+ }
33
+ li {
34
+ a {
35
+ padding: 16px 0px !important;
36
+ margin-right: 32px;
37
+ transition: 0.3s ease-in-out;
38
+ border-bottom-color: transparent;
39
+ }
40
+
41
+ &.is-active,
42
+ &:hover {
43
+ a {
44
+ border-bottom-color: transparent !important;
45
+ position: relative;
46
+
47
+ &::after {
48
+ position: absolute;
49
+ content: "";
50
+ left: 0;
51
+ bottom: -1px;
52
+ width: 100%;
53
+ height: 4px;
54
+ border-radius: 4px 4px 0 0;
55
+ z-index: 1;
56
+ background-color: $ac-primary;
57
+ }
58
+ }
59
+ }
60
+ }
61
+ }
62
+
63
+ .body-bottom {
64
+ .cluster {
65
+ background-color: $primary-light-gray;
66
+ }
67
+ }
68
+
69
+ .filterable-searchbar {
70
+ width: 100%;
71
+ position: relative;
72
+
73
+ .search-filter {
74
+ border: 1px solid $color-border;
75
+ background-color: #fff;
76
+ position: absolute;
77
+ z-index: 9;
78
+ right: 8px;
79
+ height: 36px;
80
+ top: 8px;
81
+ padding: 8px 16px;
82
+ border-radius: 4px;
83
+ &:focus {
84
+ outline: 1px solid $color-border-dark;
85
+ }
86
+ }
87
+
88
+ .searchbar {
89
+ width: 100%;
90
+ }
91
+
92
+ .ac-single-input {
93
+ input {
94
+ background-color: $primary-light-gray;
95
+ height: 52px !important;
96
+ }
97
+
98
+ label {
99
+ top: 16px !important;
100
+ }
101
+ }
102
+ }
103
+
104
+ // sidebar items
105
+ .sidebar-style-2 {
106
+ border: 1px solid $color-border;
107
+ border-radius: 4px;
108
+ box-shadow: 8px 4px 14px 0px rgba(0, 0, 0, 0.05);
109
+
110
+ .left-sidebar {
111
+ width: 250px;
112
+ }
113
+
114
+ .sidebar-items {
115
+ display: flex;
116
+ flex-direction: column;
117
+
118
+ .sidebar-item {
119
+ display: flex;
120
+ align-items: center;
121
+ gap: 8px;
122
+ color: $color-text;
123
+ padding: 8px 16px;
124
+ transition: 0.1s ease-in-out;
125
+
126
+ &:last-child {
127
+ margin-bottom: 8px;
128
+ }
129
+
130
+ &:hover {
131
+ background-color: $primary-light-gray;
132
+ }
133
+
134
+ &.is-active {
135
+ background-color: $ac-primary;
136
+ color: $white-100;
137
+ }
138
+ }
139
+
140
+ .label {
141
+ border-bottom: 1px solid $color-border;
142
+ font-weight: 400 !important;
143
+ color: $color-heading;
144
+ }
145
+ }
146
+ }
147
+
148
+ .section-card {
149
+ border: 1px solid $color-border;
150
+ border-radius: 4px;
151
+ .section-heading {
152
+ border-bottom: 1px solid $color-border;
153
+ background-color: $secondary-light-grey;
154
+ padding: 8px 16px;
155
+ }
156
+ }
157
+
158
+ .more-avatar {
159
+ display: flex;
160
+ align-items: center;
161
+ justify-content: center;
162
+ width: 24px;
163
+ height: 24px;
164
+ border: 1px solid $color-border;
165
+ border-radius: 50%;
166
+ font-size: 10px;
167
+ padding: 4px;
168
+ cursor: pointer;
169
+ background-color: $primary-light-gray;
170
+ }
171
+ .avatar-group {
172
+ display: flex;
173
+ gap: 4px;
174
+ .more-avatar {
175
+ margin-left: -8px;
176
+ z-index: 9;
177
+ }
178
+ .avatar-link {
179
+ display: inline-block;
180
+ &:hover {
181
+ z-index: 9;
182
+ }
183
+
184
+ &:not(:first-child) {
185
+ margin-left: -4px;
186
+ }
187
+ }
188
+ }
189
+ .ac-back-button {
190
+ background-color: $gray-90;
191
+ border: 1px solid $color-border;
192
+ width: 30px;
193
+ height: 30px;
194
+ border-radius: 50%;
195
+ cursor: pointer;
196
+ color: $gray-20;
197
+ transition: 0.3s ease-in-out;
198
+ display: flex;
199
+ align-items: center;
200
+
201
+ &:hover {
202
+ border: 1px solid $color-border;
203
+ background-color: $ac-primary;
204
+ color: $white-100;
205
+ }
206
+ }
@@ -26,7 +26,7 @@
26
26
  width: 100px;
27
27
  height: 100px;
28
28
  margin-bottom: 24px;
29
- padding: 24px;
29
+ padding: 4px;
30
30
 
31
31
  svg {
32
32
  color: $primary-30;
@@ -578,54 +578,3 @@
578
578
  }
579
579
  }
580
580
  }
581
-
582
- // file upload
583
- .file-upload {
584
- border: 1px dashed $color-border-dark;
585
- border-radius: 4px;
586
-
587
- &:hover {
588
- background-color: $secondary-light-gray;
589
- border-color: $ac-primary;
590
-
591
- label {
592
- .upload-icon {
593
- border: 1px solid $color-border-dark;
594
-
595
- svg {
596
- color: $color-heading;
597
- }
598
- }
599
- }
600
- }
601
-
602
- label {
603
- cursor: pointer;
604
- padding: 32px;
605
- display: block;
606
-
607
- .upload-icon {
608
- border: 1px solid $color-border;
609
- display: inline-flex;
610
- align-items: center;
611
- justify-content: center;
612
- border-radius: 50%;
613
- width: 100px;
614
- height: 100px;
615
- margin-bottom: 24px;
616
- padding: 24px;
617
-
618
- svg {
619
- color: $primary-30;
620
- }
621
- }
622
-
623
- p {
624
- color: $color-heading;
625
-
626
- strong {
627
- font-weight: 600;
628
- }
629
- }
630
- }
631
- }
@@ -0,0 +1,93 @@
1
+ <script setup lang="ts">
2
+ import { defineAsyncComponent, defineProps } from "vue";
3
+ import BuildingIcon from "../icons/BuildingIcon.vue";
4
+ import LinkIcon from "../icons/LinkIcon.vue";
5
+ import MapIcon from "../icons/MapIcon.vue";
6
+
7
+ interface TabItemType {
8
+ label: string;
9
+ href: string;
10
+ icon: any;
11
+ }
12
+
13
+ interface PropsType {
14
+ profileThumbnail?: string;
15
+ displayName?: string;
16
+ username?: string;
17
+ location?: string;
18
+ webURL?: string;
19
+ tabs?: TabItemType[];
20
+ }
21
+
22
+ withDefaults(defineProps<PropsType>(), {
23
+ profileThumbnail: "https://placehold.co/100x100",
24
+ displayName: "John Doe",
25
+ username: "@mohintest1",
26
+ location: "Dhaka",
27
+ webURL: "https://appscode.com",
28
+ tabs: () => [
29
+ { label: "Overview", href: "#", icon: BuildingIcon },
30
+ { label: "Organizations", href: "#", icon: BuildingIcon },
31
+ { label: "Activities", href: "#", icon: BuildingIcon },
32
+ { label: "Settings", href: "#", icon: BuildingIcon },
33
+ { label: "Site Administration", href: "#", icon: BuildingIcon },
34
+ ],
35
+ });
36
+
37
+ // Avatar
38
+ const Avatar = defineAsyncComponent(() => import("../avatar/Avatar.vue"));
39
+ // Tabs
40
+ const TabsComponent = defineAsyncComponent(() => import("../tab/Tabs.vue"));
41
+ const TabItemComponent = defineAsyncComponent(() => import("../tab/TabItem.vue"));
42
+ </script>
43
+
44
+ <template>
45
+ <div class="profile-banner">
46
+ <div class="container">
47
+ <div class="columns is-multiline">
48
+ <div class="column is-12">
49
+ <!-- user profile start -->
50
+ <div class="user-profile">
51
+ <div class="avatar is-flex gap-16">
52
+ <!-- <slot name="avatar"> -->
53
+ <Avatar size="48x48" :imgUrl="profileThumbnail" roundedClass="is-rounded-4" />
54
+ <!-- </slot> -->
55
+ <!-- <Avatar size="48x48" roundedClass="is-rounded-4" /> -->
56
+ <div class="is-flex is-flex-direction-column">
57
+ <h5 class="mb-0">{{ displayName }}</h5>
58
+ <div class="is-flex is-align-items-center gap-24">
59
+ <p>{{ username }}</p>
60
+ <p class="is-flex is-align-items-center gap-4">
61
+ <span class="icon"><MapIcon /></span>
62
+ <span>{{ location }}</span>
63
+ </p>
64
+
65
+ <a :href="`${webURL}`" target="_blank" class="is-flex is-align-items-center gap-4">
66
+ <span class="icon"><LinkIcon /></span>
67
+ <span class="is-underlined">{{ webURL }}</span>
68
+ </a>
69
+ </div>
70
+ </div>
71
+ </div>
72
+ </div>
73
+ <!-- user profile start -->
74
+ </div>
75
+
76
+ <!-- <slot name="tabs-menu"> -->
77
+ <!-- tabs area start -->
78
+ <div class="column is-12">
79
+ <tabs-component class="solid-tab px-3">
80
+ <tab-item-component v-for="(tab, index) in tabs" :key="index" :class="{ 'is-active': index === 0 }">
81
+ <a :href="tab.href">
82
+ <span class="icon"><component :is="tab.icon" /></span>
83
+ <span>{{ tab.label }}</span>
84
+ </a>
85
+ </tab-item-component>
86
+ </tabs-component>
87
+ </div>
88
+ <!-- tabs area end -->
89
+ <!-- </slot> -->
90
+ </div>
91
+ </div>
92
+ </div>
93
+ </template>
@@ -32,7 +32,9 @@ const OptionDots = defineAsyncComponent(() => import("../option-dots/Options.vue
32
32
  <slot name="options" />
33
33
  </option-dots>
34
34
 
35
- <div v-else class="top-right"><slot name="custom-option" /></div>
35
+ <div v-else class="top-right">
36
+ <slot name="custom-option" />
37
+ </div>
36
38
  <div class="card-details-inner" :class="modifierClasses">
37
39
  <div class="c-header">
38
40
  <div class="c-logo">
@@ -48,8 +50,8 @@ const OptionDots = defineAsyncComponent(() => import("../option-dots/Options.vue
48
50
  :key="idx + tag.value"
49
51
  :class="tag.class"
50
52
  :data-testid="idx === 0 ? 'cluster-status-text' : undefined"
51
- class="is-flex is-align-center gap-4"
52
- ><span v-if="tag?.isSpinner || false" class="is-flex gap-4"><SvgSpinners270Ring /></span
53
+ class="is-flex is-align-center gap-6"
54
+ ><span v-if="tag?.isSpinner || false" class="is-flex gap-6"> <SvgSpinners270Ring /> </span
53
55
  >{{ tag.value }}</span
54
56
  >
55
57
  </div>
@@ -0,0 +1,169 @@
1
+ <script setup lang="ts">
2
+ import Avatar from "../avatar/Avatar.vue";
3
+
4
+ interface RightSidebar {
5
+ title?: string;
6
+ logo?: string;
7
+ url?: string;
8
+ viewType?: string;
9
+ members?: Array<{
10
+ name: string;
11
+ avatar: string;
12
+ profileUrl: string;
13
+ }>;
14
+ teams?: Array<{
15
+ name: string;
16
+ color: string;
17
+ }>;
18
+ }
19
+
20
+ withDefaults(defineProps<RightSidebar>(), {
21
+ title: "Appscodelabs",
22
+ logo: "https://placehold.co/100x100",
23
+ url: "#",
24
+ viewType: "",
25
+ members: () => [
26
+ { name: "Alice", avatar: "https://randomuser.me/api/portraits/women/1.jpg", profileUrl: "#" },
27
+ { name: "Bob", avatar: "https://randomuser.me/api/portraits/men/1.jpg", profileUrl: "#" },
28
+ { name: "Charlie", avatar: "https://randomuser.me/api/portraits/women/2.jpg", profileUrl: "#" },
29
+ { name: "Dave", avatar: "https://randomuser.me/api/portraits/men/2.jpg", profileUrl: "#" },
30
+ { name: "Emma", avatar: "https://randomuser.me/api/portraits/women/3.jpg", profileUrl: "#" },
31
+ { name: "Frank", avatar: "https://randomuser.me/api/portraits/men/3.jpg", profileUrl: "#" },
32
+ { name: "Grace", avatar: "https://randomuser.me/api/portraits/women/4.jpg", profileUrl: "#" },
33
+ ],
34
+ teams: () => [
35
+ { name: "Frontend", color: "green" },
36
+ { name: "Backend", color: "purple" },
37
+ { name: "UI", color: "green-light" },
38
+ { name: "Database", color: "blue" },
39
+ { name: "Socialmedia", color: "gray" },
40
+ ],
41
+ });
42
+ </script>
43
+
44
+ <template>
45
+ <div class="teams-members-container">
46
+ <!-- Organization Thumbnail -->
47
+ <div v-if="viewType === 'organization'" class="org-section">
48
+ <h5>Organizations</h5>
49
+ <ul class="org-list">
50
+ <li v-for="i in 8" :key="i">
51
+ <a :href="url" class="org-thumb" :title="title">
52
+ <img :src="logo" :alt="title" />
53
+ </a>
54
+ </li>
55
+ </ul>
56
+ </div>
57
+
58
+ <!-- Teams Section -->
59
+ <div v-if="viewType === 'team-member'" class="teams-section">
60
+ <h5>Teams ({{ teams.length }})</h5>
61
+ <div class="teams-list">
62
+ <span v-for="(team, index) in teams" :key="index" :class="['team-tag', team.color]">
63
+ <a :href="url" :title="title" class="team-name">
64
+ {{ team.name }}
65
+ </a>
66
+ </span>
67
+ </div>
68
+ </div>
69
+
70
+ <!-- Members Section -->
71
+ <div v-if="viewType === 'team-member'" class="members-section">
72
+ <h5>Members ({{ members.length }})</h5>
73
+ <div class="members-list">
74
+ <a v-for="(member, idx) in members" :key="idx" :href="member.profileUrl" target="_blank" class="member-link">
75
+ <Avatar :rounded="true" size="24x24" :imgUrl="member.avatar" />
76
+ </a>
77
+ </div>
78
+ </div>
79
+ </div>
80
+ </template>
81
+
82
+ <style lang="scss">
83
+ .teams-members-container {
84
+ display: flex;
85
+ flex-direction: column;
86
+ gap: 16px;
87
+ }
88
+
89
+ /* Organization Thumbnail */
90
+ .org-list {
91
+ display: flex;
92
+ gap: 8px;
93
+ flex-wrap: wrap;
94
+ }
95
+ .org-thumb {
96
+ border: 1px solid $color-border;
97
+ border-radius: 4px;
98
+ padding: 6px;
99
+ height: 48px;
100
+ width: 48px;
101
+ display: flex;
102
+ align-items: center;
103
+ justify-content: center;
104
+ img {
105
+ height: 32px;
106
+ width: auto;
107
+ background-color: #ddd;
108
+ }
109
+ }
110
+
111
+ /* Teams Section */
112
+ h5 {
113
+ font-size: 1.1rem;
114
+ font-weight: 600;
115
+ }
116
+
117
+ .teams-list {
118
+ display: flex;
119
+ flex-wrap: wrap;
120
+ gap: 8px;
121
+ }
122
+
123
+ .team-tag {
124
+ padding: 6px 12px;
125
+ border-radius: 8px;
126
+ font-size: 0.9rem;
127
+ font-weight: 500;
128
+ color: white;
129
+ a {
130
+ color: white;
131
+ text-decoration: none;
132
+ }
133
+ }
134
+
135
+ /* Team Colors */
136
+ .green {
137
+ background-color: #28a745;
138
+ }
139
+ .purple {
140
+ background-color: #6f42c1;
141
+ }
142
+ .green-light {
143
+ background-color: #17a2b8;
144
+ }
145
+ .blue {
146
+ background-color: #007bff;
147
+ }
148
+ .gray {
149
+ background-color: #6c757d;
150
+ }
151
+
152
+ /* Members Section */
153
+ .members-list {
154
+ display: flex;
155
+ flex-wrap: wrap;
156
+ gap: 8px;
157
+ }
158
+
159
+ .member-link {
160
+ text-decoration: none;
161
+ }
162
+
163
+ .member-avatar {
164
+ width: 40px;
165
+ height: 40px;
166
+ border-radius: 50%;
167
+ object-fit: cover;
168
+ }
169
+ </style>
@@ -1,5 +1,5 @@
1
1
  <script setup lang="ts">
2
- import { defineAsyncComponent } from "vue";
2
+ import { defineAsyncComponent, ref, watch } from "vue";
3
3
 
4
4
  interface Props {
5
5
  placeholder?: string;
@@ -13,7 +13,13 @@ withDefaults(defineProps<Props>(), {
13
13
  loading: false,
14
14
  withResult: false,
15
15
  });
16
+ const emit = defineEmits(["handleSearch"]);
16
17
 
18
+ const searchText = ref("");
19
+
20
+ watch(searchText, (n) => {
21
+ emit("handleSearch", n.trim());
22
+ });
17
23
  const SearchIcon = defineAsyncComponent(() => import("./../icons/SearchIcon.vue"));
18
24
  const RefreshIcon = defineAsyncComponent(() => import("./../icons/RefreshIcon.vue"));
19
25
  </script>
@@ -26,7 +32,7 @@ const RefreshIcon = defineAsyncComponent(() => import("./../icons/RefreshIcon.vu
26
32
  <SearchIcon v-else />
27
33
  </span>
28
34
  </label>
29
- <input id="search" type="text" :placeholder="placeholder" class="pl-40" />
35
+ <input id="search" type="text" :placeholder="placeholder" class="pl-40" v-model="searchText" />
30
36
 
31
37
  <!-- use .is-absolute -->
32
38
  <div v-if="withResult" class="search-result-box panel is-fullwidth z-900">
@@ -15,6 +15,7 @@ interface prop {
15
15
  options?: Array<Option>;
16
16
  isLoading?: boolean;
17
17
  optionType?: "simple" | "custom";
18
+ placeholderText?: string;
18
19
  }
19
20
 
20
21
  const props = withDefaults(defineProps<prop>(), {
@@ -22,6 +23,7 @@ const props = withDefaults(defineProps<prop>(), {
22
23
  options: () => [],
23
24
  isLoading: false,
24
25
  optionType: "simple",
26
+ placeholderText: "Select Option",
25
27
  });
26
28
 
27
29
  const emit = defineEmits(["select", "remove", "onRefreshClick"]);
@@ -88,7 +90,7 @@ watch(isOpen, (n) => {
88
90
  :style="[isOpen ? { 'z-index': 2 } : '']"
89
91
  >
90
92
  <label for="custom-select" class="ac-label" :class="{ 'show-label': labelHoisted || isOpen }" @click="selectClick">
91
- Select Option
93
+ {{ placeholderText }}
92
94
  </label>
93
95
 
94
96
  <input
@@ -13,7 +13,6 @@ withDefaults(defineProps<{ direction?: "up" | "down" }>(), {
13
13
  viewBox="0 0 24 24"
14
14
  stroke-width="1.5"
15
15
  stroke="currentColor"
16
- class="size-6"
17
16
  :style="direction === 'up' ? 'transform: rotate(0deg)' : 'transform: rotate(-180deg)'"
18
17
  >
19
18
  <path stroke-linecap="round" stroke-linejoin="round" d="m19.5 8.25-7.5 7.5-7.5-7.5" />
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
3
+ <!-- Icon from HeroIcons by Refactoring UI Inc - https://github.com/tailwindlabs/heroicons/blob/master/LICENSE -->
4
+ <path
5
+ fill="none"
6
+ stroke="currentColor"
7
+ stroke-linecap="round"
8
+ stroke-linejoin="round"
9
+ stroke-width="1.5"
10
+ d="M2.25 21h19.5m-18-18v18m10.5-18v18m6-13.5V21M6.75 6.75h.75m-.75 3h.75m-.75 3h.75m3-6h.75m-.75 3h.75m-.75 3h.75M6.75 21v-3.375c0-.621.504-1.125 1.125-1.125h2.25c.621 0 1.125.504 1.125 1.125V21M3 3h12m-.75 4.5H21m-3.75 3.75h.008v.008h-.008zm0 3h.008v.008h-.008zm0 3h.008v.008h-.008z"
11
+ />
12
+ </svg>
13
+ </template>
@@ -0,0 +1,11 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
3
+ <!-- Icon from HeroIcons by Refactoring UI Inc - https://github.com/tailwindlabs/heroicons/blob/master/LICENSE -->
4
+ <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
5
+ <path
6
+ d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87q.11.06.22.127c.325.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 0 1 1.37.49l1.296 2.247a1.125 1.125 0 0 1-.26 1.431l-1.003.827c-.293.241-.438.613-.43.992a8 8 0 0 1 0 .255c-.008.378.137.75.43.991l1.004.827c.424.35.534.955.26 1.43l-1.298 2.247a1.125 1.125 0 0 1-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a7 7 0 0 1-.22.128c-.331.183-.581.495-.644.869l-.213 1.281c-.09.543-.56.94-1.11.94h-2.594c-.55 0-1.019-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a7 7 0 0 1-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 0 1-1.369-.49l-1.297-2.247a1.125 1.125 0 0 1 .26-1.431l1.004-.827c.292-.24.437-.613.43-.991a7 7 0 0 1 0-.255c.007-.38-.138-.751-.43-.992l-1.004-.827a1.125 1.125 0 0 1-.26-1.43l1.297-2.247a1.125 1.125 0 0 1 1.37-.491l1.216.456c.356.133.751.072 1.076-.124q.108-.066.22-.128c.332-.183.582-.495.644-.869z"
7
+ />
8
+ <path d="M15 12a3 3 0 1 1-6 0a3 3 0 0 1 6 0" />
9
+ </g>
10
+ </svg>
11
+ </template>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
3
+ <!-- Icon from HeroIcons by Refactoring UI Inc - https://github.com/tailwindlabs/heroicons/blob/master/LICENSE -->
4
+ <path
5
+ fill="none"
6
+ stroke="currentColor"
7
+ stroke-linecap="round"
8
+ stroke-linejoin="round"
9
+ stroke-width="1.5"
10
+ d="M13.19 8.688a4.5 4.5 0 0 1 1.242 7.244l-4.5 4.5a4.5 4.5 0 0 1-6.364-6.364l1.757-1.757m13.35-.622l1.757-1.757a4.5 4.5 0 0 0-6.364-6.364l-4.5 4.5a4.5 4.5 0 0 0 1.242 7.244"
11
+ />
12
+ </svg>
13
+ </template>
@@ -0,0 +1,9 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
3
+ <!-- Icon from HeroIcons by Refactoring UI Inc - https://github.com/tailwindlabs/heroicons/blob/master/LICENSE -->
4
+ <g fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="1.5">
5
+ <path d="M15 10.5a3 3 0 1 1-6 0a3 3 0 0 1 6 0" />
6
+ <path d="M19.5 10.5c0 7.142-7.5 11.25-7.5 11.25S4.5 17.642 4.5 10.5a7.5 7.5 0 1 1 15 0" />
7
+ </g>
8
+ </svg>
9
+ </template>
@@ -0,0 +1,13 @@
1
+ <template>
2
+ <svg xmlns="http://www.w3.org/2000/svg" width="32" height="32" viewBox="0 0 24 24">
3
+ <!-- Icon from HeroIcons by Refactoring UI Inc - https://github.com/tailwindlabs/heroicons/blob/master/LICENSE -->
4
+ <path
5
+ fill="none"
6
+ stroke="currentColor"
7
+ stroke-linecap="round"
8
+ stroke-linejoin="round"
9
+ stroke-width="1.5"
10
+ d="M12 4.5v15m7.5-7.5h-15"
11
+ />
12
+ </svg>
13
+ </template>
@@ -0,0 +1,43 @@
1
+ <script setup>
2
+ import { defineAsyncComponent, defineProps, ref, watch } from "vue";
3
+ const props = defineProps({
4
+ placeholder: {
5
+ type: String,
6
+ default: "Search what you are looking for...",
7
+ },
8
+ isFilter: {
9
+ type: Boolean,
10
+ default: false,
11
+ },
12
+ filterOptions: {
13
+ type: Array,
14
+ default: () => [
15
+ { value: "all-cluster", text: "All Cluster" },
16
+ { value: "active", text: "Active" },
17
+ { value: "pending", text: "Pending" },
18
+ ],
19
+ },
20
+ });
21
+ const emit = defineEmits(["handleSearch", "handleFilter"]);
22
+ function handleSearch(searchText) {
23
+ emit("handleSearch", searchText);
24
+ }
25
+
26
+ const selectedFilter = ref("all-cluster");
27
+
28
+ watch(selectedFilter, (n) => {
29
+ emit("handleFilter", n);
30
+ });
31
+
32
+ const Searchbar = defineAsyncComponent(() => import("../form-fields/Searchbar.vue"));
33
+ </script>
34
+ <template>
35
+ <div class="filterable-searchbar is-flex">
36
+ <Searchbar :placeholder="props.placeholder" @handleSearch="handleSearch" />
37
+ <select class="select search-filter" v-show="props.isFilter" v-model="selectedFilter">
38
+ <option v-for="option in filterOptions" :key="option.value" :value="option.value">
39
+ {{ option.text }}
40
+ </option>
41
+ </select>
42
+ </div>
43
+ </template>
@@ -0,0 +1,63 @@
1
+ <script setup lang="ts">
2
+ import { ref } from "vue";
3
+ import ArrowIcon from "../icons/ArrowIcon.vue";
4
+ import AcButton from "../button/Button.vue";
5
+ import HeaderItem from "../header/HeaderItem.vue";
6
+ import HeaderItems from "../header/HeaderItems.vue";
7
+ import HeroiconsArrowUturnLeft20Solid from "~icons/heroicons/arrow-uturn-left-20-solid.svg";
8
+
9
+ interface Props {
10
+ title?: string;
11
+ isExpandable?: boolean;
12
+ custom?: boolean;
13
+ hasBackButton?: boolean;
14
+ }
15
+
16
+ withDefaults(defineProps<Props>(), {
17
+ title: "",
18
+ isExpandable: false,
19
+ custom: false,
20
+ hasBackButton: false,
21
+ });
22
+
23
+ const isOpen = ref(true);
24
+
25
+ const emit = defineEmits(["handleBack"]);
26
+
27
+ function handleBack() {
28
+ emit("handleBack");
29
+ }
30
+ </script>
31
+
32
+ <template>
33
+ <div class="content-body">
34
+ <div class="section-card is-fullwidth">
35
+ <div class="section-heading is-fullwidth is-flex is-align-items-center is-justify-content-space-between">
36
+ <header-items>
37
+ <button v-if="hasBackButton" title="Back" class="ac-back-button" @click="handleBack">
38
+ <HeroiconsArrowUturnLeft20Solid />
39
+ </button>
40
+ <header-item>
41
+ <div v-if="custom">
42
+ <slot name="custom-header" />
43
+ </div>
44
+ <div v-else>
45
+ <h4 :key="title">{{ title }}</h4>
46
+ </div>
47
+ </header-item>
48
+ </header-items>
49
+ <!-- Add buttons here -->
50
+ <div class="buttons is-right">
51
+ <slot name="header-buttons" />
52
+ <AcButton modifier-classes="is-white" v-if="isExpandable" @click="isOpen = !isOpen">
53
+ <ArrowIcon :direction="isOpen ? 'down' : 'up'" />
54
+ </AcButton>
55
+ </div>
56
+ </div>
57
+
58
+ <div v-show="isOpen" class="section-body">
59
+ <slot name="section-body" />
60
+ </div>
61
+ </div>
62
+ </div>
63
+ </template>
@@ -0,0 +1,47 @@
1
+ <script setup lang="ts">
2
+ import { ref, watch } from "vue";
3
+ import HeroiconsChevronDown20Solid from "~icons/heroicons/chevron-down-20-solid";
4
+
5
+ interface Props {
6
+ title: string;
7
+ isOpen?: boolean;
8
+ hasIcon?: boolean;
9
+ }
10
+
11
+ const props = withDefaults(defineProps<Props>(), {
12
+ isOpen: false,
13
+ hasIcon: false,
14
+ });
15
+
16
+ const dropDownStatus = ref<"close" | "open">("close");
17
+
18
+ const toggleDropDownStatus = () => {
19
+ if (dropDownStatus.value === "open") dropDownStatus.value = "close";
20
+ else dropDownStatus.value = "open";
21
+ };
22
+
23
+ watch(
24
+ () => props.isOpen,
25
+ (n) => {
26
+ if (n) {
27
+ dropDownStatus.value = "open";
28
+ }
29
+ },
30
+ { immediate: true },
31
+ );
32
+ </script>
33
+
34
+ <template>
35
+ <div
36
+ class="label pt-10 pb-10 pl-16 pr-16 b-b-1 is-flex is-align-items-center is-justify-content-space-between is-fullwidth is-clickable"
37
+ @click="toggleDropDownStatus"
38
+ >
39
+ <div class="is-flex gap-8">
40
+ <span v-if="hasIcon" class="icon"> <slot name="drop-down-icon" /> </span>{{ title }}
41
+ </div>
42
+ <HeroiconsChevronDown20Solid
43
+ :style="{ transform: dropDownStatus === 'open' ? 'rotate(180deg)' : 'none', transition: '0.2s' }"
44
+ />
45
+ </div>
46
+ <slot v-if="dropDownStatus === 'open'" name="drop-down-items" />
47
+ </template>
@@ -0,0 +1,22 @@
1
+ <script setup lang="ts">
2
+ interface Props {
3
+ title: string;
4
+ id: string;
5
+ url?: string;
6
+ isActive?: boolean;
7
+ }
8
+
9
+ withDefaults(defineProps<Props>(), {
10
+ isActive: false,
11
+ url: "",
12
+ });
13
+ </script>
14
+
15
+ <template>
16
+ <div class="sidebar-item is-clickable" :class="{ 'is-active': isActive }">
17
+ <span class="icon">
18
+ <slot name="item-icon" />
19
+ </span>
20
+ <span>{{ title }}</span>
21
+ </div>
22
+ </template>
@@ -0,0 +1,58 @@
1
+ <script setup lang="ts"></script>
2
+
3
+ <template>
4
+ <div class="sidebar-style-2">
5
+ <div class="left-sidebar">
6
+ <div class="sidebar-items">
7
+ <slot name="items-with-drop-down" />
8
+ </div>
9
+ </div>
10
+ </div>
11
+ </template>
12
+
13
+ <style lang="scss" scoped>
14
+ .sidebar-style-2 {
15
+ border: 1px solid $color-border;
16
+ border-radius: 4px;
17
+ box-shadow: 8px 4px 14px 0px rgba(0, 0, 0, 0.05);
18
+
19
+ .left-sidebar {
20
+ width: 250px;
21
+ }
22
+
23
+ .sidebar-items {
24
+ display: flex;
25
+ flex-direction: column;
26
+
27
+ .sidebar-item {
28
+ display: flex;
29
+ align-items: center;
30
+ gap: 8px;
31
+ color: $color-text;
32
+ padding: 8px 16px;
33
+ transition: 0.1s ease-in-out;
34
+
35
+ &:last-child {
36
+ margin-bottom: 8px;
37
+ }
38
+
39
+ &:hover {
40
+ background-color: $primary-light-gray;
41
+ }
42
+
43
+ &.is-active {
44
+ background-color: $ac-primary;
45
+ color: $white-100;
46
+ }
47
+ }
48
+
49
+ label {
50
+ padding: 8px 16px;
51
+ border-bottom: 1px solid $color-border;
52
+ text-transform: uppercase;
53
+ font-weight: 400;
54
+ color: #616161;
55
+ }
56
+ }
57
+ }
58
+ </style>
@@ -0,0 +1,58 @@
1
+ <script setup lang="ts">
2
+ import Avatar from "../../avatar/Avatar.vue";
3
+
4
+ interface Organization {
5
+ name: string;
6
+ logo: string;
7
+ description: string;
8
+ clusters: number;
9
+ members: Array<{
10
+ name: string;
11
+ avatar: string;
12
+ }>;
13
+ }
14
+
15
+ defineProps<{ organizations: Organization[] }>();
16
+ </script>
17
+
18
+ <template>
19
+ <table class="table ac-table is-fullwidth b-1 is-middle">
20
+ <thead>
21
+ <tr>
22
+ <th>Organizations</th>
23
+ <th class="has-text-right">Clusters</th>
24
+ <th class="has-text-right">Members</th>
25
+ </tr>
26
+ </thead>
27
+
28
+ <tbody>
29
+ <tr v-for="(org, index) in organizations" :key="index" class="is-clickable">
30
+ <td>
31
+ <div class="is-flex gap-8">
32
+ <div class="org-thumb">
33
+ <img :src="org.logo" :alt="org.name" />
34
+ </div>
35
+ <div class="is-flex is-flex-direction-column">
36
+ <h5>{{ org.name }}</h5>
37
+ <p>{{ org.description }}</p>
38
+ </div>
39
+ </div>
40
+ </td>
41
+
42
+ <td class="has-text-right">{{ org.clusters }}</td>
43
+ <td class="has-text-right">
44
+ <div class="is-flex is-justify-content-flex-end avatar-group">
45
+ <Avatar
46
+ v-for="(member, idx) in org.members"
47
+ :key="idx"
48
+ :rounded="true"
49
+ size="24x24"
50
+ :src="member.avatar"
51
+ :alt="member.name"
52
+ />
53
+ </div>
54
+ </td>
55
+ </tr>
56
+ </tbody>
57
+ </table>
58
+ </template>