Skip to content

Commit 288560d

Browse files
committed
feat: index page button show up on hovering entry
1 parent bfcb79a commit 288560d

File tree

6 files changed

+210
-25
lines changed

6 files changed

+210
-25
lines changed

RBMWeb/frontend/favicon.ico

16.1 KB
Binary file not shown.

RBMWeb/frontend/src/index.ts

Lines changed: 128 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,18 @@ import { DataFilter } from "./dataFilter.js";
55
import {BACKENDURL, FRONTENDURL} from "./config.js";
66
import { getLogger } from "./libs/logging.js";
77
import { checkUsrInfo, eraseUsrInfo } from "./login.js";
8+
import { sleep } from "./libs/timeUtils.js";
89

910

10-
// Global storage of the dataInfoList
11+
// Global storages ===========
12+
// the dataInfoList
1113
var g_datalist: DataInfoT[];
14+
// last dataEntry with action shown
15+
var g_prevActEntry: null|DataEntryElement
16+
17+
interface DataEntryElement extends HTMLDivElement{
18+
dataInfo: DataInfoT
19+
}
1220

1321
function render(dataInfoList: DataInfoT[]){
1422

@@ -43,6 +51,99 @@ function initSearch(dataInfoList: DataInfoT[]){
4351
}
4452

4553
function showData(dataInfoList: DataInfoT[]){
54+
function showEntryAct(entry: DataEntryElement){
55+
if (entry.querySelector("div.entryAct")){
56+
// If already has act entry, skip re-triggering this function
57+
return;
58+
}
59+
// Add elements
60+
entry.innerHTML += `
61+
<div class="entryAct gradIn">
62+
</div>
63+
`
64+
const acts: HTMLInputElement[] = new Array()
65+
const entryFrame = entry.querySelector("div.entryAct")!;
66+
const actView = document.createElement("input");
67+
actView.value = "View"
68+
acts.push(actView);
69+
const actNote = document.createElement("input");
70+
actNote.value = "Note"
71+
acts.push(actNote);
72+
const actDiscuss = document.createElement("input");
73+
actDiscuss.value = "Discuss"
74+
acts.push(actDiscuss);
75+
76+
for (const act of acts){
77+
act.type = "button";
78+
act.classList.add("smoothTrans");
79+
entryFrame.appendChild(act);
80+
}
81+
82+
// Set callbacks for the actions
83+
function getOpenFileFunction(): () => void{
84+
const uid = entry.dataInfo.uuid;
85+
let destURL: string;
86+
if (entry.dataInfo["has_file"] && entry.dataInfo["file_type"]===".pdf"){
87+
destURL = `${BACKENDURL}/doc/${uid}`
88+
}
89+
else if (entry.dataInfo["has_file"] && entry.dataInfo["file_type"]===".hpack"){
90+
destURL = `${BACKENDURL}/doc/${uid}//`
91+
}
92+
else if (entry.dataInfo.url){
93+
destURL = entry.dataInfo.url;
94+
}
95+
return () => {
96+
// if (destURL) window.location.href = destURL;
97+
if (destURL) window.open(destURL, '_blank')?.focus();
98+
}
99+
}
100+
function getOpenNoteFunction(): ()=>void {
101+
const uid = entry.dataInfo.uuid;
102+
return () => {
103+
const dest = `${BACKENDURL}/comment/${uid}/`;
104+
// Simulate a mouse click:
105+
// it will somehow omit the last '/'
106+
// window.location.href = `${BACKENDURL}/comment/${uid}/`;
107+
//
108+
// Use this instead
109+
window.open(dest, '_blank')?.focus();
110+
}
111+
}
112+
function getOpenDiscussionFunction(): ()=>void {
113+
const uid = entry.dataInfo.uuid;
114+
return () => {
115+
alert("Not implemented yet.")
116+
}
117+
}
118+
actView.addEventListener("click", getOpenFileFunction());
119+
actNote.addEventListener("click", getOpenNoteFunction());
120+
actDiscuss.addEventListener("click", getOpenDiscussionFunction());
121+
122+
// May be remove other entry frame
123+
if (g_prevActEntry != null && g_prevActEntry.classList == entry.classList){
124+
// compare if prev activated entry is the current one
125+
// This may be unnecessary because we are not re-triggering this function
126+
// by previous condition checking
127+
getLogger('rbm').debug("Staying in same entry");
128+
}
129+
else{
130+
// Remove previous entry's action frame
131+
if (g_prevActEntry){
132+
removeEntryAct(g_prevActEntry);
133+
}
134+
// save this as global
135+
g_prevActEntry = entry;
136+
}
137+
}
138+
function removeEntryAct(entry: HTMLDivElement){
139+
// remove entry action frame if it exists
140+
const actFrame = entry.querySelector("div.entryAct");
141+
if (actFrame){
142+
entry.removeChild(actFrame);
143+
}
144+
}
145+
146+
46147
const selectorDiv: HTMLDivElement = document.querySelector("div.selector")!;
47148
// first, clear selector_frame
48149
removeChilds(selectorDiv);
@@ -51,41 +152,45 @@ function showData(dataInfoList: DataInfoT[]){
51152
// add valid data into it
52153
for (const dataInfo of dataInfoList){
53154
idx += 1;
54-
const dataEntry = document.createElement("div");
155+
const dataEntry = <DataEntryElement>document.createElement("div");
156+
dataEntry.dataInfo = dataInfo;
55157
dataEntry.classList.add("dataEntry");
56158
dataEntry.innerHTML = `
57-
<div class="yearAuthor">
58-
<label class="year">${dataInfo.year}</label>
59-
<label class="author">${dataInfo.author}</label>
159+
<div class="entryInfo">
160+
<div class="yearAuthor">
161+
<label class="year">${dataInfo.year}</label>
162+
<label class="author">${dataInfo.author}</label>
163+
</div>
164+
<label class="title">${dataInfo.title}</label>
60165
</div>
61-
<label class="title">${dataInfo.title}</label>
62166
`
63167
selectorDiv.appendChild(dataEntry);
64168

65169
// animation property
66170
const delay = 0.05*idx + Math.random()*0.1;
67171
dataEntry.classList.add("gradIn2");
68172
dataEntry.classList.add("smoothTrans");
69-
dataEntry.classList.add("hoverMaxout105");
70-
dataEntry.classList.add("hoverBlueWhite");
173+
dataEntry.classList.add("hoverMaxout103");
174+
// dataEntry.classList.add("hoverBlueWhite");
175+
dataEntry.classList.add(dataInfo.uuid);
71176
dataEntry.style.animationDelay = delay.toString() + "s";
72177

73-
// on click
74-
function getOnClickFunction(): () => void{
75-
const uid = dataInfo.uuid;
76-
let destURL: string;
77-
if (dataInfo["has_file"] && dataInfo["file_type"]===".pdf"){
78-
destURL = `${BACKENDURL}/doc/${uid}`
79-
}
80-
else if (dataInfo["has_file"] && dataInfo["file_type"]===".hpack"){
81-
destURL = `${BACKENDURL}/doc/${uid}`
82-
}
83-
return () => {
84-
if (destURL) window.location.href = destURL;
85-
}
86-
}
87178

88-
dataEntry.addEventListener("click", getOnClickFunction());
179+
dataEntry.addEventListener("mouseover",
180+
(function(){ return () => { showEntryAct(dataEntry); }})()
181+
)
182+
dataEntry.addEventListener("mouseleave",
183+
(function(){
184+
return async () => {
185+
// to remove act frame when mouse move out of the entry
186+
// and not entering another entry
187+
// delay execution is used to resolve
188+
// flashing when hoveing betweeing two entries
189+
await sleep(200);
190+
removeEntryAct(dataEntry);
191+
}
192+
})()
193+
)
89194
}
90195
}
91196

RBMWeb/frontend/src/libs/timeUtils.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
11
import {getLogger} from "./logging.js";
22

3+
export function sleep(ms: number): Promise<void>{
4+
return new Promise(r => setTimeout(r, ms));
5+
}
6+
37
/*
48
* return locale time in format: yyyy-mm-dd hh:mm:ss
59
* */

RBMWeb/frontend/style/common.css

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,22 @@
33
--max-width: 1600px;
44
--font-color-gray: #444;
55
--font-song: "宋体", SimSun, STSong;
6+
7+
--cw: #eee;
8+
--cd: #2f2d2e;
9+
10+
/* --c1: #c9e4ca; */
11+
/* --c2: #87bba2; */
12+
/* --c3: #55828b; */
13+
/* --c4: #3b6064; */
14+
/* --c5: #364958; */
15+
16+
--c1: #BDD8EA;
17+
--c2: #CCAAE4;
18+
--c3: #3B6B8A;
19+
--c4: #AD6CB5;
20+
--c5: #142852;
21+
--c6: #4E1B45;
622
}
723

824
*{
@@ -48,6 +64,10 @@ input[type="button"]{
4864
transform: scale(1.05);
4965
}
5066

67+
.hoverMaxout103:hover{
68+
transform: scale(1.05);
69+
}
70+
5171
.hoverBlueWhite:hover{
5272
background-color: var(--bg-blue);
5373
color: white;

RBMWeb/frontend/style/index.css

Lines changed: 55 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,16 +15,28 @@ input.search_bar{
1515
div.dataEntry {
1616
border-radius: 8px;
1717
border-width: 1px;
18-
border-color: #cde;
18+
border-color: var(--c2);
1919
border-style: solid;
2020

21-
background-color: #def;
21+
background-color: var(--c1);
2222
padding: 10px;
2323
padding-left: 20px;
2424
padding-right: 20px;
2525
margin-top: 8px;
2626
margin-bottom: 8px;
2727

28+
display: flex;
29+
flex-direction: column;
30+
}
31+
32+
33+
div.dataEntry:hover {
34+
box-shadow: 0px 1px 7px 1px rgba(0, 0, 0, 0.25);
35+
color: var(--cw);
36+
background-color: var(--c6);
37+
}
38+
39+
div.entryInfo{
2840
display: flex;
2941
flex-wrap: wrap;
3042
align-items: center;
@@ -39,3 +51,44 @@ div.yearAuthor {
3951
min-width: 150px;
4052
max-width: 200px;
4153
}
54+
55+
56+
div.entryAct{
57+
margin-top: 8px;
58+
margin-bottom: 8px;
59+
width: 100%;
60+
max-width: 800px;
61+
align-self: center;
62+
display: flex;
63+
flex-wrap: nowrap;
64+
}
65+
66+
div.entryAct input[type=button]{
67+
68+
min-height: 20px;
69+
/* min-width: 80px; */
70+
71+
width: 30%;
72+
/* max-width: 400px; */
73+
74+
margin-right: 10px;
75+
margin-left: 10px;
76+
77+
padding: 2px;
78+
background-color: var(--c4);;
79+
color: #eee;
80+
81+
border-radius: 5px;
82+
border: 1px solid var(--c2);;
83+
box-shadow: none;
84+
}
85+
86+
div.entryAct input[type=button]:hover{
87+
background-color: var(--c3);
88+
}
89+
90+
/* */
91+
/* [> sibling selector ~ <] */
92+
/* div.entryInfo:hover ~ div.entryAct, div.entryAct:hover{ */
93+
/* opacity: 0.5; */
94+
/* } */

RBMWeb/server.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,7 @@ class CommentHandler(tornado.web.StaticFileHandler, RequestHandlerBase):
131131
# Serve comment (Notes) as webpage
132132
def get(self, path):
133133
global db_reader
134+
print("Get comment request: {}".format(path))
134135
self.setDefaultHeader()
135136
psplit = path.split("/")
136137
uuid = psplit[0]
@@ -362,9 +363,11 @@ def __init__(self) -> None:
362363
frontend_root = os.path.join(root, "frontend")
363364
frontend_root_old = os.path.join(root, "frontendV0")
364365
handlers = [
366+
# Frontend
365367
(r'/(favicon.ico)', tornado.web.StaticFileHandler, {"path": frontend_root}),
366368
(r"/frontend/(.*)", tornado.web.StaticFileHandler, {"path": frontend_root, "default_filename" : "index.html"}),
367369
(r"/main/(.*)", tornado.web.StaticFileHandler, {"path": frontend_root_old, "default_filename" : "index.html"}),
370+
# Backend
368371
(r"/doc/(.*)", DocHandler),
369372
(r"/hdoc/(.*)", HDocHandler, {"path": "/"}),
370373
(r"/filelist", FileListHandler),

0 commit comments

Comments
 (0)