New repo who dis
This commit is contained in:
commit
21dbefab2d
10 changed files with 1291 additions and 0 deletions
7
.gitignore
vendored
Normal file
7
.gitignore
vendored
Normal file
|
@ -0,0 +1,7 @@
|
|||
/elm-stuff/
|
||||
/downloads/
|
||||
/guidelines/
|
||||
assets/images/420/
|
||||
assets/images/full/
|
||||
assets/other/
|
||||
assets/javascripts/main.js
|
18
add-iamge.sh
Executable file
18
add-iamge.sh
Executable file
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env bash
|
||||
|
||||
IMG=$1
|
||||
|
||||
if ! [ -f $IMG ]; then
|
||||
echo "Please specify a path to an image"
|
||||
exit
|
||||
fi
|
||||
|
||||
PATHFULL=assets/images/full
|
||||
PATH420=assets/images/420
|
||||
|
||||
mkdir -p $PATHFULL $PATH420
|
||||
|
||||
file=$(basename $IMG)
|
||||
|
||||
convert $IMG -resize 420x420 "${PATH420}/${file%.*}.png"
|
||||
convert $IMG "${PATHFULL}/${file%.*}.png"
|
BIN
assets/images/favicon/favicon.png
Normal file
BIN
assets/images/favicon/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2 KiB |
2
assets/javascripts/detect_mobile.js
Normal file
2
assets/javascripts/detect_mobile.js
Normal file
|
@ -0,0 +1,2 @@
|
|||
function isMobile(){return (function(a){if(/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4))){return true;}else{return false;}})(navigator.userAgent||navigator.vendor||window.opera);}
|
||||
|
402
assets/styles/styles.css
Normal file
402
assets/styles/styles.css
Normal file
|
@ -0,0 +1,402 @@
|
|||
body {
|
||||
background-color: #ffffff;
|
||||
margin: 0;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.mobile-nav .dropdown {
|
||||
position: relative !important;
|
||||
}
|
||||
|
||||
.mobile-nav.hidden {
|
||||
display: none;
|
||||
}
|
||||
|
||||
header {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
right: 0;
|
||||
left: 0;
|
||||
z-index: 1;
|
||||
color: #999999;
|
||||
background-color: #292929;
|
||||
border-color: #101010;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
border-bottom-width: 1px;
|
||||
}
|
||||
|
||||
.desktop-header {
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.mobile-header {
|
||||
max-height: 100vh;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.mobile-header .show-nav {
|
||||
cursor: pointer;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.mobile-header .show-nav.active {
|
||||
color: #ffffff;
|
||||
background-color: #2266ff;
|
||||
}
|
||||
|
||||
.mobile-header .show-nav p {
|
||||
margin: 0;
|
||||
padding: 0 1em;
|
||||
}
|
||||
|
||||
.carat {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 2px;
|
||||
vertical-align: middle;
|
||||
border-top: 4px dashed;
|
||||
border-top-color: currentcolor;
|
||||
border-top: 4px solid;
|
||||
border-right: 4px solid transparent;
|
||||
border-left: 4px solid transparent;
|
||||
}
|
||||
|
||||
nav {
|
||||
background-color: #292929;
|
||||
}
|
||||
|
||||
.desktop-nav {
|
||||
margin: 0 auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.nav-wrapper {
|
||||
background-color: #292929;
|
||||
}
|
||||
|
||||
.nav-wrapper ul {
|
||||
padding: 0;
|
||||
list-style-type: none;
|
||||
}
|
||||
|
||||
.nav-wrapper > ul {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.desktop-nav-wrapper > ul {
|
||||
display: flex;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.mobile-header .show-nav,
|
||||
.nav-wrapper > ul > li {
|
||||
-webkit-touch-callout: none; /* iOS Safari */
|
||||
-webkit-user-select: none; /* Safari */
|
||||
-khtml-user-select: none; /* Konqueror HTML */
|
||||
-moz-user-select: none; /* Firefox */
|
||||
-ms-user-select: none; /* Internet Explorer/Edge */
|
||||
user-select: none; /* Non-prefixed version, currently
|
||||
supported by Chrome and Opera */
|
||||
}
|
||||
|
||||
.desktop-nav-wrapper > ul > li {
|
||||
margin: 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.nav-wrapper > ul > li:hover,
|
||||
.nav-wrapper > ul > li .dropdown-active {
|
||||
color: #ffffff;
|
||||
background-color: #2266ff;
|
||||
}
|
||||
|
||||
.desktop-nav-wrapper > ul > li:hover,
|
||||
.desktop-nav-wrapper > ul > li .dropdown-active {
|
||||
border-color: #2266ff;
|
||||
border-style: solid;
|
||||
border-width: 0;
|
||||
border-bottom-width: 1px;
|
||||
margin-bottom: -1px;
|
||||
}
|
||||
|
||||
.nav-wrapper > ul > li .button-wrapper {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.desktop-nav-wrapper > ul > li .button-wrapper {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: space-around;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.desktop-nav-wrapper > ul > li .button-wrapper p {
|
||||
margin: 0;
|
||||
padding: 0 1em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.mobile-nav-wrapper > ul > li .button-wrapper p {
|
||||
margin: 0;
|
||||
padding: 1em;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.nav-wrapper > ul > li .nav-link {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
.mobile-nav .nav-item > div {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.desktop-nav-wrapper > ul > li.home-item .button-wrapper p {
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
.mobile-nav-wrapper > ul > li.home-item .button-wrapper p {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.nav-wrapper > ul > li.nav-item .button-wrapper p {
|
||||
font-weight: bold;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.desktop-nav-wrapper > ul > li .dropdown {
|
||||
color: #292929;
|
||||
background-color: #ffffff;
|
||||
text-align: left;
|
||||
min-width: 10em;
|
||||
border-bottom: 1px solid #999;
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
.mobile-nav-wrapper > ul > li .dropdown {
|
||||
color: #cccccc;
|
||||
background-color: #404040;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.mobile-nav-wrapper > ul > li .dropdown-active {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.desktop-nav-wrapper > ul > li .dropdown,
|
||||
.desktop-nav-wrapper > ul > li ul.dropdown > li.sub-item:last-child p {
|
||||
border-bottom-left-radius: 0.4em;
|
||||
border-bottom-right-radius: 0.4em;
|
||||
}
|
||||
|
||||
.desktop-nav-wrapper > ul > li .dropdown li.sub-item {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.nav-wrapper > ul > li .dropdown li.sub-item p {
|
||||
margin: 0;
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.mobile-nav-wrapper > ul > li .dropdown li.sub-item p {
|
||||
padding: 0.8em 1em;
|
||||
}
|
||||
|
||||
.desktop-nav-wrapper > ul > li .dropdown li.sub-item p {
|
||||
padding: 0.4em 1em;
|
||||
}
|
||||
|
||||
.nav-wrapper > ul > li .dropdown li.sub-item p:hover {
|
||||
color: #ffffff;
|
||||
background-color: #2266ff;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
background-color: #444444;
|
||||
background-image: url('/assets/images/full/pexels-photo-downscale.png');
|
||||
background-attachment: local;
|
||||
background-size: cover;
|
||||
position: relative;
|
||||
z-index: 0;
|
||||
height: 100vh;
|
||||
max-height: 500px;
|
||||
margin-top: 51px;
|
||||
}
|
||||
|
||||
.welcome .dimmer {
|
||||
opacity: 0.7;
|
||||
background-color: #000000;
|
||||
color: #ffffff;
|
||||
}
|
||||
|
||||
.welcome .centered {
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.welcome .content {
|
||||
padding: 0.5em 1em;
|
||||
}
|
||||
|
||||
.welcome .content h1 {
|
||||
font-size: 3em;
|
||||
}
|
||||
|
||||
.welcome .content p {
|
||||
padding: 0.5em 0;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
.welcome .content h1,
|
||||
.welcome .content p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.gallery {
|
||||
padding: 1em;
|
||||
background-color: #efefef;
|
||||
border-bottom: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.gallery .gallery-wrapper {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.gallery .gallery-info {
|
||||
margin: 0 1em;
|
||||
}
|
||||
|
||||
.gallery .gallery-columns {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
margin: 0 0.5em;
|
||||
}
|
||||
|
||||
.gallery .gallery-column {
|
||||
margin: 0 auto;
|
||||
flex: 1;
|
||||
max-width: 465px;
|
||||
}
|
||||
|
||||
.gallery .gallery-image {
|
||||
margin: 1em 0.5em;
|
||||
}
|
||||
|
||||
.gallery .gallery-image-wrapper {
|
||||
padding: 1em;
|
||||
background-color: #ffffff;
|
||||
box-shadow: 0px 2px 3px #999999;
|
||||
}
|
||||
|
||||
.gallery .gallery-image img {
|
||||
display: block;
|
||||
width: 100%;
|
||||
max-width: 420px;
|
||||
}
|
||||
|
||||
footer {
|
||||
background-color: #f7f7f7;
|
||||
}
|
||||
|
||||
footer a {
|
||||
text-decoration: none;
|
||||
color: #00b0ff;
|
||||
}
|
||||
|
||||
footer a:hover {
|
||||
color: #5555f0;
|
||||
}
|
||||
|
||||
footer a:active {
|
||||
color: #00b0ff;
|
||||
}
|
||||
|
||||
footer .footer-wrapper {
|
||||
margin: 0 auto;
|
||||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
footer .footer-column {
|
||||
margin: 0 auto;
|
||||
min-width: 300px;
|
||||
max-width: 500px;
|
||||
}
|
||||
|
||||
footer .footer-column-wrapper {
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
@media (min-width: 632px) {
|
||||
.desktop-nav {
|
||||
width: 900px;
|
||||
}
|
||||
|
||||
.gallery .gallery-wrapper {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
footer .footer-wrapper {
|
||||
width: 600px;
|
||||
}
|
||||
|
||||
footer .footer-column {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 932px) {
|
||||
.desktop-nav {
|
||||
width: 900px;
|
||||
}
|
||||
|
||||
.welcome .centered {
|
||||
width: 900px;
|
||||
}
|
||||
|
||||
.gallery .gallery-wrapper {
|
||||
width: 900px;
|
||||
}
|
||||
|
||||
footer .footer-wrapper {
|
||||
width: 900px
|
||||
}
|
||||
|
||||
footer .footer-column {
|
||||
width: 300px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (min-width: 1200px) {
|
||||
.desktop-nav {
|
||||
width: 1170px;
|
||||
}
|
||||
|
||||
.welcome .centered {
|
||||
width: 1170px;
|
||||
}
|
||||
|
||||
.gallery .gallery-wrapper {
|
||||
width: 1170px;
|
||||
}
|
||||
|
||||
footer .footer-wrapper {
|
||||
width: 1170px;
|
||||
}
|
||||
|
||||
footer .footer-column {
|
||||
width: 390px;
|
||||
}
|
||||
}
|
||||
|
28
elm.json
Normal file
28
elm.json
Normal file
|
@ -0,0 +1,28 @@
|
|||
{
|
||||
"type": "application",
|
||||
"source-directories": [
|
||||
"src"
|
||||
],
|
||||
"elm-version": "0.19.0",
|
||||
"dependencies": {
|
||||
"direct": {
|
||||
"elm/browser": "1.0.1",
|
||||
"elm/core": "1.0.2",
|
||||
"elm/html": "1.0.0",
|
||||
"hercules-ci/elm-dropdown": "1.0.1"
|
||||
},
|
||||
"indirect": {
|
||||
"elm/json": "1.1.3",
|
||||
"elm/parser": "1.1.0",
|
||||
"elm/time": "1.0.0",
|
||||
"elm/url": "1.0.0",
|
||||
"elm/virtual-dom": "1.0.2",
|
||||
"elm-community/json-extra": "4.2.0",
|
||||
"rtfeldman/elm-iso8601-date-strings": "1.1.3"
|
||||
}
|
||||
},
|
||||
"test-dependencies": {
|
||||
"direct": {},
|
||||
"indirect": {}
|
||||
}
|
||||
}
|
25
index.html
Normal file
25
index.html
Normal file
|
@ -0,0 +1,25 @@
|
|||
<html>
|
||||
<head>
|
||||
|
||||
<meta charset="utf-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||||
<title>Werefox Software</title>
|
||||
<link rel="shortcut icon" href="/assets/images/favicon/favicon.png">
|
||||
<link href="/assets/styles/styles.css" rel="stylesheet" type="text/css">
|
||||
<script src="/assets/javascripts/detect_mobile.js"></script>
|
||||
<script src="/assets/javascripts/main.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="elm-app">
|
||||
<h1>Please enable javascript to view this website</h1>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
var node = document.getElementById("elm-app");
|
||||
var app = Elm.Main.init({
|
||||
node: node,
|
||||
flags: { mobile: isMobile() }
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
1
index.php
Normal file
1
index.php
Normal file
|
@ -0,0 +1 @@
|
|||
<?php header( 'Location: /index.html' ) ; ?>
|
3
make.sh
Executable file
3
make.sh
Executable file
|
@ -0,0 +1,3 @@
|
|||
#!/bin/bash
|
||||
|
||||
elm make --output="assets/javascripts/main.js" src/Main.elm
|
805
src/Main.elm
Normal file
805
src/Main.elm
Normal file
|
@ -0,0 +1,805 @@
|
|||
module Main exposing (main)
|
||||
|
||||
{-| This is the main module of the application.
|
||||
|
||||
|
||||
# main function
|
||||
|
||||
@docs main
|
||||
|
||||
-}
|
||||
|
||||
import Html exposing (Attribute, Html, a, article, button, div, footer, h1, h2, h3, h4, header, img, li, nav, p, section, span, text, ul)
|
||||
import Html.Attributes exposing (class, href, rel, src, title)
|
||||
import Html.Events exposing (onClick)
|
||||
import Array exposing (Array)
|
||||
import Dropdown exposing (ToggleEvent(..), drawer, dropdown, toggle)
|
||||
import Browser.Events exposing (onResize)
|
||||
import Browser.Dom exposing (getViewport)
|
||||
import Browser exposing (document)
|
||||
import Task exposing (perform)
|
||||
|
||||
|
||||
-- MAIN
|
||||
|
||||
|
||||
{-| The main function of the application
|
||||
-}
|
||||
main : Program Flags Model Msg
|
||||
main =
|
||||
document
|
||||
{ init = init
|
||||
, view = (\model -> { title = "Your Mom", body = view model })
|
||||
, update = update
|
||||
, subscriptions = subscriptions
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- MODEL
|
||||
|
||||
|
||||
type alias Model =
|
||||
{ flags : Flags
|
||||
, nav : Navigation
|
||||
, welcome : WelcomeBanner
|
||||
, gallery : Gallery
|
||||
, footer : List FooterSection
|
||||
}
|
||||
|
||||
|
||||
type alias Navigation =
|
||||
{ smallNav : Bool
|
||||
, showNav : Bool
|
||||
, navText : String
|
||||
, items : Array NavItem
|
||||
}
|
||||
|
||||
|
||||
type NavItem
|
||||
= HomeLink String String
|
||||
| InitDropdown String (Array NavItem)
|
||||
| Dropdown String DropdownConfig (Array NavItem)
|
||||
| NavLink String String
|
||||
|
||||
|
||||
type alias DropdownConfig =
|
||||
{ name : String
|
||||
, event : ToggleEvent
|
||||
, attribute : Attribute Msg
|
||||
, state : Dropdown.State
|
||||
, message : Bool -> Msg
|
||||
}
|
||||
|
||||
|
||||
type alias WelcomeBanner =
|
||||
{ title : String
|
||||
, description : String
|
||||
}
|
||||
|
||||
|
||||
type alias Gallery =
|
||||
{ title : String
|
||||
, description : String
|
||||
, url : String
|
||||
, urltext : String
|
||||
, columns : Int
|
||||
, images : List GalleryItem
|
||||
}
|
||||
|
||||
|
||||
type GalleryItem
|
||||
= Image GalleryImage
|
||||
| Project GalleryProject
|
||||
| ProjectLink GalleryLink
|
||||
|
||||
|
||||
type alias GalleryImage =
|
||||
{ url : String
|
||||
, mouseoverText : String
|
||||
}
|
||||
|
||||
|
||||
type alias GalleryProject =
|
||||
{ url : String
|
||||
, mouseoverText : String
|
||||
, title : String
|
||||
, description : List String
|
||||
}
|
||||
|
||||
|
||||
type alias GalleryLink =
|
||||
{ url : String
|
||||
, mouseoverText : String
|
||||
, title : String
|
||||
, description : List String
|
||||
, links : List ( String, String )
|
||||
}
|
||||
|
||||
|
||||
type alias FooterSection =
|
||||
{ title : String
|
||||
, description : String
|
||||
, link : String
|
||||
, url : String
|
||||
}
|
||||
|
||||
|
||||
type alias Flags =
|
||||
{ mobile : Bool
|
||||
}
|
||||
|
||||
|
||||
|
||||
-- INIT
|
||||
|
||||
|
||||
init : Flags -> ( Model, Cmd Msg )
|
||||
init flags =
|
||||
( { flags = flags
|
||||
, nav =
|
||||
{ showNav = False
|
||||
, smallNav = False
|
||||
, navText = "Navigation"
|
||||
, items =
|
||||
initDropdowns
|
||||
[ HomeLink "Home" "/"
|
||||
, InitDropdown
|
||||
"Social Media"
|
||||
(initDropdowns
|
||||
[ NavLink "Facebook" "https://facebook.com/shadow8t4"
|
||||
, NavLink "Twitter" "https://twitter.com/Shadow8t4"
|
||||
, NavLink "Google+" "https://plus.google.com/u/2/118253409016956205819"
|
||||
]
|
||||
)
|
||||
, InitDropdown
|
||||
"Games"
|
||||
(initDropdowns
|
||||
[ NavLink "Angels and Demons" "https://gitea.werefoxsoftware.com/shadow8t4/angels-and-demons"
|
||||
, NavLink "Monster Chase" "https://gitea.werefoxsoftware.com/shadow8t4/MochaPine64Backup"
|
||||
, NavLink "Monster Chase Server" "https://gitea.werefoxsoftware.com/shadow8t4/MochaServerPine64Backup"
|
||||
, NavLink "Revival Survival" "/revival-survival"
|
||||
, NavLink "Project Undercover" "/project-undercover"
|
||||
, NavLink "Project Undercover Git Repo" "https://gitea.werefoxsoftware.com/shadow8t4/Project-Undercover"
|
||||
, NavLink "So Bow-y Cute" "https://double-darling-duo-deluxe.itch.io/so-bow-y-cute"
|
||||
]
|
||||
)
|
||||
, InitDropdown
|
||||
"Other Projects"
|
||||
(initDropdowns
|
||||
[ NavLink "Re-Procedural City" "https://gitea.werefoxsoftware.com/shadow8t4/Re-ProceduralCity"
|
||||
, NavLink "Procedural City" "https://gitea.werefoxsoftware.com/shadow8t4/ProceduralCity"
|
||||
, NavLink "PSVR Git Repo" "https://gitea.werefoxsoftware.com/shadow8t4/Public-Speaking-VR"
|
||||
, NavLink "PSVR Research Paper" "/assets/other/Public_Speaking_in_VR_Research_Paper.pdf"
|
||||
]
|
||||
)
|
||||
]
|
||||
}
|
||||
, welcome = welcomeBanner "Werefox Software" "A self-hosted portfolio site for and by Alex Huddleston"
|
||||
, gallery =
|
||||
initGallery
|
||||
"Portfolio Gallery"
|
||||
"A few projects I've worked on. The source code for many of these can be viewed on my self-hosted git service (hosted through Gitea)."
|
||||
"https://gitea.werefoxsoftware.com"
|
||||
"Git subdomain"
|
||||
[ galleryLink
|
||||
"angels-and-demons.png"
|
||||
"A completed game board."
|
||||
"Angels and Demons"
|
||||
[ "Angels and Demons is a board game concept I created during my time in college, specifically"
|
||||
++ " during my Game History class. I decided on my own time to take up converting it to a"
|
||||
++ " video game. Right now, it's focused for mainly Android and WebGL builds. It's currently"
|
||||
++ " (as of June 2018) in very early development. The basic mechanics and logic work, but "
|
||||
++ " it can only be played locally with very little indication of turns or progress. There"
|
||||
++ " are also no instructions aside from a rough document I made for the class, which has"
|
||||
++ " been converted to the repository's readme."
|
||||
]
|
||||
[ ( "Git Repo"
|
||||
, "https://gitea.werefoxsoftware.com/shadow8t4/angels-and-demons"
|
||||
)
|
||||
]
|
||||
, galleryLink
|
||||
"projectundercover-guard.png"
|
||||
"The guard's view in ProjectUndercover."
|
||||
"ProjectUndercover - guard view"
|
||||
[ "Project Undercover is a game about pretending to be an AI. Or at least -- that's part of it."
|
||||
++ " Players will compete against one another, with one side trying to blend in with a crowd"
|
||||
++ " of non-player characters, and the other side attempting to identify them. It takes place"
|
||||
++ " at a party, and the undercover players are agents trying to infiltrate and complete"
|
||||
++ " several missions before the guard, or overseer, catches them."
|
||||
, "The overseer is limited"
|
||||
++ " by a set of cameras, and slowly receives information over the course of the game to"
|
||||
++ " help identify the agents, thus putting the heat on them."
|
||||
, "This picture is from the perspective of one of the guard's cameras."
|
||||
]
|
||||
[ ( "Git Repo"
|
||||
, "https://gitea.werefoxsoftware.com/shadow8t4/Project-Undercover"
|
||||
)
|
||||
]
|
||||
, galleryLink
|
||||
"revival-survival.png"
|
||||
"A game where you save a soul you accidentally reaped as the grim reaper."
|
||||
"Revival Survival"
|
||||
[ "This was my entry for Chillennium 2017, a game jam held and hosted by students at Texas A&M."
|
||||
++ " I was in a group with 3 other students, and mostly worked on the sound design and"
|
||||
++ " game mechanics from the game. The game was made using Unity."
|
||||
, "In Revival Survival, you play as the Grim Reaper in a 2D platformer that has to return"
|
||||
++ " the soul of someone they accidentally reaped. Throughout the game you are met with"
|
||||
++ " adversaries - hellhounds that want to eat the soul you reaped and paladins who wish to avenge"
|
||||
++ " that soul you reaped."
|
||||
, "You can find a link to play this game in browser on the nav bar at the top of this page, as well"
|
||||
++ " as the link below. I've also provided a link to the itch.io submission page, where you"
|
||||
++ " can download a Windows standalone copy of the game."
|
||||
]
|
||||
[ ( "itch.io Page Link"
|
||||
, "https://d4-team.itch.io/revival-survival"
|
||||
)
|
||||
, ( " / "
|
||||
, "/"
|
||||
)
|
||||
, ( "In-browser Game Link"
|
||||
, "/revival-survival"
|
||||
)
|
||||
]
|
||||
, galleryLink
|
||||
"mocha_bordered.png"
|
||||
"The status screen of the game."
|
||||
"Monster Chase"
|
||||
[ "Monster Chase was a fitness game/app developed during my senior capstone class in college."
|
||||
++ " The development team consisted of me and 4 other computer science majors, and the"
|
||||
++ " objective was to create a fitness game designed to track users' steps through engaging"
|
||||
++ " gameplay of the user being virtually chased by a monster that they could customize and"
|
||||
++ " use to challenge other players online. The game makes use of the FitBit API if a device"
|
||||
++ " and subsequent account are available to help track data and utilizes and separate server"
|
||||
++ " to keep track of points for an online leaderboard functionality. While the finaly build"
|
||||
++ "is still in many cases a prototype compared to what we initially intended to develop, it"
|
||||
++ "is functional and the source code for both the app and server are available in their"
|
||||
++ "subsequent repositories."
|
||||
, "During the development of the game, I worked mainly on the front-end design and functionality"
|
||||
++ " of the game through the Unity editor. Most scripted events and transitions in the front end"
|
||||
++ " were also programmed by me, or I at least had a hand in."
|
||||
]
|
||||
[ ( "Git Repo "
|
||||
, "https://gitea.werefoxsoftware.com/shadow8t4/MochaPine64Backup"
|
||||
)
|
||||
, ( " / "
|
||||
, "/"
|
||||
)
|
||||
, ( "Server Git Repo"
|
||||
, "https://gitea.werefoxsoftware.com/shadow8t4/MochaServerPine64Backup"
|
||||
)
|
||||
]
|
||||
, galleryProject
|
||||
"projectundercover-spy.png"
|
||||
"A spy's view in ProjectUndercover."
|
||||
"Project Undercover - spy view"
|
||||
[ "This is another picture from Project Undercover, this time from the perspective"
|
||||
++ " of a spy. In this picture, the spy is finishing a waving interaction with an AI spy."
|
||||
]
|
||||
, galleryLink
|
||||
"public-speaking-vr.png"
|
||||
"A Case Study on Public Speaking in Virtual Reality."
|
||||
"Public Speaking in VR"
|
||||
[ "In this project, and the resulting research paper, a group I was assigned in and I show the results of"
|
||||
++ " a case study on the effectiveness and realism of using Virtual Reality technology to simulate the"
|
||||
++ " experience of public speaking in an effort to practice one’s speech skills."
|
||||
, "We used a Development"
|
||||
++ " Kit 2 version of the Oculus Rift and the Unity editor to create and run our simulation. In the"
|
||||
++ " simulation, we presented users a large classroom environment with a handful of characters to"
|
||||
++ " present a mock speech in front of. We provided the users with an Xbox game controller to allow"
|
||||
++ " for easier camera movement as well as a way to switch through the given slides in the mock presentation."
|
||||
, "The resulting research paper from the case study and a link to the project's github page can be found"
|
||||
++ " below."
|
||||
]
|
||||
[ ( "Git Repo "
|
||||
, "https://gitea.werefoxsoftware.com/shadow8t4/Public-Speaking-VR"
|
||||
)
|
||||
, ( " / "
|
||||
, "/"
|
||||
)
|
||||
, ( "Research Paper"
|
||||
, "/assets/other/Public_Speaking_in_VR_Research_Paper.pdf"
|
||||
)
|
||||
]
|
||||
, galleryLink
|
||||
"re-procedural-city.png"
|
||||
"An image of the re-implementation's output."
|
||||
"Re:Procedural City"
|
||||
[ "A Rust implementation of the Procedural City project. This was done to gain some insight"
|
||||
++ " into the programming language and to refamiliarize myself with the work itself. In"
|
||||
++ " addition, the project is done through solely open-source libraries as opposed to the"
|
||||
++ " ones used in the original project provided by my professor, meaning that documentation"
|
||||
++ " of the library functions is much more accessible."
|
||||
]
|
||||
[ ( "Git Repo"
|
||||
, "https://gitea.werefoxsoftware.com/shadow8t4/Re-ProceduralCity"
|
||||
)
|
||||
]
|
||||
, galleryLink
|
||||
"procedural-city.png"
|
||||
"A picture of some output from ProceduralCity"
|
||||
"Procedural City"
|
||||
[ "In this project my partner, Jeremy Martin, and I presented a way of procedurally generating a basic city by"
|
||||
++ " creating an .obj mesh file using some given template meshes. The goal was that when given a"
|
||||
++ " user input population density and some template meshes, the program would output"
|
||||
++ " a resulting mesh of a procedurally generated “city” using the template"
|
||||
++ " meshes along with some defined rules. The program takes the input files and duplicates it at"
|
||||
++ " procedurally determined intersections, creating a basic city structure in the"
|
||||
++ " resulting output .obj mesh file."
|
||||
, " Unfortunately, the program was unable to be fully"
|
||||
++ " completed before our deadline, and currently takes one input mesh file and duplicates it"
|
||||
++ " based on user-defined amounts of layers from a central point with user-defined spacing."
|
||||
, "Specific documentation on how to operate the program is detailed on the respective github page."
|
||||
]
|
||||
[ ( "Git Repo"
|
||||
, "https://gitea.werefoxsoftware.com/shadow8t4/ProceduralCity"
|
||||
)
|
||||
]
|
||||
, galleryLink
|
||||
"so-bow-y-cute.png"
|
||||
"A game where you become cute."
|
||||
"So Bow-y Cute"
|
||||
[ "This was my entry for Chillennium 2016, a game jam held and hosted by students at Texas A&M."
|
||||
++ " This was my first time at the game jam and I was randomly patnered with one other person,"
|
||||
++ " who did the art for this game. Everything else was done by me. The game was made using Unity."
|
||||
, "In So Bow-y Cute - When your parent decides to limit you cuteness potential by prohibiting all bows, you must"
|
||||
++ " rebel against their tyranny and become the cutest you possibly can. Be careful not to get"
|
||||
++ " caught! or else you're out of luck."
|
||||
, "You can find a link to the itch.io page below."
|
||||
]
|
||||
[ ( "itch.io Page Link"
|
||||
, "https://double-darling-duo-deluxe.itch.io/so-bow-y-cute"
|
||||
)
|
||||
]
|
||||
]
|
||||
, footer =
|
||||
[ initFooterSection
|
||||
""
|
||||
""
|
||||
""
|
||||
"/"
|
||||
, initFooterSection
|
||||
""
|
||||
"This website was programmed in elm with the help of my friend Riley."
|
||||
"source code"
|
||||
"https://gitea.werefox.dev/shadow8t4/portfolio-site"
|
||||
, initFooterSection
|
||||
""
|
||||
""
|
||||
""
|
||||
"/"
|
||||
]
|
||||
}
|
||||
, Task.perform WindowWidth (getViewport
|
||||
|> Task.map (\viewport -> floor viewport.scene.width))
|
||||
)
|
||||
|
||||
|
||||
smallImagePath : String -> String
|
||||
smallImagePath filename =
|
||||
"/assets/images/420/" ++ filename
|
||||
|
||||
|
||||
fullImagePath : String -> String
|
||||
fullImagePath filename =
|
||||
"/assets/images/full/" ++ filename
|
||||
|
||||
|
||||
defaultDropdownConfig : DropdownConfig
|
||||
defaultDropdownConfig =
|
||||
{ name = "dropdown1"
|
||||
, event = OnClick
|
||||
, attribute = class "dropdown-active"
|
||||
, state = False
|
||||
, message = ToggleDropdown 0
|
||||
}
|
||||
|
||||
|
||||
initDropdown : Int -> NavItem -> NavItem
|
||||
initDropdown index item =
|
||||
case item of
|
||||
Dropdown name config items ->
|
||||
Dropdown
|
||||
name
|
||||
{ config
|
||||
| name = "dropdown" ++ (String.fromInt index)
|
||||
, message = ToggleDropdown index
|
||||
}
|
||||
items
|
||||
|
||||
InitDropdown name items ->
|
||||
Dropdown
|
||||
name
|
||||
{ defaultDropdownConfig
|
||||
| name = "dropdown" ++ (String.fromInt index)
|
||||
, message = ToggleDropdown index
|
||||
}
|
||||
items
|
||||
|
||||
other ->
|
||||
other
|
||||
|
||||
|
||||
initDropdowns : List NavItem -> Array NavItem
|
||||
initDropdowns =
|
||||
Array.indexedMap initDropdown << Array.fromList
|
||||
|
||||
|
||||
welcomeBanner : String -> String -> WelcomeBanner
|
||||
welcomeBanner title description =
|
||||
{ title = title, description = description }
|
||||
|
||||
|
||||
galleryImage : String -> String -> GalleryItem
|
||||
galleryImage url text =
|
||||
Image { url = url, mouseoverText = text }
|
||||
|
||||
|
||||
galleryProject : String -> String -> String -> List String -> GalleryItem
|
||||
galleryProject url text title description =
|
||||
Project { url = url, mouseoverText = text, title = title, description = description }
|
||||
|
||||
|
||||
galleryLink : String -> String -> String -> List String -> List ( String, String ) -> GalleryItem
|
||||
galleryLink url text title description links =
|
||||
ProjectLink { url = url, mouseoverText = text, title = title, description = description, links = links }
|
||||
|
||||
|
||||
initGallery : String -> String -> String -> String -> List GalleryItem -> Gallery
|
||||
initGallery title description url urltext images =
|
||||
{ title = title, description = description, url = url, urltext = urltext, columns = 1, images = images }
|
||||
|
||||
|
||||
initFooterSection : String -> String -> String -> String -> FooterSection
|
||||
initFooterSection title description link url =
|
||||
{ title = title, description = description, link = link, url = url }
|
||||
|
||||
|
||||
|
||||
-- VIEW
|
||||
|
||||
|
||||
view : Model -> List (Html Msg)
|
||||
view model =
|
||||
[ headerBar model
|
||||
, welcome model.welcome
|
||||
, viewGallery model.gallery
|
||||
, footerSections model.footer
|
||||
]
|
||||
|
||||
|
||||
welcome : WelcomeBanner -> Html Msg
|
||||
welcome banner =
|
||||
section [ class "welcome" ]
|
||||
[ div [ class "welcome-wrapper" ]
|
||||
[ div [ class "dimmer" ]
|
||||
[ div [ class "centered" ]
|
||||
[ article [ class "content" ]
|
||||
[ h1 [ class "title" ] [ text banner.title ]
|
||||
, p [ class "description" ] [ text banner.description ]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
headerBar : Model -> Html Msg
|
||||
headerBar model =
|
||||
if model.flags.mobile || model.nav.smallNav then
|
||||
mobileHeaderbar model
|
||||
else
|
||||
desktopHeaderBar model
|
||||
|
||||
|
||||
desktopHeaderBar : Model -> Html Msg
|
||||
desktopHeaderBar model =
|
||||
header [ class "desktop-header" ]
|
||||
[ nav [ class "desktop-nav" ]
|
||||
[ div [ class "desktop-nav-wrapper", class "nav-wrapper" ]
|
||||
[ ul [] (Array.map navItem model.nav.items |> Array.toList)
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
mobileHeaderbar : Model -> Html Msg
|
||||
mobileHeaderbar model =
|
||||
header [ class "mobile-header" ]
|
||||
[ div
|
||||
([ class "show-nav", onClick (ToggleNav (not model.nav.showNav)) ]
|
||||
++ if model.nav.showNav then
|
||||
[ class "active" ]
|
||||
else
|
||||
[]
|
||||
)
|
||||
[ p []
|
||||
[ text model.nav.navText
|
||||
, span [ class "carat" ] []
|
||||
]
|
||||
]
|
||||
, nav
|
||||
([ class "mobile-nav" ]
|
||||
++ if model.nav.showNav then
|
||||
[]
|
||||
else
|
||||
[ class "hidden" ]
|
||||
)
|
||||
[ div [ class "mobile-nav-wrapper", class "nav-wrapper" ]
|
||||
[ ul [] (Array.map navItem model.nav.items |> Array.toList)
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
homeLink : String -> String -> Html Msg
|
||||
homeLink name url =
|
||||
li [ class "home-item" ]
|
||||
[ div []
|
||||
[ a [ class "nav-link", href url ]
|
||||
[ div [ class "button-wrapper" ]
|
||||
[ p [] [ text name ]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
navItem : NavItem -> Html Msg
|
||||
navItem item =
|
||||
case item of
|
||||
NavLink name url ->
|
||||
navLink name url
|
||||
|
||||
Dropdown name config items ->
|
||||
navDropdown name config items
|
||||
|
||||
HomeLink name url ->
|
||||
homeLink name url
|
||||
|
||||
_ ->
|
||||
text ""
|
||||
|
||||
|
||||
navLink : String -> String -> Html Msg
|
||||
navLink name url =
|
||||
li [ class "nav-item" ]
|
||||
[ div []
|
||||
[ a [ class "nav-link", href url ]
|
||||
[ div [ class "button-wrapper" ]
|
||||
[ p [] [ text name ]
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
navDropdown : String -> DropdownConfig -> Array NavItem -> Html Msg
|
||||
navDropdown name config items =
|
||||
li [ class "nav-item" ]
|
||||
[ dropdown div []
|
||||
[
|
||||
(toggle div
|
||||
([ class "button-wrapper" ]
|
||||
++ if config.state then
|
||||
[ class "dropdown-active" ]
|
||||
else
|
||||
[]
|
||||
)
|
||||
[ p []
|
||||
[ text name
|
||||
, span [ class "carat" ] []
|
||||
]
|
||||
]
|
||||
)
|
||||
, (drawer ul [ class "dropdown" ] (Array.map subItem items |> Array.toList))
|
||||
]
|
||||
config.state
|
||||
(dropdownConfig config)
|
||||
]
|
||||
|
||||
|
||||
subItem : NavItem -> Html Msg
|
||||
subItem item =
|
||||
case item of
|
||||
NavLink name url ->
|
||||
li [ class "sub-item" ]
|
||||
[ a [ class "nav-link", href url ]
|
||||
[ p [] [ text name ]
|
||||
]
|
||||
]
|
||||
|
||||
_ ->
|
||||
text ""
|
||||
|
||||
|
||||
dropdownConfig : DropdownConfig -> Dropdown.Config Msg
|
||||
dropdownConfig config =
|
||||
Dropdown.Config
|
||||
config.name
|
||||
config.event
|
||||
config.attribute
|
||||
config.message
|
||||
|
||||
|
||||
viewGallery : Gallery -> Html Msg
|
||||
viewGallery gallery =
|
||||
let
|
||||
arr : Array (List GalleryItem)
|
||||
arr =
|
||||
gallery.images
|
||||
|> List.indexedMap (\index item -> ( index, item ))
|
||||
|> List.foldl
|
||||
(\( index, item ) acc ->
|
||||
if (remainderBy gallery.columns index) < Array.length acc then
|
||||
case
|
||||
acc
|
||||
|> Array.get (remainderBy gallery.columns index)
|
||||
|> Maybe.map ((::) item)
|
||||
of
|
||||
Just column ->
|
||||
Array.set (remainderBy gallery.columns index) column acc
|
||||
|
||||
Nothing ->
|
||||
acc
|
||||
else
|
||||
Array.push [ item ] acc
|
||||
)
|
||||
Array.empty
|
||||
in
|
||||
div [ class "gallery" ]
|
||||
[ section [ class "gallery-wrapper" ]
|
||||
[ article [ class "gallery-info" ]
|
||||
[ h2 [] [ text gallery.title ]
|
||||
, p [] [ text gallery.description ]
|
||||
, a [ href gallery.url ] [ text gallery.urltext ]
|
||||
]
|
||||
, div [ class "gallery-columns" ]
|
||||
(arr
|
||||
|> Array.map (div [ class "gallery-column" ] << List.map displayImage << List.reverse)
|
||||
|> Array.toList
|
||||
)
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
displayImage : GalleryItem -> Html Msg
|
||||
displayImage item =
|
||||
case item of
|
||||
Image image ->
|
||||
article [ class "gallery-image" ]
|
||||
[ div [ class "gallery-image-wrapper" ]
|
||||
[ a [ href (fullImagePath image.url) ]
|
||||
[ img [ src (smallImagePath image.url), title image.mouseoverText ] []
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
Project project ->
|
||||
article [ class "gallery-image" ]
|
||||
[ div [ class "gallery-image-wrapper" ]
|
||||
[ a [ href (fullImagePath project.url) ]
|
||||
[ img [ src (smallImagePath project.url), title project.mouseoverText ] []
|
||||
]
|
||||
, div [ class "gallery-image-info" ]
|
||||
[ h4 [] [ text project.title ]
|
||||
, div [] (List.map (\t -> p [] [ text t ]) project.description)
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
ProjectLink projectlink ->
|
||||
article [ class "gallery-image" ]
|
||||
[ div [ class "gallery-image-wrapper" ]
|
||||
[ a [ href (fullImagePath projectlink.url) ]
|
||||
[ img [ src (smallImagePath projectlink.url), title projectlink.mouseoverText ] []
|
||||
]
|
||||
, div [ class "gallery-image-info" ]
|
||||
[ h4 [] [ text projectlink.title ]
|
||||
, div [] (List.map (\t -> p [] [ text t ]) projectlink.description)
|
||||
, p [] (List.map (\( t, url ) -> a [ href url ] [ text t ]) projectlink.links)
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
footerSection : FooterSection -> Html Msg
|
||||
footerSection item =
|
||||
div [ class "footer-column" ]
|
||||
[ article [ class "footer-column-wrapper" ]
|
||||
[ h3 [] [ text item.title ]
|
||||
, p [] [ text item.description ]
|
||||
, p []
|
||||
[ a [ href item.url ]
|
||||
[ text item.link ]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
|
||||
footerSections : List FooterSection -> Html Msg
|
||||
footerSections items =
|
||||
footer []
|
||||
[ section [ class "footer-wrapper" ] (List.map footerSection items)
|
||||
]
|
||||
|
||||
|
||||
|
||||
-- UPDATE
|
||||
|
||||
|
||||
type Msg
|
||||
= WindowWidth Int
|
||||
| ToggleDropdown Int Bool
|
||||
| ToggleNav Bool
|
||||
|
||||
|
||||
update : Msg -> Model -> ( Model, Cmd Msg )
|
||||
update msg model =
|
||||
case msg of
|
||||
WindowWidth x ->
|
||||
( { model
|
||||
| gallery = updateGalleryWidth model.flags.mobile x model.gallery
|
||||
, nav = updateNavWidth x model.nav
|
||||
}
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
ToggleDropdown index state ->
|
||||
( { model | nav = updateDropdown index state model.nav }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
ToggleNav state ->
|
||||
( { model | nav = toggleNav state model.nav }
|
||||
, Cmd.none
|
||||
)
|
||||
|
||||
|
||||
toggleNav : Bool -> Navigation -> Navigation
|
||||
toggleNav state nav =
|
||||
{ nav | showNav = state }
|
||||
|
||||
|
||||
updateGalleryWidth : Bool -> Int -> Gallery -> Gallery
|
||||
updateGalleryWidth mobile width gallery =
|
||||
if mobile then
|
||||
gallery
|
||||
else if width < 632 then
|
||||
{ gallery | columns = 1 }
|
||||
else if width < 932 then
|
||||
{ gallery | columns = 2 }
|
||||
else
|
||||
{ gallery | columns = 3 }
|
||||
|
||||
|
||||
updateNavWidth : Int -> Navigation -> Navigation
|
||||
updateNavWidth width nav =
|
||||
if width < 700 then
|
||||
{ nav | smallNav = True }
|
||||
else
|
||||
{ nav | smallNav = False, showNav = False }
|
||||
|
||||
|
||||
updateDropdown : Int -> Bool -> Navigation -> Navigation
|
||||
updateDropdown index state nav =
|
||||
case Array.get index nav.items of
|
||||
Just dropdown ->
|
||||
case dropdown of
|
||||
Dropdown name config items ->
|
||||
{ nav | items = Array.set index (Dropdown name { config | state = state } items) nav.items }
|
||||
|
||||
_ ->
|
||||
nav
|
||||
|
||||
Nothing ->
|
||||
nav
|
||||
|
||||
|
||||
|
||||
-- SUBSCRIPTIONS
|
||||
|
||||
|
||||
subscriptions : Model -> Sub Msg
|
||||
subscriptions model =
|
||||
onResize (\width _ -> WindowWidth width)
|
Reference in a new issue