- 전체 기록 -
- - - {{ records|length }}개 - -- 아직 작성한 기록이 없어요. -
- -- 오늘의 마음을 첫 기록으로 남겨보세요. -
- - {% comment %} - 담당 C의 실제 기록 작성 페이지로 이동합니다. - {% endcomment %} - - 기록 작성하기 - -- 감정별 기록 -
- -- 감정의 크기는 이번 달 기록 횟수를 나타내요. -
-- 표시할 감정이 없어요. -
- {% endfor %} -- 감정별 기록 -
- -- - 감사 - - 기록 -
-+ 감정별 기록 +
+ ++ 감정의 크기는 이번 달 기록 횟수를 나타내요. +
++ 표시할 감정이 없어요. +
+ {% endfor %} ++ 감정별 기록 +
+ ++ + 감사 + + 기록 +
++ 전체 기록 +
+ + + {{ records|length }}개 + +
+
+
+{% comment %}
+감정별 기록 팝업의 HTML은
+emotion_modal.html에서 관리
+{% endcomment %}
+
{% include "diary/partials/emotion_modal.html" %}
@@ -177,12 +464,17 @@
aria-labelledby="location-modal-title"
hidden
>
+
+
+
+
+
@@ -195,6 +487,8 @@
+
+
+
{% for record in records %}
+
>
{% include "diary/partials/diary_card.html" with record=record %}
+
{% endfor %}
+
{% block extra_js %}
+{% endblock %}
\ No newline at end of file
From 094d84bec94a4ac12b610e5c1de950e2ec4f3748 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 11:47:20 +0900
Subject: [PATCH 03/13] =?UTF-8?q?refactor:=20=EA=B0=90=EC=A0=95=EB=B3=84?=
=?UTF-8?q?=20=EA=B8=B0=EB=A1=9D=20=ED=83=AD=EC=9D=84=20=EB=8F=84=EB=84=9B?=
=?UTF-8?q?=20=EA=B7=B8=EB=9E=98=ED=94=84=20=EA=B5=AC=EC=A1=B0=EB=A1=9C=20?=
=?UTF-8?q?=EB=B3=80=EA=B2=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.../diary/partials/emotion_list.html | 301 ++++++++++++------
1 file changed, 210 insertions(+), 91 deletions(-)
diff --git a/diary/templates/diary/partials/emotion_list.html b/diary/templates/diary/partials/emotion_list.html
index 73e1553..bd9b4ea 100644
--- a/diary/templates/diary/partials/emotion_list.html
+++ b/diary/templates/diary/partials/emotion_list.html
@@ -1,97 +1,216 @@
-
+
+
+
+
+
감정별 기록
- ========================== -->
-
+
+
+ 기록한 대표 감정을 한눈에 확인해보세요.
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
- 감정별 기록
-
-
-
- 감정의 크기는 이번 달 기록 횟수를 나타내요.
-
-
-
-
-
- 가장 많이 기록한 감정
-
-
- {% if top_emotions %}
-
- {% for emotion in top_emotions %}
-
- {{ emotion.name }}
-
-
- {% if not forloop.last %}
-
- ·
-
- {% endif %}
- {% endfor %}
-
- {% else %}
-
- 아직 기록 없음
-
- {% endif %}
-
+
+
+
+
+
+
+
+
+
+ 전체 기록
+
+
+
+ {{ records|length }}개
+
+
-
- {% for emotion in recorded_emotions %}
-
-
- {% empty %}
-
- 표시할 감정이 없어요.
-
- {% endfor %}
+
+
+
+
+
+
+
+
+
+
+
+
+ {% for emotion in recorded_emotions %}
+
+
+
+ {% endfor %}
+
+
+
+
+ {% if not recorded_emotions %}
+
+
+
+
+ 아직 기록된 감정이 없어요.
+
+
+
+ 오늘의 대표 감정을 기록해보세요.
+
+
-
\ No newline at end of file
+
+ {% endif %}
+
+
+
\ No newline at end of file
From 1304d1cb91cf09a55c16187d9649cc4e5c4747f4 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 11:55:34 +0900
Subject: [PATCH 04/13] =?UTF-8?q?feat:=20=EB=8C=80=ED=91=9C=20=EA=B0=90?=
=?UTF-8?q?=EC=A0=95=20=EC=83=89=EC=83=81=20=EA=B7=B8=EB=A3=B9=EB=B3=84=20?=
=?UTF-8?q?=EA=B7=B8=EB=9E=98=ED=94=84=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20?=
=?UTF-8?q?=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/views.py | 680 +++++++++++++++++++++++++++++++++++++++----------
1 file changed, 545 insertions(+), 135 deletions(-)
diff --git a/diary/views.py b/diary/views.py
index 2272300..3b29f63 100644
--- a/diary/views.py
+++ b/diary/views.py
@@ -7,7 +7,7 @@
from django.shortcuts import get_object_or_404, render
from django.utils import timezone
-# 담당 C가 만든 실제 기록 모델을 가져옵니다.
+# 담당 C가 만든 실제 기록 모델을 가져옴
from records.models import Record
@@ -39,9 +39,58 @@
}
+# ============================================================
+# 감정 번호별 색상 그룹
+# ============================================================
+
+"""
+감정별 기록의 도넛 그래프에서는
+감정 하나마다 다른 색을 사용하는 것이 아니라,
+
+비슷한 색상 계열의 감정을
+하나의 그래프 조각으로 묶어서 표시
+
+예:
+사랑, 짝사랑
+→ pink 그룹
+
+기쁨, 만족, 신남, 행운, 맛있어!
+→ yellow 그룹
+
+중요:
+여기서는 감정 이름이 아니라
+감정 번호를 기준으로 그룹을 지정
+
+같은 이름의 감정이 여러 번호에 존재하더라도
+번호 기준으로 안정적으로 처리할 수 있음
+"""
+EMOTION_COLOR_GROUP_MAP = {
+ 1: "blue",
+ 2: "pink",
+ 3: "purple",
+ 4: "purple",
+ 5: "yellow",
+ 6: "blue",
+ 7: "pink",
+ 8: "red",
+ 9: "orange",
+ 10: "blue",
+ 11: "red",
+ 12: "yellow",
+ 13: "blue",
+ 14: "orange",
+ 15: "yellow",
+ 16: "yellow",
+ 17: "red",
+ 18: "yellow",
+ 19: "purple",
+ 20: "purple",
+}
+
+
def get_emotion_name(emotion_number):
"""
- 감정 번호를 화면에 표시할 감정 이름으로 변환합니다.
+ 감정 번호를 화면에 표시할 감정 이름으로 변환
예:
1 → "슬픔"
@@ -50,11 +99,14 @@ def get_emotion_name(emotion_number):
try:
# JSONField 값이나 폼 값이 문자열일 수도 있으므로
- # 비교하기 전에 정수로 변환합니다.
- emotion_number = int(emotion_number)
+ # 비교하기 전에 정수로 변환
+ emotion_number = int(
+ emotion_number,
+ )
except (TypeError, ValueError):
- # 값이 없거나 숫자로 바꿀 수 없다면 빈 문자열을 반환합니다.
+ # 값이 없거나 숫자로 바꿀 수 없다면
+ # 빈 문자열을 반환
return ""
return EMOTION_NAME_MAP.get(
@@ -63,36 +115,88 @@ def get_emotion_name(emotion_number):
)
+def get_emotion_color_group(emotion_number):
+ """
+ 감정 번호를 감정별 그래프에서 사용할
+ 색상 그룹 이름으로 변환
+
+ 예:
+ 2 → pink
+ 11 → red
+ 16 → yellow
+ """
+
+ try:
+ emotion_number = int(
+ emotion_number,
+ )
+
+ except (TypeError, ValueError):
+ return "default"
+
+ return EMOTION_COLOR_GROUP_MAP.get(
+ emotion_number,
+ "default",
+ )
+
+
@login_required
def diary_list(request):
"""
현재 로그인한 사용자가 작성한 실제 Record를 조회하여
- 전체 / 감정별 / 장소별 목록 페이지에 전달합니다.
+ 전체 / 감정별 / 장소별 목록 페이지에 전달
"""
# ========================================================
# 1. 현재 선택한 탭과 필터값
# ========================================================
- selected_tab = request.GET.get("tab", "all")
- selected_sort = request.GET.get("sort", "latest")
+ selected_tab = request.GET.get(
+ "tab",
+ "all",
+ )
- selected_start_date = request.GET.get("start_date", "")
- selected_end_date = request.GET.get("end_date", "")
+ selected_sort = request.GET.get(
+ "sort",
+ "latest",
+ )
- if selected_tab not in {"all", "emotion", "location"}:
+ selected_start_date = request.GET.get(
+ "start_date",
+ "",
+ )
+
+ selected_end_date = request.GET.get(
+ "end_date",
+ "",
+ )
+
+
+ # 허용하지 않는 탭 이름이 들어오면
+ # 전체 기록 탭으로 돌려놓음
+ if selected_tab not in {
+ "all",
+ "emotion",
+ "location",
+ }:
selected_tab = "all"
+
# ========================================================
# 2. 현재 사용자의 실제 기록 조회
# ========================================================
- records_queryset = Record.objects.filter(
- user=request.user,
- ).select_related(
- "place",
+ records_queryset = (
+ Record.objects
+ .filter(
+ user=request.user,
+ )
+ .select_related(
+ "place",
+ )
)
+
# ========================================================
# 3. 시작일 필터
# ========================================================
@@ -109,8 +213,11 @@ def diary_list(request):
)
except ValueError:
+ # 잘못된 날짜 값이 들어오면
+ # 필터를 무시
selected_start_date = ""
+
# ========================================================
# 4. 종료일 필터
# ========================================================
@@ -129,11 +236,13 @@ def diary_list(request):
except ValueError:
selected_end_date = ""
+
# ========================================================
# 5. 정렬
# ========================================================
if selected_sort == "oldest":
+
records_queryset = records_queryset.order_by(
"created_at",
)
@@ -145,227 +254,506 @@ def diary_list(request):
"-created_at",
)
- records = list(records_queryset)
+
+ # QuerySet을 리스트로 변환
+ #
+ # 이후 여러 번 반복하면서
+ # 감정/장소 데이터를 계산하기 때문에
+ # 한 번 리스트로 만들어 사용
+ records = list(
+ records_queryset,
+ )
+
# ========================================================
- # 6. 각 기록에 화면용 감정 이름 추가
+ # 6. 각 기록에 화면용 대표 감정 정보 추가
# ========================================================
for record in records:
+
+ # 대표 감정 번호를
+ # 사람이 읽을 수 있는 이름으로 바꿈
record.main_emotion_name = get_emotion_name(
record.main_emotion,
)
+ # 지도나 다른 화면에서도 필요할 수 있으므로
+ # 대표 감정의 색상 그룹도 record에 임시로 추가
+ record.main_emotion_color_group = (
+ get_emotion_color_group(
+ record.main_emotion,
+ )
+ )
+
+
# ========================================================
- # 7. 감정별 기록 횟수 계산
+ # 7. 대표 감정별 기록 횟수 계산
# ========================================================
- main_emotion_numbers = [
- int(record.main_emotion)
- for record in records
- if record.main_emotion
- ]
+ """
+ 여기서 중요한 점:
+
+ record.emotions 전체를 사용하는 것이 아니라
+ record.main_emotion만 사용
+
+ 따라서 사용자가 기록할 때 선택한 감정이 3개여도
+
+ 대표 감정 1개만
+ 감정별 그래프 집계에 포함됨
+ """
+
+ main_emotion_numbers = []
+
+ for record in records:
+ try:
+ emotion_number = int(
+ record.main_emotion,
+ )
+ except (TypeError, ValueError):
+ continue
+
+ main_emotion_numbers.append(
+ emotion_number,
+ )
+
+
+ # 예:
+ #
+ # [16, 16, 12, 11]
+ #
+ # →
+ #
+ # {
+ # 16: 2,
+ # 12: 1,
+ # 11: 1
+ # }
emotion_counts = Counter(
main_emotion_numbers,
)
+
+ # 모든 감정 정보를 담는 목록
+ #
+ # 기존 기능과의 호환을 위해
+ # 기록 횟수가 0인 감정도 emotions에는 남겨둠
emotions = []
for emotion_id, emotion_name in EMOTION_NAME_MAP.items():
+
record_count = emotion_counts.get(
emotion_id,
0,
)
- if record_count >= 5:
- size = "large"
-
- elif record_count >= 2:
- size = "medium"
-
- else:
- size = "small"
-
emotions.append(
{
"id": emotion_id,
+
"name": emotion_name,
+
+ # 해당 감정이 대표 감정으로
+ # 기록된 횟수
"record_count": record_count,
- "size": size,
+
+ # 도넛 그래프에서 사용할 색상 그룹
+ "color_group": (
+ get_emotion_color_group(
+ emotion_id,
+ )
+ ),
}
)
+
+ # ========================================================
+ # 8. 실제로 기록된 대표 감정만 추리기
+ # ========================================================
+
+ """
+ 도넛 그래프에는
+ 한 번도 기록되지 않은 감정은 나오면 안 됨
+
+ 따라서 record_count가 1 이상인 감정만
+ recorded_emotions에 넣음
+ """
+
recorded_emotions = [
emotion
for emotion in emotions
if emotion["record_count"] > 0
]
- # 실제 기록된 감정이 있다면
+
+ # ========================================================
+ # 9. 가장 많이 기록한 대표 감정 계산
+ # ========================================================
+
+ """
+ 기존 emotion_modal이나 다른 화면에서
+ top_emotions를 사용할 수도 있으므로
+ 이 데이터는 유지
+ """
+
if recorded_emotions:
- # 가장 많은 기록 횟수를 구합니다.
- #
- # 예:
- # 예민 1회, 행운 1회
- # → maximum_emotion_count = 1
+
maximum_emotion_count = max(
emotion["record_count"]
for emotion in recorded_emotions
)
- # 가장 많은 기록 횟수와 같은 감정을
- # 모두 공동 1위 목록에 넣습니다.
- #
- # 예:
- # 예민 1회, 행운 1회
- # → [예민, 행운]
top_emotions = [
emotion
for emotion in recorded_emotions
- if emotion["record_count"] == maximum_emotion_count
+ if (
+ emotion["record_count"]
+ == maximum_emotion_count
+ )
]
else:
- # 기록된 대표 감정이 없다면 빈 목록을 전달합니다.
top_emotions = []
+
# ========================================================
- # 8. 서울 지도용 장소 데이터 계산
+ # 10. 도넛 그래프용 색상 그룹 데이터 생성
+ # ========================================================
+
+ """
+ 같은 색상 그룹에 속한 감정들을 하나로 합침
+
+ 예:
+
+ 기쁨 2회
+ 만족 1회
+ 신남 1회
+
+ 모두 yellow 그룹이라면
+
+ yellow:
+ count = 4
+ emotions = ["만족", "신남", "기쁨"]
+
+ 가 됩니다.
+
+ JavaScript는 이 데이터를 사용하여
+ 원 그래프 조각 크기를 계산할 수 있음
+ """
+
+ emotion_chart_group_dict = {}
+
+
+ for emotion in recorded_emotions:
+
+ color_group = emotion[
+ "color_group"
+ ]
+
+ emotion_name = emotion[
+ "name"
+ ]
+
+ record_count = emotion[
+ "record_count"
+ ]
+
+
+ # 해당 색상 그룹이 처음 등장하면
+ # 기본 구조를 생성
+ if color_group not in emotion_chart_group_dict:
+
+ emotion_chart_group_dict[color_group] = {
+ "color_group": color_group,
+
+ # 이 색상 그룹에 속한
+ # 대표 감정 기록 수의 총합
+ "record_count": 0,
+
+ # hover 시 보여줄 감정 이름 목록
+ "emotion_names": [],
+ }
+
+
+ # 색상 그룹의 전체 기록 수에
+ # 현재 감정 기록 수를 더함
+ emotion_chart_group_dict[
+ color_group
+ ]["record_count"] += record_count
+
+
+ # 같은 이름을 중복해서
+ # 툴팁에 표시하지 않도록 함
+ #
+ # 예:
+ # 감정 번호 1, 6, 10이 모두 "슬픔"이어도
+ # hover에는 "슬픔" 한 번만 표시함
+ if (
+ emotion_name
+ not in emotion_chart_group_dict[
+ color_group
+ ]["emotion_names"]
+ ):
+ emotion_chart_group_dict[
+ color_group
+ ]["emotion_names"].append(
+ emotion_name,
+ )
+
+
+ # 딕셔너리를 템플릿에서 사용하기 쉬운
+ # 리스트 형태로 바꿈
+ emotion_chart_groups = list(
+ emotion_chart_group_dict.values()
+ )
+
+
+ # ========================================================
+ # 11. 전체 대표 감정 기록 수
+ # ========================================================
+
+ """
+ 현재 Record 하나당 대표 감정이 하나이므로
+ 정상적인 데이터라면 records 개수와 같음
+
+ 그래프 가운데의
+
+ 전체 기록
+ 8개
+
+ 를 표시할 때 사용할 값
+ """
+
+ total_record_count = sum(
+ emotion["record_count"]
+ for emotion in recorded_emotions
+ )
+
+
+ # ========================================================
+ # 12. 서울 지도용 장소 데이터 계산
# ========================================================
district_records = {}
+
for record in records:
+
# Record에 연결된 Place가 없으면
- # 지도에 표시할 구 정보를 알 수 없으므로 제외합니다.
+ # 지도에 표시할 수 없으므로 제외
if not record.place:
continue
+
# Place 모델의 district에는
- # "성북구", "강남구"처럼 서울 자치구 이름이 저장됩니다.
+ # "성북구", "강남구"와 같은
+ # 서울 자치구 이름이 저장됨
district_name = (
record.place.district.strip()
if record.place.district
else ""
)
- # district 값이 비어 있다면 제외합니다.
+
+ # 구 이름이 없다면 제외
if not district_name:
continue
- emotion_name = record.main_emotion_name
- # 대표 감정 정보가 없다면
- # 감정 색상을 계산할 수 없으므로 제외합니다.
+ emotion_name = (
+ record.main_emotion_name
+ )
+
+
+ # 대표 감정 정보가 없으면
+ # 지도 감정 색상을 정할 수 없으므로 제외
if not emotion_name:
continue
- # 해당 구가 처음 등장했다면
- # 기록을 담을 빈 배열을 만듭니다.
+
if district_name not in district_records:
- district_records[district_name] = []
- district_records[district_name].append(
+ district_records[
+ district_name
+ ] = []
+
+
+ district_records[
+ district_name
+ ].append(
{
"emotion_name": emotion_name,
- "created_at": record.created_at,
+
+ "created_at": (
+ record.created_at
+ ),
}
)
district_map_data = []
- for district_name, district_record_list in district_records.items():
- # 해당 구에서 기록된 대표 감정들의 이름
+
+ for (
+ district_name,
+ district_record_list,
+ ) in district_records.items():
+
+ # 해당 구에서 기록된
+ # 대표 감정 이름 목록
emotion_names = [
item["emotion_name"]
for item in district_record_list
]
- # 해당 구에서 가장 많이 기록된 감정
+
+ # 가장 많이 기록된 감정 계산
emotion_name_counts = Counter(
emotion_names,
)
- dominant_emotion = emotion_name_counts.most_common(1)[0][0]
+ dominant_emotion = (
+ emotion_name_counts
+ .most_common(1)[0][0]
+ )
+
- # 해당 구의 가장 최근 기록
+ # 가장 최근 기록 계산
latest_record = max(
district_record_list,
- key=lambda item: item["created_at"],
+ key=lambda item: item[
+ "created_at"
+ ],
)
+
district_map_data.append(
{
# SVG path의 id와 비교할 구 이름
- # 예: "성북구"
"district_name": district_name,
# 해당 구에서 작성한 기록 수
- "visit_count": len(district_record_list),
+ "visit_count": len(
+ district_record_list,
+ ),
# 해당 구에서 가장 많이 기록된 감정
- "dominant_emotion": dominant_emotion,
+ "dominant_emotion": (
+ dominant_emotion
+ ),
- # 해당 구에서 가장 최근에 기록된 감정
- "latest_emotion": latest_record["emotion_name"],
+ # 해당 구에서 가장 최근 대표 감정
+ "latest_emotion": (
+ latest_record[
+ "emotion_name"
+ ]
+ ),
}
)
+
# ========================================================
- # 9. 장소별 기록 횟수 계산
+ # 13. 장소별 기록 횟수 계산
# ========================================================
location_counts = Counter(
record.place.district
for record in records
- if record.place and record.place.district
+ if (
+ record.place
+ and record.place.district
+ )
)
+
locations = [
{
"name": place_name,
"record_count": count,
}
- for place_name, count in location_counts.items()
+ for (
+ place_name,
+ count,
+ ) in location_counts.items()
]
+
locations.sort(
- key=lambda location: location["record_count"],
+ key=lambda location: (
+ location["record_count"]
+ ),
reverse=True,
)
+
# ========================================================
- # 10. 템플릿에 데이터 전달
+ # 14. 템플릿에 데이터 전달
# ========================================================
context = {
+ # ----------------------------------------------------
+ # 공통 기록
+ # ----------------------------------------------------
"records": records,
+
+
+ # ----------------------------------------------------
+ # 감정별 기록
+ # ----------------------------------------------------
+
+ # 모든 감정
"emotions": emotions,
- # 실제 기록 횟수가 1회 이상인 대표 감정만 전달합니다.
+ # 실제 대표 감정으로 기록된 감정만
"recorded_emotions": recorded_emotions,
+ # 가장 많이 기록한 대표 감정
"top_emotions": top_emotions,
+
+ # 도넛 그래프용 색상 그룹 데이터
+ "emotion_chart_groups": emotion_chart_groups,
+
+ # 도넛 그래프 가운데 표시할 전체 기록 수
+ "total_record_count": total_record_count,
+
+
+ # ----------------------------------------------------
+ # 장소별 기록
+ # ----------------------------------------------------
"locations": locations,
- "district_map_data": district_map_data,
+ "district_map_data": (
+ district_map_data
+ ),
+
+
+ # ----------------------------------------------------
+ # 현재 탭 및 필터 상태
+ # ----------------------------------------------------
"selected_tab": selected_tab,
+
"selected_sort": selected_sort,
- "selected_start_date": selected_start_date,
- "selected_end_date": selected_end_date,
+
+ "selected_start_date": (
+ selected_start_date
+ ),
+
+ "selected_end_date": (
+ selected_end_date
+ ),
"selected_emotion": request.GET.get(
"emotion",
"",
),
+
"selected_location": request.GET.get(
"location",
"",
),
}
+
return render(
request,
"diary/list.html",
@@ -377,53 +765,55 @@ def diary_list(request):
def emotion_calendar(request):
"""
현재 로그인한 사용자의 기록을 날짜별로 정리하여
- 감정 캘린더 페이지에 전달합니다.
+ 감정 캘린더 페이지에 전달
같은 날짜에 기록이 여러 개 있다면
- 가장 최근에 작성한 기록의 대표 감정을 사용합니다.
+ 가장 최근에 작성한 기록의 대표 감정을 사용
"""
- # 오늘 날짜를 기준으로 캘린더의 최초 연도와 월을 설정합니다.
+ # 오늘 날짜를 기준으로
+ # 캘린더 최초 연도와 월을 설정
today = timezone.localdate()
- # 최신 기록부터 조회합니다.
+
+ # 최신 기록부터 조회
#
- # 아래에서 날짜별 데이터에 처음 들어간 기록만 사용하므로
- # 같은 날짜에 여러 기록이 있으면 가장 최신 기록이 남습니다.
+ # 아래에서 날짜별 데이터에
+ # 처음 들어간 기록만 사용하므로
+ # 같은 날짜에 여러 기록이 있으면
+ # 가장 최신 기록이 남음
records = Record.objects.filter(
user=request.user,
).order_by(
"-created_at",
)
- # JavaScript에 전달할 날짜별 감정 데이터입니다.
- #
- # 결과 예:
- # {
- # "2026-08-01": {
- # "number": 7,
- # "name": "짝사랑",
- # "image_name": "emotion-07.png"
- # }
- # }
+
+ # JavaScript에 전달할
+ # 날짜별 감정 데이터
calendar_emotions = {}
+
for record in records:
- # created_at이 시간대 정보를 가진 경우
- # 한국 시간 기준 날짜로 변환합니다.
+
+ # created_at을
+ # 한국 시간 기준 날짜로 변환
created_date = timezone.localtime(
record.created_at,
).date()
+
date_key = created_date.strftime(
"%Y-%m-%d",
)
- # 이미 같은 날짜의 최신 기록이 저장되어 있다면
- # 그보다 오래된 기록은 건너뜁니다.
+
+ # 이미 같은 날짜의 최신 기록이 있다면
+ # 오래된 기록은 건너뜀
if date_key in calendar_emotions:
continue
+
try:
emotion_number = int(
record.main_emotion,
@@ -432,95 +822,106 @@ def emotion_calendar(request):
except (TypeError, ValueError):
continue
- calendar_emotions[date_key] = {
- # 감정 이미지 번호
+
+ calendar_emotions[
+ date_key
+ ] = {
+ # 감정 번호
"number": emotion_number,
- # 이미지의 대체 텍스트와 툴팁에 사용할 감정 이름
+ # 감정 이름
"name": get_emotion_name(
emotion_number,
),
- # 실제 records 이미지 폴더의 파일명
+ # 감정 이미지 파일명
"image_name": (
f"emotion-{emotion_number:02d}.png"
),
- # 날짜 칸이나 감정 이미지를 눌렀을 때
- # 해당 기록 상세 페이지로 이동할 때 사용할 ID
+ # 상세 페이지 이동용 기록 ID
"record_id": record.pk,
}
+
context = {
- # 최초 화면에 표시할 연도와 월
+ # 최초 화면에 표시할 연도
"calendar_year": today.year,
+
+ # 최초 화면에 표시할 월
"calendar_month": today.month,
- # calendar.html의 json_script를 통해
- # JavaScript에 안전하게 전달됩니다.
- "calendar_emotions": calendar_emotions,
+ # 날짜별 대표 감정 데이터
+ "calendar_emotions": (
+ calendar_emotions
+ ),
}
+
return render(
request,
"diary/calendar.html",
context,
)
+
@login_required
def diary_detail(request, pk):
"""
현재 로그인한 사용자가 작성한 기록 하나를 조회하여
- 다이어리 상세 페이지에 전달합니다.
+ 다이어리 상세 페이지에 전달
- pk는 기록의 고유 번호입니다.
+ pk는 기록의 고유 번호
예:
/diary/2/
- → id가 2인 Record를 조회합니다.
+ → id가 2인 Record를 조회
"""
- # 다른 사용자의 기록을 주소로 직접 접근하지 못하도록
- # user=request.user 조건을 함께 사용합니다.
+ # 다른 사용자의 기록을
+ # 주소로 직접 접근하지 못하도록
+ # user=request.user 조건을 함께 사용
record = get_object_or_404(
Record,
pk=pk,
user=request.user,
)
- # 대표 감정 번호
- # 대표 감정 정보입니다.
- #
- # Record.main_emotion에는 1~20 사이 번호가 저장됩니다.
+
+ # ========================================================
+ # 대표 감정
+ # ========================================================
+
main_emotion_number = int(
record.main_emotion,
)
+
main_emotion = {
+ # 감정 번호
"number": main_emotion_number,
- # diary/views.py 위쪽에 이미 있는
- # get_emotion_name() 함수를 사용합니다.
+ # 화면에 표시할 감정 이름
"name": get_emotion_name(
main_emotion_number,
),
- # 실제 감정 이미지 파일명입니다.
- #
- # 예:
- # 1 → emotion-01.png
- # 7 → emotion-07.png
- # 20 → emotion-20.png
+ # 실제 감정 이미지 파일명
"image_name": (
f"emotion-{main_emotion_number:02d}.png"
),
}
- # 대표 감정을 제외한 나머지 감정들을
- # 보조 감정 목록으로 만듭니다.
+
+ # ========================================================
+ # 보조 감정
+ # ========================================================
+
secondary_emotions = []
+
for emotion_number in record.emotions:
+
try:
emotion_number = int(
emotion_number,
@@ -529,39 +930,47 @@ def diary_detail(request, pk):
except (TypeError, ValueError):
continue
- # 대표 감정은 가운데 카드에서 따로 표시하므로
- # 보조 감정 목록에서는 제외합니다.
- if emotion_number == main_emotion_number:
+
+ # 대표 감정은 가운데에서 따로 표시하므로
+ # 보조 감정 목록에서는 제외
+ if (
+ emotion_number
+ == main_emotion_number
+ ):
continue
+
secondary_emotions.append(
{
"number": emotion_number,
+
"name": get_emotion_name(
emotion_number,
),
+
"image_name": (
f"emotion-{emotion_number:02d}.png"
),
}
)
- # 감정은 최대 3개이므로 보조 감정은 최대 2개입니다.
- #
- # 첫 번째 보조 감정은 왼쪽,
- # 두 번째 보조 감정은 오른쪽에 표시합니다.
+
+ # 감정은 최대 3개이므로
+ # 대표 감정 제외 후 보조 감정은 최대 2개
left_emotion = (
secondary_emotions[0]
if len(secondary_emotions) >= 1
else None
)
+
right_emotion = (
secondary_emotions[1]
if len(secondary_emotions) >= 2
else None
)
+
context = {
# 기록 전체 정보
"record": record,
@@ -576,6 +985,7 @@ def diary_detail(request, pk):
"right_emotion": right_emotion,
}
+
return render(
request,
"diary/detail.html",
From c72d36fe459e160548efb4900f94c4fbe896edf0 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 12:02:07 +0900
Subject: [PATCH 05/13] =?UTF-8?q?feat:=20=EB=8C=80=ED=91=9C=20=EA=B0=90?=
=?UTF-8?q?=EC=A0=95=20=EB=8F=84=EB=84=9B=20=EA=B7=B8=EB=9E=98=ED=94=84=20?=
=?UTF-8?q?=EB=A0=8C=EB=8D=94=EB=A7=81=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94?=
=?UTF-8?q?=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
emotion_list.html 내부 전체 감정 개수 코드 변경
---
diary/static/diary/js/diary.js | 1204 +++++++++++------
.../diary/partials/emotion_list.html | 24 +-
2 files changed, 812 insertions(+), 416 deletions(-)
diff --git a/diary/static/diary/js/diary.js b/diary/static/diary/js/diary.js
index 2808cd0..884db7d 100644
--- a/diary/static/diary/js/diary.js
+++ b/diary/static/diary/js/diary.js
@@ -3,7 +3,7 @@
document.addEventListener("DOMContentLoaded", () => {
/* =========================================================
- 1. 지도 감정 색상 정보
+ 1. 감정 색상 정보
========================================================= */
const emotionColors = {
@@ -34,8 +34,21 @@ document.addEventListener("DOMContentLoaded", () => {
/*
- 지도에서 '구별로 다른 색' 모드를 선택했을 때
- 각 구에 순서대로 적용할 색상 목록입니다.
+ 감정별 도넛 그래프 색상
+ */
+ const emotionChartColors = {
+ red: "#ef9a9a",
+ blue: "#9fc5e8",
+ yellow: "#f8dc7d",
+ pink: "#efb2c7",
+ orange: "#f9c25c",
+ purple: "#b3b2db",
+ default: "#d4d4d4",
+ };
+
+
+ /*
+ 구별 색상 모드용 색상 목록
*/
const districtColors = [
"#f3b7b7",
@@ -60,38 +73,15 @@ document.addEventListener("DOMContentLoaded", () => {
2. 지도 설정 요소
========================================================= */
- /*
- 지도 오른쪽 위의 점 세 개 버튼입니다.
- */
const mapSettingsButton =
document.querySelector("#map-settings-button");
-
- /*
- 점 세 개 버튼을 눌렀을 때 열리는
- 지도 색상 설정 메뉴입니다.
- */
const mapSettingsMenu =
document.querySelector("#map-settings-menu");
-
- /*
- 지도 색상 방식을 선택하는 버튼들입니다.
-
- 예:
- - 같은 색으로 통일
- - 가장 많은 감정 색
- - 가장 최근 감정 색
- - 방문 횟수에 따른 농도
- - 구별로 다른 색
- */
const mapModeButtons =
document.querySelectorAll("[data-map-mode]");
-
- /*
- Django가 HTML에 넣어둔 구별 기록 데이터입니다.
- */
const districtDataElements =
document.querySelectorAll(
"#district-map-data [data-district]"
@@ -127,7 +117,10 @@ document.addEventListener("DOMContentLoaded", () => {
function getDistrictMapData() {
return Array.from(districtDataElements).map((element) => ({
district: element.dataset.district.trim(),
- count: Number(element.dataset.count),
+
+ count: Number(
+ element.dataset.count
+ ),
dominantEmotion:
element.dataset.dominantEmotion.trim(),
@@ -147,15 +140,10 @@ document.addEventListener("DOMContentLoaded", () => {
.querySelectorAll(".seoul-map path")
.forEach((path) => {
- /*
- JavaScript로 직접 넣었던 fill 색상을 제거합니다.
- */
+ // JavaScript fill 제거
path.style.fill = "";
-
- /*
- 기록이 있는 구에 붙었던 클래스를 제거합니다.
- */
+ // 기록 표시 클래스 제거
path.classList.remove("has-record");
});
}
@@ -167,46 +155,34 @@ document.addEventListener("DOMContentLoaded", () => {
function applyMapMode(mode) {
- /*
- 새로운 색상을 적용하기 전에
- 이전 지도 색상을 먼저 초기화합니다.
- */
+ // 기존 지도 색상 초기화
clearDistrictStyles();
- /*
- HTML에 담긴 구별 데이터를 배열로 가져옵니다.
- */
- const mapData = getDistrictMapData();
-
+ // 구별 기록 데이터 가져오기
+ const mapData =
+ getDistrictMapData();
- /*
- 방문 횟수 농도를 계산하기 위해
- 가장 방문 횟수가 많은 구의 기록 수를 구합니다.
- 기록이 없을 때 0으로 나누는 문제를 막기 위해
- 최소값을 1로 설정합니다.
- */
+ // 최대 방문 횟수 계산
const maximumCount = Math.max(
- ...mapData.map((district) => district.count),
+ ...mapData.map(
+ (district) => district.count
+ ),
1
);
mapData.forEach((district, index) => {
- /*
- district.district와 같은 id를 가진
- 서울 지도 SVG path를 찾습니다.
-
- 예:
- district.district가 "성북구"라면
- id="성북구"인 path를 찾습니다.
- */
+ // 구 이름과 같은 SVG path 탐색
const path =
- document.getElementById(district.district);
+ document.getElementById(
+ district.district
+ );
+ // SVG path가 없는 경우 제외
if (!path) {
console.warn(
`SVG에서 구를 찾지 못했습니다: ${district.district}`
@@ -216,74 +192,67 @@ document.addEventListener("DOMContentLoaded", () => {
}
- /*
- 기록이 있는 구라는 표시를 추가합니다.
- */
+ // 기록 있는 구 표시
path.classList.add("has-record");
- /*
- 모든 기록된 구를 같은 색으로 표시합니다.
- */
+ // 동일 색상 모드
if (mode === "uniform") {
path.style.fill = "#dce9b7";
}
- /*
- 해당 구에서 가장 많이 기록된 감정의 색상을 사용합니다.
- */
+ // 최다 감정 색상 모드
if (mode === "dominant") {
path.style.fill =
- emotionColors[district.dominantEmotion]
+ emotionColors[
+ district.dominantEmotion
+ ]
|| "#d4d4d4";
}
- /*
- 해당 구에서 가장 최근에 기록한 감정의 색상을 사용합니다.
- */
+ // 최근 감정 색상 모드
if (mode === "latest") {
path.style.fill =
- emotionColors[district.latestEmotion]
+ emotionColors[
+ district.latestEmotion
+ ]
|| "#d4d4d4";
}
- /*
- 방문 횟수가 많을수록 색을 진하게 표시합니다.
- */
+ // 방문 횟수 농도 모드
if (mode === "intensity") {
+
const minimumOpacity = 0.05;
const ratio =
- district.count / maximumCount;
+ district.count
+ / maximumCount;
const opacity =
minimumOpacity
- + ratio * (1 - minimumOpacity);
+ + ratio
+ * (1 - minimumOpacity);
path.style.fill =
`rgba(245, 196, 80, ${opacity})`;
}
- /*
- 각 구에 서로 다른 색상을 순서대로 적용합니다.
- */
+ // 구별 다른 색상 모드
if (mode === "rainbow") {
path.style.fill =
districtColors[
- index % districtColors.length
+ index
+ % districtColors.length
];
}
});
- /*
- 현재 선택한 지도 색상 방식 버튼에만
- active 클래스를 추가합니다.
- */
+ // 선택 버튼 active 처리
mapModeButtons.forEach((button) => {
button.classList.toggle(
"active",
@@ -292,11 +261,11 @@ document.addEventListener("DOMContentLoaded", () => {
});
- /*
- 새로고침한 뒤에도 선택한 지도 색상 방식이
- 유지되도록 브라우저 저장소에 저장합니다.
- */
- localStorage.setItem("diaryMapMode", mode);
+ // 선택 모드 저장
+ localStorage.setItem(
+ "diaryMapMode",
+ mode
+ );
}
@@ -306,31 +275,26 @@ document.addEventListener("DOMContentLoaded", () => {
function openLocationModal(locationName) {
- /*
- 장소 팝업 요소가 없는 페이지에서는
- 아래 코드를 실행하지 않습니다.
- */
- if (!locationModal || !locationModalName) {
+ // 팝업 요소 없는 경우 종료
+ if (
+ !locationModal
+ || !locationModalName
+ ) {
return;
}
- /*
- 선택한 장소와 일치하는 기록의 개수입니다.
- */
let visibleRecordCount = 0;
- /*
- 팝업 제목에 선택한 구 이름을 넣습니다.
- */
- locationModalName.textContent = locationName;
+ // 팝업 장소명 설정
+ locationModalName.textContent =
+ locationName;
- /*
- 해당 구에 작성된 기록만 표시합니다.
- */
+ // 선택한 장소 기록만 표시
locationModalRecords.forEach((record) => {
+
const recordLocation =
record.dataset.recordLocation.trim();
@@ -345,26 +309,21 @@ document.addEventListener("DOMContentLoaded", () => {
});
- /*
- 일치하는 기록이 없다면
- 빈 기록 안내 문구를 표시합니다.
- */
+ // 기록 없음 문구 처리
if (locationModalEmpty) {
locationModalEmpty.hidden =
visibleRecordCount !== 0;
}
- /*
- 장소별 기록 팝업을 엽니다.
- */
+ // 팝업 표시
locationModal.hidden = false;
- /*
- 팝업이 열린 동안 뒤쪽 페이지 스크롤을 막습니다.
- */
- document.body.classList.add("modal-open");
+ // 배경 스크롤 차단
+ document.body.classList.add(
+ "modal-open"
+ );
}
@@ -373,28 +332,40 @@ document.addEventListener("DOMContentLoaded", () => {
========================================================= */
function closeLocationModal() {
+
+ // 팝업 없는 경우 종료
if (!locationModal) {
return;
}
+
+ // 팝업 숨김
locationModal.hidden = true;
- document.body.classList.remove("modal-open");
+
+ // 배경 스크롤 복구
+ document.body.classList.remove(
+ "modal-open"
+ );
}
/* =========================================================
- 9. 기록이 있는 서울 구역 클릭 이벤트
+ 9. 기록 있는 서울 구역 클릭
========================================================= */
districtDataElements.forEach((element) => {
+
const districtName =
element.dataset.district.trim();
const districtPath =
- document.getElementById(districtName);
+ document.getElementById(
+ districtName
+ );
+ // SVG path 없는 경우 제외
if (!districtPath) {
console.warn(
`클릭 이벤트를 연결할 구를 찾지 못했습니다: ${districtName}`
@@ -404,97 +375,92 @@ document.addEventListener("DOMContentLoaded", () => {
}
- /*
- tabindex는 넣지 않습니다.
-
- SVG를 클릭했을 때 직사각형 포커스 테두리가
- 나타나는 문제를 방지합니다.
- */
- districtPath.addEventListener("click", () => {
- openLocationModal(districtName);
- });
+ // 장소 팝업 열기
+ districtPath.addEventListener(
+ "click",
+ () => {
+ openLocationModal(
+ districtName
+ );
+ }
+ );
});
/* =========================================================
- 10. 장소별 기록 팝업 닫기 버튼
+ 10. 장소 팝업 닫기
========================================================= */
- locationModalCloseButtons.forEach((button) => {
- button.addEventListener(
- "click",
- closeLocationModal
- );
- });
+ locationModalCloseButtons.forEach(
+ (button) => {
+
+ button.addEventListener(
+ "click",
+ closeLocationModal
+ );
+ }
+ );
/* =========================================================
- 11. 지도 점 세 개 설정 메뉴
+ 11. 지도 설정 메뉴
========================================================= */
- if (mapSettingsButton && mapSettingsMenu) {
+ if (
+ mapSettingsButton
+ && mapSettingsMenu
+ ) {
- /*
- 점 세 개 버튼을 누르면
- 지도 설정 메뉴를 열거나 닫습니다.
- */
- mapSettingsButton.addEventListener("click", (event) => {
+ // 점 세 개 버튼 클릭
+ mapSettingsButton.addEventListener(
+ "click",
+ (event) => {
- /*
- 버튼 클릭이 document 클릭 이벤트까지
- 전달되는 것을 막습니다.
+ // document 클릭 이벤트 전달 차단
+ event.stopPropagation();
- 이 코드가 없으면 메뉴를 열자마자
- 아래 document 클릭 이벤트가 실행되어
- 다시 닫힐 수 있습니다.
- */
- event.stopPropagation();
+ // 변경 후 열림 상태 계산
+ const willOpen =
+ mapSettingsMenu.hidden;
- /*
- 현재 메뉴가 숨겨져 있다면 true입니다.
- */
- const willOpen = mapSettingsMenu.hidden;
+ // 메뉴 열기/닫기
+ mapSettingsMenu.hidden =
+ !willOpen;
- /*
- 숨겨져 있었다면 열고,
- 열려 있었다면 닫습니다.
- */
- mapSettingsMenu.hidden = !willOpen;
+ // 접근성 상태 변경
+ mapSettingsButton.setAttribute(
+ "aria-expanded",
+ String(willOpen)
+ );
+ }
+ );
- /*
- 접근성 상태도 메뉴 상태와 맞춰 변경합니다.
- */
- mapSettingsButton.setAttribute(
- "aria-expanded",
- String(willOpen)
- );
- });
+ // 메뉴 내부 클릭 전파 차단
+ mapSettingsMenu.addEventListener(
+ "click",
+ (event) => {
+ event.stopPropagation();
+ }
+ );
- /*
- 설정 메뉴 내부를 클릭했을 때는
- document 클릭 이벤트로 전달되지 않게 합니다.
- */
- mapSettingsMenu.addEventListener("click", (event) => {
- event.stopPropagation();
- });
+ // 메뉴 바깥 클릭 시 닫기
+ document.addEventListener(
+ "click",
+ () => {
- /*
- 지도 설정 메뉴 바깥을 클릭하면
- 설정 메뉴를 닫습니다.
- */
- document.addEventListener("click", () => {
- mapSettingsMenu.hidden = true;
+ mapSettingsMenu.hidden = true;
- mapSettingsButton.setAttribute(
- "aria-expanded",
- "false"
- );
- });
+ mapSettingsButton.setAttribute(
+ "aria-expanded",
+ "false"
+ );
+ }
+ );
}
@@ -503,92 +469,97 @@ document.addEventListener("DOMContentLoaded", () => {
========================================================= */
mapModeButtons.forEach((button) => {
- button.addEventListener("click", () => {
- /*
- 클릭한 버튼의 data-map-mode 값을 가져옵니다.
- */
- const selectedMode =
- button.dataset.mapMode;
+ button.addEventListener(
+ "click",
+ () => {
+ // 선택 모드 가져오기
+ const selectedMode =
+ button.dataset.mapMode;
- /*
- 선택한 방식으로 지도 색상을 변경합니다.
- */
- applyMapMode(selectedMode);
+ // 지도 색상 변경
+ applyMapMode(
+ selectedMode
+ );
- /*
- 색상 방식 선택 후 설정 메뉴를 닫습니다.
- */
- if (mapSettingsMenu) {
- mapSettingsMenu.hidden = true;
- }
- if (mapSettingsButton) {
- mapSettingsButton.setAttribute(
- "aria-expanded",
- "false"
- );
+ // 설정 메뉴 닫기
+ if (mapSettingsMenu) {
+ mapSettingsMenu.hidden = true;
+ }
+
+
+ // 접근성 상태 변경
+ if (mapSettingsButton) {
+ mapSettingsButton.setAttribute(
+ "aria-expanded",
+ "false"
+ );
+ }
}
- });
+ );
});
/* =========================================================
- 13. 페이지 진입 시 저장된 지도 색상 방식 적용
+ 13. 저장된 지도 색상 적용
========================================================= */
if (districtDataElements.length > 0) {
+
const savedMapMode =
- localStorage.getItem("diaryMapMode")
+ localStorage.getItem(
+ "diaryMapMode"
+ )
|| "dominant";
- applyMapMode(savedMapMode);
+
+ applyMapMode(
+ savedMapMode
+ );
}
/* =========================================================
- 14. 전체 기록 정렬 및 기간 설정 요소
+ 14. 전체 기록 필터 요소
========================================================= */
- /*
- 최신순·과거순 선택창입니다.
- */
const sortSelect =
document.querySelector("#diary-sort");
-
- /*
- 최신순과 기간 설정을 포함하는 전체 폼입니다.
- */
const filterForm =
- document.querySelector("#diary-filter-form");
-
+ document.querySelector(
+ "#diary-filter-form"
+ );
- /*
- 기간 설정창을 여는 버튼입니다.
- */
const periodToggleButton =
- document.querySelector("#period-toggle-button");
-
+ document.querySelector(
+ "#period-toggle-button"
+ );
- /*
- 시작일·종료일·적용·초기화가 들어 있는
- 실제 기간 설정창입니다.
- */
const periodFilter =
- document.querySelector("#period-filter");
+ document.querySelector(
+ "#period-filter"
+ );
/* =========================================================
- 15. 최신순·과거순 선택 시 자동 제출
+ 15. 최신순·과거순 변경
========================================================= */
- if (sortSelect && filterForm) {
- sortSelect.addEventListener("change", () => {
- filterForm.submit();
- });
+ if (
+ sortSelect
+ && filterForm
+ ) {
+
+ sortSelect.addEventListener(
+ "change",
+ () => {
+ filterForm.submit();
+ }
+ );
}
@@ -598,34 +569,26 @@ document.addEventListener("DOMContentLoaded", () => {
function openPeriodFilter() {
- /*
- 현재 페이지에 기간 설정 요소가 없다면
- 함수를 종료합니다.
- */
- if (!periodToggleButton || !periodFilter) {
+ // 필수 요소 없는 경우 종료
+ if (
+ !periodToggleButton
+ || !periodFilter
+ ) {
return;
}
- /*
- hidden 속성을 제거해 기간 설정창을 표시합니다.
- */
+ // 기간 설정창 표시
periodFilter.hidden = false;
- /*
- 기간 설정 버튼에 active 클래스를 추가합니다.
-
- CSS의
- .period-toggle-button.active
- 스타일이 적용됩니다.
- */
- periodToggleButton.classList.add("active");
+ // 버튼 활성화
+ periodToggleButton.classList.add(
+ "active"
+ );
- /*
- 스크린 리더에 설정창이 열렸다는 것을 전달합니다.
- */
+ // 접근성 상태 변경
periodToggleButton.setAttribute(
"aria-expanded",
"true"
@@ -639,30 +602,26 @@ document.addEventListener("DOMContentLoaded", () => {
function closePeriodFilter() {
- /*
- 현재 페이지에 기간 설정 요소가 없다면
- 함수를 종료합니다.
- */
- if (!periodToggleButton || !periodFilter) {
+ // 필수 요소 없는 경우 종료
+ if (
+ !periodToggleButton
+ || !periodFilter
+ ) {
return;
}
- /*
- hidden 속성을 추가해 기간 설정창을 숨깁니다.
- */
+ // 기간 설정창 숨김
periodFilter.hidden = true;
- /*
- 버튼의 활성화 스타일을 제거합니다.
- */
- periodToggleButton.classList.remove("active");
+ // 버튼 활성화 제거
+ periodToggleButton.classList.remove(
+ "active"
+ );
- /*
- 스크린 리더에 설정창이 닫혔다는 것을 전달합니다.
- */
+ // 접근성 상태 변경
periodToggleButton.setAttribute(
"aria-expanded",
"false"
@@ -674,115 +633,548 @@ document.addEventListener("DOMContentLoaded", () => {
18. 기간 설정 버튼 클릭
========================================================= */
- if (periodToggleButton && periodFilter) {
- periodToggleButton.addEventListener("click", () => {
+ if (
+ periodToggleButton
+ && periodFilter
+ ) {
- /*
- 버튼을 누르기 전에 기간 설정창이
- 숨겨져 있었는지 확인합니다.
-
- true
- → 현재 닫혀 있으므로 열어야 합니다.
+ periodToggleButton.addEventListener(
+ "click",
+ () => {
- false
- → 현재 열려 있으므로 닫아야 합니다.
- */
- const shouldOpen = periodFilter.hidden;
+ const shouldOpen =
+ periodFilter.hidden;
- if (shouldOpen) {
- openPeriodFilter();
- } else {
- closePeriodFilter();
+ if (shouldOpen) {
+ openPeriodFilter();
+ } else {
+ closePeriodFilter();
+ }
}
- });
+ );
/* =====================================================
- 19. 기간 설정창 바깥 클릭 시 닫기
+ 19. 기간 설정창 바깥 클릭
===================================================== */
- document.addEventListener("click", (event) => {
+ document.addEventListener(
+ "click",
+ (event) => {
+
+ const clickedElement =
+ event.target;
+
+
+ // 기간 설정 버튼 클릭 여부
+ const clickedToggleButton =
+ periodToggleButton.contains(
+ clickedElement
+ );
+
+
+ // 기간 설정창 내부 클릭 여부
+ const clickedInsidePeriodFilter =
+ periodFilter.contains(
+ clickedElement
+ );
+
+
+ // 외부 클릭 시 닫기
+ if (
+ !clickedToggleButton
+ && !clickedInsidePeriodFilter
+ ) {
+ closePeriodFilter();
+ }
+ }
+ );
+ }
+
+
+ /* =========================================================
+ 20. 감정 도넛 그래프 요소
+ ========================================================= */
+
+ const emotionChart =
+ document.querySelector(
+ "#emotion-chart"
+ );
+
+ const emotionChartSegments =
+ document.querySelector(
+ "#emotion-chart-segments"
+ );
+
+ const emotionChartWrapper =
+ document.querySelector(
+ "#emotion-chart-wrapper"
+ );
+
+ const emotionChartTooltip =
+ document.querySelector(
+ "#emotion-chart-tooltip"
+ );
+
+ const emotionChartDataItems =
+ document.querySelectorAll(
+ "#emotion-chart-data .emotion-chart-data-item"
+ );
+
+
+ /* =========================================================
+ 21. 감정 데이터를 색상 그룹별로 합치기
+ ========================================================= */
+
+ function getEmotionChartGroups() {
+
+ // 색상 그룹별 임시 저장 객체
+ const groupedData = {};
+
+
+ emotionChartDataItems.forEach(
+ (element) => {
+
+ // 감정 이름
+ const emotionName =
+ element.dataset.emotionName.trim();
+
+
+ // 대표 감정 기록 횟수
+ const emotionCount =
+ Number(
+ element.dataset.emotionCount
+ );
+
+
+ // 색상 그룹 이름
+ const colorGroup =
+ element.dataset.emotionColorGroup.trim();
+
+
+ // 잘못된 데이터 제외
+ if (
+ !emotionName
+ || emotionCount <= 0
+ ) {
+ return;
+ }
+
+
+ // 최초 색상 그룹 생성
+ if (!groupedData[colorGroup]) {
+
+ groupedData[colorGroup] = {
+ colorGroup: colorGroup,
+ count: 0,
+ emotionNames: [],
+ };
+ }
+
+
+ // 기록 횟수 합산
+ groupedData[colorGroup].count +=
+ emotionCount;
+
+
+ // 감정 이름 중복 제거 후 추가
+ if (
+ !groupedData[
+ colorGroup
+ ].emotionNames.includes(
+ emotionName
+ )
+ ) {
+ groupedData[
+ colorGroup
+ ].emotionNames.push(
+ emotionName
+ );
+ }
+ }
+ );
+
+
+ // 객체를 배열로 변환
+ return Object.values(
+ groupedData
+ );
+ }
+
+
+ /* =========================================================
+ 22. 감정 그래프 툴팁 표시
+ ========================================================= */
+
+ function showEmotionChartTooltip(
+ emotionNames,
+ event
+ ) {
+
+ // 필수 요소 없는 경우 종료
+ if (
+ !emotionChartTooltip
+ || !emotionChartWrapper
+ ) {
+ return;
+ }
+
+
+ // 감정 이름만 표시
+ emotionChartTooltip.textContent =
+ emotionNames.join(" · ");
+
+
+ // 툴팁 표시
+ emotionChartTooltip.hidden = false;
+
+
+ // wrapper 위치 계산
+ const wrapperRect =
+ emotionChartWrapper.getBoundingClientRect();
+
+
+ // 마우스 기준 툴팁 위치
+ const tooltipX =
+ event.clientX
+ - wrapperRect.left;
+
+ const tooltipY =
+ event.clientY
+ - wrapperRect.top;
+
+
+ // 마우스보다 약간 위쪽에 배치
+ emotionChartTooltip.style.left =
+ `${tooltipX}px`;
+
+ emotionChartTooltip.style.top =
+ `${tooltipY - 16}px`;
+ }
+
+
+ /* =========================================================
+ 23. 감정 그래프 툴팁 숨기기
+ ========================================================= */
+
+ function hideEmotionChartTooltip() {
+
+ // 툴팁 없는 경우 종료
+ if (!emotionChartTooltip) {
+ return;
+ }
+
+
+ // 툴팁 숨김
+ emotionChartTooltip.hidden = true;
+ }
+
+
+ /* =========================================================
+ 24. 감정 SVG 도넛 그래프 생성
+ ========================================================= */
+
+ function renderEmotionChart() {
+
+ // 그래프 요소 없는 경우 종료
+ if (
+ !emotionChart
+ || !emotionChartSegments
+ ) {
+ return;
+ }
+
+
+ // 기존 그래프 조각 제거
+ emotionChartSegments.innerHTML = "";
+
+
+ // 색상 그룹별 데이터 생성
+ const chartGroups =
+ getEmotionChartGroups();
+
+
+ // 기록 없는 경우 종료
+ if (chartGroups.length === 0) {
+ return;
+ }
+
+
+ // 전체 대표 감정 기록 수 계산
+ const totalCount =
+ chartGroups.reduce(
+ (sum, group) =>
+ sum + group.count,
+ 0
+ );
+
+
+ // 전체 기록 없는 경우 종료
+ if (totalCount <= 0) {
+ return;
+ }
+
+
+ /*
+ SVG 원 정보
+
+ emotion_list.html:
+ cx = 160
+ cy = 160
+ r = 110
+ */
+ const centerX = 160;
+ const centerY = 160;
+ const radius = 110;
+
+
+ // 원 둘레 계산
+ const circumference =
+ 2 * Math.PI * radius;
+
+
+ /*
+ 조각 사이 간격
+
+ 값 증가
+ → 조각 사이 간격 증가
+
+ 값 감소
+ → 조각 사이 간격 감소
+ */
+ const segmentGap = 7;
+
+
+ /*
+ 시작 위치
+
+ SVG 원은 기본적으로 오른쪽에서 시작함.
+
+ -circumference / 4 적용
+ → 그래프 시작점을 12시 방향으로 이동
+ */
+ let currentOffset =
+ -circumference / 4;
+
+
+ chartGroups.forEach((group) => {
+
+ // 그룹 비율 계산
+ const ratio =
+ group.count / totalCount;
+
+
+ // 그룹이 차지하는 원 둘레 길이
+ const segmentLength =
+ circumference * ratio;
+
/*
- 실제로 클릭된 HTML 요소입니다.
+ 실제 표시 길이에서 간격 제거
+
+ 너무 작은 조각이 사라지는 문제 방지용
+ 최소 길이 1 적용
*/
- const clickedElement = event.target;
+ const visibleLength =
+ Math.max(
+ segmentLength
+ - segmentGap,
+ 1
+ );
+
+
+ // SVG circle 생성
+ const segment =
+ document.createElementNS(
+ "http://www.w3.org/2000/svg",
+ "circle"
+ );
+
+
+ // 기본 원 위치 설정
+ segment.setAttribute(
+ "cx",
+ centerX
+ );
+
+ segment.setAttribute(
+ "cy",
+ centerY
+ );
+
+ segment.setAttribute(
+ "r",
+ radius
+ );
+
+
+ // CSS 클래스 추가
+ segment.classList.add(
+ "emotion-chart-segment"
+ );
+
+
+ // 색상 그룹 클래스 추가
+ segment.classList.add(
+ `emotion-chart-segment--${group.colorGroup}`
+ );
+
+
+ // 실제 stroke 색상 적용
+ segment.style.stroke =
+ emotionChartColors[
+ group.colorGroup
+ ]
+ || emotionChartColors.default;
/*
- 클릭한 위치가 기간 설정 버튼 내부인지 확인합니다.
+ 현재 조각 길이 설정
+
+ 첫 번째 값
+ → 실제 표시되는 선 길이
- 버튼의 글자 부분을 클릭해도 true가 됩니다.
+ 두 번째 값
+ → 나머지 비어 있는 선 길이
*/
- const clickedToggleButton =
- periodToggleButton.contains(clickedElement);
+ segment.style.strokeDasharray =
+ `${visibleLength} ${
+ circumference
+ - visibleLength
+ }`;
+
+
+ // 현재 조각 시작 위치 설정
+ segment.style.strokeDashoffset =
+ `${-currentOffset}`;
/*
- 클릭한 위치가 기간 설정창 내부인지 확인합니다.
+ hover에서 사용할 감정 이름 저장
+
+ 예:
+ "기쁨|만족|신남"
+ */
+ segment.dataset.emotionNames =
+ group.emotionNames.join("|");
- 다음 요소를 클릭했을 때 true가 됩니다.
- - 시작 날짜 입력창
- - 종료 날짜 입력창
- - 적용 버튼
- - 초기화 버튼
- - 기간 설정창의 빈 공간
+ /*
+ 마우스 진입 시 툴팁 표시
*/
- const clickedInsidePeriodFilter =
- periodFilter.contains(clickedElement);
+ segment.addEventListener(
+ "mouseenter",
+ (event) => {
+
+ showEmotionChartTooltip(
+ group.emotionNames,
+ event
+ );
+ }
+ );
/*
- 기간 설정 버튼도 아니고,
- 기간 설정창 내부도 아니라면
- 팝업 바깥을 클릭한 것입니다.
+ 조각 위에서 마우스 이동 시
+ 툴팁 위치도 함께 이동
*/
- if (
- !clickedToggleButton
- && !clickedInsidePeriodFilter
- ) {
- closePeriodFilter();
- }
+ segment.addEventListener(
+ "mousemove",
+ (event) => {
+
+ showEmotionChartTooltip(
+ group.emotionNames,
+ event
+ );
+ }
+ );
+
+
+ /*
+ 마우스 이탈 시 툴팁 제거
+ */
+ segment.addEventListener(
+ "mouseleave",
+ hideEmotionChartTooltip
+ );
+
+
+ // SVG에 조각 추가
+ emotionChartSegments.appendChild(
+ segment
+ );
+
+
+ // 다음 조각 시작 위치 계산
+ currentOffset +=
+ segmentLength;
});
}
/* =========================================================
- 20. 감정별 기록 팝업 요소
+ 25. 감정 도넛 그래프 초기 생성
+ ========================================================= */
+
+ renderEmotionChart();
+
+
+ /* =========================================================
+ 26. 감정별 기록 팝업 요소
========================================================= */
- const emotionButtons =
- document.querySelectorAll(".emotion-bubble");
+ /*
+ 기존 emotion_modal.html 기능 유지.
+
+ 현재 도넛 그래프에서는
+ 감정 클릭 기능을 연결하지 않음.
+
+ 추후 그래프 클릭 또는 별도 감정 버튼 추가 시
+ openEmotionModal() 재사용 가능.
+ */
const modal =
- document.querySelector("#emotion-modal");
+ document.querySelector(
+ "#emotion-modal"
+ );
const modalName =
- document.querySelector("#emotion-modal-name");
+ document.querySelector(
+ "#emotion-modal-name"
+ );
const modalRecords =
- document.querySelectorAll(".emotion-modal-record");
+ document.querySelectorAll(
+ ".emotion-modal-record"
+ );
const modalEmpty =
- document.querySelector("#emotion-modal-empty");
+ document.querySelector(
+ "#emotion-modal-empty"
+ );
const closeButtons =
- document.querySelectorAll("[data-modal-close]");
+ document.querySelectorAll(
+ "[data-modal-close]"
+ );
/* =========================================================
- 21. 감정별 기록 팝업 열기
+ 27. 감정별 기록 팝업 열기
========================================================= */
- function openEmotionModal(emotionId, emotionName) {
-
- /*
- 감정별 기록 팝업 요소가 없다면
- 함수를 실행하지 않습니다.
- */
- if (!modal || !modalName || !modalEmpty) {
+ function openEmotionModal(
+ emotionId,
+ emotionName
+ ) {
+
+ // 팝업 요소 없는 경우 종료
+ if (
+ !modal
+ || !modalName
+ || !modalEmpty
+ ) {
return;
}
@@ -790,20 +1182,22 @@ document.addEventListener("DOMContentLoaded", () => {
let visibleRecordCount = 0;
- /*
- 팝업 제목에 선택한 감정 이름을 넣습니다.
- */
- modalName.textContent = emotionName;
+ // 팝업 감정 이름 설정
+ modalName.textContent =
+ emotionName;
- /*
- 선택한 감정과 일치하는 기록만 표시합니다.
- */
+ // 선택 감정 기록만 표시
modalRecords.forEach((record) => {
+
const isMatched =
- record.dataset.recordEmotion === emotionId;
+ record.dataset.recordEmotion
+ === emotionId;
+
+
+ record.hidden =
+ !isMatched;
- record.hidden = !isMatched;
if (isMatched) {
visibleRecordCount += 1;
@@ -811,59 +1205,51 @@ document.addEventListener("DOMContentLoaded", () => {
});
- /*
- 표시할 기록이 없다면 빈 안내 문구를 표시합니다.
- */
- modalEmpty.hidden = visibleRecordCount !== 0;
+ // 기록 없음 문구 처리
+ modalEmpty.hidden =
+ visibleRecordCount !== 0;
- /*
- 감정별 기록 팝업을 표시합니다.
- */
+ // 팝업 표시
modal.hidden = false;
- /*
- 팝업 뒤쪽 페이지의 스크롤을 막습니다.
- */
- document.body.classList.add("modal-open");
+ // 배경 스크롤 차단
+ document.body.classList.add(
+ "modal-open"
+ );
}
/* =========================================================
- 22. 감정별 기록 팝업 닫기
+ 28. 감정별 기록 팝업 닫기
========================================================= */
function closeEmotionModal() {
+
+ // 팝업 없는 경우 종료
if (!modal) {
return;
}
- modal.hidden = true;
-
- document.body.classList.remove("modal-open");
- }
+ // 팝업 숨김
+ modal.hidden = true;
- /* =========================================================
- 23. 감정 조약돌 클릭
- ========================================================= */
- emotionButtons.forEach((button) => {
- button.addEventListener("click", () => {
- openEmotionModal(
- button.dataset.emotionId,
- button.dataset.emotionName
- );
- });
- });
+ // 배경 스크롤 복구
+ document.body.classList.remove(
+ "modal-open"
+ );
+ }
/* =========================================================
- 24. 감정 팝업 닫기 버튼 및 배경 클릭
+ 29. 감정 팝업 닫기
========================================================= */
closeButtons.forEach((button) => {
+
button.addEventListener(
"click",
closeEmotionModal
@@ -872,56 +1258,66 @@ document.addEventListener("DOMContentLoaded", () => {
/* =========================================================
- 25. Escape 키로 열린 요소 닫기
+ 30. Escape 키 처리
========================================================= */
- document.addEventListener("keydown", (event) => {
+ document.addEventListener(
+ "keydown",
+ (event) => {
- /*
- 누른 키가 Escape가 아니라면
- 아래 코드를 실행하지 않습니다.
- */
- if (event.key !== "Escape") {
- return;
- }
+ // Escape 아닌 경우 종료
+ if (event.key !== "Escape") {
+ return;
+ }
- /*
- 감정별 기록 팝업이 열려 있다면 닫습니다.
- */
- if (modal && !modal.hidden) {
- closeEmotionModal();
- }
+ // 감정 팝업 닫기
+ if (
+ modal
+ && !modal.hidden
+ ) {
+ closeEmotionModal();
+ }
- /*
- 장소별 기록 팝업이 열려 있다면 닫습니다.
- */
- if (locationModal && !locationModal.hidden) {
- closeLocationModal();
- }
+ // 장소 팝업 닫기
+ if (
+ locationModal
+ && !locationModal.hidden
+ ) {
+ closeLocationModal();
+ }
- /*
- 지도 점 세 개 설정 메뉴가 열려 있다면 닫습니다.
- */
- if (mapSettingsMenu && !mapSettingsMenu.hidden) {
- mapSettingsMenu.hidden = true;
+ // 지도 설정 메뉴 닫기
+ if (
+ mapSettingsMenu
+ && !mapSettingsMenu.hidden
+ ) {
+ mapSettingsMenu.hidden = true;
- if (mapSettingsButton) {
- mapSettingsButton.setAttribute(
- "aria-expanded",
- "false"
- );
+
+ if (mapSettingsButton) {
+
+ mapSettingsButton.setAttribute(
+ "aria-expanded",
+ "false"
+ );
+ }
}
- }
- /*
- 기간 설정창이 열려 있다면 닫습니다.
- */
- if (periodFilter && !periodFilter.hidden) {
- closePeriodFilter();
+ // 기간 설정창 닫기
+ if (
+ periodFilter
+ && !periodFilter.hidden
+ ) {
+ closePeriodFilter();
+ }
+
+
+ // 감정 툴팁 제거
+ hideEmotionChartTooltip();
}
- });
+ );
});
\ No newline at end of file
diff --git a/diary/templates/diary/partials/emotion_list.html b/diary/templates/diary/partials/emotion_list.html
index bd9b4ea..3c956d3 100644
--- a/diary/templates/diary/partials/emotion_list.html
+++ b/diary/templates/diary/partials/emotion_list.html
@@ -41,11 +41,11 @@
- {{ records|length }}개
+ {{ total_record_count }}개
@@ -111,14 +111,14 @@
========================== -->
Date: Fri, 7 Aug 2026 12:07:42 +0900
Subject: [PATCH 06/13] =?UTF-8?q?style:=20=EA=B0=90=EC=A0=95=EB=B3=84=20?=
=?UTF-8?q?=EB=8F=84=EB=84=9B=20=EA=B7=B8=EB=9E=98=ED=94=84=20=EC=8A=A4?=
=?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=A0=81=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 744 ++++++++++++-------------------
1 file changed, 294 insertions(+), 450 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index ab0ad27..731e74d 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -3,22 +3,13 @@
========================================================= */
.diary-list-container {
- /*
- 페이지 콘텐츠의 최대 너비입니다.
-
- 화면이 1280px 이상이면 최대 1200px,
- 그보다 좁으면 좌우에 각각 40px 여백을 남깁니다.
- */
+ /* 페이지 콘텐츠 최대 너비 */
width: min(1200px, calc(100% - 80px));
- /*
- 컨테이너를 화면 가로 중앙에 배치합니다.
- */
+ /* 화면 가로 중앙 배치 */
margin: 0 auto;
- /*
- 위쪽 28px, 아래쪽 50px의 내부 여백입니다.
- */
+ /* 위아래 내부 여백 */
padding: 28px 0 50px;
}
@@ -28,26 +19,19 @@
========================================================= */
.diary-tabs-wrapper {
- /*
- 탭 묶음을 가로 중앙에 배치합니다.
- */
+ /* 탭 중앙 배치 */
display: flex;
justify-content: center;
width: 100%;
- /*
- 탭과 아래 콘텐츠 사이의 간격입니다.
- */
+ /* 탭과 콘텐츠 사이 간격 */
margin-bottom: 12px;
}
.diary-tabs {
- /*
- 각 탭 버튼을 한 줄로 배치합니다.
- 내용만큼만 너비를 차지합니다.
- */
+ /* 탭 한 줄 배치 */
display: inline-flex;
align-items: center;
gap: 4px;
@@ -64,9 +48,7 @@
.diary-tab {
- /*
- a 태그 또는 버튼으로 만들어진 각각의 탭입니다.
- */
+ /* 각 탭 기본 스타일 */
padding: 10px 18px;
border: 0;
@@ -83,9 +65,7 @@
.diary-tab.active {
- /*
- 현재 선택된 탭입니다.
- */
+ /* 현재 선택된 탭 */
background: #fff4cb;
color: #222222;
@@ -98,50 +78,31 @@
========================================================= */
.diary-list-header {
- /*
- 전체 기록 제목, 기록 개수, 필터를
- 왼쪽부터 가로 한 줄로 배치합니다.
-
- 화면:
- 전체 기록 7개 최신순 기간 설정
- */
+ /* 제목과 필터 가로 배치 */
display: flex;
align-items: center;
justify-content: flex-start;
- /*
- 제목·개수 묶음과 필터 사이의 간격입니다.
-
- 값을 키우면 필터가 제목에서 더 멀어집니다.
- */
+ /* 제목과 필터 사이 간격 */
gap: 18px;
- /*
- 헤더와 카드 목록 사이의 간격입니다.
- */
+ /* 카드 목록과의 간격 */
margin-bottom: 24px;
}
.diary-list-heading {
- /*
- '전체 기록'과 '7개'를 같은 줄에 배치합니다.
- */
+ /* 제목과 기록 개수 한 줄 배치 */
display: flex;
align-items: center;
gap: 10px;
- /*
- 남는 공간을 불필요하게 차지하지 않습니다.
- */
flex: 0 0 auto;
}
.diary-list-title {
- /*
- h1 태그의 브라우저 기본 여백을 제거합니다.
- */
+ /* h1 기본 여백 제거 */
margin: 0;
color: #222222;
@@ -152,10 +113,19 @@
}
+.diary-list-description {
+ /* 제목 아래 설명 문구 */
+ margin: 8px 0 0;
+
+ color: #8b857d;
+
+ font-size: 14px;
+ line-height: 1.5;
+}
+
+
.diary-list-count {
- /*
- 기록 개수를 작은 캡슐 모양으로 표시합니다.
- */
+ /* 기록 개수 캡슐 */
padding: 5px 10px;
border-radius: 999px;
@@ -167,9 +137,6 @@
font-weight: 700;
line-height: 1;
- /*
- '7'과 '개'가 서로 다른 줄로 나뉘지 않게 합니다.
- */
white-space: nowrap;
}
@@ -180,31 +147,21 @@
.diary-filter-form,
.diary-list-controls {
- /*
- 최신순 선택창과 기간 설정 버튼을
- 같은 줄에 배치합니다.
- */
+ /* 필터 버튼 가로 배치 */
display: flex;
align-items: center;
gap: 8px;
- /*
- 기간 설정 팝업의 위치 기준이 됩니다.
- */
+ /* 기간 설정창 위치 기준 */
position: relative;
- /*
- 필터 영역이 남은 가로 공간 전체를 차지하지 않습니다.
- */
flex: 0 0 auto;
}
.diary-sort-select,
.period-toggle-button {
- /*
- 최신순 선택창과 기간 설정 버튼의 공통 스타일입니다.
- */
+ /* 필터 공통 스타일 */
min-height: 42px;
padding: 0 16px;
@@ -214,9 +171,6 @@
background: #ffffff;
color: #333333;
- /*
- 페이지에서 사용하는 글꼴을 그대로 상속합니다.
- */
font: inherit;
cursor: pointer;
@@ -226,10 +180,6 @@
.diary-sort-select:focus,
.period-toggle-button:focus,
.period-filter input:focus {
- /*
- 키보드나 마우스로 요소를 선택했을 때
- 노란색 외곽선을 표시합니다.
- */
/*
outline: 2px solid #e9c767;
outline-offset: 2px;
@@ -238,9 +188,7 @@
.period-toggle-button.active {
- /*
- 기간 설정 창이 열려 있을 때의 버튼입니다.
- */
+ /* 기간 설정 활성 상태 */
border-color: #e1bd55;
background: #fff7d9;
@@ -250,57 +198,22 @@
/* =========================================================
- 기간 설정 팝업
+ 5. 기간 설정 팝업
========================================================= */
.period-filter {
- /*
- 최신순 선택창과 기간 설정 버튼을 감싸는
- .diary-filter-form을 기준으로 배치됩니다.
-
- 기존에는 필터 아래쪽에 표시되었지만,
- 이제는 필터의 오른쪽에 나란히 표시합니다.
- */
+ /* 필터 폼 기준 절대 배치 */
position: absolute;
z-index: 50;
- /*
- top: 0
- → 최신순·기간 설정 버튼과 날짜 선택창의
- 위쪽 높이를 똑같이 맞춥니다.
-
- 값을 키우면 날짜 선택창이 아래로 내려가고,
- 음수로 바꾸면 위로 올라갑니다.
- */
top: 0;
-
- /*
- left: calc(100% + 16px)
- → 필터 폼의 오른쪽 끝에서 16px 떨어진 위치에
- 날짜 선택창을 표시합니다.
-
- 16px을 크게 하면 기간 설정 버튼과 더 멀어지고,
- 작게 하면 버튼과 더 가까워집니다.
- */
left: calc(100% + 16px);
-
- /*
- 기존의 right: 0을 해제합니다.
-
- right: 0이 남아 있으면
- left와 right가 동시에 적용되어
- 너비나 위치가 예상과 다르게 계산될 수 있습니다.
- */
right: auto;
display: flex;
align-items: flex-end;
gap: 10px;
- /*
- 시작일·종료일·버튼이 들어갈 최소 너비입니다.
- 현재 화면에서는 약 500px 정도가 적당합니다.
- */
min-width: 500px;
padding: 18px;
@@ -315,18 +228,13 @@
.period-filter[hidden] {
- /*
- hidden 속성이 있을 때 완전히 숨깁니다.
- */
+ /* hidden 상태 제거 */
display: none;
}
.period-input-group {
- /*
- 시작일 또는 종료일의 label과 input을
- 위아래로 배치합니다.
- */
+ /* 라벨과 날짜 입력창 세로 배치 */
display: flex;
flex-direction: column;
gap: 7px;
@@ -354,9 +262,7 @@
.period-separator {
- /*
- 시작일과 종료일 사이의 물결표입니다.
- */
+ /* 시작일과 종료일 구분 */
padding-bottom: 10px;
color: #999999;
@@ -365,9 +271,7 @@
.period-apply-button,
.period-reset-button {
- /*
- 적용 버튼과 초기화 버튼의 공통 스타일입니다.
- */
+ /* 적용 / 초기화 공통 스타일 */
display: inline-flex;
align-items: center;
justify-content: center;
@@ -405,9 +309,7 @@
========================================================= */
.diary-grid {
- /*
- 기본 화면에서 한 줄에 카드 5개를 배치합니다.
- */
+ /* 한 줄에 카드 5개 배치 */
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 22px;
@@ -417,9 +319,6 @@
.diary-card {
- /*
- 카드가 grid 칸보다 커지는 것을 방지합니다.
- */
min-width: 0;
overflow: hidden;
@@ -440,9 +339,7 @@
.diary-card:hover {
- /*
- 마우스를 올리면 카드가 살짝 위로 이동합니다.
- */
+ /* hover 시 카드 이동 */
transform: translateY(-5px);
box-shadow:
@@ -452,9 +349,7 @@
.diary-card-link {
- /*
- 카드 전체 영역을 클릭할 수 있게 합니다.
- */
+ /* 카드 전체 클릭 영역 */
display: block;
height: 100%;
@@ -469,16 +364,11 @@
========================================================= */
.diary-card-image-wrapper {
- /*
- 카드 이미지 영역입니다.
- */
position: relative;
width: 100%;
- /*
- 이미지 영역의 가로:세로 비율을 4:3으로 유지합니다.
- */
+ /* 이미지 4:3 비율 유지 */
aspect-ratio: 4 / 3;
overflow: hidden;
@@ -493,10 +383,7 @@
width: 100%;
height: 100%;
- /*
- 사진이 이미지 영역을 빈틈없이 채우도록 합니다.
- 넘치는 부분은 잘립니다.
- */
+ /* 이미지 영역 채우기 */
object-fit: cover;
transition: transform 0.3s ease;
@@ -504,17 +391,13 @@
.diary-card:hover .diary-card-image {
- /*
- 카드에 마우스를 올리면 사진이 살짝 확대됩니다.
- */
+ /* hover 시 이미지 확대 */
transform: scale(1.04);
}
.diary-card-image-placeholder {
- /*
- 사진이 없을 때 보이는 대체 영역입니다.
- */
+ /* 이미지 없음 영역 */
display: flex;
align-items: center;
justify-content: center;
@@ -534,9 +417,7 @@
========================================================= */
.diary-card-content {
- /*
- 날짜, 기록 내용, 장소 줄을 위아래로 배치합니다.
- */
+ /* 날짜 / 내용 / 장소 세로 배치 */
display: flex;
flex-direction: column;
@@ -556,9 +437,7 @@
.diary-card-title {
- /*
- 기록 내용을 최대 두 줄까지만 보여줍니다.
- */
+ /* 기록 내용 최대 두 줄 제한 */
display: -webkit-box;
min-height: 44px;
@@ -584,35 +463,20 @@
========================================================= */
.diary-card-bottom-row {
- /*
- 장소는 왼쪽,
- 감정 아이콘은 오른쪽에 배치합니다.
- */
+ /* 장소 왼쪽 / 감정 오른쪽 */
display: flex;
align-items: center;
justify-content: space-between;
- /*
- 장소 이름과 감정 아이콘 사이의 최소 간격입니다.
- */
gap: 12px;
- /*
- 제목과 장소 줄 사이의 간격입니다.
- */
margin-top: 12px;
}
.diary-card-location {
- /*
- 장소 영역이 남은 공간을 사용합니다.
- */
flex: 1;
- /*
- flex 자식의 글자가 정상적으로 줄어들 수 있게 합니다.
- */
min-width: 0;
margin: 0;
@@ -630,23 +494,14 @@
.diary-card-location--empty {
- /*
- 장소 정보가 없을 때 문구를 더 연하게 표시합니다.
- */
+ /* 장소 없음 스타일 */
color: #aaa397;
}
.diary-card-emotion {
- /*
- 기존의 absolute 배치를 해제하고,
- 장소 오른쪽에서 일반적인 요소로 배치합니다.
- */
position: static;
- /*
- 장소 이름이 길어져도 아이콘이 작아지지 않게 합니다.
- */
flex-shrink: 0;
display: flex;
@@ -670,9 +525,7 @@
========================================================= */
.diary-empty {
- /*
- 빈 화면 안내가 grid 전체 열을 차지하게 합니다.
- */
+ /* grid 전체 사용 */
grid-column: 1 / -1;
padding: 80px 20px;
@@ -736,286 +589,267 @@
========================================================= */
.emotion-page-header {
- /*
- 왼쪽의 제목과 오른쪽의 이번 달 감정 요약을
- 양쪽에 나누어 배치합니다.
- */
+ /* 감정별 제목 영역 */
display: flex;
align-items: flex-start;
justify-content: space-between;
- gap: 30px;
- margin-bottom: 30px;
+ margin-bottom: 12px;
}
-.emotion-month-summary {
+.emotion-page-heading {
+ /* 제목과 설명 세로 배치 */
display: flex;
- align-items: center;
- gap: 10px;
-
- padding: 12px 18px;
-
- border: 1px solid #eadfce;
- border-radius: 14px;
+ flex-direction: column;
+}
- background: #ffffff;
- font-size: 13px;
-}
+/* =========================================================
+ 12. 감정 도넛 그래프 전체 영역
+ ========================================================= */
+.emotion-chart-section {
+ /* 그래프 중앙 배치 */
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
-.emotion-month-summary strong {
- color: #d98d36;
+ width: 100%;
- font-size: 18px;
+ min-height: 500px;
}
-.top-emotion-list {
+/* =========================================================
+ 13. 감정 도넛 그래프 wrapper
+ ========================================================= */
+
+.emotion-chart-wrapper {
/*
- 공동 1위 감정이 여러 개일 경우
- 한 줄 또는 여러 줄로 자연스럽게 표시
+ SVG / 중앙 기록 수 / 툴팁
+ 위치 기준
*/
- display: flex;
- align-items: center;
- justify-content: flex-end;
- flex-wrap: wrap;
- gap: 4px;
+ position: relative;
+
+ width: min(460px, 100%);
+
+ aspect-ratio: 1 / 1;
}
-.top-emotion-name {
- color: #d98d36;
+/* =========================================================
+ 14. SVG 감정 도넛 그래프
+ ========================================================= */
- white-space: nowrap;
-}
+.emotion-chart {
+ display: block;
+ width: 100%;
+ height: 100%;
-.top-emotion-separator {
- color: #b7aa97;
+ overflow: visible;
}
-.emotion-bubble-area {
+/* =========================================================
+ 15. 도넛 그래프 배경 원
+ ========================================================= */
+
+.emotion-chart-background {
/*
- 감정 조약돌들의 absolute 배치 기준입니다.
+ 기록이 없거나
+ 색상 조각 뒤쪽에 보이는 기본 원
*/
- position: relative;
-
- width: 100%;
- min-height: 560px;
+ fill: none;
- border-radius: 24px;
+ stroke: #ebe8e2;
+ stroke-width: 46;
- /*background: #ffffff;*/
+ pointer-events: none;
}
-.emotion-bubble {
- position: absolute;
-
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 4px;
+/* =========================================================
+ 16. 도넛 그래프 색상 조각
+ ========================================================= */
+.emotion-chart-segment {
/*
- 기존 테두리를 제거
+ 원 내부 채움 제거
+ stroke만 사용해 도넛 생성
*/
- border: none;
+ fill: none;
/*
- 조약돌 모양은 그대로 유지
+ 배경 원과 동일한 두께 적용
*/
- border-radius: 42% 58% 53% 47% / 50% 50% 50% 50%;
-
- background: rgba(255, 250, 231, 0.95);
- color: #333333;
+ stroke-width: 46;
/*
- 테두리 대신 기본 상태에서도 그림자를 표시합니다.
+ 각 조각 끝을 둥글게 처리
+ */
+ stroke-linecap: round;
- 0 → 좌우 이동 없음
- 5px → 아래로 5px 이동
- 14px → 그림자 흐림 정도
- rgba → 검정색, 투명도 12%
+ /*
+ 원 둘레 stroke 영역에서만
+ 마우스 이벤트 발생
*/
- box-shadow: 0 5px 14px rgba(0, 0, 0, 0.12);
+ pointer-events: stroke;
- font: inherit;
cursor: pointer;
transition:
- transform 0.2s ease,
- box-shadow 0.2s ease;
+ stroke-width 0.2s ease,
+ opacity 0.2s ease,
+ filter 0.2s ease;
}
-.emotion-bubble:hover {
- z-index: 5;
-
- transform: scale(1.08);
-
- box-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
-}
-
-
-.emotion-bubble-name {
- font-size: 20px;
- font-weight: 700;
-}
-
-
-.emotion-bubble-count {
- color: #8e877c;
-
- font-size: 15px;
-}
-
-
-/* 감정별 색상 */
-
-.emotion-bubble--yellow {
- /*border-color: #f9d472;*/
-
- background: #fff7e3;
-}
-
-
-.emotion-bubble--blue {
- /*border-color: #dae6f3;*/
+.emotion-chart-segment:hover {
+ /*
+ hover 조각 강조
+ */
+ stroke-width: 51;
- background: #eff7ff;
+ filter: brightness(0.97);
}
-.emotion-bubble--pink {
- /*border-color: #f8b3b4;*/
-
- background: #fff2f2;
-}
-
+/* =========================================================
+ 17. 감정 그래프 중앙 기록 수
+ ========================================================= */
-.emotion-bubble--purple {
- /*border-color: #9d72ce;*/
+.emotion-chart-center {
+ /*
+ SVG 중앙 위에 텍스트 배치
+ */
+ position: absolute;
- background: #f8f1ff;
-}
+ top: 50%;
+ left: 50%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 7px;
-.emotion-bubble--red {
- /*border-color: #ce7772;*/
+ transform: translate(-50%, -50%);
- background: #fff4f1;
+ /*
+ SVG hover 이벤트 방해 제거
+ */
+ pointer-events: none;
}
-.emotion-bubble--orange {
- /*border-color: #cea972;*/
+.emotion-chart-center-label {
+ color: #817b72;
- background: #fff9f1;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 1;
}
-.emotion-bubble--default {
- /*border-color: #c5c5c5;*/
+.emotion-chart-center-count {
+ color: #24211d;
- background: #dedede;
+ font-size: 36px;
+ font-weight: 700;
+ line-height: 1;
}
-/* 기록 개수에 따른 감정 조약돌 크기 */
-
-.emotion-bubble--large {
- width: 130px;
- height: 130px;
-}
-
+/* =========================================================
+ 18. 감정 그래프 툴팁
+ ========================================================= */
-.emotion-bubble--medium {
- width: 92px;
- height: 92px;
-}
+.emotion-chart-tooltip {
+ /*
+ 그래프 wrapper 기준 배치
+ */
+ position: absolute;
+ z-index: 20;
+ /*
+ JS에서 마우스 위치에 맞춰
+ left / top 값 변경
+ */
+ left: 0;
+ top: 0;
-.emotion-bubble--small {
- width: 64px;
- height: 64px;
-}
+ /*
+ 툴팁이 마우스 정중앙에 위치하도록 조정
+ */
+ transform: translate(-50%, -100%);
+ padding: 9px 13px;
-/* 감정 조약돌의 임시 배치 위치 */
+ border: 1px solid #e2ddd4;
+ border-radius: 10px;
-.emotion-bubble:nth-child(1) {
- top: 30px;
- left: 9%;
-}
+ background: rgba(255, 255, 255, 0.96);
+ color: #37332e;
+ font-size: 13px;
+ font-weight: 600;
+ line-height: 1.3;
-.emotion-bubble:nth-child(2) {
- top: 70px;
- left: 25%;
-}
+ white-space: nowrap;
+ pointer-events: none;
-.emotion-bubble:nth-child(3) {
- top: 160px;
- left: 15%;
+ box-shadow:
+ 0 7px 20px rgba(0, 0, 0, 0.1);
}
-.emotion-bubble:nth-child(4) {
- top: 110px;
- left: 44%;
+.emotion-chart-tooltip[hidden] {
+ /* 툴팁 숨김 */
+ display: none;
}
-.emotion-bubble:nth-child(5) {
- top: 260px;
- left: 34%;
-}
+/* =========================================================
+ 19. 감정 기록 없음
+ ========================================================= */
+.emotion-chart-empty {
+ /* 그래프 아래 빈 기록 안내 */
+ margin-top: 12px;
-.emotion-bubble:nth-child(6) {
- top: 80px;
- left: 68%;
+ text-align: center;
}
-.emotion-bubble:nth-child(7) {
- top: 210px;
- left: 75%;
-}
+.emotion-chart-empty-title {
+ margin: 0 0 7px;
+ color: #403c36;
-.emotion-bubble:nth-child(8) {
- top: 340px;
- left: 60%;
+ font-size: 16px;
+ font-weight: 700;
}
-.emotion-bubble:nth-child(9) {
- top: 390px;
- left: 20%;
-}
+.emotion-chart-empty-description {
+ margin: 0;
+ color: #918a81;
-.emotion-bubble:nth-child(10) {
- top: 370px;
- left: 80%;
+ font-size: 13px;
}
/* =========================================================
- 12. 감정별 기록 팝업
+ 20. 감정별 기록 팝업
========================================================= */
body.modal-open {
- /*
- 팝업이 열렸을 때 뒤쪽 페이지가 스크롤되지 않게 합니다.
- */
+ /* 팝업 뒤 페이지 스크롤 차단 */
overflow: hidden;
}
@@ -1120,13 +954,11 @@ body.modal-open {
/* =========================================================
- 13. 장소별 기록 화면 및 지도 설정
+ 21. 장소별 기록 화면 및 지도 설정
========================================================= */
.location-map-section {
- /*
- 지도 설정 툴바와 지도를 위아래로 배치합니다.
- */
+ /* 툴바와 지도 세로 배치 */
display: flex;
flex-direction: column;
gap: 10px;
@@ -1136,9 +968,7 @@ body.modal-open {
.map-toolbar {
- /*
- 점 세 개 버튼을 오른쪽 끝에 배치합니다.
- */
+ /* 설정 버튼 오른쪽 배치 */
display: flex;
align-items: center;
justify-content: flex-end;
@@ -1148,9 +978,7 @@ body.modal-open {
.map-display-settings {
- /*
- 설정 메뉴가 점 세 개 버튼을 기준으로 표시되게 합니다.
- */
+ /* 설정 메뉴 위치 기준 */
position: relative;
z-index: 30;
}
@@ -1188,8 +1016,6 @@ body.modal-open {
.map-settings-button:hover {
transform: translateY(-1px);
- /*background: #fff8df;*/
-
box-shadow: 0 5px 14px rgba(0, 0, 0, 0.12);
}
@@ -1201,9 +1027,7 @@ body.modal-open {
.map-settings-menu {
- /*
- 점 세 개 버튼 바로 아래에 표시되는 설정 메뉴입니다.
- */
+ /* 점 세 개 버튼 아래 배치 */
position: absolute;
z-index: 40;
@@ -1269,13 +1093,11 @@ body.modal-open {
/* =========================================================
- 14. 서울 지도
+ 22. 서울 지도
========================================================= */
.location-map-wrapper {
- /*
- 서울 지도 SVG의 크기와 가운데 정렬을 담당합니다.
- */
+ /* 지도 크기 및 중앙 정렬 */
position: relative;
width: min(800px, 100%);
@@ -1293,9 +1115,7 @@ body.modal-open {
.seoul-map path {
- /*
- 모든 서울 구역 path의 기본 스타일입니다.
- */
+ /* 서울 구역 기본 스타일 */
fill: #ffffff;
stroke: #777777;
stroke-width: 1;
@@ -1309,18 +1129,13 @@ body.modal-open {
.seoul-map path:focus {
- /*
- SVG 구역을 클릭했을 때
- 기본 브라우저 외곽선을 제거합니다.
- */
+ /* SVG 기본 포커스 제거 */
outline: none;
}
.seoul-map path.has-record {
- /*
- 기록이 있는 구역에 적용됩니다.
- */
+ /* 기록 있는 구역 */
stroke-width: 2;
cursor: pointer;
@@ -1328,20 +1143,13 @@ body.modal-open {
.seoul-map path.has-record:hover {
- /*
- 기록이 있는 구역에 마우스를 올렸을 때
- 살짝 어두워지고 테두리가 굵어집니다.
- */
- /*stroke-width: 3;*/
-
+ /* 기록 있는 구역 hover */
filter: brightness(0.94);
}
.district-labels {
- /*
- 구 이름 텍스트가 지도 클릭을 방해하지 않게 합니다.
- */
+ /* 지도 라벨 클릭 방해 제거 */
pointer-events: none;
}
@@ -1358,16 +1166,12 @@ body.modal-open {
text-anchor: middle;
dominant-baseline: middle;
- /*
- 흰색 외곽선을 먼저 그리고
- 그 위에 글자를 표시합니다.
- */
paint-order: stroke;
}
/* =========================================================
- 15. 장소별 기록 팝업
+ 23. 장소별 기록 팝업
========================================================= */
.location-modal {
@@ -1470,115 +1274,155 @@ body.modal-open {
/* =========================================================
- 16. 반응형 스타일
+ 24. 반응형 스타일
========================================================= */
-/*
- 화면 너비가 1200px 이하일 때
- 카드 개수를 한 줄에 4개로 줄입니다.
-*/
+/* 1200px 이하 */
@media (max-width: 1200px) {
+
+ /* 카드 한 줄 4개 */
.diary-grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
-/*
- 화면 너비가 900px 이하일 때
- 카드와 팝업 목록을 한 줄에 3개로 줄입니다.
-*/
+/* 900px 이하 */
@media (max-width: 900px) {
+
+ /* 카드 및 팝업 한 줄 3개 */
.diary-grid,
.emotion-modal-grid,
.location-modal-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
+
+
+ /* 감정 그래프 크기 축소 */
+ .emotion-chart-wrapper {
+ width: min(420px, 100%);
+ }
}
-/*
- 화면 너비가 760px 이하일 때
- 전체 기록 제목과 필터를 위아래로 배치합니다.
-*/
+/* 760px 이하 */
@media (max-width: 760px) {
+
.diary-list-container {
- /*
- 작은 화면에서는 좌우 여백을 각각 20px로 줄입니다.
- */
+ /* 좌우 여백 축소 */
width: calc(100% - 40px);
}
+
.diary-list-header {
+ /* 제목과 필터 세로 배치 */
align-items: flex-start;
flex-direction: column;
gap: 14px;
}
+
.diary-filter-form,
.diary-list-controls {
width: 100%;
}
+
.period-filter {
- /*
- 작은 화면에서는 오른쪽에 표시할 공간이 부족하므로
- 다시 필터 버튼 아래쪽에 표시합니다.
- */
+ /* 기간 설정창 아래 배치 */
top: calc(100% + 10px);
- /*
- 필터 폼의 왼쪽에 날짜창을 맞춥니다.
- */
left: 0;
right: auto;
- /*
- 날짜 입력창과 버튼이 공간이 부족할 때
- 다음 줄로 내려가도록 합니다.
- */
flex-wrap: wrap;
- /*
- 모바일 화면보다 날짜창이 커지지 않게 제한합니다.
- */
- min-width: min(500px, calc(100vw - 40px));
+ min-width: min(
+ 500px,
+ calc(100vw - 40px)
+ );
+ }
+
+
+ .emotion-page-header {
+ /* 감정 제목 세로 배치 */
+ flex-direction: column;
+ }
+
+
+ .emotion-chart-section {
+ /* 모바일 그래프 높이 축소 */
+ min-height: 450px;
+ }
+
+
+ .emotion-chart-wrapper {
+ width: min(400px, 100%);
}
}
-/*
- 화면 너비가 650px 이하일 때
- 카드 및 팝업 목록을 한 줄에 2개로 줄입니다.
-*/
+/* 650px 이하 */
@media (max-width: 650px) {
+
.diary-grid,
.emotion-modal-grid,
.location-modal-grid {
+ /* 카드 및 팝업 한 줄 2개 */
grid-template-columns: repeat(2, minmax(0, 1fr));
+
gap: 14px;
}
+
.diary-card-content {
min-height: 118px;
padding: 12px;
}
+
.diary-card-title {
font-size: 14px;
}
- .emotion-page-header {
- flex-direction: column;
+
+ .emotion-chart-section {
+ min-height: 390px;
+ }
+
+
+ .emotion-chart-wrapper {
+ width: min(350px, 100%);
+ }
+
+
+ .emotion-chart-background,
+ .emotion-chart-segment {
+ /* 작은 화면에서 도넛 두께 축소 */
+ stroke-width: 40;
+ }
+
+
+ .emotion-chart-segment:hover {
+ stroke-width: 44;
+ }
+
+
+ .emotion-chart-center-count {
+ /* 가운데 기록 수 크기 축소 */
+ font-size: 30px;
}
- .emotion-bubble-area {
- min-height: 720px;
+
+ .emotion-chart-center-label {
+ font-size: 13px;
}
+
.emotion-modal-content,
.location-modal-content {
width: calc(100% - 28px);
+
padding: 20px;
}
}
\ No newline at end of file
From 8869098a211083d509a549577b0a05f24ee11728 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 12:36:53 +0900
Subject: [PATCH 07/13] =?UTF-8?q?style:=20=EA=B0=90=EC=A0=95=20=EA=B7=B8?=
=?UTF-8?q?=EB=9E=98=ED=94=84=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EA=B0=9C?=
=?UTF-8?q?=EC=84=A0=20=EB=B0=8F=20=EA=B0=90=EC=A0=95=20=EC=B9=B4=EB=93=9C?=
=?UTF-8?q?=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 373 +++++++++++--
diary/static/diary/js/diary.js | 506 ++++++++----------
.../diary/partials/emotion_list.html | 275 +++++-----
3 files changed, 683 insertions(+), 471 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 731e74d..49bec31 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -610,15 +610,13 @@
========================================================= */
.emotion-chart-section {
- /* 그래프 중앙 배치 */
display: flex;
- flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
- min-height: 500px;
+ min-height: 360px;
}
@@ -627,18 +625,17 @@
========================================================= */
.emotion-chart-wrapper {
- /*
- SVG / 중앙 기록 수 / 툴팁
- 위치 기준
- */
position: relative;
- width: min(460px, 100%);
+ /* 참고 이미지에 가까운 크기 */
+ width: 360px;
+ max-width: 100%;
aspect-ratio: 1 / 1;
}
+
/* =========================================================
14. SVG 감정 도넛 그래프
========================================================= */
@@ -654,66 +651,126 @@
/* =========================================================
- 15. 도넛 그래프 배경 원
+ 15. 도넛 그래프 배경
========================================================= */
.emotion-chart-background {
- /*
- 기록이 없거나
- 색상 조각 뒤쪽에 보이는 기본 원
- */
+ /* 도넛 뒤 기본 원 */
fill: none;
- stroke: #ebe8e2;
- stroke-width: 46;
+ stroke: #f2eee7;
+ stroke-width: 52;
pointer-events: none;
}
+/* 그래프 안쪽 원 */
+
+.emotion-chart-inner-circle {
+ fill: #fffdfa;
+
+ stroke: #eee5da;
+ stroke-width: 2;
+
+ pointer-events: none;
+
+ filter:
+ drop-shadow(
+ 0 2px 4px rgba(80, 65, 50, 0.08)
+ );
+}
+
+
/* =========================================================
- 16. 도넛 그래프 색상 조각
+ 16. 도넛 그래프 조각
========================================================= */
.emotion-chart-segment {
- /*
- 원 내부 채움 제거
- stroke만 사용해 도넛 생성
- */
fill: none;
- /*
- 배경 원과 동일한 두께 적용
- */
- stroke-width: 46;
+ /* 참고 이미지와 비슷한 두꺼운 띠 */
+ stroke-width: 52;
/*
- 각 조각 끝을 둥글게 처리
+ 끝을 완전한 반원으로 만들지 않음
+
+ butt
+ → 참고 이미지처럼 조각 경계가 깔끔함
*/
- stroke-linecap: round;
+ stroke-linecap: butt;
/*
- 원 둘레 stroke 영역에서만
- 마우스 이벤트 발생
+ 그래프 시작 위치를
+ 12시 방향으로 이동
*/
+ transform: rotate(-90deg);
+
+ transform-origin: 160px 160px;
+
pointer-events: stroke;
cursor: pointer;
+ filter:
+ drop-shadow(
+ 0 2px 3px rgba(74, 58, 45, 0.06)
+ );
+
transition:
- stroke-width 0.2s ease,
- opacity 0.2s ease,
- filter 0.2s ease;
+ stroke-width 0.18s ease,
+ filter 0.18s ease,
+ opacity 0.18s ease;
}
+/* 감정 색상 */
+
+.emotion-chart-segment--yellow {
+ stroke: #f9dda0;
+}
+
+
+.emotion-chart-segment--blue {
+ stroke: #bcdcf7;
+}
+
+
+.emotion-chart-segment--pink {
+ stroke: #f6c9cf;
+}
+
+
+.emotion-chart-segment--purple {
+ stroke: #cfc2f2;
+}
+
+
+.emotion-chart-segment--red {
+ stroke: #f2b6b2;
+}
+
+
+.emotion-chart-segment--orange {
+ stroke: #f7c995;
+}
+
+
+.emotion-chart-segment--default {
+ stroke: #dedede;
+}
+
+
+/* hover 강조 */
+
.emotion-chart-segment:hover {
- /*
- hover 조각 강조
- */
- stroke-width: 51;
+ stroke-width: 57;
- filter: brightness(0.97);
+ filter:
+ brightness(1.02)
+ drop-shadow(
+ 0 4px 5px rgba(65, 50, 40, 0.11)
+ );
}
@@ -722,9 +779,6 @@
========================================================= */
.emotion-chart-center {
- /*
- SVG 중앙 위에 텍스트 배치
- */
position: absolute;
top: 50%;
@@ -734,31 +788,29 @@
flex-direction: column;
align-items: center;
justify-content: center;
- gap: 7px;
+
+ gap: 9px;
transform: translate(-50%, -50%);
- /*
- SVG hover 이벤트 방해 제거
- */
pointer-events: none;
}
.emotion-chart-center-label {
- color: #817b72;
+ color: #776f69;
- font-size: 14px;
- font-weight: 500;
- line-height: 1;
+ font-size: 15px;
+ font-weight: 600;
}
.emotion-chart-center-count {
- color: #24211d;
+ color: #8b71d1;
+
+ font-size: 34px;
+ font-weight: 800;
- font-size: 36px;
- font-weight: 700;
line-height: 1;
}
@@ -843,6 +895,227 @@
font-size: 13px;
}
+.emotion-summary-board {
+ width: 100%;
+
+ padding:
+ 30px
+ 28px
+ 26px;
+
+ border: 1px solid #eee7dd;
+ border-radius: 28px;
+
+ background:
+ rgba(255, 253, 248, 0.82);
+
+ box-shadow:
+ 0 8px 24px rgba(69, 52, 38, 0.06);
+}
+
+.emotion-card-guide {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ gap: 8px;
+
+ margin:
+ -2px
+ 0
+ 20px;
+
+ color: #9a9188;
+
+ font-size: 12px;
+}
+
+
+.emotion-card-guide-line {
+ width: 45px;
+
+ border-top:
+ 2px dotted #ddd1ee;
+}
+
+
+.emotion-card-guide-heart {
+ color: #bda8e1;
+
+ font-size: 10px;
+}
+
+/* =========================================================
+ 감정별 기록 카드 목록
+ ========================================================= */
+
+.emotion-record-card-list {
+ display: flex;
+ align-items: stretch;
+ justify-content: center;
+ flex-wrap: wrap;
+
+ gap: 14px;
+
+ width: 100%;
+}
+
+
+/* 감정 카드 */
+
+.emotion-record-card {
+ display: flex;
+ align-items: center;
+
+ gap: 10px;
+
+ min-width: 150px;
+
+ padding:
+ 10px
+ 14px;
+
+ border: 1px solid #eee7dd;
+ border-radius: 17px;
+
+ background: #ffffff;
+
+ font: inherit;
+
+ cursor: pointer;
+
+ box-shadow:
+ 0 4px 10px rgba(70, 55, 40, 0.06);
+
+ transition:
+ transform 0.18s ease,
+ box-shadow 0.18s ease;
+}
+
+
+.emotion-record-card:hover {
+ transform: translateY(-3px);
+
+ box-shadow:
+ 0 8px 16px rgba(70, 55, 40, 0.1);
+}
+
+
+/* 감정 이미지 */
+
+.emotion-record-card-image {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ width: 48px;
+ height: 48px;
+
+ flex-shrink: 0;
+}
+
+
+.emotion-record-card-image img {
+ display: block;
+
+ width: 44px;
+ height: 44px;
+
+ object-fit: contain;
+}
+
+
+/* 감정 정보 */
+
+.emotion-record-card-info {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+
+ gap: 4px;
+}
+
+
+.emotion-record-card-name {
+ color: #403b36;
+
+ font-size: 14px;
+ font-weight: 700;
+}
+
+
+.emotion-record-card-count {
+ font-size: 13px;
+ font-weight: 700;
+}
+
+
+/* 카드별 포인트 색상 */
+
+.emotion-record-card--blue {
+ background: #f5faff;
+}
+
+
+.emotion-record-card--blue
+.emotion-record-card-count {
+ color: #67a7df;
+}
+
+
+.emotion-record-card--pink {
+ background: #fff7f8;
+}
+
+
+.emotion-record-card--pink
+.emotion-record-card-count {
+ color: #e58ea0;
+}
+
+
+.emotion-record-card--yellow {
+ background: #fffdf5;
+}
+
+
+.emotion-record-card--yellow
+.emotion-record-card-count {
+ color: #d6aa36;
+}
+
+
+.emotion-record-card--purple {
+ background: #faf7ff;
+}
+
+
+.emotion-record-card--purple
+.emotion-record-card-count {
+ color: #9276cf;
+}
+
+
+.emotion-record-card--red {
+ background: #fff7f5;
+}
+
+
+.emotion-record-card--red
+.emotion-record-card-count {
+ color: #db827b;
+}
+
+
+.emotion-record-card--orange {
+ background: #fff9f3;
+}
+
+
+.emotion-record-card--orange
+.emotion-record-card-count {
+ color: #d99c59;
+}
/* =========================================================
20. 감정별 기록 팝업
diff --git a/diary/static/diary/js/diary.js b/diary/static/diary/js/diary.js
index 884db7d..442fd57 100644
--- a/diary/static/diary/js/diary.js
+++ b/diary/static/diary/js/diary.js
@@ -692,9 +692,133 @@ document.addEventListener("DOMContentLoaded", () => {
);
}
+ /* =========================================================
+ 20. 감정별 기록 팝업 요소
+ ========================================================= */
+
+ const modal =
+ document.querySelector("#emotion-modal");
+
+ const modalName =
+ document.querySelector("#emotion-modal-name");
+
+ const modalRecords =
+ document.querySelectorAll(".emotion-modal-record");
+
+ const modalEmpty =
+ document.querySelector("#emotion-modal-empty");
+
+ const closeButtons =
+ document.querySelectorAll("[data-modal-close]");
+
+
+ /* =========================================================
+ 21. 감정별 기록 팝업 열기
+ ========================================================= */
+
+ function openEmotionModal(
+ emotionIds,
+ emotionNames
+ ) {
+
+ // 필수 요소 없는 경우 종료
+ if (
+ !modal
+ || !modalName
+ || !modalEmpty
+ ) {
+ return;
+ }
+
+
+ // 단일 ID도 배열로 변환
+ const normalizedIds =
+ Array.isArray(emotionIds)
+ ? emotionIds.map(String)
+ : [String(emotionIds)];
+
+
+ // 감정명도 배열로 변환
+ const normalizedNames =
+ Array.isArray(emotionNames)
+ ? emotionNames
+ : [emotionNames];
+
+
+ let visibleRecordCount = 0;
+
+
+ // 팝업 제목 설정
+ modalName.textContent =
+ normalizedNames.join(" · ");
+
+
+ // 선택 감정 그룹 기록 표시
+ modalRecords.forEach((record) => {
+
+ const recordEmotion =
+ String(
+ record.dataset.recordEmotion
+ );
+
+
+ const isMatched =
+ normalizedIds.includes(
+ recordEmotion
+ );
+
+
+ record.hidden =
+ !isMatched;
+
+
+ if (isMatched) {
+ visibleRecordCount += 1;
+ }
+ });
+
+
+ // 기록 없음 문구 처리
+ modalEmpty.hidden =
+ visibleRecordCount !== 0;
+
+
+ // 팝업 표시
+ modal.hidden = false;
+
+
+ // 배경 스크롤 차단
+ document.body.classList.add(
+ "modal-open"
+ );
+ }
+
+
+ /* =========================================================
+ 22. 감정별 기록 팝업 닫기
+ ========================================================= */
+
+ function closeEmotionModal() {
+
+ // 팝업 없는 경우 종료
+ if (!modal) {
+ return;
+ }
+
+
+ // 팝업 숨김
+ modal.hidden = true;
+
+
+ // 배경 스크롤 복구
+ document.body.classList.remove(
+ "modal-open"
+ );
+ }
+
/* =========================================================
- 20. 감정 도넛 그래프 요소
+ 23. 감정 도넛 그래프 요소
========================================================= */
const emotionChart =
@@ -724,50 +848,49 @@ document.addEventListener("DOMContentLoaded", () => {
/* =========================================================
- 21. 감정 데이터를 색상 그룹별로 합치기
+ 24. 색상 그룹별 감정 데이터 생성
========================================================= */
function getEmotionChartGroups() {
- // 색상 그룹별 임시 저장 객체
const groupedData = {};
emotionChartDataItems.forEach(
(element) => {
- // 감정 이름
+ const emotionId =
+ element.dataset.emotionId.trim();
+
const emotionName =
element.dataset.emotionName.trim();
-
- // 대표 감정 기록 횟수
const emotionCount =
Number(
element.dataset.emotionCount
);
-
- // 색상 그룹 이름
const colorGroup =
element.dataset.emotionColorGroup.trim();
// 잘못된 데이터 제외
if (
- !emotionName
+ !emotionId
+ || !emotionName
|| emotionCount <= 0
) {
return;
}
- // 최초 색상 그룹 생성
+ // 색상 그룹 최초 생성
if (!groupedData[colorGroup]) {
groupedData[colorGroup] = {
- colorGroup: colorGroup,
+ colorGroup,
count: 0,
+ emotionIds: [],
emotionNames: [],
};
}
@@ -778,7 +901,23 @@ document.addEventListener("DOMContentLoaded", () => {
emotionCount;
- // 감정 이름 중복 제거 후 추가
+ // 감정 ID 추가
+ if (
+ !groupedData[
+ colorGroup
+ ].emotionIds.includes(
+ emotionId
+ )
+ ) {
+ groupedData[
+ colorGroup
+ ].emotionIds.push(
+ emotionId
+ );
+ }
+
+
+ // 감정 이름 추가
if (
!groupedData[
colorGroup
@@ -796,7 +935,6 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // 객체를 배열로 변환
return Object.values(
groupedData
);
@@ -804,7 +942,7 @@ document.addEventListener("DOMContentLoaded", () => {
/* =========================================================
- 22. 감정 그래프 툴팁 표시
+ 25. 도넛 그래프 툴팁 표시
========================================================= */
function showEmotionChartTooltip(
@@ -821,7 +959,7 @@ document.addEventListener("DOMContentLoaded", () => {
}
- // 감정 이름만 표시
+ // 감정 이름 표시
emotionChartTooltip.textContent =
emotionNames.join(" · ");
@@ -830,12 +968,10 @@ document.addEventListener("DOMContentLoaded", () => {
emotionChartTooltip.hidden = false;
- // wrapper 위치 계산
const wrapperRect =
emotionChartWrapper.getBoundingClientRect();
- // 마우스 기준 툴팁 위치
const tooltipX =
event.clientX
- wrapperRect.left;
@@ -845,39 +981,37 @@ document.addEventListener("DOMContentLoaded", () => {
- wrapperRect.top;
- // 마우스보다 약간 위쪽에 배치
+ // 마우스 위쪽 배치
emotionChartTooltip.style.left =
`${tooltipX}px`;
emotionChartTooltip.style.top =
- `${tooltipY - 16}px`;
+ `${tooltipY - 18}px`;
}
/* =========================================================
- 23. 감정 그래프 툴팁 숨기기
+ 26. 도넛 그래프 툴팁 제거
========================================================= */
function hideEmotionChartTooltip() {
- // 툴팁 없는 경우 종료
if (!emotionChartTooltip) {
return;
}
- // 툴팁 숨김
emotionChartTooltip.hidden = true;
}
/* =========================================================
- 24. 감정 SVG 도넛 그래프 생성
+ 27. SVG 도넛 그래프 생성
========================================================= */
function renderEmotionChart() {
- // 그래프 요소 없는 경우 종료
+ // 그래프 없는 경우 종료
if (
!emotionChart
|| !emotionChartSegments
@@ -886,22 +1020,21 @@ document.addEventListener("DOMContentLoaded", () => {
}
- // 기존 그래프 조각 제거
+ // 기존 조각 제거
emotionChartSegments.innerHTML = "";
- // 색상 그룹별 데이터 생성
const chartGroups =
getEmotionChartGroups();
- // 기록 없는 경우 종료
+ // 데이터 없는 경우 종료
if (chartGroups.length === 0) {
return;
}
- // 전체 대표 감정 기록 수 계산
+ // 전체 기록 수 계산
const totalCount =
chartGroups.reduce(
(sum, group) =>
@@ -910,26 +1043,18 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // 전체 기록 없는 경우 종료
if (totalCount <= 0) {
return;
}
- /*
- SVG 원 정보
-
- emotion_list.html:
- cx = 160
- cy = 160
- r = 110
- */
+ // SVG 원 정보
const centerX = 160;
const centerY = 160;
- const radius = 110;
+ const radius = 112;
- // 원 둘레 계산
+ // 전체 원 둘레
const circumference =
2 * Math.PI * radius;
@@ -938,44 +1063,33 @@ document.addEventListener("DOMContentLoaded", () => {
조각 사이 간격
값 증가
- → 조각 사이 간격 증가
-
- 값 감소
- → 조각 사이 간격 감소
+ → 조각 사이 흰색 간격 증가
*/
- const segmentGap = 7;
+ const segmentGap = 5;
/*
- 시작 위치
+ 12시 방향 시작
- SVG 원은 기본적으로 오른쪽에서 시작함.
-
- -circumference / 4 적용
- → 그래프 시작점을 12시 방향으로 이동
+ 기본 circle 시작점은 오른쪽이므로
+ rotate(-90deg) 적용
*/
- let currentOffset =
- -circumference / 4;
+ let currentLength = 0;
chartGroups.forEach((group) => {
- // 그룹 비율 계산
const ratio =
- group.count / totalCount;
+ group.count
+ / totalCount;
- // 그룹이 차지하는 원 둘레 길이
+ // 실제 그룹 길이
const segmentLength =
circumference * ratio;
- /*
- 실제 표시 길이에서 간격 제거
-
- 너무 작은 조각이 사라지는 문제 방지용
- 최소 길이 1 적용
- */
+ // 흰색 간격 제외
const visibleLength =
Math.max(
segmentLength
@@ -984,7 +1098,6 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // SVG circle 생성
const segment =
document.createElementNS(
"http://www.w3.org/2000/svg",
@@ -992,7 +1105,6 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // 기본 원 위치 설정
segment.setAttribute(
"cx",
centerX
@@ -1009,60 +1121,47 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // CSS 클래스 추가
segment.classList.add(
"emotion-chart-segment"
);
- // 색상 그룹 클래스 추가
segment.classList.add(
`emotion-chart-segment--${group.colorGroup}`
);
- // 실제 stroke 색상 적용
- segment.style.stroke =
- emotionChartColors[
- group.colorGroup
- ]
- || emotionChartColors.default;
-
-
/*
- 현재 조각 길이 설정
+ 현재 조각만 표시
- 첫 번째 값
- → 실제 표시되는 선 길이
+ visibleLength
+ → 보이는 부분
- 두 번째 값
- → 나머지 비어 있는 선 길이
+ 나머지
+ → 숨겨지는 부분
*/
segment.style.strokeDasharray =
`${visibleLength} ${
- circumference
- - visibleLength
+ circumference - visibleLength
}`;
- // 현재 조각 시작 위치 설정
+ /*
+ 조각 시작 위치 이동
+ */
segment.style.strokeDashoffset =
- `${-currentOffset}`;
+ `${-currentLength}`;
- /*
- hover에서 사용할 감정 이름 저장
+ // hover 데이터 저장
+ segment.dataset.emotionIds =
+ group.emotionIds.join(",");
- 예:
- "기쁨|만족|신남"
- */
segment.dataset.emotionNames =
group.emotionNames.join("|");
- /*
- 마우스 진입 시 툴팁 표시
- */
+ // hover 툴팁 표시
segment.addEventListener(
"mouseenter",
(event) => {
@@ -1075,10 +1174,7 @@ document.addEventListener("DOMContentLoaded", () => {
);
- /*
- 조각 위에서 마우스 이동 시
- 툴팁 위치도 함께 이동
- */
+ // 툴팁 위치 이동
segment.addEventListener(
"mousemove",
(event) => {
@@ -1091,161 +1187,81 @@ document.addEventListener("DOMContentLoaded", () => {
);
- /*
- 마우스 이탈 시 툴팁 제거
- */
+ // 툴팁 제거
segment.addEventListener(
"mouseleave",
hideEmotionChartTooltip
);
- // SVG에 조각 추가
+ /*
+ 색상 조각 클릭
+
+ 해당 그룹에 포함된 모든 대표 감정 기록 표시
+ */
+ segment.addEventListener(
+ "click",
+ () => {
+
+ hideEmotionChartTooltip();
+
+
+ openEmotionModal(
+ group.emotionIds,
+ group.emotionNames
+ );
+ }
+ );
+
+
emotionChartSegments.appendChild(
segment
);
// 다음 조각 시작 위치 계산
- currentOffset +=
+ currentLength +=
segmentLength;
});
}
/* =========================================================
- 25. 감정 도넛 그래프 초기 생성
+ 28. 도넛 그래프 생성
========================================================= */
renderEmotionChart();
/* =========================================================
- 26. 감정별 기록 팝업 요소
+ 29. 감정 카드 클릭
========================================================= */
- /*
- 기존 emotion_modal.html 기능 유지.
-
- 현재 도넛 그래프에서는
- 감정 클릭 기능을 연결하지 않음.
-
- 추후 그래프 클릭 또는 별도 감정 버튼 추가 시
- openEmotionModal() 재사용 가능.
- */
-
- const modal =
- document.querySelector(
- "#emotion-modal"
- );
-
- const modalName =
- document.querySelector(
- "#emotion-modal-name"
- );
-
- const modalRecords =
+ const emotionRecordCards =
document.querySelectorAll(
- ".emotion-modal-record"
- );
-
- const modalEmpty =
- document.querySelector(
- "#emotion-modal-empty"
- );
-
- const closeButtons =
- document.querySelectorAll(
- "[data-modal-close]"
- );
-
-
- /* =========================================================
- 27. 감정별 기록 팝업 열기
- ========================================================= */
-
- function openEmotionModal(
- emotionId,
- emotionName
- ) {
-
- // 팝업 요소 없는 경우 종료
- if (
- !modal
- || !modalName
- || !modalEmpty
- ) {
- return;
- }
-
-
- let visibleRecordCount = 0;
-
-
- // 팝업 감정 이름 설정
- modalName.textContent =
- emotionName;
-
-
- // 선택 감정 기록만 표시
- modalRecords.forEach((record) => {
-
- const isMatched =
- record.dataset.recordEmotion
- === emotionId;
-
-
- record.hidden =
- !isMatched;
-
-
- if (isMatched) {
- visibleRecordCount += 1;
- }
- });
-
-
- // 기록 없음 문구 처리
- modalEmpty.hidden =
- visibleRecordCount !== 0;
-
-
- // 팝업 표시
- modal.hidden = false;
-
-
- // 배경 스크롤 차단
- document.body.classList.add(
- "modal-open"
+ "[data-emotion-card]"
);
- }
- /* =========================================================
- 28. 감정별 기록 팝업 닫기
- ========================================================= */
+ emotionRecordCards.forEach(
+ (card) => {
- function closeEmotionModal() {
+ card.addEventListener(
+ "click",
+ () => {
- // 팝업 없는 경우 종료
- if (!modal) {
- return;
+ openEmotionModal(
+ card.dataset.emotionId,
+ card.dataset.emotionName
+ );
+ }
+ );
}
-
-
- // 팝업 숨김
- modal.hidden = true;
-
-
- // 배경 스크롤 복구
- document.body.classList.remove(
- "modal-open"
- );
- }
+ );
/* =========================================================
- 29. 감정 팝업 닫기
+ 30. 감정 팝업 닫기
========================================================= */
closeButtons.forEach((button) => {
@@ -1256,68 +1272,4 @@ document.addEventListener("DOMContentLoaded", () => {
);
});
-
- /* =========================================================
- 30. Escape 키 처리
- ========================================================= */
-
- document.addEventListener(
- "keydown",
- (event) => {
-
- // Escape 아닌 경우 종료
- if (event.key !== "Escape") {
- return;
- }
-
-
- // 감정 팝업 닫기
- if (
- modal
- && !modal.hidden
- ) {
- closeEmotionModal();
- }
-
-
- // 장소 팝업 닫기
- if (
- locationModal
- && !locationModal.hidden
- ) {
- closeLocationModal();
- }
-
-
- // 지도 설정 메뉴 닫기
- if (
- mapSettingsMenu
- && !mapSettingsMenu.hidden
- ) {
- mapSettingsMenu.hidden = true;
-
-
- if (mapSettingsButton) {
-
- mapSettingsButton.setAttribute(
- "aria-expanded",
- "false"
- );
- }
- }
-
-
- // 기간 설정창 닫기
- if (
- periodFilter
- && !periodFilter.hidden
- ) {
- closePeriodFilter();
- }
-
-
- // 감정 툴팁 제거
- hideEmotionChartTooltip();
- }
- );
-});
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/diary/templates/diary/partials/emotion_list.html b/diary/templates/diary/partials/emotion_list.html
index 3c956d3..f3d025e 100644
--- a/diary/templates/diary/partials/emotion_list.html
+++ b/diary/templates/diary/partials/emotion_list.html
@@ -1,27 +1,23 @@
+{% load static %}
+
{% comment %}
감정별 기록 탭
-구성:
-1. 감정별 기록 제목
-2. SVG 도넛 그래프
-3. 가운데 전체 기록 수
-4. 그래프 영역 hover 시 감정 이름 툴팁
-5. JavaScript가 읽을 대표 감정 데이터
+구성
+- 대표 감정 도넛 그래프
+- 전체 기록 수
+- 색상 그룹 hover 툴팁
+- 기록된 대표 감정 카드
{% endcomment %}
-
-
+
-
-
감정별 기록
@@ -29,127 +25,150 @@
기록한 대표 감정을 한눈에 확인해보세요.
-
-
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
- 전체 기록
-
-
-
- {{ total_record_count }}개
-
-
+
+
+
+
+
+
+ 전체 기록
+
+
+
+ {{ total_record_count }}개
+
+
+
+
+
+
+
-
-
+ {% if recorded_emotions %}
+
+
- 예:
- 행복 · 설렘
+
+ ♥
+
- 횟수는 표시 X
+
+ 다른 감정들도 살펴볼까요?
+
- 위치와 내용은 diary.js에서 변경함
- -->
-
+
+ ♥
+
-
+
+
+ {% endif %}
+ {% if recorded_emotions %}
+
+
+ {% for emotion in recorded_emotions %}
+
+
+
+ {% endfor %}
-
+
@@ -157,49 +176,19 @@
{% endfor %}
-
+
{% if not recorded_emotions %}
-
-
아직 기록된 감정이 없어요.
@@ -207,9 +196,7 @@
오늘의 대표 감정을 기록해보세요.
-
-
{% endif %}
From f5f9656df41ec07a417bfe4386ec50f5d2ee2a49 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 19:40:14 +0900
Subject: [PATCH 08/13] =?UTF-8?q?style:=20=EB=94=94=ED=85=8C=EC=9D=BC=20?=
=?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9C=84=EC=95=84=EB=9E=98=20?=
=?UTF-8?q?=EC=97=AC=EB=B0=B1=20=EC=A1=B0=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/detail.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/diary/static/diary/css/detail.css b/diary/static/diary/css/detail.css
index b1ca107..25fad0a 100644
--- a/diary/static/diary/css/detail.css
+++ b/diary/static/diary/css/detail.css
@@ -37,7 +37,7 @@
첫 번째 숫자를 키우면:
→ 콘텐츠가 아래로 내려갑니다.
*/
- padding: 100px 0 100px;
+ padding: 20px 0 20px;
/*
width 안에 padding과 border까지 포함하도록 합니다.
From c4403ad677ca8857a6370618ae5726734b41dd38 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 19:54:19 +0900
Subject: [PATCH 09/13] =?UTF-8?q?style:=20=EC=9E=A5=EC=86=8C=EB=B3=84=20?=
=?UTF-8?q?=EC=84=A4=EC=A0=95=20=EB=B2=84=ED=8A=BC=20=ED=83=AD=20=EC=9A=B0?=
=?UTF-8?q?=EC=B8=A1=20=EB=B0=B0=EC=B9=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 65 +++++++++++--
diary/templates/diary/list.html | 154 ++++++++++++++++---------------
2 files changed, 135 insertions(+), 84 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 49bec31..98a1ae1 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -18,9 +18,16 @@
2. 전체 / 감정별 / 장소별 탭
========================================================= */
-.diary-tabs-wrapper {
- /* 탭 중앙 배치 */
+.diary-tabs-row {
+ /*
+ 탭과 점 세 개 버튼을 같은 줄에 배치
+ 탭은 화면 중앙 유지
+ 점 세 개 버튼의 위치 기준 역할
+ */
+ position: relative;
+
display: flex;
+ align-items: center;
justify-content: center;
width: 100%;
@@ -30,6 +37,19 @@
}
+.diary-tabs-wrapper {
+ /*
+ 탭 자체만 중앙 배치
+ width: 100% 제거하여 오른쪽 버튼이 탭 위치에 영향 주지 않도록 함
+ */
+ display: flex;
+ justify-content: center;
+
+ width: auto;
+ margin-bottom: 0;
+}
+
+
.diary-tabs {
/* 탭 한 줄 배치 */
display: inline-flex;
@@ -1231,22 +1251,20 @@ body.modal-open {
========================================================= */
.location-map-section {
- /* 툴바와 지도 세로 배치 */
+ /* 지도만 세로 배치 */
display: flex;
flex-direction: column;
- gap: 10px;
width: 100%;
}
+/*
+ 기존 지도 위 별도 툴바는 사용하지 않음
+ list.html에서 .map-toolbar 영역을 제거한 상태 기준
+*/
.map-toolbar {
- /* 설정 버튼 오른쪽 배치 */
- display: flex;
- align-items: center;
- justify-content: flex-end;
-
- width: 100%;
+ display: none;
}
@@ -1257,6 +1275,20 @@ body.modal-open {
}
+/*
+ 탭 행 안에 있는 지도 설정 버튼 영역
+ 탭은 가운데 유지하고 설정 버튼만 오른쪽 끝에 고정
+*/
+.diary-tabs-row > .map-display-settings {
+ position: absolute;
+
+ top: 50%;
+ right: 0;
+
+ transform: translateY(-50%);
+}
+
+
.map-settings-button {
display: flex;
align-items: center;
@@ -1587,6 +1619,19 @@ body.modal-open {
}
+
+ .diary-tabs-row > .map-display-settings {
+ right: 0;
+ }
+
+
+ .diary-tabs-row .map-settings-button {
+ width: 38px;
+ height: 38px;
+
+ font-size: 22px;
+ }
+
.diary-list-header {
/* 제목과 필터 세로 배치 */
align-items: flex-start;
diff --git a/diary/templates/diary/list.html b/diary/templates/diary/list.html
index 5cc23e0..44b11e5 100644
--- a/diary/templates/diary/list.html
+++ b/diary/templates/diary/list.html
@@ -35,8 +35,86 @@
-
- {% include "diary/partials/diary_tab.html" %}
+
+
+
+
+
+ {% include "diary/partials/diary_tab.html" %}
+
+
+
+
+ {% if selected_tab == "location" %}
+
+
+
+
+
+
+
+
+
+ 지도 색상 방식
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% endif %}
+
@@ -320,78 +398,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
- 지도 색상 방식
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 743d334a0407c70d67fe65e262a17c812fdf78eb Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 20:04:57 +0900
Subject: [PATCH 10/13] =?UTF-8?q?style:=20=EC=9E=A5=EC=86=8C=EB=B3=84=20?=
=?UTF-8?q?=EC=A7=80=EB=8F=84=20=EC=9C=84=EC=B9=98=20=EC=83=81=ED=96=A5=20?=
=?UTF-8?q?=EC=A1=B0=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 98a1ae1..82f403e 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -1407,7 +1407,9 @@ body.modal-open {
width: min(800px, 100%);
- margin: 0 auto;
+ margin: -50px auto -100px;
+
+ overflow: hidden;
}
From 4079776ae40101fd770a7e0edda37ba0570fc506 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 20:13:30 +0900
Subject: [PATCH 11/13] =?UTF-8?q?fix:=20=EC=A7=80=EB=8F=84=20=EC=9C=84?=
=?UTF-8?q?=EC=B9=98=20=EC=A1=B0=EC=A0=95=EC=9C=BC=EB=A1=9C=20=EC=9D=B8?=
=?UTF-8?q?=ED=95=B4=20=ED=83=AD=EB=B0=94=20=EC=95=88=EB=88=8C=EB=A6=AC?=
=?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 2 +-
diary/templates/diary/partials/seoul_map.html | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 82f403e..40a2640 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -1407,7 +1407,7 @@ body.modal-open {
width: min(800px, 100%);
- margin: -50px auto -100px;
+ margin: 0 auto;
overflow: hidden;
}
diff --git a/diary/templates/diary/partials/seoul_map.html b/diary/templates/diary/partials/seoul_map.html
index 51812ca..45f1250 100644
--- a/diary/templates/diary/partials/seoul_map.html
+++ b/diary/templates/diary/partials/seoul_map.html
@@ -1,6 +1,6 @@
@@ -195,6 +487,8 @@
감정별 기록
- ========================== -->
-
+
+
+ 기록한 대표 감정을 한눈에 확인해보세요.
+
+
+
+ 기록한 대표 감정을 한눈에 확인해보세요. +
+ +- 감정별 기록 -
- -- 감정의 크기는 이번 달 기록 횟수를 나타내요. -
-- 표시할 감정이 없어요. -
- {% endfor %} + + + + + ++ 아직 기록된 감정이 없어요. +
+ ++ 오늘의 대표 감정을 기록해보세요. +
+
- {{ records|length }}개
+ {{ total_record_count }}개
@@ -111,14 +111,14 @@
========================== -->
Date: Fri, 7 Aug 2026 12:07:42 +0900
Subject: [PATCH 06/13] =?UTF-8?q?style:=20=EA=B0=90=EC=A0=95=EB=B3=84=20?=
=?UTF-8?q?=EB=8F=84=EB=84=9B=20=EA=B7=B8=EB=9E=98=ED=94=84=20=EC=8A=A4?=
=?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=A0=81=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 744 ++++++++++++-------------------
1 file changed, 294 insertions(+), 450 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index ab0ad27..731e74d 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -3,22 +3,13 @@
========================================================= */
.diary-list-container {
- /*
- 페이지 콘텐츠의 최대 너비입니다.
-
- 화면이 1280px 이상이면 최대 1200px,
- 그보다 좁으면 좌우에 각각 40px 여백을 남깁니다.
- */
+ /* 페이지 콘텐츠 최대 너비 */
width: min(1200px, calc(100% - 80px));
- /*
- 컨테이너를 화면 가로 중앙에 배치합니다.
- */
+ /* 화면 가로 중앙 배치 */
margin: 0 auto;
- /*
- 위쪽 28px, 아래쪽 50px의 내부 여백입니다.
- */
+ /* 위아래 내부 여백 */
padding: 28px 0 50px;
}
@@ -28,26 +19,19 @@
========================================================= */
.diary-tabs-wrapper {
- /*
- 탭 묶음을 가로 중앙에 배치합니다.
- */
+ /* 탭 중앙 배치 */
display: flex;
justify-content: center;
width: 100%;
- /*
- 탭과 아래 콘텐츠 사이의 간격입니다.
- */
+ /* 탭과 콘텐츠 사이 간격 */
margin-bottom: 12px;
}
.diary-tabs {
- /*
- 각 탭 버튼을 한 줄로 배치합니다.
- 내용만큼만 너비를 차지합니다.
- */
+ /* 탭 한 줄 배치 */
display: inline-flex;
align-items: center;
gap: 4px;
@@ -64,9 +48,7 @@
.diary-tab {
- /*
- a 태그 또는 버튼으로 만들어진 각각의 탭입니다.
- */
+ /* 각 탭 기본 스타일 */
padding: 10px 18px;
border: 0;
@@ -83,9 +65,7 @@
.diary-tab.active {
- /*
- 현재 선택된 탭입니다.
- */
+ /* 현재 선택된 탭 */
background: #fff4cb;
color: #222222;
@@ -98,50 +78,31 @@
========================================================= */
.diary-list-header {
- /*
- 전체 기록 제목, 기록 개수, 필터를
- 왼쪽부터 가로 한 줄로 배치합니다.
-
- 화면:
- 전체 기록 7개 최신순 기간 설정
- */
+ /* 제목과 필터 가로 배치 */
display: flex;
align-items: center;
justify-content: flex-start;
- /*
- 제목·개수 묶음과 필터 사이의 간격입니다.
-
- 값을 키우면 필터가 제목에서 더 멀어집니다.
- */
+ /* 제목과 필터 사이 간격 */
gap: 18px;
- /*
- 헤더와 카드 목록 사이의 간격입니다.
- */
+ /* 카드 목록과의 간격 */
margin-bottom: 24px;
}
.diary-list-heading {
- /*
- '전체 기록'과 '7개'를 같은 줄에 배치합니다.
- */
+ /* 제목과 기록 개수 한 줄 배치 */
display: flex;
align-items: center;
gap: 10px;
- /*
- 남는 공간을 불필요하게 차지하지 않습니다.
- */
flex: 0 0 auto;
}
.diary-list-title {
- /*
- h1 태그의 브라우저 기본 여백을 제거합니다.
- */
+ /* h1 기본 여백 제거 */
margin: 0;
color: #222222;
@@ -152,10 +113,19 @@
}
+.diary-list-description {
+ /* 제목 아래 설명 문구 */
+ margin: 8px 0 0;
+
+ color: #8b857d;
+
+ font-size: 14px;
+ line-height: 1.5;
+}
+
+
.diary-list-count {
- /*
- 기록 개수를 작은 캡슐 모양으로 표시합니다.
- */
+ /* 기록 개수 캡슐 */
padding: 5px 10px;
border-radius: 999px;
@@ -167,9 +137,6 @@
font-weight: 700;
line-height: 1;
- /*
- '7'과 '개'가 서로 다른 줄로 나뉘지 않게 합니다.
- */
white-space: nowrap;
}
@@ -180,31 +147,21 @@
.diary-filter-form,
.diary-list-controls {
- /*
- 최신순 선택창과 기간 설정 버튼을
- 같은 줄에 배치합니다.
- */
+ /* 필터 버튼 가로 배치 */
display: flex;
align-items: center;
gap: 8px;
- /*
- 기간 설정 팝업의 위치 기준이 됩니다.
- */
+ /* 기간 설정창 위치 기준 */
position: relative;
- /*
- 필터 영역이 남은 가로 공간 전체를 차지하지 않습니다.
- */
flex: 0 0 auto;
}
.diary-sort-select,
.period-toggle-button {
- /*
- 최신순 선택창과 기간 설정 버튼의 공통 스타일입니다.
- */
+ /* 필터 공통 스타일 */
min-height: 42px;
padding: 0 16px;
@@ -214,9 +171,6 @@
background: #ffffff;
color: #333333;
- /*
- 페이지에서 사용하는 글꼴을 그대로 상속합니다.
- */
font: inherit;
cursor: pointer;
@@ -226,10 +180,6 @@
.diary-sort-select:focus,
.period-toggle-button:focus,
.period-filter input:focus {
- /*
- 키보드나 마우스로 요소를 선택했을 때
- 노란색 외곽선을 표시합니다.
- */
/*
outline: 2px solid #e9c767;
outline-offset: 2px;
@@ -238,9 +188,7 @@
.period-toggle-button.active {
- /*
- 기간 설정 창이 열려 있을 때의 버튼입니다.
- */
+ /* 기간 설정 활성 상태 */
border-color: #e1bd55;
background: #fff7d9;
@@ -250,57 +198,22 @@
/* =========================================================
- 기간 설정 팝업
+ 5. 기간 설정 팝업
========================================================= */
.period-filter {
- /*
- 최신순 선택창과 기간 설정 버튼을 감싸는
- .diary-filter-form을 기준으로 배치됩니다.
-
- 기존에는 필터 아래쪽에 표시되었지만,
- 이제는 필터의 오른쪽에 나란히 표시합니다.
- */
+ /* 필터 폼 기준 절대 배치 */
position: absolute;
z-index: 50;
- /*
- top: 0
- → 최신순·기간 설정 버튼과 날짜 선택창의
- 위쪽 높이를 똑같이 맞춥니다.
-
- 값을 키우면 날짜 선택창이 아래로 내려가고,
- 음수로 바꾸면 위로 올라갑니다.
- */
top: 0;
-
- /*
- left: calc(100% + 16px)
- → 필터 폼의 오른쪽 끝에서 16px 떨어진 위치에
- 날짜 선택창을 표시합니다.
-
- 16px을 크게 하면 기간 설정 버튼과 더 멀어지고,
- 작게 하면 버튼과 더 가까워집니다.
- */
left: calc(100% + 16px);
-
- /*
- 기존의 right: 0을 해제합니다.
-
- right: 0이 남아 있으면
- left와 right가 동시에 적용되어
- 너비나 위치가 예상과 다르게 계산될 수 있습니다.
- */
right: auto;
display: flex;
align-items: flex-end;
gap: 10px;
- /*
- 시작일·종료일·버튼이 들어갈 최소 너비입니다.
- 현재 화면에서는 약 500px 정도가 적당합니다.
- */
min-width: 500px;
padding: 18px;
@@ -315,18 +228,13 @@
.period-filter[hidden] {
- /*
- hidden 속성이 있을 때 완전히 숨깁니다.
- */
+ /* hidden 상태 제거 */
display: none;
}
.period-input-group {
- /*
- 시작일 또는 종료일의 label과 input을
- 위아래로 배치합니다.
- */
+ /* 라벨과 날짜 입력창 세로 배치 */
display: flex;
flex-direction: column;
gap: 7px;
@@ -354,9 +262,7 @@
.period-separator {
- /*
- 시작일과 종료일 사이의 물결표입니다.
- */
+ /* 시작일과 종료일 구분 */
padding-bottom: 10px;
color: #999999;
@@ -365,9 +271,7 @@
.period-apply-button,
.period-reset-button {
- /*
- 적용 버튼과 초기화 버튼의 공통 스타일입니다.
- */
+ /* 적용 / 초기화 공통 스타일 */
display: inline-flex;
align-items: center;
justify-content: center;
@@ -405,9 +309,7 @@
========================================================= */
.diary-grid {
- /*
- 기본 화면에서 한 줄에 카드 5개를 배치합니다.
- */
+ /* 한 줄에 카드 5개 배치 */
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 22px;
@@ -417,9 +319,6 @@
.diary-card {
- /*
- 카드가 grid 칸보다 커지는 것을 방지합니다.
- */
min-width: 0;
overflow: hidden;
@@ -440,9 +339,7 @@
.diary-card:hover {
- /*
- 마우스를 올리면 카드가 살짝 위로 이동합니다.
- */
+ /* hover 시 카드 이동 */
transform: translateY(-5px);
box-shadow:
@@ -452,9 +349,7 @@
.diary-card-link {
- /*
- 카드 전체 영역을 클릭할 수 있게 합니다.
- */
+ /* 카드 전체 클릭 영역 */
display: block;
height: 100%;
@@ -469,16 +364,11 @@
========================================================= */
.diary-card-image-wrapper {
- /*
- 카드 이미지 영역입니다.
- */
position: relative;
width: 100%;
- /*
- 이미지 영역의 가로:세로 비율을 4:3으로 유지합니다.
- */
+ /* 이미지 4:3 비율 유지 */
aspect-ratio: 4 / 3;
overflow: hidden;
@@ -493,10 +383,7 @@
width: 100%;
height: 100%;
- /*
- 사진이 이미지 영역을 빈틈없이 채우도록 합니다.
- 넘치는 부분은 잘립니다.
- */
+ /* 이미지 영역 채우기 */
object-fit: cover;
transition: transform 0.3s ease;
@@ -504,17 +391,13 @@
.diary-card:hover .diary-card-image {
- /*
- 카드에 마우스를 올리면 사진이 살짝 확대됩니다.
- */
+ /* hover 시 이미지 확대 */
transform: scale(1.04);
}
.diary-card-image-placeholder {
- /*
- 사진이 없을 때 보이는 대체 영역입니다.
- */
+ /* 이미지 없음 영역 */
display: flex;
align-items: center;
justify-content: center;
@@ -534,9 +417,7 @@
========================================================= */
.diary-card-content {
- /*
- 날짜, 기록 내용, 장소 줄을 위아래로 배치합니다.
- */
+ /* 날짜 / 내용 / 장소 세로 배치 */
display: flex;
flex-direction: column;
@@ -556,9 +437,7 @@
.diary-card-title {
- /*
- 기록 내용을 최대 두 줄까지만 보여줍니다.
- */
+ /* 기록 내용 최대 두 줄 제한 */
display: -webkit-box;
min-height: 44px;
@@ -584,35 +463,20 @@
========================================================= */
.diary-card-bottom-row {
- /*
- 장소는 왼쪽,
- 감정 아이콘은 오른쪽에 배치합니다.
- */
+ /* 장소 왼쪽 / 감정 오른쪽 */
display: flex;
align-items: center;
justify-content: space-between;
- /*
- 장소 이름과 감정 아이콘 사이의 최소 간격입니다.
- */
gap: 12px;
- /*
- 제목과 장소 줄 사이의 간격입니다.
- */
margin-top: 12px;
}
.diary-card-location {
- /*
- 장소 영역이 남은 공간을 사용합니다.
- */
flex: 1;
- /*
- flex 자식의 글자가 정상적으로 줄어들 수 있게 합니다.
- */
min-width: 0;
margin: 0;
@@ -630,23 +494,14 @@
.diary-card-location--empty {
- /*
- 장소 정보가 없을 때 문구를 더 연하게 표시합니다.
- */
+ /* 장소 없음 스타일 */
color: #aaa397;
}
.diary-card-emotion {
- /*
- 기존의 absolute 배치를 해제하고,
- 장소 오른쪽에서 일반적인 요소로 배치합니다.
- */
position: static;
- /*
- 장소 이름이 길어져도 아이콘이 작아지지 않게 합니다.
- */
flex-shrink: 0;
display: flex;
@@ -670,9 +525,7 @@
========================================================= */
.diary-empty {
- /*
- 빈 화면 안내가 grid 전체 열을 차지하게 합니다.
- */
+ /* grid 전체 사용 */
grid-column: 1 / -1;
padding: 80px 20px;
@@ -736,286 +589,267 @@
========================================================= */
.emotion-page-header {
- /*
- 왼쪽의 제목과 오른쪽의 이번 달 감정 요약을
- 양쪽에 나누어 배치합니다.
- */
+ /* 감정별 제목 영역 */
display: flex;
align-items: flex-start;
justify-content: space-between;
- gap: 30px;
- margin-bottom: 30px;
+ margin-bottom: 12px;
}
-.emotion-month-summary {
+.emotion-page-heading {
+ /* 제목과 설명 세로 배치 */
display: flex;
- align-items: center;
- gap: 10px;
-
- padding: 12px 18px;
-
- border: 1px solid #eadfce;
- border-radius: 14px;
+ flex-direction: column;
+}
- background: #ffffff;
- font-size: 13px;
-}
+/* =========================================================
+ 12. 감정 도넛 그래프 전체 영역
+ ========================================================= */
+.emotion-chart-section {
+ /* 그래프 중앙 배치 */
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
-.emotion-month-summary strong {
- color: #d98d36;
+ width: 100%;
- font-size: 18px;
+ min-height: 500px;
}
-.top-emotion-list {
+/* =========================================================
+ 13. 감정 도넛 그래프 wrapper
+ ========================================================= */
+
+.emotion-chart-wrapper {
/*
- 공동 1위 감정이 여러 개일 경우
- 한 줄 또는 여러 줄로 자연스럽게 표시
+ SVG / 중앙 기록 수 / 툴팁
+ 위치 기준
*/
- display: flex;
- align-items: center;
- justify-content: flex-end;
- flex-wrap: wrap;
- gap: 4px;
+ position: relative;
+
+ width: min(460px, 100%);
+
+ aspect-ratio: 1 / 1;
}
-.top-emotion-name {
- color: #d98d36;
+/* =========================================================
+ 14. SVG 감정 도넛 그래프
+ ========================================================= */
- white-space: nowrap;
-}
+.emotion-chart {
+ display: block;
+ width: 100%;
+ height: 100%;
-.top-emotion-separator {
- color: #b7aa97;
+ overflow: visible;
}
-.emotion-bubble-area {
+/* =========================================================
+ 15. 도넛 그래프 배경 원
+ ========================================================= */
+
+.emotion-chart-background {
/*
- 감정 조약돌들의 absolute 배치 기준입니다.
+ 기록이 없거나
+ 색상 조각 뒤쪽에 보이는 기본 원
*/
- position: relative;
-
- width: 100%;
- min-height: 560px;
+ fill: none;
- border-radius: 24px;
+ stroke: #ebe8e2;
+ stroke-width: 46;
- /*background: #ffffff;*/
+ pointer-events: none;
}
-.emotion-bubble {
- position: absolute;
-
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 4px;
+/* =========================================================
+ 16. 도넛 그래프 색상 조각
+ ========================================================= */
+.emotion-chart-segment {
/*
- 기존 테두리를 제거
+ 원 내부 채움 제거
+ stroke만 사용해 도넛 생성
*/
- border: none;
+ fill: none;
/*
- 조약돌 모양은 그대로 유지
+ 배경 원과 동일한 두께 적용
*/
- border-radius: 42% 58% 53% 47% / 50% 50% 50% 50%;
-
- background: rgba(255, 250, 231, 0.95);
- color: #333333;
+ stroke-width: 46;
/*
- 테두리 대신 기본 상태에서도 그림자를 표시합니다.
+ 각 조각 끝을 둥글게 처리
+ */
+ stroke-linecap: round;
- 0 → 좌우 이동 없음
- 5px → 아래로 5px 이동
- 14px → 그림자 흐림 정도
- rgba → 검정색, 투명도 12%
+ /*
+ 원 둘레 stroke 영역에서만
+ 마우스 이벤트 발생
*/
- box-shadow: 0 5px 14px rgba(0, 0, 0, 0.12);
+ pointer-events: stroke;
- font: inherit;
cursor: pointer;
transition:
- transform 0.2s ease,
- box-shadow 0.2s ease;
+ stroke-width 0.2s ease,
+ opacity 0.2s ease,
+ filter 0.2s ease;
}
-.emotion-bubble:hover {
- z-index: 5;
-
- transform: scale(1.08);
-
- box-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
-}
-
-
-.emotion-bubble-name {
- font-size: 20px;
- font-weight: 700;
-}
-
-
-.emotion-bubble-count {
- color: #8e877c;
-
- font-size: 15px;
-}
-
-
-/* 감정별 색상 */
-
-.emotion-bubble--yellow {
- /*border-color: #f9d472;*/
-
- background: #fff7e3;
-}
-
-
-.emotion-bubble--blue {
- /*border-color: #dae6f3;*/
+.emotion-chart-segment:hover {
+ /*
+ hover 조각 강조
+ */
+ stroke-width: 51;
- background: #eff7ff;
+ filter: brightness(0.97);
}
-.emotion-bubble--pink {
- /*border-color: #f8b3b4;*/
-
- background: #fff2f2;
-}
-
+/* =========================================================
+ 17. 감정 그래프 중앙 기록 수
+ ========================================================= */
-.emotion-bubble--purple {
- /*border-color: #9d72ce;*/
+.emotion-chart-center {
+ /*
+ SVG 중앙 위에 텍스트 배치
+ */
+ position: absolute;
- background: #f8f1ff;
-}
+ top: 50%;
+ left: 50%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 7px;
-.emotion-bubble--red {
- /*border-color: #ce7772;*/
+ transform: translate(-50%, -50%);
- background: #fff4f1;
+ /*
+ SVG hover 이벤트 방해 제거
+ */
+ pointer-events: none;
}
-.emotion-bubble--orange {
- /*border-color: #cea972;*/
+.emotion-chart-center-label {
+ color: #817b72;
- background: #fff9f1;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 1;
}
-.emotion-bubble--default {
- /*border-color: #c5c5c5;*/
+.emotion-chart-center-count {
+ color: #24211d;
- background: #dedede;
+ font-size: 36px;
+ font-weight: 700;
+ line-height: 1;
}
-/* 기록 개수에 따른 감정 조약돌 크기 */
-
-.emotion-bubble--large {
- width: 130px;
- height: 130px;
-}
-
+/* =========================================================
+ 18. 감정 그래프 툴팁
+ ========================================================= */
-.emotion-bubble--medium {
- width: 92px;
- height: 92px;
-}
+.emotion-chart-tooltip {
+ /*
+ 그래프 wrapper 기준 배치
+ */
+ position: absolute;
+ z-index: 20;
+ /*
+ JS에서 마우스 위치에 맞춰
+ left / top 값 변경
+ */
+ left: 0;
+ top: 0;
-.emotion-bubble--small {
- width: 64px;
- height: 64px;
-}
+ /*
+ 툴팁이 마우스 정중앙에 위치하도록 조정
+ */
+ transform: translate(-50%, -100%);
+ padding: 9px 13px;
-/* 감정 조약돌의 임시 배치 위치 */
+ border: 1px solid #e2ddd4;
+ border-radius: 10px;
-.emotion-bubble:nth-child(1) {
- top: 30px;
- left: 9%;
-}
+ background: rgba(255, 255, 255, 0.96);
+ color: #37332e;
+ font-size: 13px;
+ font-weight: 600;
+ line-height: 1.3;
-.emotion-bubble:nth-child(2) {
- top: 70px;
- left: 25%;
-}
+ white-space: nowrap;
+ pointer-events: none;
-.emotion-bubble:nth-child(3) {
- top: 160px;
- left: 15%;
+ box-shadow:
+ 0 7px 20px rgba(0, 0, 0, 0.1);
}
-.emotion-bubble:nth-child(4) {
- top: 110px;
- left: 44%;
+.emotion-chart-tooltip[hidden] {
+ /* 툴팁 숨김 */
+ display: none;
}
-.emotion-bubble:nth-child(5) {
- top: 260px;
- left: 34%;
-}
+/* =========================================================
+ 19. 감정 기록 없음
+ ========================================================= */
+.emotion-chart-empty {
+ /* 그래프 아래 빈 기록 안내 */
+ margin-top: 12px;
-.emotion-bubble:nth-child(6) {
- top: 80px;
- left: 68%;
+ text-align: center;
}
-.emotion-bubble:nth-child(7) {
- top: 210px;
- left: 75%;
-}
+.emotion-chart-empty-title {
+ margin: 0 0 7px;
+ color: #403c36;
-.emotion-bubble:nth-child(8) {
- top: 340px;
- left: 60%;
+ font-size: 16px;
+ font-weight: 700;
}
-.emotion-bubble:nth-child(9) {
- top: 390px;
- left: 20%;
-}
+.emotion-chart-empty-description {
+ margin: 0;
+ color: #918a81;
-.emotion-bubble:nth-child(10) {
- top: 370px;
- left: 80%;
+ font-size: 13px;
}
/* =========================================================
- 12. 감정별 기록 팝업
+ 20. 감정별 기록 팝업
========================================================= */
body.modal-open {
- /*
- 팝업이 열렸을 때 뒤쪽 페이지가 스크롤되지 않게 합니다.
- */
+ /* 팝업 뒤 페이지 스크롤 차단 */
overflow: hidden;
}
@@ -1120,13 +954,11 @@ body.modal-open {
/* =========================================================
- 13. 장소별 기록 화면 및 지도 설정
+ 21. 장소별 기록 화면 및 지도 설정
========================================================= */
.location-map-section {
- /*
- 지도 설정 툴바와 지도를 위아래로 배치합니다.
- */
+ /* 툴바와 지도 세로 배치 */
display: flex;
flex-direction: column;
gap: 10px;
@@ -1136,9 +968,7 @@ body.modal-open {
.map-toolbar {
- /*
- 점 세 개 버튼을 오른쪽 끝에 배치합니다.
- */
+ /* 설정 버튼 오른쪽 배치 */
display: flex;
align-items: center;
justify-content: flex-end;
@@ -1148,9 +978,7 @@ body.modal-open {
.map-display-settings {
- /*
- 설정 메뉴가 점 세 개 버튼을 기준으로 표시되게 합니다.
- */
+ /* 설정 메뉴 위치 기준 */
position: relative;
z-index: 30;
}
@@ -1188,8 +1016,6 @@ body.modal-open {
.map-settings-button:hover {
transform: translateY(-1px);
- /*background: #fff8df;*/
-
box-shadow: 0 5px 14px rgba(0, 0, 0, 0.12);
}
@@ -1201,9 +1027,7 @@ body.modal-open {
.map-settings-menu {
- /*
- 점 세 개 버튼 바로 아래에 표시되는 설정 메뉴입니다.
- */
+ /* 점 세 개 버튼 아래 배치 */
position: absolute;
z-index: 40;
@@ -1269,13 +1093,11 @@ body.modal-open {
/* =========================================================
- 14. 서울 지도
+ 22. 서울 지도
========================================================= */
.location-map-wrapper {
- /*
- 서울 지도 SVG의 크기와 가운데 정렬을 담당합니다.
- */
+ /* 지도 크기 및 중앙 정렬 */
position: relative;
width: min(800px, 100%);
@@ -1293,9 +1115,7 @@ body.modal-open {
.seoul-map path {
- /*
- 모든 서울 구역 path의 기본 스타일입니다.
- */
+ /* 서울 구역 기본 스타일 */
fill: #ffffff;
stroke: #777777;
stroke-width: 1;
@@ -1309,18 +1129,13 @@ body.modal-open {
.seoul-map path:focus {
- /*
- SVG 구역을 클릭했을 때
- 기본 브라우저 외곽선을 제거합니다.
- */
+ /* SVG 기본 포커스 제거 */
outline: none;
}
.seoul-map path.has-record {
- /*
- 기록이 있는 구역에 적용됩니다.
- */
+ /* 기록 있는 구역 */
stroke-width: 2;
cursor: pointer;
@@ -1328,20 +1143,13 @@ body.modal-open {
.seoul-map path.has-record:hover {
- /*
- 기록이 있는 구역에 마우스를 올렸을 때
- 살짝 어두워지고 테두리가 굵어집니다.
- */
- /*stroke-width: 3;*/
-
+ /* 기록 있는 구역 hover */
filter: brightness(0.94);
}
.district-labels {
- /*
- 구 이름 텍스트가 지도 클릭을 방해하지 않게 합니다.
- */
+ /* 지도 라벨 클릭 방해 제거 */
pointer-events: none;
}
@@ -1358,16 +1166,12 @@ body.modal-open {
text-anchor: middle;
dominant-baseline: middle;
- /*
- 흰색 외곽선을 먼저 그리고
- 그 위에 글자를 표시합니다.
- */
paint-order: stroke;
}
/* =========================================================
- 15. 장소별 기록 팝업
+ 23. 장소별 기록 팝업
========================================================= */
.location-modal {
@@ -1470,115 +1274,155 @@ body.modal-open {
/* =========================================================
- 16. 반응형 스타일
+ 24. 반응형 스타일
========================================================= */
-/*
- 화면 너비가 1200px 이하일 때
- 카드 개수를 한 줄에 4개로 줄입니다.
-*/
+/* 1200px 이하 */
@media (max-width: 1200px) {
+
+ /* 카드 한 줄 4개 */
.diary-grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
-/*
- 화면 너비가 900px 이하일 때
- 카드와 팝업 목록을 한 줄에 3개로 줄입니다.
-*/
+/* 900px 이하 */
@media (max-width: 900px) {
+
+ /* 카드 및 팝업 한 줄 3개 */
.diary-grid,
.emotion-modal-grid,
.location-modal-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
+
+
+ /* 감정 그래프 크기 축소 */
+ .emotion-chart-wrapper {
+ width: min(420px, 100%);
+ }
}
-/*
- 화면 너비가 760px 이하일 때
- 전체 기록 제목과 필터를 위아래로 배치합니다.
-*/
+/* 760px 이하 */
@media (max-width: 760px) {
+
.diary-list-container {
- /*
- 작은 화면에서는 좌우 여백을 각각 20px로 줄입니다.
- */
+ /* 좌우 여백 축소 */
width: calc(100% - 40px);
}
+
.diary-list-header {
+ /* 제목과 필터 세로 배치 */
align-items: flex-start;
flex-direction: column;
gap: 14px;
}
+
.diary-filter-form,
.diary-list-controls {
width: 100%;
}
+
.period-filter {
- /*
- 작은 화면에서는 오른쪽에 표시할 공간이 부족하므로
- 다시 필터 버튼 아래쪽에 표시합니다.
- */
+ /* 기간 설정창 아래 배치 */
top: calc(100% + 10px);
- /*
- 필터 폼의 왼쪽에 날짜창을 맞춥니다.
- */
left: 0;
right: auto;
- /*
- 날짜 입력창과 버튼이 공간이 부족할 때
- 다음 줄로 내려가도록 합니다.
- */
flex-wrap: wrap;
- /*
- 모바일 화면보다 날짜창이 커지지 않게 제한합니다.
- */
- min-width: min(500px, calc(100vw - 40px));
+ min-width: min(
+ 500px,
+ calc(100vw - 40px)
+ );
+ }
+
+
+ .emotion-page-header {
+ /* 감정 제목 세로 배치 */
+ flex-direction: column;
+ }
+
+
+ .emotion-chart-section {
+ /* 모바일 그래프 높이 축소 */
+ min-height: 450px;
+ }
+
+
+ .emotion-chart-wrapper {
+ width: min(400px, 100%);
}
}
-/*
- 화면 너비가 650px 이하일 때
- 카드 및 팝업 목록을 한 줄에 2개로 줄입니다.
-*/
+/* 650px 이하 */
@media (max-width: 650px) {
+
.diary-grid,
.emotion-modal-grid,
.location-modal-grid {
+ /* 카드 및 팝업 한 줄 2개 */
grid-template-columns: repeat(2, minmax(0, 1fr));
+
gap: 14px;
}
+
.diary-card-content {
min-height: 118px;
padding: 12px;
}
+
.diary-card-title {
font-size: 14px;
}
- .emotion-page-header {
- flex-direction: column;
+
+ .emotion-chart-section {
+ min-height: 390px;
+ }
+
+
+ .emotion-chart-wrapper {
+ width: min(350px, 100%);
+ }
+
+
+ .emotion-chart-background,
+ .emotion-chart-segment {
+ /* 작은 화면에서 도넛 두께 축소 */
+ stroke-width: 40;
+ }
+
+
+ .emotion-chart-segment:hover {
+ stroke-width: 44;
+ }
+
+
+ .emotion-chart-center-count {
+ /* 가운데 기록 수 크기 축소 */
+ font-size: 30px;
}
- .emotion-bubble-area {
- min-height: 720px;
+
+ .emotion-chart-center-label {
+ font-size: 13px;
}
+
.emotion-modal-content,
.location-modal-content {
width: calc(100% - 28px);
+
padding: 20px;
}
}
\ No newline at end of file
From 8869098a211083d509a549577b0a05f24ee11728 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 12:36:53 +0900
Subject: [PATCH 07/13] =?UTF-8?q?style:=20=EA=B0=90=EC=A0=95=20=EA=B7=B8?=
=?UTF-8?q?=EB=9E=98=ED=94=84=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EA=B0=9C?=
=?UTF-8?q?=EC=84=A0=20=EB=B0=8F=20=EA=B0=90=EC=A0=95=20=EC=B9=B4=EB=93=9C?=
=?UTF-8?q?=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 373 +++++++++++--
diary/static/diary/js/diary.js | 506 ++++++++----------
.../diary/partials/emotion_list.html | 275 +++++-----
3 files changed, 683 insertions(+), 471 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 731e74d..49bec31 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -610,15 +610,13 @@
========================================================= */
.emotion-chart-section {
- /* 그래프 중앙 배치 */
display: flex;
- flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
- min-height: 500px;
+ min-height: 360px;
}
@@ -627,18 +625,17 @@
========================================================= */
.emotion-chart-wrapper {
- /*
- SVG / 중앙 기록 수 / 툴팁
- 위치 기준
- */
position: relative;
- width: min(460px, 100%);
+ /* 참고 이미지에 가까운 크기 */
+ width: 360px;
+ max-width: 100%;
aspect-ratio: 1 / 1;
}
+
/* =========================================================
14. SVG 감정 도넛 그래프
========================================================= */
@@ -654,66 +651,126 @@
/* =========================================================
- 15. 도넛 그래프 배경 원
+ 15. 도넛 그래프 배경
========================================================= */
.emotion-chart-background {
- /*
- 기록이 없거나
- 색상 조각 뒤쪽에 보이는 기본 원
- */
+ /* 도넛 뒤 기본 원 */
fill: none;
- stroke: #ebe8e2;
- stroke-width: 46;
+ stroke: #f2eee7;
+ stroke-width: 52;
pointer-events: none;
}
+/* 그래프 안쪽 원 */
+
+.emotion-chart-inner-circle {
+ fill: #fffdfa;
+
+ stroke: #eee5da;
+ stroke-width: 2;
+
+ pointer-events: none;
+
+ filter:
+ drop-shadow(
+ 0 2px 4px rgba(80, 65, 50, 0.08)
+ );
+}
+
+
/* =========================================================
- 16. 도넛 그래프 색상 조각
+ 16. 도넛 그래프 조각
========================================================= */
.emotion-chart-segment {
- /*
- 원 내부 채움 제거
- stroke만 사용해 도넛 생성
- */
fill: none;
- /*
- 배경 원과 동일한 두께 적용
- */
- stroke-width: 46;
+ /* 참고 이미지와 비슷한 두꺼운 띠 */
+ stroke-width: 52;
/*
- 각 조각 끝을 둥글게 처리
+ 끝을 완전한 반원으로 만들지 않음
+
+ butt
+ → 참고 이미지처럼 조각 경계가 깔끔함
*/
- stroke-linecap: round;
+ stroke-linecap: butt;
/*
- 원 둘레 stroke 영역에서만
- 마우스 이벤트 발생
+ 그래프 시작 위치를
+ 12시 방향으로 이동
*/
+ transform: rotate(-90deg);
+
+ transform-origin: 160px 160px;
+
pointer-events: stroke;
cursor: pointer;
+ filter:
+ drop-shadow(
+ 0 2px 3px rgba(74, 58, 45, 0.06)
+ );
+
transition:
- stroke-width 0.2s ease,
- opacity 0.2s ease,
- filter 0.2s ease;
+ stroke-width 0.18s ease,
+ filter 0.18s ease,
+ opacity 0.18s ease;
}
+/* 감정 색상 */
+
+.emotion-chart-segment--yellow {
+ stroke: #f9dda0;
+}
+
+
+.emotion-chart-segment--blue {
+ stroke: #bcdcf7;
+}
+
+
+.emotion-chart-segment--pink {
+ stroke: #f6c9cf;
+}
+
+
+.emotion-chart-segment--purple {
+ stroke: #cfc2f2;
+}
+
+
+.emotion-chart-segment--red {
+ stroke: #f2b6b2;
+}
+
+
+.emotion-chart-segment--orange {
+ stroke: #f7c995;
+}
+
+
+.emotion-chart-segment--default {
+ stroke: #dedede;
+}
+
+
+/* hover 강조 */
+
.emotion-chart-segment:hover {
- /*
- hover 조각 강조
- */
- stroke-width: 51;
+ stroke-width: 57;
- filter: brightness(0.97);
+ filter:
+ brightness(1.02)
+ drop-shadow(
+ 0 4px 5px rgba(65, 50, 40, 0.11)
+ );
}
@@ -722,9 +779,6 @@
========================================================= */
.emotion-chart-center {
- /*
- SVG 중앙 위에 텍스트 배치
- */
position: absolute;
top: 50%;
@@ -734,31 +788,29 @@
flex-direction: column;
align-items: center;
justify-content: center;
- gap: 7px;
+
+ gap: 9px;
transform: translate(-50%, -50%);
- /*
- SVG hover 이벤트 방해 제거
- */
pointer-events: none;
}
.emotion-chart-center-label {
- color: #817b72;
+ color: #776f69;
- font-size: 14px;
- font-weight: 500;
- line-height: 1;
+ font-size: 15px;
+ font-weight: 600;
}
.emotion-chart-center-count {
- color: #24211d;
+ color: #8b71d1;
+
+ font-size: 34px;
+ font-weight: 800;
- font-size: 36px;
- font-weight: 700;
line-height: 1;
}
@@ -843,6 +895,227 @@
font-size: 13px;
}
+.emotion-summary-board {
+ width: 100%;
+
+ padding:
+ 30px
+ 28px
+ 26px;
+
+ border: 1px solid #eee7dd;
+ border-radius: 28px;
+
+ background:
+ rgba(255, 253, 248, 0.82);
+
+ box-shadow:
+ 0 8px 24px rgba(69, 52, 38, 0.06);
+}
+
+.emotion-card-guide {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ gap: 8px;
+
+ margin:
+ -2px
+ 0
+ 20px;
+
+ color: #9a9188;
+
+ font-size: 12px;
+}
+
+
+.emotion-card-guide-line {
+ width: 45px;
+
+ border-top:
+ 2px dotted #ddd1ee;
+}
+
+
+.emotion-card-guide-heart {
+ color: #bda8e1;
+
+ font-size: 10px;
+}
+
+/* =========================================================
+ 감정별 기록 카드 목록
+ ========================================================= */
+
+.emotion-record-card-list {
+ display: flex;
+ align-items: stretch;
+ justify-content: center;
+ flex-wrap: wrap;
+
+ gap: 14px;
+
+ width: 100%;
+}
+
+
+/* 감정 카드 */
+
+.emotion-record-card {
+ display: flex;
+ align-items: center;
+
+ gap: 10px;
+
+ min-width: 150px;
+
+ padding:
+ 10px
+ 14px;
+
+ border: 1px solid #eee7dd;
+ border-radius: 17px;
+
+ background: #ffffff;
+
+ font: inherit;
+
+ cursor: pointer;
+
+ box-shadow:
+ 0 4px 10px rgba(70, 55, 40, 0.06);
+
+ transition:
+ transform 0.18s ease,
+ box-shadow 0.18s ease;
+}
+
+
+.emotion-record-card:hover {
+ transform: translateY(-3px);
+
+ box-shadow:
+ 0 8px 16px rgba(70, 55, 40, 0.1);
+}
+
+
+/* 감정 이미지 */
+
+.emotion-record-card-image {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ width: 48px;
+ height: 48px;
+
+ flex-shrink: 0;
+}
+
+
+.emotion-record-card-image img {
+ display: block;
+
+ width: 44px;
+ height: 44px;
+
+ object-fit: contain;
+}
+
+
+/* 감정 정보 */
+
+.emotion-record-card-info {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+
+ gap: 4px;
+}
+
+
+.emotion-record-card-name {
+ color: #403b36;
+
+ font-size: 14px;
+ font-weight: 700;
+}
+
+
+.emotion-record-card-count {
+ font-size: 13px;
+ font-weight: 700;
+}
+
+
+/* 카드별 포인트 색상 */
+
+.emotion-record-card--blue {
+ background: #f5faff;
+}
+
+
+.emotion-record-card--blue
+.emotion-record-card-count {
+ color: #67a7df;
+}
+
+
+.emotion-record-card--pink {
+ background: #fff7f8;
+}
+
+
+.emotion-record-card--pink
+.emotion-record-card-count {
+ color: #e58ea0;
+}
+
+
+.emotion-record-card--yellow {
+ background: #fffdf5;
+}
+
+
+.emotion-record-card--yellow
+.emotion-record-card-count {
+ color: #d6aa36;
+}
+
+
+.emotion-record-card--purple {
+ background: #faf7ff;
+}
+
+
+.emotion-record-card--purple
+.emotion-record-card-count {
+ color: #9276cf;
+}
+
+
+.emotion-record-card--red {
+ background: #fff7f5;
+}
+
+
+.emotion-record-card--red
+.emotion-record-card-count {
+ color: #db827b;
+}
+
+
+.emotion-record-card--orange {
+ background: #fff9f3;
+}
+
+
+.emotion-record-card--orange
+.emotion-record-card-count {
+ color: #d99c59;
+}
/* =========================================================
20. 감정별 기록 팝업
diff --git a/diary/static/diary/js/diary.js b/diary/static/diary/js/diary.js
index 884db7d..442fd57 100644
--- a/diary/static/diary/js/diary.js
+++ b/diary/static/diary/js/diary.js
@@ -692,9 +692,133 @@ document.addEventListener("DOMContentLoaded", () => {
);
}
+ /* =========================================================
+ 20. 감정별 기록 팝업 요소
+ ========================================================= */
+
+ const modal =
+ document.querySelector("#emotion-modal");
+
+ const modalName =
+ document.querySelector("#emotion-modal-name");
+
+ const modalRecords =
+ document.querySelectorAll(".emotion-modal-record");
+
+ const modalEmpty =
+ document.querySelector("#emotion-modal-empty");
+
+ const closeButtons =
+ document.querySelectorAll("[data-modal-close]");
+
+
+ /* =========================================================
+ 21. 감정별 기록 팝업 열기
+ ========================================================= */
+
+ function openEmotionModal(
+ emotionIds,
+ emotionNames
+ ) {
+
+ // 필수 요소 없는 경우 종료
+ if (
+ !modal
+ || !modalName
+ || !modalEmpty
+ ) {
+ return;
+ }
+
+
+ // 단일 ID도 배열로 변환
+ const normalizedIds =
+ Array.isArray(emotionIds)
+ ? emotionIds.map(String)
+ : [String(emotionIds)];
+
+
+ // 감정명도 배열로 변환
+ const normalizedNames =
+ Array.isArray(emotionNames)
+ ? emotionNames
+ : [emotionNames];
+
+
+ let visibleRecordCount = 0;
+
+
+ // 팝업 제목 설정
+ modalName.textContent =
+ normalizedNames.join(" · ");
+
+
+ // 선택 감정 그룹 기록 표시
+ modalRecords.forEach((record) => {
+
+ const recordEmotion =
+ String(
+ record.dataset.recordEmotion
+ );
+
+
+ const isMatched =
+ normalizedIds.includes(
+ recordEmotion
+ );
+
+
+ record.hidden =
+ !isMatched;
+
+
+ if (isMatched) {
+ visibleRecordCount += 1;
+ }
+ });
+
+
+ // 기록 없음 문구 처리
+ modalEmpty.hidden =
+ visibleRecordCount !== 0;
+
+
+ // 팝업 표시
+ modal.hidden = false;
+
+
+ // 배경 스크롤 차단
+ document.body.classList.add(
+ "modal-open"
+ );
+ }
+
+
+ /* =========================================================
+ 22. 감정별 기록 팝업 닫기
+ ========================================================= */
+
+ function closeEmotionModal() {
+
+ // 팝업 없는 경우 종료
+ if (!modal) {
+ return;
+ }
+
+
+ // 팝업 숨김
+ modal.hidden = true;
+
+
+ // 배경 스크롤 복구
+ document.body.classList.remove(
+ "modal-open"
+ );
+ }
+
/* =========================================================
- 20. 감정 도넛 그래프 요소
+ 23. 감정 도넛 그래프 요소
========================================================= */
const emotionChart =
@@ -724,50 +848,49 @@ document.addEventListener("DOMContentLoaded", () => {
/* =========================================================
- 21. 감정 데이터를 색상 그룹별로 합치기
+ 24. 색상 그룹별 감정 데이터 생성
========================================================= */
function getEmotionChartGroups() {
- // 색상 그룹별 임시 저장 객체
const groupedData = {};
emotionChartDataItems.forEach(
(element) => {
- // 감정 이름
+ const emotionId =
+ element.dataset.emotionId.trim();
+
const emotionName =
element.dataset.emotionName.trim();
-
- // 대표 감정 기록 횟수
const emotionCount =
Number(
element.dataset.emotionCount
);
-
- // 색상 그룹 이름
const colorGroup =
element.dataset.emotionColorGroup.trim();
// 잘못된 데이터 제외
if (
- !emotionName
+ !emotionId
+ || !emotionName
|| emotionCount <= 0
) {
return;
}
- // 최초 색상 그룹 생성
+ // 색상 그룹 최초 생성
if (!groupedData[colorGroup]) {
groupedData[colorGroup] = {
- colorGroup: colorGroup,
+ colorGroup,
count: 0,
+ emotionIds: [],
emotionNames: [],
};
}
@@ -778,7 +901,23 @@ document.addEventListener("DOMContentLoaded", () => {
emotionCount;
- // 감정 이름 중복 제거 후 추가
+ // 감정 ID 추가
+ if (
+ !groupedData[
+ colorGroup
+ ].emotionIds.includes(
+ emotionId
+ )
+ ) {
+ groupedData[
+ colorGroup
+ ].emotionIds.push(
+ emotionId
+ );
+ }
+
+
+ // 감정 이름 추가
if (
!groupedData[
colorGroup
@@ -796,7 +935,6 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // 객체를 배열로 변환
return Object.values(
groupedData
);
@@ -804,7 +942,7 @@ document.addEventListener("DOMContentLoaded", () => {
/* =========================================================
- 22. 감정 그래프 툴팁 표시
+ 25. 도넛 그래프 툴팁 표시
========================================================= */
function showEmotionChartTooltip(
@@ -821,7 +959,7 @@ document.addEventListener("DOMContentLoaded", () => {
}
- // 감정 이름만 표시
+ // 감정 이름 표시
emotionChartTooltip.textContent =
emotionNames.join(" · ");
@@ -830,12 +968,10 @@ document.addEventListener("DOMContentLoaded", () => {
emotionChartTooltip.hidden = false;
- // wrapper 위치 계산
const wrapperRect =
emotionChartWrapper.getBoundingClientRect();
- // 마우스 기준 툴팁 위치
const tooltipX =
event.clientX
- wrapperRect.left;
@@ -845,39 +981,37 @@ document.addEventListener("DOMContentLoaded", () => {
- wrapperRect.top;
- // 마우스보다 약간 위쪽에 배치
+ // 마우스 위쪽 배치
emotionChartTooltip.style.left =
`${tooltipX}px`;
emotionChartTooltip.style.top =
- `${tooltipY - 16}px`;
+ `${tooltipY - 18}px`;
}
/* =========================================================
- 23. 감정 그래프 툴팁 숨기기
+ 26. 도넛 그래프 툴팁 제거
========================================================= */
function hideEmotionChartTooltip() {
- // 툴팁 없는 경우 종료
if (!emotionChartTooltip) {
return;
}
- // 툴팁 숨김
emotionChartTooltip.hidden = true;
}
/* =========================================================
- 24. 감정 SVG 도넛 그래프 생성
+ 27. SVG 도넛 그래프 생성
========================================================= */
function renderEmotionChart() {
- // 그래프 요소 없는 경우 종료
+ // 그래프 없는 경우 종료
if (
!emotionChart
|| !emotionChartSegments
@@ -886,22 +1020,21 @@ document.addEventListener("DOMContentLoaded", () => {
}
- // 기존 그래프 조각 제거
+ // 기존 조각 제거
emotionChartSegments.innerHTML = "";
- // 색상 그룹별 데이터 생성
const chartGroups =
getEmotionChartGroups();
- // 기록 없는 경우 종료
+ // 데이터 없는 경우 종료
if (chartGroups.length === 0) {
return;
}
- // 전체 대표 감정 기록 수 계산
+ // 전체 기록 수 계산
const totalCount =
chartGroups.reduce(
(sum, group) =>
@@ -910,26 +1043,18 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // 전체 기록 없는 경우 종료
if (totalCount <= 0) {
return;
}
- /*
- SVG 원 정보
-
- emotion_list.html:
- cx = 160
- cy = 160
- r = 110
- */
+ // SVG 원 정보
const centerX = 160;
const centerY = 160;
- const radius = 110;
+ const radius = 112;
- // 원 둘레 계산
+ // 전체 원 둘레
const circumference =
2 * Math.PI * radius;
@@ -938,44 +1063,33 @@ document.addEventListener("DOMContentLoaded", () => {
조각 사이 간격
값 증가
- → 조각 사이 간격 증가
-
- 값 감소
- → 조각 사이 간격 감소
+ → 조각 사이 흰색 간격 증가
*/
- const segmentGap = 7;
+ const segmentGap = 5;
/*
- 시작 위치
+ 12시 방향 시작
- SVG 원은 기본적으로 오른쪽에서 시작함.
-
- -circumference / 4 적용
- → 그래프 시작점을 12시 방향으로 이동
+ 기본 circle 시작점은 오른쪽이므로
+ rotate(-90deg) 적용
*/
- let currentOffset =
- -circumference / 4;
+ let currentLength = 0;
chartGroups.forEach((group) => {
- // 그룹 비율 계산
const ratio =
- group.count / totalCount;
+ group.count
+ / totalCount;
- // 그룹이 차지하는 원 둘레 길이
+ // 실제 그룹 길이
const segmentLength =
circumference * ratio;
- /*
- 실제 표시 길이에서 간격 제거
-
- 너무 작은 조각이 사라지는 문제 방지용
- 최소 길이 1 적용
- */
+ // 흰색 간격 제외
const visibleLength =
Math.max(
segmentLength
@@ -984,7 +1098,6 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // SVG circle 생성
const segment =
document.createElementNS(
"http://www.w3.org/2000/svg",
@@ -992,7 +1105,6 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // 기본 원 위치 설정
segment.setAttribute(
"cx",
centerX
@@ -1009,60 +1121,47 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // CSS 클래스 추가
segment.classList.add(
"emotion-chart-segment"
);
- // 색상 그룹 클래스 추가
segment.classList.add(
`emotion-chart-segment--${group.colorGroup}`
);
- // 실제 stroke 색상 적용
- segment.style.stroke =
- emotionChartColors[
- group.colorGroup
- ]
- || emotionChartColors.default;
-
-
/*
- 현재 조각 길이 설정
+ 현재 조각만 표시
- 첫 번째 값
- → 실제 표시되는 선 길이
+ visibleLength
+ → 보이는 부분
- 두 번째 값
- → 나머지 비어 있는 선 길이
+ 나머지
+ → 숨겨지는 부분
*/
segment.style.strokeDasharray =
`${visibleLength} ${
- circumference
- - visibleLength
+ circumference - visibleLength
}`;
- // 현재 조각 시작 위치 설정
+ /*
+ 조각 시작 위치 이동
+ */
segment.style.strokeDashoffset =
- `${-currentOffset}`;
+ `${-currentLength}`;
- /*
- hover에서 사용할 감정 이름 저장
+ // hover 데이터 저장
+ segment.dataset.emotionIds =
+ group.emotionIds.join(",");
- 예:
- "기쁨|만족|신남"
- */
segment.dataset.emotionNames =
group.emotionNames.join("|");
- /*
- 마우스 진입 시 툴팁 표시
- */
+ // hover 툴팁 표시
segment.addEventListener(
"mouseenter",
(event) => {
@@ -1075,10 +1174,7 @@ document.addEventListener("DOMContentLoaded", () => {
);
- /*
- 조각 위에서 마우스 이동 시
- 툴팁 위치도 함께 이동
- */
+ // 툴팁 위치 이동
segment.addEventListener(
"mousemove",
(event) => {
@@ -1091,161 +1187,81 @@ document.addEventListener("DOMContentLoaded", () => {
);
- /*
- 마우스 이탈 시 툴팁 제거
- */
+ // 툴팁 제거
segment.addEventListener(
"mouseleave",
hideEmotionChartTooltip
);
- // SVG에 조각 추가
+ /*
+ 색상 조각 클릭
+
+ 해당 그룹에 포함된 모든 대표 감정 기록 표시
+ */
+ segment.addEventListener(
+ "click",
+ () => {
+
+ hideEmotionChartTooltip();
+
+
+ openEmotionModal(
+ group.emotionIds,
+ group.emotionNames
+ );
+ }
+ );
+
+
emotionChartSegments.appendChild(
segment
);
// 다음 조각 시작 위치 계산
- currentOffset +=
+ currentLength +=
segmentLength;
});
}
/* =========================================================
- 25. 감정 도넛 그래프 초기 생성
+ 28. 도넛 그래프 생성
========================================================= */
renderEmotionChart();
/* =========================================================
- 26. 감정별 기록 팝업 요소
+ 29. 감정 카드 클릭
========================================================= */
- /*
- 기존 emotion_modal.html 기능 유지.
-
- 현재 도넛 그래프에서는
- 감정 클릭 기능을 연결하지 않음.
-
- 추후 그래프 클릭 또는 별도 감정 버튼 추가 시
- openEmotionModal() 재사용 가능.
- */
-
- const modal =
- document.querySelector(
- "#emotion-modal"
- );
-
- const modalName =
- document.querySelector(
- "#emotion-modal-name"
- );
-
- const modalRecords =
+ const emotionRecordCards =
document.querySelectorAll(
- ".emotion-modal-record"
- );
-
- const modalEmpty =
- document.querySelector(
- "#emotion-modal-empty"
- );
-
- const closeButtons =
- document.querySelectorAll(
- "[data-modal-close]"
- );
-
-
- /* =========================================================
- 27. 감정별 기록 팝업 열기
- ========================================================= */
-
- function openEmotionModal(
- emotionId,
- emotionName
- ) {
-
- // 팝업 요소 없는 경우 종료
- if (
- !modal
- || !modalName
- || !modalEmpty
- ) {
- return;
- }
-
-
- let visibleRecordCount = 0;
-
-
- // 팝업 감정 이름 설정
- modalName.textContent =
- emotionName;
-
-
- // 선택 감정 기록만 표시
- modalRecords.forEach((record) => {
-
- const isMatched =
- record.dataset.recordEmotion
- === emotionId;
-
-
- record.hidden =
- !isMatched;
-
-
- if (isMatched) {
- visibleRecordCount += 1;
- }
- });
-
-
- // 기록 없음 문구 처리
- modalEmpty.hidden =
- visibleRecordCount !== 0;
-
-
- // 팝업 표시
- modal.hidden = false;
-
-
- // 배경 스크롤 차단
- document.body.classList.add(
- "modal-open"
+ "[data-emotion-card]"
);
- }
- /* =========================================================
- 28. 감정별 기록 팝업 닫기
- ========================================================= */
+ emotionRecordCards.forEach(
+ (card) => {
- function closeEmotionModal() {
+ card.addEventListener(
+ "click",
+ () => {
- // 팝업 없는 경우 종료
- if (!modal) {
- return;
+ openEmotionModal(
+ card.dataset.emotionId,
+ card.dataset.emotionName
+ );
+ }
+ );
}
-
-
- // 팝업 숨김
- modal.hidden = true;
-
-
- // 배경 스크롤 복구
- document.body.classList.remove(
- "modal-open"
- );
- }
+ );
/* =========================================================
- 29. 감정 팝업 닫기
+ 30. 감정 팝업 닫기
========================================================= */
closeButtons.forEach((button) => {
@@ -1256,68 +1272,4 @@ document.addEventListener("DOMContentLoaded", () => {
);
});
-
- /* =========================================================
- 30. Escape 키 처리
- ========================================================= */
-
- document.addEventListener(
- "keydown",
- (event) => {
-
- // Escape 아닌 경우 종료
- if (event.key !== "Escape") {
- return;
- }
-
-
- // 감정 팝업 닫기
- if (
- modal
- && !modal.hidden
- ) {
- closeEmotionModal();
- }
-
-
- // 장소 팝업 닫기
- if (
- locationModal
- && !locationModal.hidden
- ) {
- closeLocationModal();
- }
-
-
- // 지도 설정 메뉴 닫기
- if (
- mapSettingsMenu
- && !mapSettingsMenu.hidden
- ) {
- mapSettingsMenu.hidden = true;
-
-
- if (mapSettingsButton) {
-
- mapSettingsButton.setAttribute(
- "aria-expanded",
- "false"
- );
- }
- }
-
-
- // 기간 설정창 닫기
- if (
- periodFilter
- && !periodFilter.hidden
- ) {
- closePeriodFilter();
- }
-
-
- // 감정 툴팁 제거
- hideEmotionChartTooltip();
- }
- );
-});
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/diary/templates/diary/partials/emotion_list.html b/diary/templates/diary/partials/emotion_list.html
index 3c956d3..f3d025e 100644
--- a/diary/templates/diary/partials/emotion_list.html
+++ b/diary/templates/diary/partials/emotion_list.html
@@ -1,27 +1,23 @@
+{% load static %}
+
{% comment %}
감정별 기록 탭
-구성:
-1. 감정별 기록 제목
-2. SVG 도넛 그래프
-3. 가운데 전체 기록 수
-4. 그래프 영역 hover 시 감정 이름 툴팁
-5. JavaScript가 읽을 대표 감정 데이터
+구성
+- 대표 감정 도넛 그래프
+- 전체 기록 수
+- 색상 그룹 hover 툴팁
+- 기록된 대표 감정 카드
{% endcomment %}
-
-
+
-
-
감정별 기록
@@ -29,127 +25,150 @@
기록한 대표 감정을 한눈에 확인해보세요.
-
-
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
- 전체 기록
-
-
-
- {{ total_record_count }}개
-
-
+
+
+
+
+
+
+ 전체 기록
+
+
+
+ {{ total_record_count }}개
+
+
+
+
+
+
+
-
-
+ {% if recorded_emotions %}
+
+
- 예:
- 행복 · 설렘
+
+ ♥
+
- 횟수는 표시 X
+
+ 다른 감정들도 살펴볼까요?
+
- 위치와 내용은 diary.js에서 변경함
- -->
-
+
+ ♥
+
-
+
+
+ {% endif %}
+ {% if recorded_emotions %}
+
+
+ {% for emotion in recorded_emotions %}
+
+
+
+ {% endfor %}
-
+
@@ -157,49 +176,19 @@
{% endfor %}
-
+
{% if not recorded_emotions %}
-
-
아직 기록된 감정이 없어요.
@@ -207,9 +196,7 @@
오늘의 대표 감정을 기록해보세요.
-
-
{% endif %}
From f5f9656df41ec07a417bfe4386ec50f5d2ee2a49 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 19:40:14 +0900
Subject: [PATCH 08/13] =?UTF-8?q?style:=20=EB=94=94=ED=85=8C=EC=9D=BC=20?=
=?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9C=84=EC=95=84=EB=9E=98=20?=
=?UTF-8?q?=EC=97=AC=EB=B0=B1=20=EC=A1=B0=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/detail.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/diary/static/diary/css/detail.css b/diary/static/diary/css/detail.css
index b1ca107..25fad0a 100644
--- a/diary/static/diary/css/detail.css
+++ b/diary/static/diary/css/detail.css
@@ -37,7 +37,7 @@
첫 번째 숫자를 키우면:
→ 콘텐츠가 아래로 내려갑니다.
*/
- padding: 100px 0 100px;
+ padding: 20px 0 20px;
/*
width 안에 padding과 border까지 포함하도록 합니다.
From c4403ad677ca8857a6370618ae5726734b41dd38 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 19:54:19 +0900
Subject: [PATCH 09/13] =?UTF-8?q?style:=20=EC=9E=A5=EC=86=8C=EB=B3=84=20?=
=?UTF-8?q?=EC=84=A4=EC=A0=95=20=EB=B2=84=ED=8A=BC=20=ED=83=AD=20=EC=9A=B0?=
=?UTF-8?q?=EC=B8=A1=20=EB=B0=B0=EC=B9=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 65 +++++++++++--
diary/templates/diary/list.html | 154 ++++++++++++++++---------------
2 files changed, 135 insertions(+), 84 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 49bec31..98a1ae1 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -18,9 +18,16 @@
2. 전체 / 감정별 / 장소별 탭
========================================================= */
-.diary-tabs-wrapper {
- /* 탭 중앙 배치 */
+.diary-tabs-row {
+ /*
+ 탭과 점 세 개 버튼을 같은 줄에 배치
+ 탭은 화면 중앙 유지
+ 점 세 개 버튼의 위치 기준 역할
+ */
+ position: relative;
+
display: flex;
+ align-items: center;
justify-content: center;
width: 100%;
@@ -30,6 +37,19 @@
}
+.diary-tabs-wrapper {
+ /*
+ 탭 자체만 중앙 배치
+ width: 100% 제거하여 오른쪽 버튼이 탭 위치에 영향 주지 않도록 함
+ */
+ display: flex;
+ justify-content: center;
+
+ width: auto;
+ margin-bottom: 0;
+}
+
+
.diary-tabs {
/* 탭 한 줄 배치 */
display: inline-flex;
@@ -1231,22 +1251,20 @@ body.modal-open {
========================================================= */
.location-map-section {
- /* 툴바와 지도 세로 배치 */
+ /* 지도만 세로 배치 */
display: flex;
flex-direction: column;
- gap: 10px;
width: 100%;
}
+/*
+ 기존 지도 위 별도 툴바는 사용하지 않음
+ list.html에서 .map-toolbar 영역을 제거한 상태 기준
+*/
.map-toolbar {
- /* 설정 버튼 오른쪽 배치 */
- display: flex;
- align-items: center;
- justify-content: flex-end;
-
- width: 100%;
+ display: none;
}
@@ -1257,6 +1275,20 @@ body.modal-open {
}
+/*
+ 탭 행 안에 있는 지도 설정 버튼 영역
+ 탭은 가운데 유지하고 설정 버튼만 오른쪽 끝에 고정
+*/
+.diary-tabs-row > .map-display-settings {
+ position: absolute;
+
+ top: 50%;
+ right: 0;
+
+ transform: translateY(-50%);
+}
+
+
.map-settings-button {
display: flex;
align-items: center;
@@ -1587,6 +1619,19 @@ body.modal-open {
}
+
+ .diary-tabs-row > .map-display-settings {
+ right: 0;
+ }
+
+
+ .diary-tabs-row .map-settings-button {
+ width: 38px;
+ height: 38px;
+
+ font-size: 22px;
+ }
+
.diary-list-header {
/* 제목과 필터 세로 배치 */
align-items: flex-start;
diff --git a/diary/templates/diary/list.html b/diary/templates/diary/list.html
index 5cc23e0..44b11e5 100644
--- a/diary/templates/diary/list.html
+++ b/diary/templates/diary/list.html
@@ -35,8 +35,86 @@
-
- {% include "diary/partials/diary_tab.html" %}
+
+
+
+
+
+ {% include "diary/partials/diary_tab.html" %}
+
+
+
+
+ {% if selected_tab == "location" %}
+
+
+
+
+
+
+
+
+
+ 지도 색상 방식
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% endif %}
+
@@ -320,78 +398,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
- 지도 색상 방식
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 743d334a0407c70d67fe65e262a17c812fdf78eb Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 20:04:57 +0900
Subject: [PATCH 10/13] =?UTF-8?q?style:=20=EC=9E=A5=EC=86=8C=EB=B3=84=20?=
=?UTF-8?q?=EC=A7=80=EB=8F=84=20=EC=9C=84=EC=B9=98=20=EC=83=81=ED=96=A5=20?=
=?UTF-8?q?=EC=A1=B0=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 98a1ae1..82f403e 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -1407,7 +1407,9 @@ body.modal-open {
width: min(800px, 100%);
- margin: 0 auto;
+ margin: -50px auto -100px;
+
+ overflow: hidden;
}
From 4079776ae40101fd770a7e0edda37ba0570fc506 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 20:13:30 +0900
Subject: [PATCH 11/13] =?UTF-8?q?fix:=20=EC=A7=80=EB=8F=84=20=EC=9C=84?=
=?UTF-8?q?=EC=B9=98=20=EC=A1=B0=EC=A0=95=EC=9C=BC=EB=A1=9C=20=EC=9D=B8?=
=?UTF-8?q?=ED=95=B4=20=ED=83=AD=EB=B0=94=20=EC=95=88=EB=88=8C=EB=A6=AC?=
=?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 2 +-
diary/templates/diary/partials/seoul_map.html | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 82f403e..40a2640 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -1407,7 +1407,7 @@ body.modal-open {
width: min(800px, 100%);
- margin: -50px auto -100px;
+ margin: 0 auto;
overflow: hidden;
}
diff --git a/diary/templates/diary/partials/seoul_map.html b/diary/templates/diary/partials/seoul_map.html
index 51812ca..45f1250 100644
--- a/diary/templates/diary/partials/seoul_map.html
+++ b/diary/templates/diary/partials/seoul_map.html
@@ -1,6 +1,6 @@
========================== -->
Date: Fri, 7 Aug 2026 12:07:42 +0900
Subject: [PATCH 06/13] =?UTF-8?q?style:=20=EA=B0=90=EC=A0=95=EB=B3=84=20?=
=?UTF-8?q?=EB=8F=84=EB=84=9B=20=EA=B7=B8=EB=9E=98=ED=94=84=20=EC=8A=A4?=
=?UTF-8?q?=ED=83=80=EC=9D=BC=20=EC=A0=81=EC=9A=A9?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 744 ++++++++++++-------------------
1 file changed, 294 insertions(+), 450 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index ab0ad27..731e74d 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -3,22 +3,13 @@
========================================================= */
.diary-list-container {
- /*
- 페이지 콘텐츠의 최대 너비입니다.
-
- 화면이 1280px 이상이면 최대 1200px,
- 그보다 좁으면 좌우에 각각 40px 여백을 남깁니다.
- */
+ /* 페이지 콘텐츠 최대 너비 */
width: min(1200px, calc(100% - 80px));
- /*
- 컨테이너를 화면 가로 중앙에 배치합니다.
- */
+ /* 화면 가로 중앙 배치 */
margin: 0 auto;
- /*
- 위쪽 28px, 아래쪽 50px의 내부 여백입니다.
- */
+ /* 위아래 내부 여백 */
padding: 28px 0 50px;
}
@@ -28,26 +19,19 @@
========================================================= */
.diary-tabs-wrapper {
- /*
- 탭 묶음을 가로 중앙에 배치합니다.
- */
+ /* 탭 중앙 배치 */
display: flex;
justify-content: center;
width: 100%;
- /*
- 탭과 아래 콘텐츠 사이의 간격입니다.
- */
+ /* 탭과 콘텐츠 사이 간격 */
margin-bottom: 12px;
}
.diary-tabs {
- /*
- 각 탭 버튼을 한 줄로 배치합니다.
- 내용만큼만 너비를 차지합니다.
- */
+ /* 탭 한 줄 배치 */
display: inline-flex;
align-items: center;
gap: 4px;
@@ -64,9 +48,7 @@
.diary-tab {
- /*
- a 태그 또는 버튼으로 만들어진 각각의 탭입니다.
- */
+ /* 각 탭 기본 스타일 */
padding: 10px 18px;
border: 0;
@@ -83,9 +65,7 @@
.diary-tab.active {
- /*
- 현재 선택된 탭입니다.
- */
+ /* 현재 선택된 탭 */
background: #fff4cb;
color: #222222;
@@ -98,50 +78,31 @@
========================================================= */
.diary-list-header {
- /*
- 전체 기록 제목, 기록 개수, 필터를
- 왼쪽부터 가로 한 줄로 배치합니다.
-
- 화면:
- 전체 기록 7개 최신순 기간 설정
- */
+ /* 제목과 필터 가로 배치 */
display: flex;
align-items: center;
justify-content: flex-start;
- /*
- 제목·개수 묶음과 필터 사이의 간격입니다.
-
- 값을 키우면 필터가 제목에서 더 멀어집니다.
- */
+ /* 제목과 필터 사이 간격 */
gap: 18px;
- /*
- 헤더와 카드 목록 사이의 간격입니다.
- */
+ /* 카드 목록과의 간격 */
margin-bottom: 24px;
}
.diary-list-heading {
- /*
- '전체 기록'과 '7개'를 같은 줄에 배치합니다.
- */
+ /* 제목과 기록 개수 한 줄 배치 */
display: flex;
align-items: center;
gap: 10px;
- /*
- 남는 공간을 불필요하게 차지하지 않습니다.
- */
flex: 0 0 auto;
}
.diary-list-title {
- /*
- h1 태그의 브라우저 기본 여백을 제거합니다.
- */
+ /* h1 기본 여백 제거 */
margin: 0;
color: #222222;
@@ -152,10 +113,19 @@
}
+.diary-list-description {
+ /* 제목 아래 설명 문구 */
+ margin: 8px 0 0;
+
+ color: #8b857d;
+
+ font-size: 14px;
+ line-height: 1.5;
+}
+
+
.diary-list-count {
- /*
- 기록 개수를 작은 캡슐 모양으로 표시합니다.
- */
+ /* 기록 개수 캡슐 */
padding: 5px 10px;
border-radius: 999px;
@@ -167,9 +137,6 @@
font-weight: 700;
line-height: 1;
- /*
- '7'과 '개'가 서로 다른 줄로 나뉘지 않게 합니다.
- */
white-space: nowrap;
}
@@ -180,31 +147,21 @@
.diary-filter-form,
.diary-list-controls {
- /*
- 최신순 선택창과 기간 설정 버튼을
- 같은 줄에 배치합니다.
- */
+ /* 필터 버튼 가로 배치 */
display: flex;
align-items: center;
gap: 8px;
- /*
- 기간 설정 팝업의 위치 기준이 됩니다.
- */
+ /* 기간 설정창 위치 기준 */
position: relative;
- /*
- 필터 영역이 남은 가로 공간 전체를 차지하지 않습니다.
- */
flex: 0 0 auto;
}
.diary-sort-select,
.period-toggle-button {
- /*
- 최신순 선택창과 기간 설정 버튼의 공통 스타일입니다.
- */
+ /* 필터 공통 스타일 */
min-height: 42px;
padding: 0 16px;
@@ -214,9 +171,6 @@
background: #ffffff;
color: #333333;
- /*
- 페이지에서 사용하는 글꼴을 그대로 상속합니다.
- */
font: inherit;
cursor: pointer;
@@ -226,10 +180,6 @@
.diary-sort-select:focus,
.period-toggle-button:focus,
.period-filter input:focus {
- /*
- 키보드나 마우스로 요소를 선택했을 때
- 노란색 외곽선을 표시합니다.
- */
/*
outline: 2px solid #e9c767;
outline-offset: 2px;
@@ -238,9 +188,7 @@
.period-toggle-button.active {
- /*
- 기간 설정 창이 열려 있을 때의 버튼입니다.
- */
+ /* 기간 설정 활성 상태 */
border-color: #e1bd55;
background: #fff7d9;
@@ -250,57 +198,22 @@
/* =========================================================
- 기간 설정 팝업
+ 5. 기간 설정 팝업
========================================================= */
.period-filter {
- /*
- 최신순 선택창과 기간 설정 버튼을 감싸는
- .diary-filter-form을 기준으로 배치됩니다.
-
- 기존에는 필터 아래쪽에 표시되었지만,
- 이제는 필터의 오른쪽에 나란히 표시합니다.
- */
+ /* 필터 폼 기준 절대 배치 */
position: absolute;
z-index: 50;
- /*
- top: 0
- → 최신순·기간 설정 버튼과 날짜 선택창의
- 위쪽 높이를 똑같이 맞춥니다.
-
- 값을 키우면 날짜 선택창이 아래로 내려가고,
- 음수로 바꾸면 위로 올라갑니다.
- */
top: 0;
-
- /*
- left: calc(100% + 16px)
- → 필터 폼의 오른쪽 끝에서 16px 떨어진 위치에
- 날짜 선택창을 표시합니다.
-
- 16px을 크게 하면 기간 설정 버튼과 더 멀어지고,
- 작게 하면 버튼과 더 가까워집니다.
- */
left: calc(100% + 16px);
-
- /*
- 기존의 right: 0을 해제합니다.
-
- right: 0이 남아 있으면
- left와 right가 동시에 적용되어
- 너비나 위치가 예상과 다르게 계산될 수 있습니다.
- */
right: auto;
display: flex;
align-items: flex-end;
gap: 10px;
- /*
- 시작일·종료일·버튼이 들어갈 최소 너비입니다.
- 현재 화면에서는 약 500px 정도가 적당합니다.
- */
min-width: 500px;
padding: 18px;
@@ -315,18 +228,13 @@
.period-filter[hidden] {
- /*
- hidden 속성이 있을 때 완전히 숨깁니다.
- */
+ /* hidden 상태 제거 */
display: none;
}
.period-input-group {
- /*
- 시작일 또는 종료일의 label과 input을
- 위아래로 배치합니다.
- */
+ /* 라벨과 날짜 입력창 세로 배치 */
display: flex;
flex-direction: column;
gap: 7px;
@@ -354,9 +262,7 @@
.period-separator {
- /*
- 시작일과 종료일 사이의 물결표입니다.
- */
+ /* 시작일과 종료일 구분 */
padding-bottom: 10px;
color: #999999;
@@ -365,9 +271,7 @@
.period-apply-button,
.period-reset-button {
- /*
- 적용 버튼과 초기화 버튼의 공통 스타일입니다.
- */
+ /* 적용 / 초기화 공통 스타일 */
display: inline-flex;
align-items: center;
justify-content: center;
@@ -405,9 +309,7 @@
========================================================= */
.diary-grid {
- /*
- 기본 화면에서 한 줄에 카드 5개를 배치합니다.
- */
+ /* 한 줄에 카드 5개 배치 */
display: grid;
grid-template-columns: repeat(5, minmax(0, 1fr));
gap: 22px;
@@ -417,9 +319,6 @@
.diary-card {
- /*
- 카드가 grid 칸보다 커지는 것을 방지합니다.
- */
min-width: 0;
overflow: hidden;
@@ -440,9 +339,7 @@
.diary-card:hover {
- /*
- 마우스를 올리면 카드가 살짝 위로 이동합니다.
- */
+ /* hover 시 카드 이동 */
transform: translateY(-5px);
box-shadow:
@@ -452,9 +349,7 @@
.diary-card-link {
- /*
- 카드 전체 영역을 클릭할 수 있게 합니다.
- */
+ /* 카드 전체 클릭 영역 */
display: block;
height: 100%;
@@ -469,16 +364,11 @@
========================================================= */
.diary-card-image-wrapper {
- /*
- 카드 이미지 영역입니다.
- */
position: relative;
width: 100%;
- /*
- 이미지 영역의 가로:세로 비율을 4:3으로 유지합니다.
- */
+ /* 이미지 4:3 비율 유지 */
aspect-ratio: 4 / 3;
overflow: hidden;
@@ -493,10 +383,7 @@
width: 100%;
height: 100%;
- /*
- 사진이 이미지 영역을 빈틈없이 채우도록 합니다.
- 넘치는 부분은 잘립니다.
- */
+ /* 이미지 영역 채우기 */
object-fit: cover;
transition: transform 0.3s ease;
@@ -504,17 +391,13 @@
.diary-card:hover .diary-card-image {
- /*
- 카드에 마우스를 올리면 사진이 살짝 확대됩니다.
- */
+ /* hover 시 이미지 확대 */
transform: scale(1.04);
}
.diary-card-image-placeholder {
- /*
- 사진이 없을 때 보이는 대체 영역입니다.
- */
+ /* 이미지 없음 영역 */
display: flex;
align-items: center;
justify-content: center;
@@ -534,9 +417,7 @@
========================================================= */
.diary-card-content {
- /*
- 날짜, 기록 내용, 장소 줄을 위아래로 배치합니다.
- */
+ /* 날짜 / 내용 / 장소 세로 배치 */
display: flex;
flex-direction: column;
@@ -556,9 +437,7 @@
.diary-card-title {
- /*
- 기록 내용을 최대 두 줄까지만 보여줍니다.
- */
+ /* 기록 내용 최대 두 줄 제한 */
display: -webkit-box;
min-height: 44px;
@@ -584,35 +463,20 @@
========================================================= */
.diary-card-bottom-row {
- /*
- 장소는 왼쪽,
- 감정 아이콘은 오른쪽에 배치합니다.
- */
+ /* 장소 왼쪽 / 감정 오른쪽 */
display: flex;
align-items: center;
justify-content: space-between;
- /*
- 장소 이름과 감정 아이콘 사이의 최소 간격입니다.
- */
gap: 12px;
- /*
- 제목과 장소 줄 사이의 간격입니다.
- */
margin-top: 12px;
}
.diary-card-location {
- /*
- 장소 영역이 남은 공간을 사용합니다.
- */
flex: 1;
- /*
- flex 자식의 글자가 정상적으로 줄어들 수 있게 합니다.
- */
min-width: 0;
margin: 0;
@@ -630,23 +494,14 @@
.diary-card-location--empty {
- /*
- 장소 정보가 없을 때 문구를 더 연하게 표시합니다.
- */
+ /* 장소 없음 스타일 */
color: #aaa397;
}
.diary-card-emotion {
- /*
- 기존의 absolute 배치를 해제하고,
- 장소 오른쪽에서 일반적인 요소로 배치합니다.
- */
position: static;
- /*
- 장소 이름이 길어져도 아이콘이 작아지지 않게 합니다.
- */
flex-shrink: 0;
display: flex;
@@ -670,9 +525,7 @@
========================================================= */
.diary-empty {
- /*
- 빈 화면 안내가 grid 전체 열을 차지하게 합니다.
- */
+ /* grid 전체 사용 */
grid-column: 1 / -1;
padding: 80px 20px;
@@ -736,286 +589,267 @@
========================================================= */
.emotion-page-header {
- /*
- 왼쪽의 제목과 오른쪽의 이번 달 감정 요약을
- 양쪽에 나누어 배치합니다.
- */
+ /* 감정별 제목 영역 */
display: flex;
align-items: flex-start;
justify-content: space-between;
- gap: 30px;
- margin-bottom: 30px;
+ margin-bottom: 12px;
}
-.emotion-month-summary {
+.emotion-page-heading {
+ /* 제목과 설명 세로 배치 */
display: flex;
- align-items: center;
- gap: 10px;
-
- padding: 12px 18px;
-
- border: 1px solid #eadfce;
- border-radius: 14px;
+ flex-direction: column;
+}
- background: #ffffff;
- font-size: 13px;
-}
+/* =========================================================
+ 12. 감정 도넛 그래프 전체 영역
+ ========================================================= */
+.emotion-chart-section {
+ /* 그래프 중앙 배치 */
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
-.emotion-month-summary strong {
- color: #d98d36;
+ width: 100%;
- font-size: 18px;
+ min-height: 500px;
}
-.top-emotion-list {
+/* =========================================================
+ 13. 감정 도넛 그래프 wrapper
+ ========================================================= */
+
+.emotion-chart-wrapper {
/*
- 공동 1위 감정이 여러 개일 경우
- 한 줄 또는 여러 줄로 자연스럽게 표시
+ SVG / 중앙 기록 수 / 툴팁
+ 위치 기준
*/
- display: flex;
- align-items: center;
- justify-content: flex-end;
- flex-wrap: wrap;
- gap: 4px;
+ position: relative;
+
+ width: min(460px, 100%);
+
+ aspect-ratio: 1 / 1;
}
-.top-emotion-name {
- color: #d98d36;
+/* =========================================================
+ 14. SVG 감정 도넛 그래프
+ ========================================================= */
- white-space: nowrap;
-}
+.emotion-chart {
+ display: block;
+ width: 100%;
+ height: 100%;
-.top-emotion-separator {
- color: #b7aa97;
+ overflow: visible;
}
-.emotion-bubble-area {
+/* =========================================================
+ 15. 도넛 그래프 배경 원
+ ========================================================= */
+
+.emotion-chart-background {
/*
- 감정 조약돌들의 absolute 배치 기준입니다.
+ 기록이 없거나
+ 색상 조각 뒤쪽에 보이는 기본 원
*/
- position: relative;
-
- width: 100%;
- min-height: 560px;
+ fill: none;
- border-radius: 24px;
+ stroke: #ebe8e2;
+ stroke-width: 46;
- /*background: #ffffff;*/
+ pointer-events: none;
}
-.emotion-bubble {
- position: absolute;
-
- display: flex;
- flex-direction: column;
- align-items: center;
- justify-content: center;
- gap: 4px;
+/* =========================================================
+ 16. 도넛 그래프 색상 조각
+ ========================================================= */
+.emotion-chart-segment {
/*
- 기존 테두리를 제거
+ 원 내부 채움 제거
+ stroke만 사용해 도넛 생성
*/
- border: none;
+ fill: none;
/*
- 조약돌 모양은 그대로 유지
+ 배경 원과 동일한 두께 적용
*/
- border-radius: 42% 58% 53% 47% / 50% 50% 50% 50%;
-
- background: rgba(255, 250, 231, 0.95);
- color: #333333;
+ stroke-width: 46;
/*
- 테두리 대신 기본 상태에서도 그림자를 표시합니다.
+ 각 조각 끝을 둥글게 처리
+ */
+ stroke-linecap: round;
- 0 → 좌우 이동 없음
- 5px → 아래로 5px 이동
- 14px → 그림자 흐림 정도
- rgba → 검정색, 투명도 12%
+ /*
+ 원 둘레 stroke 영역에서만
+ 마우스 이벤트 발생
*/
- box-shadow: 0 5px 14px rgba(0, 0, 0, 0.12);
+ pointer-events: stroke;
- font: inherit;
cursor: pointer;
transition:
- transform 0.2s ease,
- box-shadow 0.2s ease;
+ stroke-width 0.2s ease,
+ opacity 0.2s ease,
+ filter 0.2s ease;
}
-.emotion-bubble:hover {
- z-index: 5;
-
- transform: scale(1.08);
-
- box-shadow: 0 10px 24px rgba(0, 0, 0, 0.12);
-}
-
-
-.emotion-bubble-name {
- font-size: 20px;
- font-weight: 700;
-}
-
-
-.emotion-bubble-count {
- color: #8e877c;
-
- font-size: 15px;
-}
-
-
-/* 감정별 색상 */
-
-.emotion-bubble--yellow {
- /*border-color: #f9d472;*/
-
- background: #fff7e3;
-}
-
-
-.emotion-bubble--blue {
- /*border-color: #dae6f3;*/
+.emotion-chart-segment:hover {
+ /*
+ hover 조각 강조
+ */
+ stroke-width: 51;
- background: #eff7ff;
+ filter: brightness(0.97);
}
-.emotion-bubble--pink {
- /*border-color: #f8b3b4;*/
-
- background: #fff2f2;
-}
-
+/* =========================================================
+ 17. 감정 그래프 중앙 기록 수
+ ========================================================= */
-.emotion-bubble--purple {
- /*border-color: #9d72ce;*/
+.emotion-chart-center {
+ /*
+ SVG 중앙 위에 텍스트 배치
+ */
+ position: absolute;
- background: #f8f1ff;
-}
+ top: 50%;
+ left: 50%;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ gap: 7px;
-.emotion-bubble--red {
- /*border-color: #ce7772;*/
+ transform: translate(-50%, -50%);
- background: #fff4f1;
+ /*
+ SVG hover 이벤트 방해 제거
+ */
+ pointer-events: none;
}
-.emotion-bubble--orange {
- /*border-color: #cea972;*/
+.emotion-chart-center-label {
+ color: #817b72;
- background: #fff9f1;
+ font-size: 14px;
+ font-weight: 500;
+ line-height: 1;
}
-.emotion-bubble--default {
- /*border-color: #c5c5c5;*/
+.emotion-chart-center-count {
+ color: #24211d;
- background: #dedede;
+ font-size: 36px;
+ font-weight: 700;
+ line-height: 1;
}
-/* 기록 개수에 따른 감정 조약돌 크기 */
-
-.emotion-bubble--large {
- width: 130px;
- height: 130px;
-}
-
+/* =========================================================
+ 18. 감정 그래프 툴팁
+ ========================================================= */
-.emotion-bubble--medium {
- width: 92px;
- height: 92px;
-}
+.emotion-chart-tooltip {
+ /*
+ 그래프 wrapper 기준 배치
+ */
+ position: absolute;
+ z-index: 20;
+ /*
+ JS에서 마우스 위치에 맞춰
+ left / top 값 변경
+ */
+ left: 0;
+ top: 0;
-.emotion-bubble--small {
- width: 64px;
- height: 64px;
-}
+ /*
+ 툴팁이 마우스 정중앙에 위치하도록 조정
+ */
+ transform: translate(-50%, -100%);
+ padding: 9px 13px;
-/* 감정 조약돌의 임시 배치 위치 */
+ border: 1px solid #e2ddd4;
+ border-radius: 10px;
-.emotion-bubble:nth-child(1) {
- top: 30px;
- left: 9%;
-}
+ background: rgba(255, 255, 255, 0.96);
+ color: #37332e;
+ font-size: 13px;
+ font-weight: 600;
+ line-height: 1.3;
-.emotion-bubble:nth-child(2) {
- top: 70px;
- left: 25%;
-}
+ white-space: nowrap;
+ pointer-events: none;
-.emotion-bubble:nth-child(3) {
- top: 160px;
- left: 15%;
+ box-shadow:
+ 0 7px 20px rgba(0, 0, 0, 0.1);
}
-.emotion-bubble:nth-child(4) {
- top: 110px;
- left: 44%;
+.emotion-chart-tooltip[hidden] {
+ /* 툴팁 숨김 */
+ display: none;
}
-.emotion-bubble:nth-child(5) {
- top: 260px;
- left: 34%;
-}
+/* =========================================================
+ 19. 감정 기록 없음
+ ========================================================= */
+.emotion-chart-empty {
+ /* 그래프 아래 빈 기록 안내 */
+ margin-top: 12px;
-.emotion-bubble:nth-child(6) {
- top: 80px;
- left: 68%;
+ text-align: center;
}
-.emotion-bubble:nth-child(7) {
- top: 210px;
- left: 75%;
-}
+.emotion-chart-empty-title {
+ margin: 0 0 7px;
+ color: #403c36;
-.emotion-bubble:nth-child(8) {
- top: 340px;
- left: 60%;
+ font-size: 16px;
+ font-weight: 700;
}
-.emotion-bubble:nth-child(9) {
- top: 390px;
- left: 20%;
-}
+.emotion-chart-empty-description {
+ margin: 0;
+ color: #918a81;
-.emotion-bubble:nth-child(10) {
- top: 370px;
- left: 80%;
+ font-size: 13px;
}
/* =========================================================
- 12. 감정별 기록 팝업
+ 20. 감정별 기록 팝업
========================================================= */
body.modal-open {
- /*
- 팝업이 열렸을 때 뒤쪽 페이지가 스크롤되지 않게 합니다.
- */
+ /* 팝업 뒤 페이지 스크롤 차단 */
overflow: hidden;
}
@@ -1120,13 +954,11 @@ body.modal-open {
/* =========================================================
- 13. 장소별 기록 화면 및 지도 설정
+ 21. 장소별 기록 화면 및 지도 설정
========================================================= */
.location-map-section {
- /*
- 지도 설정 툴바와 지도를 위아래로 배치합니다.
- */
+ /* 툴바와 지도 세로 배치 */
display: flex;
flex-direction: column;
gap: 10px;
@@ -1136,9 +968,7 @@ body.modal-open {
.map-toolbar {
- /*
- 점 세 개 버튼을 오른쪽 끝에 배치합니다.
- */
+ /* 설정 버튼 오른쪽 배치 */
display: flex;
align-items: center;
justify-content: flex-end;
@@ -1148,9 +978,7 @@ body.modal-open {
.map-display-settings {
- /*
- 설정 메뉴가 점 세 개 버튼을 기준으로 표시되게 합니다.
- */
+ /* 설정 메뉴 위치 기준 */
position: relative;
z-index: 30;
}
@@ -1188,8 +1016,6 @@ body.modal-open {
.map-settings-button:hover {
transform: translateY(-1px);
- /*background: #fff8df;*/
-
box-shadow: 0 5px 14px rgba(0, 0, 0, 0.12);
}
@@ -1201,9 +1027,7 @@ body.modal-open {
.map-settings-menu {
- /*
- 점 세 개 버튼 바로 아래에 표시되는 설정 메뉴입니다.
- */
+ /* 점 세 개 버튼 아래 배치 */
position: absolute;
z-index: 40;
@@ -1269,13 +1093,11 @@ body.modal-open {
/* =========================================================
- 14. 서울 지도
+ 22. 서울 지도
========================================================= */
.location-map-wrapper {
- /*
- 서울 지도 SVG의 크기와 가운데 정렬을 담당합니다.
- */
+ /* 지도 크기 및 중앙 정렬 */
position: relative;
width: min(800px, 100%);
@@ -1293,9 +1115,7 @@ body.modal-open {
.seoul-map path {
- /*
- 모든 서울 구역 path의 기본 스타일입니다.
- */
+ /* 서울 구역 기본 스타일 */
fill: #ffffff;
stroke: #777777;
stroke-width: 1;
@@ -1309,18 +1129,13 @@ body.modal-open {
.seoul-map path:focus {
- /*
- SVG 구역을 클릭했을 때
- 기본 브라우저 외곽선을 제거합니다.
- */
+ /* SVG 기본 포커스 제거 */
outline: none;
}
.seoul-map path.has-record {
- /*
- 기록이 있는 구역에 적용됩니다.
- */
+ /* 기록 있는 구역 */
stroke-width: 2;
cursor: pointer;
@@ -1328,20 +1143,13 @@ body.modal-open {
.seoul-map path.has-record:hover {
- /*
- 기록이 있는 구역에 마우스를 올렸을 때
- 살짝 어두워지고 테두리가 굵어집니다.
- */
- /*stroke-width: 3;*/
-
+ /* 기록 있는 구역 hover */
filter: brightness(0.94);
}
.district-labels {
- /*
- 구 이름 텍스트가 지도 클릭을 방해하지 않게 합니다.
- */
+ /* 지도 라벨 클릭 방해 제거 */
pointer-events: none;
}
@@ -1358,16 +1166,12 @@ body.modal-open {
text-anchor: middle;
dominant-baseline: middle;
- /*
- 흰색 외곽선을 먼저 그리고
- 그 위에 글자를 표시합니다.
- */
paint-order: stroke;
}
/* =========================================================
- 15. 장소별 기록 팝업
+ 23. 장소별 기록 팝업
========================================================= */
.location-modal {
@@ -1470,115 +1274,155 @@ body.modal-open {
/* =========================================================
- 16. 반응형 스타일
+ 24. 반응형 스타일
========================================================= */
-/*
- 화면 너비가 1200px 이하일 때
- 카드 개수를 한 줄에 4개로 줄입니다.
-*/
+/* 1200px 이하 */
@media (max-width: 1200px) {
+
+ /* 카드 한 줄 4개 */
.diary-grid {
grid-template-columns: repeat(4, minmax(0, 1fr));
}
}
-/*
- 화면 너비가 900px 이하일 때
- 카드와 팝업 목록을 한 줄에 3개로 줄입니다.
-*/
+/* 900px 이하 */
@media (max-width: 900px) {
+
+ /* 카드 및 팝업 한 줄 3개 */
.diary-grid,
.emotion-modal-grid,
.location-modal-grid {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
+
+
+ /* 감정 그래프 크기 축소 */
+ .emotion-chart-wrapper {
+ width: min(420px, 100%);
+ }
}
-/*
- 화면 너비가 760px 이하일 때
- 전체 기록 제목과 필터를 위아래로 배치합니다.
-*/
+/* 760px 이하 */
@media (max-width: 760px) {
+
.diary-list-container {
- /*
- 작은 화면에서는 좌우 여백을 각각 20px로 줄입니다.
- */
+ /* 좌우 여백 축소 */
width: calc(100% - 40px);
}
+
.diary-list-header {
+ /* 제목과 필터 세로 배치 */
align-items: flex-start;
flex-direction: column;
gap: 14px;
}
+
.diary-filter-form,
.diary-list-controls {
width: 100%;
}
+
.period-filter {
- /*
- 작은 화면에서는 오른쪽에 표시할 공간이 부족하므로
- 다시 필터 버튼 아래쪽에 표시합니다.
- */
+ /* 기간 설정창 아래 배치 */
top: calc(100% + 10px);
- /*
- 필터 폼의 왼쪽에 날짜창을 맞춥니다.
- */
left: 0;
right: auto;
- /*
- 날짜 입력창과 버튼이 공간이 부족할 때
- 다음 줄로 내려가도록 합니다.
- */
flex-wrap: wrap;
- /*
- 모바일 화면보다 날짜창이 커지지 않게 제한합니다.
- */
- min-width: min(500px, calc(100vw - 40px));
+ min-width: min(
+ 500px,
+ calc(100vw - 40px)
+ );
+ }
+
+
+ .emotion-page-header {
+ /* 감정 제목 세로 배치 */
+ flex-direction: column;
+ }
+
+
+ .emotion-chart-section {
+ /* 모바일 그래프 높이 축소 */
+ min-height: 450px;
+ }
+
+
+ .emotion-chart-wrapper {
+ width: min(400px, 100%);
}
}
-/*
- 화면 너비가 650px 이하일 때
- 카드 및 팝업 목록을 한 줄에 2개로 줄입니다.
-*/
+/* 650px 이하 */
@media (max-width: 650px) {
+
.diary-grid,
.emotion-modal-grid,
.location-modal-grid {
+ /* 카드 및 팝업 한 줄 2개 */
grid-template-columns: repeat(2, minmax(0, 1fr));
+
gap: 14px;
}
+
.diary-card-content {
min-height: 118px;
padding: 12px;
}
+
.diary-card-title {
font-size: 14px;
}
- .emotion-page-header {
- flex-direction: column;
+
+ .emotion-chart-section {
+ min-height: 390px;
+ }
+
+
+ .emotion-chart-wrapper {
+ width: min(350px, 100%);
+ }
+
+
+ .emotion-chart-background,
+ .emotion-chart-segment {
+ /* 작은 화면에서 도넛 두께 축소 */
+ stroke-width: 40;
+ }
+
+
+ .emotion-chart-segment:hover {
+ stroke-width: 44;
+ }
+
+
+ .emotion-chart-center-count {
+ /* 가운데 기록 수 크기 축소 */
+ font-size: 30px;
}
- .emotion-bubble-area {
- min-height: 720px;
+
+ .emotion-chart-center-label {
+ font-size: 13px;
}
+
.emotion-modal-content,
.location-modal-content {
width: calc(100% - 28px);
+
padding: 20px;
}
}
\ No newline at end of file
From 8869098a211083d509a549577b0a05f24ee11728 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 12:36:53 +0900
Subject: [PATCH 07/13] =?UTF-8?q?style:=20=EA=B0=90=EC=A0=95=20=EA=B7=B8?=
=?UTF-8?q?=EB=9E=98=ED=94=84=20=EB=94=94=EC=9E=90=EC=9D=B8=20=EA=B0=9C?=
=?UTF-8?q?=EC=84=A0=20=EB=B0=8F=20=EA=B0=90=EC=A0=95=20=EC=B9=B4=EB=93=9C?=
=?UTF-8?q?=20=EC=B6=94=EA=B0=80?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 373 +++++++++++--
diary/static/diary/js/diary.js | 506 ++++++++----------
.../diary/partials/emotion_list.html | 275 +++++-----
3 files changed, 683 insertions(+), 471 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 731e74d..49bec31 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -610,15 +610,13 @@
========================================================= */
.emotion-chart-section {
- /* 그래프 중앙 배치 */
display: flex;
- flex-direction: column;
align-items: center;
justify-content: center;
width: 100%;
- min-height: 500px;
+ min-height: 360px;
}
@@ -627,18 +625,17 @@
========================================================= */
.emotion-chart-wrapper {
- /*
- SVG / 중앙 기록 수 / 툴팁
- 위치 기준
- */
position: relative;
- width: min(460px, 100%);
+ /* 참고 이미지에 가까운 크기 */
+ width: 360px;
+ max-width: 100%;
aspect-ratio: 1 / 1;
}
+
/* =========================================================
14. SVG 감정 도넛 그래프
========================================================= */
@@ -654,66 +651,126 @@
/* =========================================================
- 15. 도넛 그래프 배경 원
+ 15. 도넛 그래프 배경
========================================================= */
.emotion-chart-background {
- /*
- 기록이 없거나
- 색상 조각 뒤쪽에 보이는 기본 원
- */
+ /* 도넛 뒤 기본 원 */
fill: none;
- stroke: #ebe8e2;
- stroke-width: 46;
+ stroke: #f2eee7;
+ stroke-width: 52;
pointer-events: none;
}
+/* 그래프 안쪽 원 */
+
+.emotion-chart-inner-circle {
+ fill: #fffdfa;
+
+ stroke: #eee5da;
+ stroke-width: 2;
+
+ pointer-events: none;
+
+ filter:
+ drop-shadow(
+ 0 2px 4px rgba(80, 65, 50, 0.08)
+ );
+}
+
+
/* =========================================================
- 16. 도넛 그래프 색상 조각
+ 16. 도넛 그래프 조각
========================================================= */
.emotion-chart-segment {
- /*
- 원 내부 채움 제거
- stroke만 사용해 도넛 생성
- */
fill: none;
- /*
- 배경 원과 동일한 두께 적용
- */
- stroke-width: 46;
+ /* 참고 이미지와 비슷한 두꺼운 띠 */
+ stroke-width: 52;
/*
- 각 조각 끝을 둥글게 처리
+ 끝을 완전한 반원으로 만들지 않음
+
+ butt
+ → 참고 이미지처럼 조각 경계가 깔끔함
*/
- stroke-linecap: round;
+ stroke-linecap: butt;
/*
- 원 둘레 stroke 영역에서만
- 마우스 이벤트 발생
+ 그래프 시작 위치를
+ 12시 방향으로 이동
*/
+ transform: rotate(-90deg);
+
+ transform-origin: 160px 160px;
+
pointer-events: stroke;
cursor: pointer;
+ filter:
+ drop-shadow(
+ 0 2px 3px rgba(74, 58, 45, 0.06)
+ );
+
transition:
- stroke-width 0.2s ease,
- opacity 0.2s ease,
- filter 0.2s ease;
+ stroke-width 0.18s ease,
+ filter 0.18s ease,
+ opacity 0.18s ease;
}
+/* 감정 색상 */
+
+.emotion-chart-segment--yellow {
+ stroke: #f9dda0;
+}
+
+
+.emotion-chart-segment--blue {
+ stroke: #bcdcf7;
+}
+
+
+.emotion-chart-segment--pink {
+ stroke: #f6c9cf;
+}
+
+
+.emotion-chart-segment--purple {
+ stroke: #cfc2f2;
+}
+
+
+.emotion-chart-segment--red {
+ stroke: #f2b6b2;
+}
+
+
+.emotion-chart-segment--orange {
+ stroke: #f7c995;
+}
+
+
+.emotion-chart-segment--default {
+ stroke: #dedede;
+}
+
+
+/* hover 강조 */
+
.emotion-chart-segment:hover {
- /*
- hover 조각 강조
- */
- stroke-width: 51;
+ stroke-width: 57;
- filter: brightness(0.97);
+ filter:
+ brightness(1.02)
+ drop-shadow(
+ 0 4px 5px rgba(65, 50, 40, 0.11)
+ );
}
@@ -722,9 +779,6 @@
========================================================= */
.emotion-chart-center {
- /*
- SVG 중앙 위에 텍스트 배치
- */
position: absolute;
top: 50%;
@@ -734,31 +788,29 @@
flex-direction: column;
align-items: center;
justify-content: center;
- gap: 7px;
+
+ gap: 9px;
transform: translate(-50%, -50%);
- /*
- SVG hover 이벤트 방해 제거
- */
pointer-events: none;
}
.emotion-chart-center-label {
- color: #817b72;
+ color: #776f69;
- font-size: 14px;
- font-weight: 500;
- line-height: 1;
+ font-size: 15px;
+ font-weight: 600;
}
.emotion-chart-center-count {
- color: #24211d;
+ color: #8b71d1;
+
+ font-size: 34px;
+ font-weight: 800;
- font-size: 36px;
- font-weight: 700;
line-height: 1;
}
@@ -843,6 +895,227 @@
font-size: 13px;
}
+.emotion-summary-board {
+ width: 100%;
+
+ padding:
+ 30px
+ 28px
+ 26px;
+
+ border: 1px solid #eee7dd;
+ border-radius: 28px;
+
+ background:
+ rgba(255, 253, 248, 0.82);
+
+ box-shadow:
+ 0 8px 24px rgba(69, 52, 38, 0.06);
+}
+
+.emotion-card-guide {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ gap: 8px;
+
+ margin:
+ -2px
+ 0
+ 20px;
+
+ color: #9a9188;
+
+ font-size: 12px;
+}
+
+
+.emotion-card-guide-line {
+ width: 45px;
+
+ border-top:
+ 2px dotted #ddd1ee;
+}
+
+
+.emotion-card-guide-heart {
+ color: #bda8e1;
+
+ font-size: 10px;
+}
+
+/* =========================================================
+ 감정별 기록 카드 목록
+ ========================================================= */
+
+.emotion-record-card-list {
+ display: flex;
+ align-items: stretch;
+ justify-content: center;
+ flex-wrap: wrap;
+
+ gap: 14px;
+
+ width: 100%;
+}
+
+
+/* 감정 카드 */
+
+.emotion-record-card {
+ display: flex;
+ align-items: center;
+
+ gap: 10px;
+
+ min-width: 150px;
+
+ padding:
+ 10px
+ 14px;
+
+ border: 1px solid #eee7dd;
+ border-radius: 17px;
+
+ background: #ffffff;
+
+ font: inherit;
+
+ cursor: pointer;
+
+ box-shadow:
+ 0 4px 10px rgba(70, 55, 40, 0.06);
+
+ transition:
+ transform 0.18s ease,
+ box-shadow 0.18s ease;
+}
+
+
+.emotion-record-card:hover {
+ transform: translateY(-3px);
+
+ box-shadow:
+ 0 8px 16px rgba(70, 55, 40, 0.1);
+}
+
+
+/* 감정 이미지 */
+
+.emotion-record-card-image {
+ display: flex;
+ align-items: center;
+ justify-content: center;
+
+ width: 48px;
+ height: 48px;
+
+ flex-shrink: 0;
+}
+
+
+.emotion-record-card-image img {
+ display: block;
+
+ width: 44px;
+ height: 44px;
+
+ object-fit: contain;
+}
+
+
+/* 감정 정보 */
+
+.emotion-record-card-info {
+ display: flex;
+ flex-direction: column;
+ align-items: flex-start;
+
+ gap: 4px;
+}
+
+
+.emotion-record-card-name {
+ color: #403b36;
+
+ font-size: 14px;
+ font-weight: 700;
+}
+
+
+.emotion-record-card-count {
+ font-size: 13px;
+ font-weight: 700;
+}
+
+
+/* 카드별 포인트 색상 */
+
+.emotion-record-card--blue {
+ background: #f5faff;
+}
+
+
+.emotion-record-card--blue
+.emotion-record-card-count {
+ color: #67a7df;
+}
+
+
+.emotion-record-card--pink {
+ background: #fff7f8;
+}
+
+
+.emotion-record-card--pink
+.emotion-record-card-count {
+ color: #e58ea0;
+}
+
+
+.emotion-record-card--yellow {
+ background: #fffdf5;
+}
+
+
+.emotion-record-card--yellow
+.emotion-record-card-count {
+ color: #d6aa36;
+}
+
+
+.emotion-record-card--purple {
+ background: #faf7ff;
+}
+
+
+.emotion-record-card--purple
+.emotion-record-card-count {
+ color: #9276cf;
+}
+
+
+.emotion-record-card--red {
+ background: #fff7f5;
+}
+
+
+.emotion-record-card--red
+.emotion-record-card-count {
+ color: #db827b;
+}
+
+
+.emotion-record-card--orange {
+ background: #fff9f3;
+}
+
+
+.emotion-record-card--orange
+.emotion-record-card-count {
+ color: #d99c59;
+}
/* =========================================================
20. 감정별 기록 팝업
diff --git a/diary/static/diary/js/diary.js b/diary/static/diary/js/diary.js
index 884db7d..442fd57 100644
--- a/diary/static/diary/js/diary.js
+++ b/diary/static/diary/js/diary.js
@@ -692,9 +692,133 @@ document.addEventListener("DOMContentLoaded", () => {
);
}
+ /* =========================================================
+ 20. 감정별 기록 팝업 요소
+ ========================================================= */
+
+ const modal =
+ document.querySelector("#emotion-modal");
+
+ const modalName =
+ document.querySelector("#emotion-modal-name");
+
+ const modalRecords =
+ document.querySelectorAll(".emotion-modal-record");
+
+ const modalEmpty =
+ document.querySelector("#emotion-modal-empty");
+
+ const closeButtons =
+ document.querySelectorAll("[data-modal-close]");
+
+
+ /* =========================================================
+ 21. 감정별 기록 팝업 열기
+ ========================================================= */
+
+ function openEmotionModal(
+ emotionIds,
+ emotionNames
+ ) {
+
+ // 필수 요소 없는 경우 종료
+ if (
+ !modal
+ || !modalName
+ || !modalEmpty
+ ) {
+ return;
+ }
+
+
+ // 단일 ID도 배열로 변환
+ const normalizedIds =
+ Array.isArray(emotionIds)
+ ? emotionIds.map(String)
+ : [String(emotionIds)];
+
+
+ // 감정명도 배열로 변환
+ const normalizedNames =
+ Array.isArray(emotionNames)
+ ? emotionNames
+ : [emotionNames];
+
+
+ let visibleRecordCount = 0;
+
+
+ // 팝업 제목 설정
+ modalName.textContent =
+ normalizedNames.join(" · ");
+
+
+ // 선택 감정 그룹 기록 표시
+ modalRecords.forEach((record) => {
+
+ const recordEmotion =
+ String(
+ record.dataset.recordEmotion
+ );
+
+
+ const isMatched =
+ normalizedIds.includes(
+ recordEmotion
+ );
+
+
+ record.hidden =
+ !isMatched;
+
+
+ if (isMatched) {
+ visibleRecordCount += 1;
+ }
+ });
+
+
+ // 기록 없음 문구 처리
+ modalEmpty.hidden =
+ visibleRecordCount !== 0;
+
+
+ // 팝업 표시
+ modal.hidden = false;
+
+
+ // 배경 스크롤 차단
+ document.body.classList.add(
+ "modal-open"
+ );
+ }
+
+
+ /* =========================================================
+ 22. 감정별 기록 팝업 닫기
+ ========================================================= */
+
+ function closeEmotionModal() {
+
+ // 팝업 없는 경우 종료
+ if (!modal) {
+ return;
+ }
+
+
+ // 팝업 숨김
+ modal.hidden = true;
+
+
+ // 배경 스크롤 복구
+ document.body.classList.remove(
+ "modal-open"
+ );
+ }
+
/* =========================================================
- 20. 감정 도넛 그래프 요소
+ 23. 감정 도넛 그래프 요소
========================================================= */
const emotionChart =
@@ -724,50 +848,49 @@ document.addEventListener("DOMContentLoaded", () => {
/* =========================================================
- 21. 감정 데이터를 색상 그룹별로 합치기
+ 24. 색상 그룹별 감정 데이터 생성
========================================================= */
function getEmotionChartGroups() {
- // 색상 그룹별 임시 저장 객체
const groupedData = {};
emotionChartDataItems.forEach(
(element) => {
- // 감정 이름
+ const emotionId =
+ element.dataset.emotionId.trim();
+
const emotionName =
element.dataset.emotionName.trim();
-
- // 대표 감정 기록 횟수
const emotionCount =
Number(
element.dataset.emotionCount
);
-
- // 색상 그룹 이름
const colorGroup =
element.dataset.emotionColorGroup.trim();
// 잘못된 데이터 제외
if (
- !emotionName
+ !emotionId
+ || !emotionName
|| emotionCount <= 0
) {
return;
}
- // 최초 색상 그룹 생성
+ // 색상 그룹 최초 생성
if (!groupedData[colorGroup]) {
groupedData[colorGroup] = {
- colorGroup: colorGroup,
+ colorGroup,
count: 0,
+ emotionIds: [],
emotionNames: [],
};
}
@@ -778,7 +901,23 @@ document.addEventListener("DOMContentLoaded", () => {
emotionCount;
- // 감정 이름 중복 제거 후 추가
+ // 감정 ID 추가
+ if (
+ !groupedData[
+ colorGroup
+ ].emotionIds.includes(
+ emotionId
+ )
+ ) {
+ groupedData[
+ colorGroup
+ ].emotionIds.push(
+ emotionId
+ );
+ }
+
+
+ // 감정 이름 추가
if (
!groupedData[
colorGroup
@@ -796,7 +935,6 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // 객체를 배열로 변환
return Object.values(
groupedData
);
@@ -804,7 +942,7 @@ document.addEventListener("DOMContentLoaded", () => {
/* =========================================================
- 22. 감정 그래프 툴팁 표시
+ 25. 도넛 그래프 툴팁 표시
========================================================= */
function showEmotionChartTooltip(
@@ -821,7 +959,7 @@ document.addEventListener("DOMContentLoaded", () => {
}
- // 감정 이름만 표시
+ // 감정 이름 표시
emotionChartTooltip.textContent =
emotionNames.join(" · ");
@@ -830,12 +968,10 @@ document.addEventListener("DOMContentLoaded", () => {
emotionChartTooltip.hidden = false;
- // wrapper 위치 계산
const wrapperRect =
emotionChartWrapper.getBoundingClientRect();
- // 마우스 기준 툴팁 위치
const tooltipX =
event.clientX
- wrapperRect.left;
@@ -845,39 +981,37 @@ document.addEventListener("DOMContentLoaded", () => {
- wrapperRect.top;
- // 마우스보다 약간 위쪽에 배치
+ // 마우스 위쪽 배치
emotionChartTooltip.style.left =
`${tooltipX}px`;
emotionChartTooltip.style.top =
- `${tooltipY - 16}px`;
+ `${tooltipY - 18}px`;
}
/* =========================================================
- 23. 감정 그래프 툴팁 숨기기
+ 26. 도넛 그래프 툴팁 제거
========================================================= */
function hideEmotionChartTooltip() {
- // 툴팁 없는 경우 종료
if (!emotionChartTooltip) {
return;
}
- // 툴팁 숨김
emotionChartTooltip.hidden = true;
}
/* =========================================================
- 24. 감정 SVG 도넛 그래프 생성
+ 27. SVG 도넛 그래프 생성
========================================================= */
function renderEmotionChart() {
- // 그래프 요소 없는 경우 종료
+ // 그래프 없는 경우 종료
if (
!emotionChart
|| !emotionChartSegments
@@ -886,22 +1020,21 @@ document.addEventListener("DOMContentLoaded", () => {
}
- // 기존 그래프 조각 제거
+ // 기존 조각 제거
emotionChartSegments.innerHTML = "";
- // 색상 그룹별 데이터 생성
const chartGroups =
getEmotionChartGroups();
- // 기록 없는 경우 종료
+ // 데이터 없는 경우 종료
if (chartGroups.length === 0) {
return;
}
- // 전체 대표 감정 기록 수 계산
+ // 전체 기록 수 계산
const totalCount =
chartGroups.reduce(
(sum, group) =>
@@ -910,26 +1043,18 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // 전체 기록 없는 경우 종료
if (totalCount <= 0) {
return;
}
- /*
- SVG 원 정보
-
- emotion_list.html:
- cx = 160
- cy = 160
- r = 110
- */
+ // SVG 원 정보
const centerX = 160;
const centerY = 160;
- const radius = 110;
+ const radius = 112;
- // 원 둘레 계산
+ // 전체 원 둘레
const circumference =
2 * Math.PI * radius;
@@ -938,44 +1063,33 @@ document.addEventListener("DOMContentLoaded", () => {
조각 사이 간격
값 증가
- → 조각 사이 간격 증가
-
- 값 감소
- → 조각 사이 간격 감소
+ → 조각 사이 흰색 간격 증가
*/
- const segmentGap = 7;
+ const segmentGap = 5;
/*
- 시작 위치
+ 12시 방향 시작
- SVG 원은 기본적으로 오른쪽에서 시작함.
-
- -circumference / 4 적용
- → 그래프 시작점을 12시 방향으로 이동
+ 기본 circle 시작점은 오른쪽이므로
+ rotate(-90deg) 적용
*/
- let currentOffset =
- -circumference / 4;
+ let currentLength = 0;
chartGroups.forEach((group) => {
- // 그룹 비율 계산
const ratio =
- group.count / totalCount;
+ group.count
+ / totalCount;
- // 그룹이 차지하는 원 둘레 길이
+ // 실제 그룹 길이
const segmentLength =
circumference * ratio;
- /*
- 실제 표시 길이에서 간격 제거
-
- 너무 작은 조각이 사라지는 문제 방지용
- 최소 길이 1 적용
- */
+ // 흰색 간격 제외
const visibleLength =
Math.max(
segmentLength
@@ -984,7 +1098,6 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // SVG circle 생성
const segment =
document.createElementNS(
"http://www.w3.org/2000/svg",
@@ -992,7 +1105,6 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // 기본 원 위치 설정
segment.setAttribute(
"cx",
centerX
@@ -1009,60 +1121,47 @@ document.addEventListener("DOMContentLoaded", () => {
);
- // CSS 클래스 추가
segment.classList.add(
"emotion-chart-segment"
);
- // 색상 그룹 클래스 추가
segment.classList.add(
`emotion-chart-segment--${group.colorGroup}`
);
- // 실제 stroke 색상 적용
- segment.style.stroke =
- emotionChartColors[
- group.colorGroup
- ]
- || emotionChartColors.default;
-
-
/*
- 현재 조각 길이 설정
+ 현재 조각만 표시
- 첫 번째 값
- → 실제 표시되는 선 길이
+ visibleLength
+ → 보이는 부분
- 두 번째 값
- → 나머지 비어 있는 선 길이
+ 나머지
+ → 숨겨지는 부분
*/
segment.style.strokeDasharray =
`${visibleLength} ${
- circumference
- - visibleLength
+ circumference - visibleLength
}`;
- // 현재 조각 시작 위치 설정
+ /*
+ 조각 시작 위치 이동
+ */
segment.style.strokeDashoffset =
- `${-currentOffset}`;
+ `${-currentLength}`;
- /*
- hover에서 사용할 감정 이름 저장
+ // hover 데이터 저장
+ segment.dataset.emotionIds =
+ group.emotionIds.join(",");
- 예:
- "기쁨|만족|신남"
- */
segment.dataset.emotionNames =
group.emotionNames.join("|");
- /*
- 마우스 진입 시 툴팁 표시
- */
+ // hover 툴팁 표시
segment.addEventListener(
"mouseenter",
(event) => {
@@ -1075,10 +1174,7 @@ document.addEventListener("DOMContentLoaded", () => {
);
- /*
- 조각 위에서 마우스 이동 시
- 툴팁 위치도 함께 이동
- */
+ // 툴팁 위치 이동
segment.addEventListener(
"mousemove",
(event) => {
@@ -1091,161 +1187,81 @@ document.addEventListener("DOMContentLoaded", () => {
);
- /*
- 마우스 이탈 시 툴팁 제거
- */
+ // 툴팁 제거
segment.addEventListener(
"mouseleave",
hideEmotionChartTooltip
);
- // SVG에 조각 추가
+ /*
+ 색상 조각 클릭
+
+ 해당 그룹에 포함된 모든 대표 감정 기록 표시
+ */
+ segment.addEventListener(
+ "click",
+ () => {
+
+ hideEmotionChartTooltip();
+
+
+ openEmotionModal(
+ group.emotionIds,
+ group.emotionNames
+ );
+ }
+ );
+
+
emotionChartSegments.appendChild(
segment
);
// 다음 조각 시작 위치 계산
- currentOffset +=
+ currentLength +=
segmentLength;
});
}
/* =========================================================
- 25. 감정 도넛 그래프 초기 생성
+ 28. 도넛 그래프 생성
========================================================= */
renderEmotionChart();
/* =========================================================
- 26. 감정별 기록 팝업 요소
+ 29. 감정 카드 클릭
========================================================= */
- /*
- 기존 emotion_modal.html 기능 유지.
-
- 현재 도넛 그래프에서는
- 감정 클릭 기능을 연결하지 않음.
-
- 추후 그래프 클릭 또는 별도 감정 버튼 추가 시
- openEmotionModal() 재사용 가능.
- */
-
- const modal =
- document.querySelector(
- "#emotion-modal"
- );
-
- const modalName =
- document.querySelector(
- "#emotion-modal-name"
- );
-
- const modalRecords =
+ const emotionRecordCards =
document.querySelectorAll(
- ".emotion-modal-record"
- );
-
- const modalEmpty =
- document.querySelector(
- "#emotion-modal-empty"
- );
-
- const closeButtons =
- document.querySelectorAll(
- "[data-modal-close]"
- );
-
-
- /* =========================================================
- 27. 감정별 기록 팝업 열기
- ========================================================= */
-
- function openEmotionModal(
- emotionId,
- emotionName
- ) {
-
- // 팝업 요소 없는 경우 종료
- if (
- !modal
- || !modalName
- || !modalEmpty
- ) {
- return;
- }
-
-
- let visibleRecordCount = 0;
-
-
- // 팝업 감정 이름 설정
- modalName.textContent =
- emotionName;
-
-
- // 선택 감정 기록만 표시
- modalRecords.forEach((record) => {
-
- const isMatched =
- record.dataset.recordEmotion
- === emotionId;
-
-
- record.hidden =
- !isMatched;
-
-
- if (isMatched) {
- visibleRecordCount += 1;
- }
- });
-
-
- // 기록 없음 문구 처리
- modalEmpty.hidden =
- visibleRecordCount !== 0;
-
-
- // 팝업 표시
- modal.hidden = false;
-
-
- // 배경 스크롤 차단
- document.body.classList.add(
- "modal-open"
+ "[data-emotion-card]"
);
- }
- /* =========================================================
- 28. 감정별 기록 팝업 닫기
- ========================================================= */
+ emotionRecordCards.forEach(
+ (card) => {
- function closeEmotionModal() {
+ card.addEventListener(
+ "click",
+ () => {
- // 팝업 없는 경우 종료
- if (!modal) {
- return;
+ openEmotionModal(
+ card.dataset.emotionId,
+ card.dataset.emotionName
+ );
+ }
+ );
}
-
-
- // 팝업 숨김
- modal.hidden = true;
-
-
- // 배경 스크롤 복구
- document.body.classList.remove(
- "modal-open"
- );
- }
+ );
/* =========================================================
- 29. 감정 팝업 닫기
+ 30. 감정 팝업 닫기
========================================================= */
closeButtons.forEach((button) => {
@@ -1256,68 +1272,4 @@ document.addEventListener("DOMContentLoaded", () => {
);
});
-
- /* =========================================================
- 30. Escape 키 처리
- ========================================================= */
-
- document.addEventListener(
- "keydown",
- (event) => {
-
- // Escape 아닌 경우 종료
- if (event.key !== "Escape") {
- return;
- }
-
-
- // 감정 팝업 닫기
- if (
- modal
- && !modal.hidden
- ) {
- closeEmotionModal();
- }
-
-
- // 장소 팝업 닫기
- if (
- locationModal
- && !locationModal.hidden
- ) {
- closeLocationModal();
- }
-
-
- // 지도 설정 메뉴 닫기
- if (
- mapSettingsMenu
- && !mapSettingsMenu.hidden
- ) {
- mapSettingsMenu.hidden = true;
-
-
- if (mapSettingsButton) {
-
- mapSettingsButton.setAttribute(
- "aria-expanded",
- "false"
- );
- }
- }
-
-
- // 기간 설정창 닫기
- if (
- periodFilter
- && !periodFilter.hidden
- ) {
- closePeriodFilter();
- }
-
-
- // 감정 툴팁 제거
- hideEmotionChartTooltip();
- }
- );
-});
\ No newline at end of file
+});
\ No newline at end of file
diff --git a/diary/templates/diary/partials/emotion_list.html b/diary/templates/diary/partials/emotion_list.html
index 3c956d3..f3d025e 100644
--- a/diary/templates/diary/partials/emotion_list.html
+++ b/diary/templates/diary/partials/emotion_list.html
@@ -1,27 +1,23 @@
+{% load static %}
+
{% comment %}
감정별 기록 탭
-구성:
-1. 감정별 기록 제목
-2. SVG 도넛 그래프
-3. 가운데 전체 기록 수
-4. 그래프 영역 hover 시 감정 이름 툴팁
-5. JavaScript가 읽을 대표 감정 데이터
+구성
+- 대표 감정 도넛 그래프
+- 전체 기록 수
+- 색상 그룹 hover 툴팁
+- 기록된 대표 감정 카드
{% endcomment %}
-
-
+
-
-
감정별 기록
@@ -29,127 +25,150 @@
기록한 대표 감정을 한눈에 확인해보세요.
-
-
-
-
+
+
-
-
+
+
-
-
-
-
-
-
-
-
- 전체 기록
-
-
-
- {{ total_record_count }}개
-
-
+
+
+
+
+
+
+ 전체 기록
+
+
+
+ {{ total_record_count }}개
+
+
+
+
+
+
+
-
-
+ {% if recorded_emotions %}
+
+
- 예:
- 행복 · 설렘
+
+ ♥
+
- 횟수는 표시 X
+
+ 다른 감정들도 살펴볼까요?
+
- 위치와 내용은 diary.js에서 변경함
- -->
-
+
+ ♥
+
-
+
+
+ {% endif %}
+ {% if recorded_emotions %}
+
+
+ {% for emotion in recorded_emotions %}
+
+
+
+ {% endfor %}
-
+
@@ -157,49 +176,19 @@
{% endfor %}
-
+
{% if not recorded_emotions %}
-
-
아직 기록된 감정이 없어요.
@@ -207,9 +196,7 @@
오늘의 대표 감정을 기록해보세요.
-
-
{% endif %}
From f5f9656df41ec07a417bfe4386ec50f5d2ee2a49 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 19:40:14 +0900
Subject: [PATCH 08/13] =?UTF-8?q?style:=20=EB=94=94=ED=85=8C=EC=9D=BC=20?=
=?UTF-8?q?=ED=8E=98=EC=9D=B4=EC=A7=80=20=EC=9C=84=EC=95=84=EB=9E=98=20?=
=?UTF-8?q?=EC=97=AC=EB=B0=B1=20=EC=A1=B0=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/detail.css | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/diary/static/diary/css/detail.css b/diary/static/diary/css/detail.css
index b1ca107..25fad0a 100644
--- a/diary/static/diary/css/detail.css
+++ b/diary/static/diary/css/detail.css
@@ -37,7 +37,7 @@
첫 번째 숫자를 키우면:
→ 콘텐츠가 아래로 내려갑니다.
*/
- padding: 100px 0 100px;
+ padding: 20px 0 20px;
/*
width 안에 padding과 border까지 포함하도록 합니다.
From c4403ad677ca8857a6370618ae5726734b41dd38 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 19:54:19 +0900
Subject: [PATCH 09/13] =?UTF-8?q?style:=20=EC=9E=A5=EC=86=8C=EB=B3=84=20?=
=?UTF-8?q?=EC=84=A4=EC=A0=95=20=EB=B2=84=ED=8A=BC=20=ED=83=AD=20=EC=9A=B0?=
=?UTF-8?q?=EC=B8=A1=20=EB=B0=B0=EC=B9=98?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 65 +++++++++++--
diary/templates/diary/list.html | 154 ++++++++++++++++---------------
2 files changed, 135 insertions(+), 84 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 49bec31..98a1ae1 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -18,9 +18,16 @@
2. 전체 / 감정별 / 장소별 탭
========================================================= */
-.diary-tabs-wrapper {
- /* 탭 중앙 배치 */
+.diary-tabs-row {
+ /*
+ 탭과 점 세 개 버튼을 같은 줄에 배치
+ 탭은 화면 중앙 유지
+ 점 세 개 버튼의 위치 기준 역할
+ */
+ position: relative;
+
display: flex;
+ align-items: center;
justify-content: center;
width: 100%;
@@ -30,6 +37,19 @@
}
+.diary-tabs-wrapper {
+ /*
+ 탭 자체만 중앙 배치
+ width: 100% 제거하여 오른쪽 버튼이 탭 위치에 영향 주지 않도록 함
+ */
+ display: flex;
+ justify-content: center;
+
+ width: auto;
+ margin-bottom: 0;
+}
+
+
.diary-tabs {
/* 탭 한 줄 배치 */
display: inline-flex;
@@ -1231,22 +1251,20 @@ body.modal-open {
========================================================= */
.location-map-section {
- /* 툴바와 지도 세로 배치 */
+ /* 지도만 세로 배치 */
display: flex;
flex-direction: column;
- gap: 10px;
width: 100%;
}
+/*
+ 기존 지도 위 별도 툴바는 사용하지 않음
+ list.html에서 .map-toolbar 영역을 제거한 상태 기준
+*/
.map-toolbar {
- /* 설정 버튼 오른쪽 배치 */
- display: flex;
- align-items: center;
- justify-content: flex-end;
-
- width: 100%;
+ display: none;
}
@@ -1257,6 +1275,20 @@ body.modal-open {
}
+/*
+ 탭 행 안에 있는 지도 설정 버튼 영역
+ 탭은 가운데 유지하고 설정 버튼만 오른쪽 끝에 고정
+*/
+.diary-tabs-row > .map-display-settings {
+ position: absolute;
+
+ top: 50%;
+ right: 0;
+
+ transform: translateY(-50%);
+}
+
+
.map-settings-button {
display: flex;
align-items: center;
@@ -1587,6 +1619,19 @@ body.modal-open {
}
+
+ .diary-tabs-row > .map-display-settings {
+ right: 0;
+ }
+
+
+ .diary-tabs-row .map-settings-button {
+ width: 38px;
+ height: 38px;
+
+ font-size: 22px;
+ }
+
.diary-list-header {
/* 제목과 필터 세로 배치 */
align-items: flex-start;
diff --git a/diary/templates/diary/list.html b/diary/templates/diary/list.html
index 5cc23e0..44b11e5 100644
--- a/diary/templates/diary/list.html
+++ b/diary/templates/diary/list.html
@@ -35,8 +35,86 @@
-
- {% include "diary/partials/diary_tab.html" %}
+
+
+
+
+
+ {% include "diary/partials/diary_tab.html" %}
+
+
+
+
+ {% if selected_tab == "location" %}
+
+
+
+
+
+
+
+
+
+ 지도 색상 방식
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {% endif %}
+
@@ -320,78 +398,6 @@
-
-
-
-
-
-
-
-
-
-
-
-
- 지도 색상 방식
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 743d334a0407c70d67fe65e262a17c812fdf78eb Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 20:04:57 +0900
Subject: [PATCH 10/13] =?UTF-8?q?style:=20=EC=9E=A5=EC=86=8C=EB=B3=84=20?=
=?UTF-8?q?=EC=A7=80=EB=8F=84=20=EC=9C=84=EC=B9=98=20=EC=83=81=ED=96=A5=20?=
=?UTF-8?q?=EC=A1=B0=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 98a1ae1..82f403e 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -1407,7 +1407,9 @@ body.modal-open {
width: min(800px, 100%);
- margin: 0 auto;
+ margin: -50px auto -100px;
+
+ overflow: hidden;
}
From 4079776ae40101fd770a7e0edda37ba0570fc506 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 20:13:30 +0900
Subject: [PATCH 11/13] =?UTF-8?q?fix:=20=EC=A7=80=EB=8F=84=20=EC=9C=84?=
=?UTF-8?q?=EC=B9=98=20=EC=A1=B0=EC=A0=95=EC=9C=BC=EB=A1=9C=20=EC=9D=B8?=
=?UTF-8?q?=ED=95=B4=20=ED=83=AD=EB=B0=94=20=EC=95=88=EB=88=8C=EB=A6=AC?=
=?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 2 +-
diary/templates/diary/partials/seoul_map.html | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 82f403e..40a2640 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -1407,7 +1407,7 @@ body.modal-open {
width: min(800px, 100%);
- margin: -50px auto -100px;
+ margin: 0 auto;
overflow: hidden;
}
diff --git a/diary/templates/diary/partials/seoul_map.html b/diary/templates/diary/partials/seoul_map.html
index 51812ca..45f1250 100644
--- a/diary/templates/diary/partials/seoul_map.html
+++ b/diary/templates/diary/partials/seoul_map.html
@@ -1,6 +1,6 @@
감정별 기록
@@ -29,127 +25,150 @@기록한 대표 감정을 한눈에 확인해보세요.
-{% endfor %}
아직 기록된 감정이 없어요.
@@ -207,9 +196,7 @@오늘의 대표 감정을 기록해보세요.
-+ 지도 색상 방식 +
+ + + + + + + + + + +
-
-
-
-
-
-
-
-
-
-
-
-
- 지도 색상 방식
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
From 743d334a0407c70d67fe65e262a17c812fdf78eb Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 20:04:57 +0900
Subject: [PATCH 10/13] =?UTF-8?q?style:=20=EC=9E=A5=EC=86=8C=EB=B3=84=20?=
=?UTF-8?q?=EC=A7=80=EB=8F=84=20=EC=9C=84=EC=B9=98=20=EC=83=81=ED=96=A5=20?=
=?UTF-8?q?=EC=A1=B0=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 4 +++-
1 file changed, 3 insertions(+), 1 deletion(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 98a1ae1..82f403e 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -1407,7 +1407,9 @@ body.modal-open {
width: min(800px, 100%);
- margin: 0 auto;
+ margin: -50px auto -100px;
+
+ overflow: hidden;
}
From 4079776ae40101fd770a7e0edda37ba0570fc506 Mon Sep 17 00:00:00 2001
From: 1wltn <1jisoo0120@gmail.com>
Date: Fri, 7 Aug 2026 20:13:30 +0900
Subject: [PATCH 11/13] =?UTF-8?q?fix:=20=EC=A7=80=EB=8F=84=20=EC=9C=84?=
=?UTF-8?q?=EC=B9=98=20=EC=A1=B0=EC=A0=95=EC=9C=BC=EB=A1=9C=20=EC=9D=B8?=
=?UTF-8?q?=ED=95=B4=20=ED=83=AD=EB=B0=94=20=EC=95=88=EB=88=8C=EB=A6=AC?=
=?UTF-8?q?=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
diary/static/diary/css/diary.css | 2 +-
diary/templates/diary/partials/seoul_map.html | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/diary/static/diary/css/diary.css b/diary/static/diary/css/diary.css
index 82f403e..40a2640 100644
--- a/diary/static/diary/css/diary.css
+++ b/diary/static/diary/css/diary.css
@@ -1407,7 +1407,7 @@ body.modal-open {
width: min(800px, 100%);
- margin: -50px auto -100px;
+ margin: 0 auto;
overflow: hidden;
}
diff --git a/diary/templates/diary/partials/seoul_map.html b/diary/templates/diary/partials/seoul_map.html
index 51812ca..45f1250 100644
--- a/diary/templates/diary/partials/seoul_map.html
+++ b/diary/templates/diary/partials/seoul_map.html
@@ -1,6 +1,6 @@
- 지도 색상 방식 -
- - - - - - - - - - -