Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

frontend fixes #970

Open
wants to merge 1 commit into
base: notes-feature
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/assets/constants/colors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export const reviewColors = {
};

export const coursesColorSet = [
// note: can delete lighterhex now
{
text: 'Red',
hex: '#FF3B30',
Expand Down
2 changes: 1 addition & 1 deletion src/assets/images/notes/arrow.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
3 changes: 3 additions & 0 deletions src/assets/images/notes/checkmark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
2 changes: 1 addition & 1 deletion src/components/BottomBar/BottomBar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default defineComponent({
.bottombar {
display: flex;
flex-direction: column;
z-index: 1;
z-index: 2;
position: fixed;
width: calc(100vw - 29.5rem);
margin-left: 29.5rem;
Expand Down
38 changes: 18 additions & 20 deletions src/components/Course/Course.vue
Original file line number Diff line number Diff line change
Expand Up @@ -80,27 +80,25 @@
</div>
</div>
</div>
<course-menu
v-if="menuOpen"
:courseObj="courseObj"
:semesterIndex="semesterIndex"
:isCompact="compact"
:courseColor="courseObj.color"
:courseCode="courseObj.code"
@open-note-modal="openNoteModal"
@open-edit-color-modal="openEditColorModal"
@delete-course="deleteCourse"
@edit-course-credit="editCourseCredit"
@open-save-course-modal="openSaveCourseModal"
:getCreditRange="getCreditRange || []"
v-click-outside="closeMenuIfOpen"
/>
</div>
<course-menu
v-if="menuOpen"
:courseObj="courseObj"
:semesterIndex="semesterIndex"
:isCompact="compact"
:courseColor="courseObj.color"
:courseCode="courseObj.code"
@open-note-modal="openNoteModal"
@open-edit-color-modal="openEditColorModal"
@delete-course="deleteCourse"
@edit-course-credit="editCourseCredit"
@open-save-course-modal="openSaveCourseModal"
:getCreditRange="getCreditRange || []"
v-click-outside="closeMenuIfOpen"
/>
<Note
v-if="isNoteVisible"
class="note"
:initialTranslateY="'-67px'"
:expandedTranslateY="'-35px'"
:width="'calc(102.8% - 10px)'"
:color="cssVars['--bg-color']"
:initialNote="courseObj.note || ''"
Expand All @@ -109,6 +107,7 @@
@open-delete-note-modal="openDeleteNoteModal"
ref="note"
v-click-outside="handleClickOutsideNote"
:noteId="`course-${courseObj.uniqueID}`"
/>
</div>
</template>
Expand Down Expand Up @@ -137,7 +136,7 @@ interface MinimalNoteComponent {
isDirty: boolean;
isExpanded: boolean;
collapseNote: () => void;
expandNote: () => void;
expandNote: () => void; // Note: This function does not exist in the Note component, but is used here.
}

export default defineComponent({
Expand Down Expand Up @@ -387,7 +386,6 @@ export default defineComponent({

.course-container {
position: relative;
padding-bottom: 20px;
}

// Emulates a slight side-to-side sway à la Figma micro-interaction.
Expand Down Expand Up @@ -417,14 +415,14 @@ export default defineComponent({
.course {
box-shadow: 0px 0px 10px 4px rgba(0, 0, 0, 0.055);
position: relative;
z-index: 1;
box-sizing: border-box;
border-radius: 0.5rem;
display: flex;
flex-direction: row;
background-color: $white;
height: 5.625rem;
cursor: grab;
z-index: 1;

&:hover {
background: $white;
Expand Down
4 changes: 3 additions & 1 deletion src/components/Modals/CourseMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
class="courseMenu-icon"
src="@/assets/images/noteIconSmall.svg"
alt="note course icon"
width="12"
height="11"
/>
<span class="courseMenu-text">Note</span>
</div>
Expand Down Expand Up @@ -277,7 +279,7 @@ export default defineComponent({
position: absolute;
right: -3rem;
top: 2rem;
z-index: 1;
z-index: 1000;

&-content {
background: $white;
Expand Down
152 changes: 119 additions & 33 deletions src/components/Notes/Note.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
<template>
<div class="note" :class="{ expanded: isExpanded }" :style="noteStyle" @click="handleClick">
<div class="note-content" :class="{ visible: isExpanded, editing: isEditing }">
<div class="note" :style="noteStyle" @click="handleClick">

Check failure on line 2 in src/components/Notes/Note.vue

View workflow job for this annotation

GitHub Actions / check

Type '{ transform: string; height: string; width: string; position: string; botton: string; backgroundColor: string; }' is not assignable to type 'string | CSSProperties | StyleValue[] | undefined'.
<div
class="note-content"
:class="{
visibleEmpty: isExpanded && initialNote === '',
visible: isExpanded,
editing: isEditing,
}"
>
<!-- NOTE: the disabled attribute is used to prevent the user from being able to edit the note
when it is expanded, but the edit icon has not been pressed. This only applies when the note
is not empty (already had something written in it previously and read from the database). -->
Expand All @@ -13,9 +20,20 @@
@input="handleInput"
/>
<img
v-if="isExpanded && initialNote === ''"
src="@/assets/images/notes/arrow.svg"
alt="Arrow icon"
class="note-icon"
:style="{ opacity: isDisabled || note.trim() === '' ? '0.5' : '1' }"
:data-id="noteId"
@click.stop="saveNote"
/>
<img
v-if="isEditing && initialNote !== ''"
src="@/assets/images/notes/checkmark.svg"
alt="Checkmark icon"
class="note-icon"
:data-id="noteId"
@click.stop="saveNote"
/>
</div>
Expand Down Expand Up @@ -43,15 +61,22 @@
</template>

<script lang="ts">
import { defineComponent } from 'vue';
import { defineComponent, nextTick } from 'vue';
import { Timestamp } from 'firebase/firestore';
import { coursesColorSet } from '@/assets/constants/colors';

export default defineComponent({
name: 'Note',
props: {
initialTranslateY: { type: String, default: '-50px', required: true },
expandedTranslateY: { type: String, default: '-10px', required: true },
// NOTE: each node has an ID (just the courseID)
// Each note has three types of heights and translateY values: initial, expandedInitial (empty input), and expanded. For now,
// all translateY values are the same, but this can be changed in the future.
noteId: { type: String, required: true },
initialTranslateY: { type: String, default: '-10px' },
expandedInitialTranslateY: { type: String, default: '-10px' },
expandedTranslateY: { type: String, default: '-10px' },
initialHeight: { type: String, default: '20px' },
expandedInitialHeight: { type: String, default: '40px' },
expandedHeight: { type: String, default: '60px' },
width: { type: String, default: '200px' },
color: { type: String, default: '#a8e6cf' },
initialNote: { type: String, default: '' },
Expand All @@ -75,15 +100,41 @@
expand(newVal) {
if (newVal) this.isExpanded = true;
},
isEditing: 'updateSvgState',
isDisabled: 'updateSvgState',
note: 'updateSvgState', // Update SVG whenever note changes
},
computed: {
noteStyle() {
// Parse the heights and translate values
const trueExpandedTranslateY =
this.isExpanded && this.initialNote === '' // note is empty
? this.expandedInitialTranslateY
: this.expandedTranslateY;

const trueExpandedHeight =
this.isExpanded && this.initialNote === '' // note is empty
? this.expandedInitialHeight
: this.expandedHeight;

const translateY = this.isExpanded
? parseInt(trueExpandedTranslateY, 10)
: parseInt(this.initialTranslateY, 10);

const baseHeight = this.isExpanded
? parseInt(trueExpandedHeight, 10)
: parseInt(this.initialHeight, 10);

const visibleHeight = baseHeight + Math.max(0, -translateY); // Add the visible part of the translated note
console.log('visibleHeight', visibleHeight);

Check warning on line 129 in src/components/Notes/Note.vue

View workflow job for this annotation

GitHub Actions / check

Unexpected console statement

return {
transform: `translateX(-50%) translateY(${
this.isExpanded ? this.expandedTranslateY : this.initialTranslateY
})`,
transform: `translateX(-50%) translateY(${translateY}px)`,
height: `${visibleHeight}px`, // The height of the note itself
width: this.width,
backgroundColor: this.getLighterColor(this.color),
position: 'relative',
botton: '0',
backgroundColor: this.getLighterColor(this.color, 0.8),
};
},
// Displays
Expand All @@ -101,6 +152,10 @@

return `${date.getMonth() + 1}/${date.getDate()}`;
},
isDisabled() {
// Disabled when not editing or when input is empty
return (!this.isEditing && this.initialNote !== '') || this.note.trim() === '';
},
},
methods: {
handleClick() {
Expand All @@ -113,6 +168,7 @@
this.isExpanded = true;
},
saveNote() {
if (this.isDisabled) return;
this.$emit('save-note', this.note);
this.isDirty = false;
this.isEditing = false;
Expand All @@ -123,65 +179,93 @@
this.isEditing = false;
}
},
getLighterColor(color: string) {
const colorObj = coursesColorSet.find(c => c.hex.toUpperCase() === color.toUpperCase());
return colorObj ? colorObj.lighterHex : color;
// getLighterColor(color: string) {
// const colorObj = coursesColorSet.find(c => c.hex.toUpperCase() === color.toUpperCase());
// return colorObj ? colorObj.lighterHex : color;
// },
/**
* Lighten a color in hex code by a percentage
* @param {string} unconvertedColor - The color to lighten in hex code
* @param {number} percentage - The percentage to lighten the color by (i.e: light by 80%)
*/
getLighterColor(unconvertedColor: string, percentage: number) {
const hexColor = unconvertedColor.replace('#', '');
// Convert each color channel and lighten it
const lightenedHex = hexColor
.match(/.{2}/g)
?.map(channel => {
// Convert channel from HEX to decimal
const decimal = parseInt(channel, 16);
// Lighten the channel
const lightened = Math.min(255, Math.floor(decimal + (255 - decimal) * percentage));
// Convert back to HEX, ensuring two characters
return lightened.toString(16).padStart(2, '0');
})
.join('');
return `#${lightenedHex}`;
},
updateSvgState() {
this.updateSvgColor(this.noteId);
},
// Note: this is a dynamic style update, so nextTick is used to ensure the SVG element is rendered
updateSvgColor(noteId: string) {
const color = this.isDisabled ? 'grey' : this.color;
const opacity = this.isDisabled ? '0.5' : '1';

nextTick(() => {
const svgElement = document.querySelector(`.note-icon[data-id="${noteId}"]`);
if (!svgElement) return;
(svgElement as HTMLElement).style.setProperty('--svg-filter-color', color);
(svgElement as HTMLElement).style.setProperty('opacity', opacity);
});
},
handleInput() {
this.isDirty = this.note !== this.initialNote;
},
},
});
</script>

<style scoped>
.note {
box-shadow: 0px 0px 10px 4px rgba(0, 0, 0, 0.055);
position: absolute;
box-shadow: -4px -4px 10px 4px rgba(160, 91, 91, 0.055);
left: 50%;
transform: translateX(-50%);
height: 80px;
border-radius: 12.49px;
cursor: pointer;
z-index: 0;
transition: transform 0.3s ease, filter 0.3s ease;
transition: transform 0.3s ease, height 0.3s ease, filter 0.3s ease;
display: flex;
justify-content: center;
align-items: flex-end;
padding-bottom: 10px;
overflow: hidden;
}

.note.expanded {
height: 80px;
}

.note-content {
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
padding-top: 15px;
padding-left: 10px;
padding: 5px 0px 10px 10px;
opacity: 0;
transform: translateY(100%);
transition: opacity 0.3s ease, transform 0.3s ease;
}

.note-content.visible {
opacity: 1;
transform: translateY(0);
transform: translateY(-15px);
}
.note-content.visibleEmpty {
opacity: 1;
transform: translateY(0px);
}

.note-input {
flex: 1;
padding: 0;
border: none;
outline: none;
background-color: transparent;
color: #555;
font-size: 14.48px;
font-family: inherit;
padding: 5px 0;
}

.note-icon {
Expand All @@ -194,10 +278,12 @@
cursor: pointer;
transition: filter 0.3s ease;
margin-right: 10px;
}
--svg-filter-color: grey; /* Default color */
filter: drop-shadow(0 0 0 var(--svg-filter-color)) saturate(5);

.note-icon:hover {
filter: brightness(0.7);
&:hover {
filter: opacity(0.5) drop-shadow(0 0 0 var(--svg-filter-color));
}
}

.note-footer {
Expand Down
Loading