projects_dropdown_filter.vue 5.12 KB
Newer Older
1
2
3
<script>
import $ from 'jquery';
import _ from 'underscore';
4
import { GlLoadingIcon, GlButton, GlAvatar } from '@gitlab/ui';
5
6
import Icon from '~/vue_shared/components/icon.vue';
import { sprintf, n__, s__, __ } from '~/locale';
7
import Api from '~/api';
8
import { renderAvatar, renderIdenticon } from '~/helpers/avatar_helper';
9
10
11
12
13
14
15

export default {
  name: 'ProjectsDropdownFilter',
  components: {
    Icon,
    GlLoadingIcon,
    GlButton,
16
    GlAvatar,
17
18
19
20
21
22
  },
  props: {
    groupId: {
      type: Number,
      required: true,
    },
23
24
25
26
27
    multiSelect: {
      type: Boolean,
      required: false,
      default: false,
    },
28
29
30
31
32
    label: {
      type: String,
      required: false,
      default: s__('CycleAnalytics|project dropdown filter'),
    },
33
34
35
36
37
    queryParams: {
      type: Object,
      required: false,
      default: () => ({}),
    },
38
39
40
41
42
    defaultProjects: {
      type: Array,
      required: false,
      default: () => [],
    },
43
44
45
46
  },
  data() {
    return {
      loading: true,
47
      selectedProjects: this.defaultProjects || [],
48
49
50
    };
  },
  computed: {
51
52
53
54
55
56
57
58
59
60
61
62
63
64
    selectedProjectsLabel() {
      return this.selectedProjects.length
        ? sprintf(
            n__(
              'CycleAnalytics|%{projectName}',
              'CycleAnalytics|%d projects selected',
              this.selectedProjects.length,
            ),
            { projectName: this.selectedProjects[0].name },
          )
        : this.selectedProjectsPlaceholder;
    },
    selectedProjectsPlaceholder() {
      return this.multiSelect ? __('Select projects') : __('Select a project');
65
    },
66
67
68
    isOnlyOneProjectSelected() {
      return this.selectedProjects.length === 1;
    },
69
70
71
72
73
74
75
  },
  mounted() {
    $(this.$refs.projectsDropdown).glDropdown({
      selectable: true,
      filterable: true,
      filterRemote: true,
      fieldName: 'project_id',
76
      multiSelect: this.multiSelect,
77
78
79
      search: {
        fields: ['name'],
      },
80
81
      clicked: this.onClick.bind(this),
      data: this.fetchData.bind(this),
82
      renderRow: project => this.rowTemplate(project),
83
      text: project => project.name,
84
      opened: e => e.target.querySelector('.dropdown-input-field').focus(),
85
86
87
    });
  },
  methods: {
88
89
90
91
92
    getSelectedProjects(selectedProject, isMarking) {
      return isMarking
        ? this.selectedProjects.concat([selectedProject])
        : this.selectedProjects.filter(project => project.id !== selectedProject.id);
    },
93
94
95
    singleSelectedProject(selectedObj, isMarking) {
      return isMarking ? [selectedObj] : [];
    },
96
97
98
    setSelectedProjects(selectedObj, isMarking) {
      this.selectedProjects = this.multiSelect
        ? this.getSelectedProjects(selectedObj, isMarking)
99
        : this.singleSelectedProject(selectedObj, isMarking);
100
101
    },
    onClick({ selectedObj, e, isMarking }) {
102
      e.preventDefault();
103
104
      this.setSelectedProjects(selectedObj, isMarking);
      this.$emit('selected', this.selectedProjects);
105
106
107
    },
    fetchData(term, callback) {
      this.loading = true;
108
      return Api.groupProjects(this.groupId, term, this.queryParams, projects => {
109
110
111
112
113
        this.loading = false;
        callback(projects);
      });
    },
    rowTemplate(project) {
114
115
116
117
      const selected = this.defaultProjects.length
        ? this.defaultProjects.find(p => p.id === project.id)
        : false;
      const isActiveClass = selected ? 'is-active' : '';
118
119
      return `
          <li>
120
            <a href='#' class='dropdown-menu-link ${isActiveClass}'>
121
122
              ${this.avatarTemplate(project)}
              <div class="align-middle">${_.escape(project.name)}</div>
123
124
125
126
            </a>
          </li>
        `;
    },
127
128
129
130
131
132
133
134
    avatarTemplate(project) {
      const identiconSizeClass = 's16 rect-avatar d-flex justify-content-center flex-column';
      return project.avatar_url
        ? renderAvatar(project, { sizeClass: 's16 rect-avatar' })
        : renderIdenticon(project, {
            sizeClass: identiconSizeClass,
          });
    },
135
136
137
138
139
140
141
142
143
144
145
146
  },
};
</script>

<template>
  <div>
    <div ref="projectsDropdown" class="dropdown dropdown-projects">
      <gl-button
        class="dropdown-menu-toggle wide shadow-none bg-white"
        type="button"
        data-toggle="dropdown"
        aria-expanded="false"
147
        :aria-label="label"
148
      >
149
150
151
152
153
154
155
156
        <gl-avatar
          v-if="isOnlyOneProjectSelected"
          :src="selectedProjects[0].avatar_url"
          :entity-id="selectedProjects[0].id"
          :entity-name="selectedProjects[0].name"
          :size="16"
          shape="rect"
          :alt="selectedProjects[0].name"
157
          class="d-inline-flex align-text-bottom"
158
        />
159
160
        {{ selectedProjectsLabel }}
        <icon name="chevron-down" />
161
162
163
164
165
166
167
168
      </gl-button>
      <div class="dropdown-menu dropdown-menu-selectable dropdown-menu-full-width">
        <div class="dropdown-title">{{ __('Projects') }}</div>
        <div class="dropdown-input">
          <input class="dropdown-input-field" type="search" :placeholder="__('Search projects')" />
          <icon name="search" class="dropdown-input-search" data-hidden="true" />
        </div>
        <div class="dropdown-content"></div>
169
        <gl-loading-icon class="dropdown-loading" />
170
171
172
173
      </div>
    </div>
  </div>
</template>