@ -0,0 +1,13 @@
|
||||
UI design principles - mostly adapted from https://maze.co/collections/ux-ui-design/ui-design-principles/
|
||||
* Consistency (within the application and with similar applications)
|
||||
* Provide easy navigation (logical hierarchy: navigating menus or tools for more options; multiple ways to carry out an action where it's natural to do so)
|
||||
* Provide informative feedback (e.g. password strength)
|
||||
* Design workflows to yield closure (e.g. "purchase complete", "file saved")
|
||||
* Prevent user errors (e.g. "are you sure you want to exit without saving?")
|
||||
* Allow users ways to reverse their actions easily (e.g. undo, redo, cancel, and exit)
|
||||
* Avoid surprises and changes in behavior
|
||||
* Minimize the user's memory load (avoid having to remember things in order to perform a task, e.g. "I forgot that I already bought that book.")
|
||||
* Aim for clarity (quickly and easily understand what something does)
|
||||
* Use visual hierarchy (size, color, font, white space, and negative space) to illustrate what is important and what groups together
|
||||
* Design for flexibility: Accommodate different user skill levels (e.g. mouse vs keyboard shortcuts, advanced features, filters) and disabilities (color contrast, screen reader, easy tab key navigation, for web: different devices)
|
||||
* Lead the user to formulate a good mental model of what happens internally
|
After Width: | Height: | Size: 5.7 KiB |
After Width: | Height: | Size: 658 B |
After Width: | Height: | Size: 616 B |
@ -0,0 +1,264 @@
|
||||
function scaleMap(page, r) {
|
||||
var map = page.querySelector("map");
|
||||
var container = page.querySelector(".ImageContainer > .Links");
|
||||
if (!container) {
|
||||
container = document.createElement("div");
|
||||
container.setAttribute("class", "Links");
|
||||
page.querySelector(".ImageContainer").appendChild(container);
|
||||
} else {
|
||||
container.innerHTML = "";
|
||||
}
|
||||
|
||||
map.querySelectorAll("area").forEach(function (area) {
|
||||
var original = area._originalCoords || area.getAttribute("coords");
|
||||
area._originalCoords = original;
|
||||
var coords = original.split(/\,/).map(function (v) {
|
||||
return Math.round(parseFloat(v) / r);
|
||||
});
|
||||
|
||||
area.setAttribute("coords", coords.join(","));
|
||||
var a = document.createElement("a");
|
||||
a.style.left = convertRatio(coords[0]) + "px";
|
||||
a.style.top = convertRatio(coords[1]) + "px";
|
||||
a.style.width = convertRatio(coords[2] - coords[0]) + "px";
|
||||
a.style.height = convertRatio(coords[3] - coords[1]) + "px";
|
||||
a.setAttribute("href", area.getAttribute("href"));
|
||||
|
||||
container.appendChild(a);
|
||||
});
|
||||
}
|
||||
function convertRatio(value) {
|
||||
return parseFloat(value); // * window.devicePixelRatio;
|
||||
}
|
||||
|
||||
function fitImages() {
|
||||
var pages = document.querySelectorAll("body > div.Page");
|
||||
var W = 0;
|
||||
var H = 0;
|
||||
|
||||
var activePage = null;
|
||||
|
||||
pages.forEach(function (page) {
|
||||
if (page.offsetWidth == 0 || page.offsetHeight == 0) return;
|
||||
|
||||
W = page.offsetWidth - (window.useExpandedMode ? 200 : 40);
|
||||
H = page.offsetHeight - 40;
|
||||
activePage = page;
|
||||
});
|
||||
|
||||
if (activePage && window.lastActivePage != activePage) {
|
||||
window.useExpandedMode = false;
|
||||
invalidateExpandMode("dontFit");
|
||||
window.lastSize = null;
|
||||
|
||||
document.body.querySelectorAll(".TOC > div").forEach(function (item) {
|
||||
var matched = item.classList.contains("Page_" + activePage.id);
|
||||
if (matched) {
|
||||
document.title = item._name + " - " + window.originalTitle;
|
||||
item.classList.add("Focused");
|
||||
item.focus();
|
||||
} else {
|
||||
item.classList.remove("Focused");
|
||||
}
|
||||
});
|
||||
|
||||
window.lastActivePage = activePage;
|
||||
}
|
||||
|
||||
|
||||
if (W && H) {
|
||||
if (window.lastSize && window.lastSize.W == W && window.lastSize.H == H) return;
|
||||
|
||||
var imgs = document.querySelectorAll("body > div.Page img");
|
||||
imgs.forEach(function (img) {
|
||||
var r = window.useExpandedMode ? Math.min(img.naturalWidth / W, img.naturalHeight / H) : Math.max(img.naturalWidth / W, img.naturalHeight / H);
|
||||
|
||||
if (r < 1 && !window.useExpandedMode) r = 1;
|
||||
var w = Math.round(img.naturalWidth / r);
|
||||
var h = Math.round(img.naturalHeight / r);
|
||||
|
||||
img.style.width = w + "px";
|
||||
img.style.height = h + "px";
|
||||
|
||||
img.setAttribute("width", w);
|
||||
img.setAttribute("height", h);
|
||||
|
||||
var page = img;
|
||||
while (!page.classList.contains("Page")) page = page.parentNode;
|
||||
|
||||
scaleMap(page, r * (img._originalWidth / img.naturalWidth));
|
||||
});
|
||||
|
||||
window.lastSize = {W: W, H: H};
|
||||
}
|
||||
}
|
||||
function checkActivePage() {
|
||||
var pages = document.querySelectorAll("body > div.Page");
|
||||
var found = false;
|
||||
var firstPage = null;
|
||||
pages.forEach(function (page) {
|
||||
if (!firstPage) firstPage = page;
|
||||
if (page.offsetWidth != 0 && page.offsetHeight != 0) found = true;
|
||||
});
|
||||
|
||||
if (!found && firstPage) {
|
||||
location.hash = "#" + firstPage.id;
|
||||
fitImages();
|
||||
}
|
||||
}
|
||||
|
||||
function workingThreadFunction() {
|
||||
try {
|
||||
try {
|
||||
fitImages();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
try {
|
||||
checkActivePage();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
} finally {
|
||||
window.setTimeout(workingThreadFunction, 300);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var idleTimeout = null;
|
||||
function handleMouseMove() {
|
||||
if (!document.body.classList.contains("Active")) {
|
||||
document.body.classList.add("Active");
|
||||
}
|
||||
|
||||
if (idleTimeout) window.clearTimeout(idleTimeout);
|
||||
idleTimeout = window.setTimeout(function () {
|
||||
document.body.classList.remove("Active");
|
||||
idleTimeout = null;
|
||||
}, 400);
|
||||
}
|
||||
|
||||
var THUMB_WIDTH = 250;
|
||||
var THUMB_HEIGHT = 160;
|
||||
var THUMB_DISPLAY_SIZE = 160;
|
||||
|
||||
function buildThumbnail(url, callback) {
|
||||
var image = new Image();
|
||||
image.onload = function () {
|
||||
var canvas = document.createElement('canvas');
|
||||
var ctx = canvas.getContext('2d');
|
||||
|
||||
var r = Math.max(image.width / THUMB_WIDTH, image.height / THUMB_HEIGHT);
|
||||
var w = image.width / r, h = image.height / r;
|
||||
canvas.width = w;
|
||||
canvas.height = h;
|
||||
|
||||
ctx.drawImage(image, 0, 0, w, h);
|
||||
|
||||
callback(canvas.toDataURL('image/png'), w, h);
|
||||
};
|
||||
|
||||
image.src = url;
|
||||
}
|
||||
|
||||
function generateTOC() {
|
||||
var toc = document.createElement("div");
|
||||
toc.setAttribute("class", "TOC");
|
||||
var pages = document.querySelectorAll("body > div.Page");
|
||||
pages.forEach(function (page) {
|
||||
var title = page.querySelector("h2");
|
||||
var img = page.querySelector(".ImageContainer img");
|
||||
|
||||
var item = document.createElement("div");
|
||||
var imageWrapper = document.createElement("a");
|
||||
var itemImage = document.createElement("img");
|
||||
|
||||
item.classList.add("Page_" + page.id);
|
||||
item.setAttribute("tabindex", 0);
|
||||
item._name = title.textContent;
|
||||
|
||||
imageWrapper.setAttribute("href", "#" + page.id);
|
||||
|
||||
item.appendChild(imageWrapper);
|
||||
var name = document.createElement("strong");
|
||||
name.innerHTML = title.innerHTML;
|
||||
|
||||
toc.appendChild(item);
|
||||
|
||||
buildThumbnail(img.src, function (dataUrl, w, h) {
|
||||
var r = Math.max(w / THUMB_DISPLAY_SIZE, h / THUMB_DISPLAY_SIZE);
|
||||
var w = w / r, h = h / r;
|
||||
|
||||
imageWrapper.appendChild(itemImage);
|
||||
itemImage.style.width = w + "px";
|
||||
itemImage.style.height = h + "px";
|
||||
itemImage.src = dataUrl;
|
||||
|
||||
imageWrapper.appendChild(name);
|
||||
});
|
||||
});
|
||||
|
||||
document.body.appendChild(toc);
|
||||
}
|
||||
|
||||
function invalidateExpandMode(dontFit) {
|
||||
if (window.useExpandedMode) {
|
||||
document.body.classList.add("ExpandMode");
|
||||
} else {
|
||||
document.body.classList.remove("ExpandMode");
|
||||
}
|
||||
|
||||
if (!dontFit) fitImages();
|
||||
}
|
||||
|
||||
function boot() {
|
||||
window.originalTitle = document.title;
|
||||
document.addEventListener("mousemove", handleMouseMove);
|
||||
var style = document.createElement("link");
|
||||
style.setAttribute("rel", "stylesheet");
|
||||
style.setAttribute("href", "Resources/style.css");
|
||||
document.querySelector("head").appendChild(style);
|
||||
|
||||
var imgs = document.querySelectorAll("body > div.Page img");
|
||||
imgs.forEach(function (img) {
|
||||
img._originalWidth = parseInt(img.getAttribute("width"), 10);
|
||||
img._originalHeight = parseInt(img.getAttribute("height"), 10);
|
||||
});
|
||||
|
||||
|
||||
generateTOC();
|
||||
workingThreadFunction();
|
||||
|
||||
|
||||
window.zoomToggleButton = document.createElement("button");
|
||||
window.zoomToggleButton.classList.add("ToggleZoomButton");
|
||||
window.zoomToggleButton.setAttribute("title", "Toggle expand/fit mode");
|
||||
document.body.appendChild(window.zoomToggleButton);
|
||||
|
||||
window.zoomToggleButton.addEventListener("click", function () {
|
||||
window.useExpandedMode = window.useExpandedMode ? false : true;
|
||||
window.lastSize = null;
|
||||
invalidateExpandMode();
|
||||
}, false);
|
||||
}
|
||||
|
||||
window.onload = boot;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,169 @@
|
||||
/*
|
||||
place extra stylesheet here
|
||||
*/
|
||||
|
||||
body, html {
|
||||
margin: 0px;
|
||||
padding: 0px;
|
||||
overflow: hidden;
|
||||
font-family: "Product Sans", "Liberation Sans", Arial, sans-serif;
|
||||
}
|
||||
|
||||
#documentMetadata,
|
||||
#documentTitle {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.Page {
|
||||
position: absolute;
|
||||
left: 230px;
|
||||
top: 0px;
|
||||
right: 0px;
|
||||
bottom: 0px;
|
||||
background: #00000077;
|
||||
text-align: center;
|
||||
}
|
||||
body:not(.ExpandMode) .Page {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
body.ExpandMode .Page {
|
||||
overflow: auto;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
}
|
||||
.Page:not(:target) {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.Page > h2 {
|
||||
display: none;
|
||||
}
|
||||
.Page .ImageContainer {
|
||||
box-shadow: 0px 0px 1em #00000077;
|
||||
}
|
||||
body.ExpandMode .Page .ImageContainer {
|
||||
display: inline-block;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.Page .ImageContainer {
|
||||
position: relative;
|
||||
}
|
||||
.Page .ImageContainer > .Links > a {
|
||||
position: absolute;
|
||||
background-color: transparent;
|
||||
transition: background-color 0.2s ease;
|
||||
}
|
||||
body.Active .Page .ImageContainer > .Links > a {
|
||||
background-color: #E79A0F11;
|
||||
}
|
||||
.Page .ImageContainer > .Links > a:hover {
|
||||
background-color: #FFFFFF55 !important;
|
||||
}
|
||||
|
||||
.TOC {
|
||||
position: absolute;
|
||||
top: 0px;
|
||||
left: 0px;
|
||||
bottom: 0px;
|
||||
width: 230px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
overflow-y: auto;
|
||||
overflow-x: visible;
|
||||
z-index: 2;
|
||||
background: #444;
|
||||
}
|
||||
.TOC > div {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
.TOC > div.Focused > a {
|
||||
background: #5294E2;
|
||||
}
|
||||
.TOC > div:not(.Focused):hover > a {
|
||||
background: #5294E266;
|
||||
}
|
||||
.TOC > div:not(.Focused):not(:hover) {
|
||||
opacity: 0.7;
|
||||
}
|
||||
.TOC > div + div {
|
||||
margin-top: 1em;
|
||||
}
|
||||
|
||||
.TOC > div > a {
|
||||
flex: 1 1 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
padding: 1em;
|
||||
background: transparent;
|
||||
transition: background 0.2s ease;
|
||||
text-decoration: none;
|
||||
}
|
||||
.TOC > div > a > img {
|
||||
display: block;
|
||||
border: solid 2px #FFF;
|
||||
box-shadow: 0px 0px 5px #000;
|
||||
}
|
||||
.TOC > div > a > strong {
|
||||
display: block;
|
||||
text-align: center;
|
||||
color: #FFF;
|
||||
font-weight: normal;
|
||||
margin-top: 0.3em;
|
||||
}
|
||||
|
||||
.ToggleZoomButton {
|
||||
position: fixed;
|
||||
top: 0.5em;
|
||||
right: 0.5em;
|
||||
width: 1.5em;
|
||||
height: 1.5em;
|
||||
padding: 0.3em;
|
||||
box-sizing: content-box;
|
||||
background: url(expand.png) 50% 50% no-repeat #CCCCCC;
|
||||
background-origin: content-box;
|
||||
background-size: contain;
|
||||
|
||||
border: solid 1px #777;
|
||||
}
|
||||
.ToggleZoomButton:active {
|
||||
margin-top: 1px;
|
||||
margin-left: 1px;
|
||||
}
|
||||
.ToggleZoomButton:not(:hover) {
|
||||
opacity: 0.3;
|
||||
}
|
||||
body.ExpandMode .ToggleZoomButton {
|
||||
background-image: url(fit.png)
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
@ -0,0 +1,2 @@
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:p="http://www.evolus.vn/Namespace/Pencil" xmlns:html="http://www.w3.org/1999/xhtml"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
|
||||
<title>Campus Visit Prototype</title><link rel="stylesheet" href="Resources/style.css" /><script type="application/javascript" src="Resources/script.js"></script></head><body><h1 id="documentTitle">Campus Visit Prototype</h1><p id="documentMetadata">Exported at: Wed Sep 18 2024 09:38:34 GMT-0400 (Eastern Daylight Time)</p><div class="Page" id="users_page"><h2>Users</h2><div class="ImageContainer"><img src="pages\users.png" width="887" height="470" usemap="#map_users" /></div><map name="map_users"><area shape="rect" coords="103.00007629394531,41.452125549316406,193.000068664551,75.7999305725098" href="#visits_page" title="Go to page 'Visits'" /><area shape="rect" coords="648,184.0000457763672,755,209.000045776367" href="#availability_page" title="Go to page 'Availability'" /><area shape="rect" coords="648,149.00003051757812,755,174.00004196167" href="#availability_page" title="Go to page 'Availability'" /><area shape="rect" coords="648,114.00005340576172,755,139.750064849854" href="#availability_page" title="Go to page 'Availability'" /></map></div><div class="Page" id="availability_page"><h2>Availability</h2><div class="ImageContainer"><img src="pages\availability.png" width="355" height="216" usemap="#map_availability" /></div><map name="map_availability"><area shape="rect" coords="0.9999974966049194,0.0000032186512726184446,354.999997496605,215.000003218651" href="#users_page" title="Go to page 'Users'" /></map></div><div class="Page" id="visits_page"><h2>Visits</h2><div class="ImageContainer"><img src="pages\visits.png" width="887" height="496" usemap="#map_visits" /></div><map name="map_visits"><area shape="rect" coords="747,408.00006103515625,837,433.000061035156" href="#visitdetails_page" title="Go to page 'VisitDetails'" /><area shape="rect" coords="747,379.00006103515625,837,404.000061035156" href="#visitdetails_page" title="Go to page 'VisitDetails'" /><area shape="rect" coords="747,321.00006103515625,837,346.000061035156" href="#visitdetails_page" title="Go to page 'VisitDetails'" /><area shape="rect" coords="747,350.00006103515625,837,375.000061035156" href="#visitdetails_page" title="Go to page 'VisitDetails'" /><area shape="rect" coords="747,291.00006103515625,837,316.000061035156" href="#visitdetails_page" title="Go to page 'VisitDetails'" /><area shape="rect" coords="747,236.0000457763672,837,261.000066757202" href="#visitdetails_page" title="Go to page 'VisitDetails'" /><area shape="rect" coords="747,263.00006103515625,837,288.000061035156" href="#visitdetails_page" title="Go to page 'VisitDetails'" /><area shape="rect" coords="747,207.0000457763672,837,232.000045776367" href="#visitdetails_page" title="Go to page 'VisitDetails'" /><area shape="rect" coords="747,179.0000457763672,837,204.000045776367" href="#visitdetails_page" title="Go to page 'VisitDetails'" /><area shape="rect" coords="746,151.00003051757812,836,176.00004196167" href="#visitdetails_page" title="Go to page 'VisitDetails'" /><area shape="rect" coords="17.000059127807617,40.452110290527344,107.000059127808,74.7999153137207" href="#users_page" title="Go to page 'Users'" /></map></div><div class="Page" id="visitdetails_page"><h2>VisitDetails</h2><div class="ImageContainer"><img src="pages\visitdetails.png" width="691" height="492" usemap="#map_visitdetails" /></div><map name="map_visitdetails"><area shape="rect" coords="205.0000457763672,456,359.000091552734,481" href="#visits_page" title="Go to page 'Visits'" /></map></div></body></html>
|
After Width: | Height: | Size: 37 KiB |
After Width: | Height: | Size: 94 KiB |
After Width: | Height: | Size: 143 KiB |
After Width: | Height: | Size: 213 KiB |