@bimdata/bcf-components 6.6.2 → 6.7.1

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bimdata/bcf-components",
3
- "version": "6.6.2",
3
+ "version": "6.7.1",
4
4
  "files": [
5
5
  "src",
6
6
  "vue3-plugin.js"
@@ -23,7 +23,7 @@
23
23
  "@semantic-release/changelog": "^6.0.3",
24
24
  "@semantic-release/commit-analyzer": "^13.0.1",
25
25
  "@semantic-release/git": "^10.0.1",
26
- "@semantic-release/github": "^12.0.0",
26
+ "@semantic-release/github": "^12.0.1",
27
27
  "@semantic-release/npm": "^13.1.1",
28
28
  "@semantic-release/release-notes-generator": "^14.1.0",
29
29
  "conventional-changelog-eslint": "^6.0.0",
@@ -4,7 +4,6 @@
4
4
  &__content {
5
5
  position: relative;
6
6
  display: flex;
7
- align-items: center;
8
7
 
9
8
  &__chart {
10
9
  flex: 1;
@@ -101,6 +101,10 @@
101
101
  resize: vertical;
102
102
  }
103
103
  }
104
+
105
+ :deep(.bcf-topic-documents-selector) {
106
+ margin-bottom: 30px;
107
+ }
104
108
  }
105
109
  }
106
110
 
@@ -164,6 +164,11 @@
164
164
  :documents="topicDocuments"
165
165
  @selection-change="topicDocuments = $event"
166
166
  />
167
+ <BcfTopicGroups
168
+ :project="project"
169
+ :groups="topicGroups"
170
+ @selection-change="topicGroups = $event"
171
+ />
167
172
  </div>
168
173
  </div>
169
174
 
@@ -210,10 +215,12 @@ import BcfTopicDocumentsSelector from "./bcf-topic-documents-selector/BcfTopicDo
210
215
  import BcfTopicImages from "./bcf-topic-images/BcfTopicImages.vue";
211
216
  import BcfTopicSnapshots from "./bcf-topic-snapshots/BcfTopicSnapshots.vue";
212
217
  import BcfTopicSnapshotsActions from "./bcf-topic-snapshots-actions/BcfTopicSnapshotsActions.vue";
218
+ import BcfTopicGroups from "./bcf-topic-groups/BcfTopicGroups.vue";
213
219
 
214
220
  export default {
215
221
  components: {
216
222
  BcfTopicDocumentsSelector,
223
+ BcfTopicGroups,
217
224
  BcfTopicImages,
218
225
  BcfTopicSnapshots,
219
226
  BcfTopicSnapshotsActions,
@@ -297,6 +304,7 @@ export default {
297
304
  const topicDescription = ref("");
298
305
  const topicLabels = ref([]);
299
306
  const topicDocuments = ref([]);
307
+ const topicGroups = ref([]);
300
308
  const viewpoints = ref([]);
301
309
 
302
310
  const viewpointsToCreate = ref([]);
@@ -326,6 +334,7 @@ export default {
326
334
  topicDescription.value = "";
327
335
  topicLabels.value = [];
328
336
  topicDocuments.value = [];
337
+ topicGroups.value = [];
329
338
  viewpoints.value = [];
330
339
  viewpointsToCreate.value = [];
331
340
  viewpointsToUpdate.value = [];
@@ -349,6 +358,7 @@ export default {
349
358
  topicDescription.value = topic.description || "";
350
359
  topicLabels.value = topic.labels || [];
351
360
  topicDocuments.value = topic.documents || [];
361
+ topicGroups.value = topic.groups || [];
352
362
  viewpoints.value = topic.viewpoints || [];
353
363
  } else {
354
364
  reset();
@@ -466,6 +476,7 @@ export default {
466
476
  due_date: topicDueDate.value,
467
477
  description: topicDescription.value,
468
478
  labels: topicLabels.value,
479
+ groups: topicGroups.value,
469
480
  bimdata_viewer_layout: viewpointsToCreate.value.length > 1 && viewerLayout ? viewerLayout : props.topic?.bimdata_viewer_layout,
470
481
  };
471
482
 
@@ -520,6 +531,7 @@ export default {
520
531
  topicDescription,
521
532
  topicDocuments,
522
533
  topicDueDate,
534
+ topicGroups,
523
535
  topicPriority,
524
536
  topicStage,
525
537
  topicStatus,
@@ -15,7 +15,7 @@
15
15
  <div v-if="isOpenDMS" class="bcf-topic-form__dms">
16
16
  <BIMDataButton ghost radius @click="isOpenDMS = false">
17
17
  <BIMDataIconArrow size="xxs" />
18
- <span class="m-l-6">{{ "BcfComponents.back" }}</span>
18
+ <span class="m-l-6">{{ $t("BcfComponents.back") }}</span>
19
19
  </BIMDataButton>
20
20
  <BIMDataFileManager
21
21
  :locale="$i18n.locale"
@@ -37,7 +37,7 @@
37
37
  radius
38
38
  @click="isOpenDMS = false, $emit('selection-change', topicDocuments)"
39
39
  >
40
- {{ $t("BcfComponents.BcfTopicForm.validateDocuments") }}
40
+ {{ $t("BcfComponents.BcfTopicForm.validate") }}
41
41
  </BIMDataButton>
42
42
  </div>
43
43
  </template>
@@ -0,0 +1,148 @@
1
+ <template>
2
+ <div class="bcf-topic-groups" @click="isOpenGroups = true">
3
+ <span class="label">
4
+ <template v-if="groups.length > 0">
5
+ {{ $t("BcfComponents.BcfTopicForm.groupsCount", { count: groups.length }) }}
6
+ </template>
7
+ <template v-else>
8
+ {{ $t("BcfComponents.BcfTopicForm.groupsLabel") }}
9
+ </template>
10
+ </span>
11
+ <BIMDataIconChevron size="xxs" />
12
+ <div class="underline"></div>
13
+ </div>
14
+
15
+ <div v-if="isOpenGroups" class="bcf-topic-groups__selector">
16
+ <BIMDataButton ghost radius @click="isOpenGroups = false">
17
+ <BIMDataIconArrow size="xxs" />
18
+ <span class="m-l-6">{{ $t("BcfComponents.back") }}</span>
19
+ </BIMDataButton>
20
+ <div class="body">
21
+ <div class="text">
22
+ {{ $t("BcfComponents.BcfTopicForm.groupsText") }}
23
+ </div>
24
+ <div class="list">
25
+ <GroupItem
26
+ v-for="group in projectGroups"
27
+ :key="group.id"
28
+ :group="group"
29
+ :selected="topicGroups.includes(group.id)"
30
+ @toggle="toggleGroup"
31
+ />
32
+ </div>
33
+ </div>
34
+ <BIMDataButton
35
+ width="100%"
36
+ color="primary"
37
+ fill
38
+ radius
39
+ @click="isOpenGroups = false, $emit('selection-change', topicGroups)"
40
+ >
41
+ {{ $t("BcfComponents.BcfTopicForm.validate") }}
42
+ </BIMDataButton>
43
+ </div>
44
+ </template>
45
+
46
+ <script setup>
47
+ import { computed, onMounted, ref } from "vue";
48
+ import service from "../../../service.js";
49
+ import GroupItem from "./GroupItem.vue";
50
+
51
+ const props = defineProps({
52
+ project: {
53
+ type: Object,
54
+ required: true,
55
+ },
56
+ groups: {
57
+ type: Array,
58
+ required: true,
59
+ }
60
+ });
61
+
62
+ defineEmits(["selection-change"]);
63
+
64
+ const isOpenGroups = ref(false);
65
+ const projectGroups = ref([]);
66
+ const selectedGroups = ref([]);
67
+
68
+ const topicGroups = computed(
69
+ () => selectedGroups.value.map(group => group.id)
70
+ );
71
+
72
+ const toggleGroup = group => {
73
+ const index = selectedGroups.value.findIndex(g => g.id === group.id);
74
+ if (index !== -1) {
75
+ selectedGroups.value.splice(index, 1);
76
+ } else {
77
+ selectedGroups.value.push(group);
78
+ }
79
+ };
80
+
81
+ onMounted(async () => {
82
+ projectGroups.value = await service.fetchProjectGroups(props.project);
83
+ selectedGroups.value = projectGroups.value.filter(group => props.groups.includes(group.id));
84
+ });
85
+ </script>
86
+
87
+ <style scoped>
88
+ .bcf-topic-groups {
89
+ position: relative;
90
+ height: 32px;
91
+ display: flex;
92
+ justify-content: space-between;
93
+ align-items: center;
94
+ cursor: pointer;
95
+
96
+ .label {
97
+ font-size: 12px;
98
+ color: var(--color-granite-light);
99
+ }
100
+
101
+ .underline {
102
+ position: absolute;
103
+ bottom: 0;
104
+ width: 100%;
105
+ height: 1px;
106
+ display: block;
107
+ background: var(--color-silver);
108
+ }
109
+ }
110
+
111
+ .bcf-topic-groups__selector {
112
+ position: absolute;
113
+ z-index: 2;
114
+ top: 0;
115
+ left: 0;
116
+ width: 100%;
117
+ height: 100%;
118
+ padding: var(--spacing-unit);
119
+ background-color: var(--color-white);
120
+
121
+ display: flex;
122
+ flex-direction: column;
123
+ align-items: flex-start;
124
+ gap: var(--spacing-unit);
125
+
126
+ .body {
127
+ flex: 1;
128
+ overflow: hidden;
129
+ display: flex;
130
+ flex-direction: column;
131
+
132
+ .text {
133
+ text-align: center;
134
+ color: var(--color-granite);
135
+ }
136
+
137
+ .list {
138
+ margin-top: var(--spacing-unit);
139
+ padding: var(--spacing-unit) 0;
140
+ flex: 1;
141
+ overflow: auto;
142
+ display: flex;
143
+ flex-direction: column;
144
+ gap: var(--spacing-unit);
145
+ }
146
+ }
147
+ }
148
+ </style>
@@ -0,0 +1,70 @@
1
+ <template>
2
+ <div class="group-item">
3
+ <BIMDataCheckbox v-model="checked" />
4
+ <div class="card">
5
+ <div class="stripe" :style="{ backgroundColor: group.color }"></div>
6
+ <BIMDataIconGroup />
7
+ <BIMDataTextbox class="name" cut-position="end" :text="group.name" />
8
+ <UserAvatarList :users="group.members" />
9
+ </div>
10
+ </div>
11
+ </template>
12
+
13
+ <script setup>
14
+ import { ref, watch } from "vue";
15
+ import UserAvatarList from "../../user-avatar/UserAvatarList.vue";
16
+
17
+ const props = defineProps({
18
+ group: {
19
+ type: Object,
20
+ required: true,
21
+ },
22
+ selected: {
23
+ type: Boolean,
24
+ default: false,
25
+ },
26
+ });
27
+
28
+ const emit = defineEmits(["toggle"]);
29
+
30
+ const checked = ref(props.selected);
31
+
32
+ watch(checked, () => emit("toggle", props.group));
33
+ </script>
34
+
35
+ <style scoped>
36
+ .group-item {
37
+ min-height: 50px;
38
+ padding-right: 6px;
39
+
40
+ display: flex;
41
+ justify-content: space-between;
42
+ align-items: center;
43
+ gap: var(--spacing-unit);
44
+
45
+ .card {
46
+ position: relative;
47
+ height: 100%;
48
+ flex: 1;
49
+ padding: var(--spacing-unit) calc(var(--spacing-unit) * 2);
50
+ box-shadow: var(--box-shadow);
51
+
52
+ display: flex;
53
+ align-items: center;
54
+ gap: var(--spacing-unit);
55
+
56
+ .stripe {
57
+ position: absolute;
58
+ top: 0;
59
+ left: 0;
60
+ height: 100%;
61
+ width: 6px;
62
+ }
63
+
64
+ .name {
65
+ width: 0 !important;
66
+ flex: 1;
67
+ }
68
+ }
69
+ }
70
+ </style>
@@ -58,4 +58,35 @@ export default {
58
58
  };
59
59
  </script>
60
60
 
61
- <style scoped lang="scss" src="./UserAvatar.scss"></style>
61
+ <style scoped>
62
+ .user-avatar {
63
+ min-width: 32px;
64
+ min-height: 32px;
65
+
66
+ display: flex;
67
+ justify-content: center;
68
+ align-items: center;
69
+ border-radius: 50%;
70
+ overflow: hidden;
71
+
72
+ img {
73
+ width: 100%;
74
+ height: 100%;
75
+ }
76
+
77
+ &.user-avatar--primary {
78
+ background-color: var(--color-primary);
79
+ color: var(--color-silver-light);
80
+ }
81
+
82
+ &.user-avatar--secondary {
83
+ background-color: var(--color-secondary);
84
+ color: var(--color-primary);
85
+ }
86
+
87
+ &.user-avatar--silver-light {
88
+ background-color: var(--color-silver-light);
89
+ color: var(--color-primary);
90
+ }
91
+ }
92
+ </style>
@@ -0,0 +1,113 @@
1
+ <template>
2
+ <div
3
+ class="user-avatar-list"
4
+ :style="{
5
+ width: `${listWidth}px`,
6
+ gridTemplateColumns: `repeat(${listLength}, ${itemGap}px)`
7
+ }"
8
+ >
9
+ <UserAvatar
10
+ class="user-avatar-list__item"
11
+ v-for="user of displayedUsers"
12
+ :key="user.id"
13
+ :user="user"
14
+ :size="itemSize"
15
+ />
16
+ <template v-if="users.length > length">
17
+ <div
18
+ class="user-avatar-list__tail-item"
19
+ :style="{ width: `${itemSize}px`, height: `${itemSize}px` }"
20
+ >
21
+ {{ `+ ${users.length - length + 1}` }}
22
+ </div>
23
+ </template>
24
+ </div>
25
+ </template>
26
+
27
+ <script>
28
+ import { computed } from "vue";
29
+ // Components
30
+ import UserAvatar from "./UserAvatar.vue";
31
+
32
+ export default {
33
+ components: {
34
+ UserAvatar
35
+ },
36
+ props: {
37
+ users: {
38
+ type: Array,
39
+ required: true
40
+ },
41
+ length: {
42
+ type: Number,
43
+ default: 4
44
+ },
45
+ itemSize: {
46
+ type: [Number, String],
47
+ default: 32,
48
+ validate: value => value >= 32
49
+ },
50
+ itemGap: {
51
+ type: [Number, String],
52
+ default: 24,
53
+ validate: value => value > 0
54
+ },
55
+ fixedWidth: {
56
+ type: Boolean,
57
+ default: false
58
+ }
59
+ },
60
+ setup(props) {
61
+ const displayedUsers = computed(() => {
62
+ const n = props.users.length;
63
+ const l = props.length;
64
+ return props.users.slice(0, n > l ? l - 1 : l);
65
+ });
66
+
67
+ const listLength = computed(() => {
68
+ const n = props.users.length;
69
+ const l = props.length;
70
+ return props.fixedWidth || n > l ? l : n;
71
+ });
72
+
73
+ const listWidth = computed(() => {
74
+ const l = listLength.value;
75
+ const w = +props.itemSize;
76
+ const g = +props.itemGap;
77
+ return l * g + (w > g ? w - g : 0);
78
+ });
79
+
80
+ return {
81
+ // References
82
+ displayedUsers,
83
+ listLength,
84
+ listWidth
85
+ };
86
+ }
87
+ };
88
+ </script>
89
+
90
+ <style scoped>
91
+ .user-avatar-list {
92
+ display: grid;
93
+
94
+ .user-avatar-list__item {
95
+ border: 1px solid var(--color-white);
96
+ }
97
+
98
+ .user-avatar-list__tail-item {
99
+ min-width: 32px;
100
+ min-height: 32px;
101
+
102
+ display: flex;
103
+ justify-content: center;
104
+ align-items: center;
105
+ border: 1px solid var(--color-white);
106
+ border-radius: 50%;
107
+ font-weight: bold;
108
+ background-color: var(--color-primary);
109
+ color: var(--color-silver-light);
110
+ z-index: 1;
111
+ }
112
+ }
113
+ </style>
@@ -73,7 +73,10 @@
73
73
  "emptySearch": "No result",
74
74
  "documentsLabel": "Add a file",
75
75
  "documentsCount": "Files - {count} selected files",
76
- "validateDocuments": "Validate"
76
+ "groupsLabel": "Permissions",
77
+ "groupsCount": "Permissions - {count} selected groups",
78
+ "groupsText": "You are now setting the access rights: choose the user group(s) that will be exclusively authorized to see and interact with this topic.",
79
+ "validate": "Validate"
77
80
  },
78
81
  "BcfTopicOverview": {
79
82
  "openViewer": "Open in viewer",
@@ -73,7 +73,10 @@
73
73
  "emptySearch": "Pas de résultat",
74
74
  "documentsLabel": "Documents",
75
75
  "documentsCount": "Documents - {count} documents sélectionnés",
76
- "validateDocuments": "Valider"
76
+ "groupsLabel": "Permissions",
77
+ "groupsCount": "Permissions - {count} groupes sélectionnés",
78
+ "groupsText": "Sélectionner les groupes utilisateur qui peuvent voir ce topic et intéragir avec.",
79
+ "validate": "Valider"
77
80
  },
78
81
  "BcfTopicOverview": {
79
82
  "openViewer": "Ouvrir dans le viewer",
package/src/service.js CHANGED
@@ -35,6 +35,10 @@ class Service {
35
35
  return this.apiClient.bcfApi.getUser();
36
36
  }
37
37
 
38
+ fetchProjectGroups(project) {
39
+ return this.apiClient.collaborationApi.getManageGroups(project.cloud.id, project.id);
40
+ }
41
+
38
42
  // --- BCF Topics API ---
39
43
 
40
44
  /**
@@ -1,30 +0,0 @@
1
- .user-avatar {
2
- min-width: 32px;
3
- min-height: 32px;
4
-
5
- display: flex;
6
- justify-content: center;
7
- align-items: center;
8
- border-radius: 50%;
9
- overflow: hidden;
10
-
11
- img {
12
- width: 100%;
13
- height: 100%;
14
- }
15
-
16
- &--primary {
17
- background-color: var(--color-primary);
18
- color: var(--color-silver-light);
19
- }
20
-
21
- &--secondary {
22
- background-color: var(--color-secondary);
23
- color: var(--color-primary);
24
- }
25
-
26
- &--silver-light {
27
- background-color: var(--color-silver-light);
28
- color: var(--color-primary);
29
- }
30
- }