Skip to content

Commit af77679

Browse files
Merge pull request codex-team#285 from codex-team/select-component-integration
feat: select component integration
2 parents 6eb3502 + d92b7f6 commit af77679

7 files changed

Lines changed: 127 additions & 57 deletions

File tree

codex-ui/dev/pages/components/Select.vue

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,20 +5,73 @@
55
Component of the form that allows you to select one or more options from the list (currently one)
66
</template>
77
</PageHeader>
8-
<Select
9-
:items="options"
10-
:default-item="defaultItem"
11-
/>
8+
<div class="ex">
9+
Default
10+
<Select
11+
v-model="currentItem"
12+
:align="{vertically: 'below', horizontally: 'left'}"
13+
:is-disabled="false"
14+
:items="options"
15+
/>
16+
Disabled
17+
<Select
18+
v-model="currentItem"
19+
:align="{vertically: 'below', horizontally: 'left'}"
20+
:is-disabled="true"
21+
:items="options"
22+
/>
23+
</div>
24+
<Heading :level="2">
25+
Getting selected option
26+
</Heading>
27+
Selected option replaces default value and can be easily got from the same object:
28+
<div class="ex">
29+
<i>{{ currentItem }}</i>
30+
</div>
31+
<Heading
32+
:level="2"
33+
>
34+
Aligning
35+
</Heading>
36+
You can choose vertical aligning from <code>below</code> and <code>above</code> and horizontal aligning from <code>left</code> and <code>right</code>
37+
<div class="ex">
38+
<Select
39+
v-model="currentItem"
40+
:align="{vertically: 'below', horizontally: 'left'}"
41+
:is-disabled="false"
42+
:items="options"
43+
/>
44+
<Select
45+
v-model="currentItem"
46+
:align="{vertically: 'below', horizontally: 'right'}"
47+
:is-disabled="false"
48+
:items="options"
49+
/>
50+
<Select
51+
v-model="currentItem"
52+
:align="{vertically: 'above', horizontally: 'left'}"
53+
:is-disabled="false"
54+
:items="options"
55+
/>
56+
<Select
57+
v-model="currentItem"
58+
:align="{vertically: 'above', horizontally: 'right'}"
59+
:is-disabled="false"
60+
:items="options"
61+
/>
62+
</div>
1263
</template>
1364

1465
<script setup lang="ts">
1566
import PageHeader from '../../components/PageHeader.vue';
16-
import { ContextMenuItem, DefaultItem, Select } from '../../../src';
67+
import { ContextMenuItem, Heading, Select } from '../../../src';
68+
import { ref } from 'vue';
1769
18-
const defaultItem: DefaultItem = {
70+
const currentItem = ref({
1971
title: 'Choose an option',
2072
onActivate: () => {},
21-
};
73+
});
74+
2275
const options: ContextMenuItem[] = [
2376
{
2477
type: 'default',
@@ -50,4 +103,11 @@ const options: ContextMenuItem[] = [
50103
</script>
51104

52105
<style scoped>
106+
.ex {
107+
display: grid;
108+
gap: var(--spacing-xl);
109+
margin: var(--spacing-xl) 0 var(--spacing-xxl);
110+
grid-template-columns: repeat(2, max-content);
111+
align-items: center;
112+
}
53113
</style>

codex-ui/src/vue/components/popover/Popover.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,10 @@ onClickOutside(popoverEl, hide, {
5151
border-radius: var(--radius-field);
5252
border: 1px solid var(--base--border);
5353
padding: var(--h-padding);
54+
box-sizing: border-box;
5455
left: v-bind('position.left');
5556
top: v-bind('position.top');
5657
transform: v-bind('position.transform');
5758
width: v-bind('width');
58-
box-sizing: border-box;
5959
}
6060
</style>

codex-ui/src/vue/components/popover/usePopover.ts

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -36,8 +36,8 @@ export const usePopover = createSharedComposable(() => {
3636
* Popover position info used in the Popover component
3737
*/
3838
const position = reactive({
39-
left: 0,
40-
top: 0,
39+
left: '0px',
40+
top: '0px',
4141
transform: 'translate(0, 0)',
4242
});
4343

@@ -68,29 +68,29 @@ export const usePopover = createSharedComposable(() => {
6868

6969
const rect = targetEl.getBoundingClientRect();
7070

71-
let top = 0;
72-
let left = 0;
71+
let top = '0px';
72+
let left = '0px';
7373
let transformX = '0';
7474
let transformY = '0';
7575

7676
switch (align.vertically) {
7777
case 'above':
78-
top = rect.top - MARGIN + window.scrollY;
78+
top = `${rect.top - MARGIN + window.scrollY}px`;
7979
transformY = '-100%';
8080
break;
8181
case 'below':
82-
top = rect.bottom + MARGIN + window.scrollY;
82+
top = `${rect.bottom + MARGIN + window.scrollY}px`;
8383
transformY = '0';
8484
break;
8585
}
8686

8787
switch (align.horizontally) {
8888
case 'left':
89-
left = rect.left;
89+
left = `${rect.left}px`;
9090
transformX = '0';
9191
break;
9292
case 'right':
93-
left = rect.right;
93+
left = `${rect.right}px`;
9494
transformX = '-100';
9595
break;
9696
}
@@ -145,8 +145,8 @@ export const usePopover = createSharedComposable(() => {
145145
function resetPopover(): void {
146146
targetElement.value = null;
147147
content.value = null;
148-
position.left = 0;
149-
position.top = 0;
148+
position.left = '0px';
149+
position.top = '0px';
150150
position.transform = 'translate(0, 0)';
151151

152152
isOpen.value = false;

codex-ui/src/vue/components/select/Select.vue

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,33 @@
11
<template>
22
<Button
3-
:icon="activeItem.icon"
3+
:icon="currentItem.icon"
4+
:disabled="isDisabled"
45
trailing-icon="BracketsVertical"
56
secondary
6-
@click="togglePopover($event.currentTarget, {vertically: 'below', horizontally: 'left'})"
7+
@click="togglePopover($event.currentTarget)"
78
>
8-
{{ activeItem.title }}
9+
{{ currentItem.title }}
910
</Button>
1011
</template>
1112
<script setup lang="ts">
1213
import type { ContextMenuItem as SelectItem, DefaultItem } from '../context-menu/ContextMenu.types';
1314
import { ContextMenu } from '../context-menu';
14-
import { onMounted, ref } from 'vue';
15+
import { onMounted } from 'vue';
1516
import { usePopover, PopoverShowParams } from '../popover';
1617
import { Button } from '../button';
1718
1819
const props = defineProps<{
20+
align: PopoverShowParams['align'];
21+
isDisabled: boolean;
1922
items: SelectItem[];
20-
defaultItem: DefaultItem;
2123
}>();
2224
25+
const align = props.align;
2326
const items = props.items;
2427
const { showPopover, hide, isOpen } = usePopover();
2528
26-
const togglePopover = (el: HTMLElement, align: PopoverShowParams['align']) => {
27-
if (!isOpen.value) {
29+
const togglePopover = (el: HTMLElement) => {
30+
if (!isOpen.value && !props.isDisabled) {
2831
showPopover({
2932
targetEl: el,
3033
with: {
@@ -41,12 +44,11 @@ const togglePopover = (el: HTMLElement, align: PopoverShowParams['align']) => {
4144
};
4245
4346
/* Default item value for select on page load */
44-
const defaultValue: SelectItem = props.defaultItem;
45-
const activeItem = ref(defaultValue);
47+
const currentItem = defineModel<DefaultItem>({ required: true });
4648
4749
/* Main function to update selected item */
4850
const updateActiveItem = (item: DefaultItem) => {
49-
activeItem.value = item;
51+
currentItem.value = item;
5052
hide();
5153
};
5254

src/domain/entities/Note.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,4 +51,9 @@ export interface Note {
5151
* @todo Resolve the optionality issue
5252
*/
5353
updatedAt?: string;
54+
55+
/**
56+
* Id of user, who created the note
57+
*/
58+
creatorId?: number;
5459
}
Lines changed: 31 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,22 @@
11
<template>
2-
<div v-if="teamMember.user.id != user?.id">
3-
<select
2+
<div>
3+
<Select
44
v-model="selectedRole"
5-
@change="updateMemberRole"
6-
>
7-
<option
8-
v-for="(role, index) in roleOptions"
9-
:key="index"
10-
:value="role"
11-
>
12-
{{ t(`noteSettings.team.roles.${role}`) }}
13-
</option>
14-
</select>
5+
:align="{ vertically: 'below', horizontally: 'right' }"
6+
:is-disabled="teamMember.user.id == user?.id || (note !== null && (note as Note).creatorId === teamMember.id)"
7+
:items="roleItems"
8+
/>
159
</div>
1610
</template>
1711

1812
<script setup lang="ts">
1913
import { MemberRole, TeamMember } from '@/domain/entities/Team.ts';
20-
import { NoteId } from '@/domain/entities/Note.ts';
21-
import { computed, ref } from 'vue';
14+
import { Note, NoteId } from '@/domain/entities/Note.ts';
15+
import { computed, ref, watch } from 'vue';
2216
import useNoteSettings from '@/application/services/useNoteSettings.ts';
2317
import { useAppState } from '@/application/services/useAppState';
24-
import { useI18n } from 'vue-i18n';
18+
import { ContextMenuItem, DefaultItem, Select } from 'codex-ui/vue';
19+
import useNote from '@/application/services/useNote.ts';
2520
2621
/**
2722
* TeamMember props
@@ -37,31 +32,38 @@ const props = defineProps<{
3732
noteId: NoteId;
3833
}>();
3934
40-
const selectedRole = ref(MemberRole[props.teamMember.role]);
35+
const selectedRole = ref<DefaultItem>({
36+
title: MemberRole[props.teamMember.role],
37+
onActivate: () => {},
38+
});
4139
4240
const roleOptions = computed(() => Object.values(MemberRole).filter(value => typeof value === 'string'));
41+
const roleItems: ContextMenuItem[] = [];
4342
44-
const { changeRole } = useNoteSettings();
43+
roleOptions.value.forEach((role) => {
44+
roleItems.push({
45+
title: role.toString(),
46+
onActivate: () => {},
47+
});
48+
});
4549
50+
const { changeRole } = useNoteSettings();
51+
const { note } = useNote({ id: props.noteId });
4652
const { user } = useAppState();
4753
48-
const { t } = useI18n();
49-
54+
/* Watch role's update */
55+
watch(selectedRole, (newRole) => {
56+
updateMemberRole(newRole.title);
57+
});
5058
/**
5159
* Updates the user role if it has been changed
60+
*
61+
* @param updatedRole - new role needed to set
5262
*/
53-
async function updateMemberRole() {
54-
changeRole(props.noteId, props.teamMember.user.id, MemberRole[selectedRole.value as keyof typeof MemberRole]);
63+
async function updateMemberRole(updatedRole: string | any) {
64+
changeRole(props.noteId, props.teamMember.user.id, MemberRole[updatedRole as keyof typeof MemberRole]);
5565
}
5666
</script>
5767

5868
<style scoped>
59-
.member {
60-
margin-top: var(--spacing-l);
61-
}
62-
.member-name {
63-
display: flex;
64-
align-items: center;
65-
gap: var(--spacing-very-x);
66-
}
6769
</style>

src/presentation/components/team/Team.vue

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
:key="member.id"
99
:title="member.user.name || member.user.email"
1010
:has-delimiter="memberIndex !== team.length - 1"
11+
data-dimensions="medium"
1112
>
1213
<template #right>
1314
<RoleSelect

0 commit comments

Comments
 (0)