Skip to content

Commit

Permalink
Merge pull request #100 from center-for-threat-informed-defense/AF-17…
Browse files Browse the repository at this point in the history
…7-splash-screen

AF-177 Create a splash screen
  • Loading branch information
mikecarenzo committed Aug 24, 2023
2 parents f7debc3 + 68291bd commit ecb1175
Show file tree
Hide file tree
Showing 16 changed files with 367 additions and 19 deletions.
16 changes: 11 additions & 5 deletions src/attack_flow_builder/src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
<div id="app-body" ref="body" :style="gridLayout">
<div class="frame center">
<BlockDiagram id="block-diagram"/>
<SplashMenu id="splash-menu" />
</div>
<div class="frame right">
<div class="resize-handle" @pointerdown="startResize($event, Handle.Right)"></div>
Expand All @@ -23,14 +24,16 @@ import Configuration from "@/assets/builder.config"
import { clamp } from "./assets/scripts/BlockDiagram";
import { PointerTracker } from "./assets/scripts/PointerTracker";
import { mapMutations, mapState } from 'vuex';
import { LoadFile, LoadSettings } from './store/Commands/AppCommands';
import * as App from './store/Commands/AppCommands';
import { defineComponent, markRaw, ref } from 'vue';
// Components
import SplashMenu from "@/components/Controls/SplashMenu.vue";
import AppTitleBar from "@/components/Elements/AppTitleBar.vue";
import AppHotkeyBox from "@/components/Elements/AppHotkeyBox.vue";
import BlockDiagram from "@/components/Elements/BlockDiagram.vue";
import AppFooterBar from "@/components/Elements/AppFooterBar.vue";
import EditorSidebar from "@/components/Elements/EditorSidebar.vue";
import { ShowSplashMenu } from "./store/Commands/AppCommands/ShowSplashMenu";
const Handle = {
None : 0,
Expand Down Expand Up @@ -156,20 +159,22 @@ export default defineComponent({
settings = require("../public/settings.json");
}
// Load settings
this.execute(new LoadSettings(this.context, settings));
this.execute(new App.LoadSettings(this.context, settings));
// Load empty file
this.execute(await LoadFile.fromNew(this.context));
this.execute(await App.LoadFile.fromNew(this.context));
// Load file from query parameters, if possible
let params = new URLSearchParams(window.location.search);
let src = params.get("src");
if(src) {
try {
// TODO: Incorporate loading dialog
this.execute(await LoadFile.fromUrl(this.context, src));
this.execute(await App.LoadFile.fromUrl(this.context, src));
} catch(ex) {
console.error(`Failed to load file from url: '${ src }'`);
console.error(ex);
}
} else {
this.execute(new ShowSplashMenu(this.context));
}
},
mounted() {
Expand All @@ -193,7 +198,8 @@ export default defineComponent({
AppTitleBar,
BlockDiagram,
AppFooterBar,
EditorSidebar
EditorSidebar,
SplashMenu,
},
});
</script>
Expand Down
Binary file added src/attack_flow_builder/src/assets/afb.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
29 changes: 29 additions & 0 deletions src/attack_flow_builder/src/assets/builder.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,35 @@ const config: AppConfiguration = {
application_name: "Attack Flow Builder",
file_type_name: "Attack Flow",
file_type_extension: "afb",
menu_icon: "./ctid-small.png",
splash: {
product: "./afb.png",
organization: "./ctid.png",
buttons: [
{
action: "new",
name: "New Flow",
description: "Create a new, blank flow",
},
{
action: "open",
name: "Open Flow",
description: "Open an existing flow from your computer"
},
{
action: "link",
name: "Example Flows",
description: "View a list of example flows",
url: "https://center-for-threat-informed-defense.github.io/attack-flow/example_flows/"
},
{
action: "link",
name: "Builder Help",
description: "View help for Attack Flow Builder",
url: "https://center-for-threat-informed-defense.github.io/attack-flow/builder/"
},
],
},
schema: {
page_template: "flow",
templates: [
Expand Down
Binary file added src/attack_flow_builder/src/assets/ctid-small.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added src/attack_flow_builder/src/assets/ctid.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
188 changes: 188 additions & 0 deletions src/attack_flow_builder/src/components/Controls/SplashMenu.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
<template>
<div class="splash-menu-control" :class="{
hidden: !isShowingSplash
}">
<div class="splash-menu-container">
<div class="header">
<img class="product" :src="product" />
<img class="organization" v-if="organization" :src="organization" />
</div>
<div class="section-grid">
<div class="button" v-for="button of buttons" @click="handleClick(button)">
<h1 class="name">
{{ button.name }}
<NewFlow class="icon" v-if="button.action === 'new'" />
<OpenFlow class="icon" v-else-if="button.action === 'open'" />
<Link class="icon" v-else-if="button.action === 'link'" />
</h1>
<p class="description">{{ button.description }}</p>
</div>
</div>
</div>
</div>
</template>

<script lang="ts">
const Images = require.context("../../assets", false);
import Configuration from "@/assets/builder.config"
import { mapGetters, mapMutations, mapState } from 'vuex';
// Dependencies
import { defineComponent } from 'vue';
import { ApplicationStore, SplashButton, SplashButtonAction } from "@/store/StoreTypes";
import * as App from "@/store/Commands/AppCommands";
// Components
import NewFlow from "@/components/Icons/NewFlow.vue";
import OpenFlow from "@/components/Icons/OpenFlow.vue";
import Link from "@/components/Icons/Link.vue";
export default defineComponent({
name: 'SplashMenu',
data() {
let organization;
if (Configuration.splash.organization) {
organization = Images(Configuration.splash.organization);
}
return {
product: Images(Configuration.splash.product),
organization,
buttons: Configuration.splash.buttons,
}
},
computed: {
...mapState("ApplicationStore", {
ctx(state: ApplicationStore): ApplicationStore {
return state;
}
}),
...mapGetters("ApplicationStore", ["isShowingSplash"]),
},
methods: {
/**
* Application Store mutations
*/
...mapMutations("ApplicationStore", ["execute"]),
async handleClick(button: SplashButton) {
switch (button.action) {
case SplashButtonAction.New:
this.execute(await App.LoadFile.fromNew(this.ctx));
this.execute(new App.HideSplashMenu(this.ctx));
break;
case SplashButtonAction.Open:
this.execute(await App.LoadFile.fromFileSystem(this.ctx));
this.execute(new App.HideSplashMenu(this.ctx));
break;
case SplashButtonAction.Link:
if (button.url !== undefined) {
this.execute(new App.OpenHyperlink(this.ctx, button.url));
}
break;
default:
throw new Error(`Unknown splash button action: ${button.action}`);
}
}
},
components: {
Link,
NewFlow,
OpenFlow,
},
});
</script>

<style scoped>
/** === Main Control === */
.splash-menu-control {
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
z-index: 1;
display: flex;
justify-content: center;
align-items: center;
}
.hidden {
visibility: hidden;
}
.splash-menu-container {
border: solid 1px #474747;
border-radius: 5px;
width: 500px;
margin: 30px 0px;
background: #242424;
overflow: hidden;
box-shadow: #000000 0px 0px 10px 3px;
}
/** === Header === */
.header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 15px 25px;
border-bottom: solid 1px #474747;
background: #383838;
pointer-events: none;
user-select: none;
}
.product,
.organization {
height: 50px;
}
/** === Sections === */
.section-grid {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
padding: 25px;
}
/** === Buttons === */
.button {
width: calc(50% - 20px);
padding: 25px;
border: solid 1px #383838;
border-radius: 3px;
box-sizing: border-box;
user-select: none;
margin: 10px;
}
.button:hover {
background: #383838;
;
}
.button .name {
color: #726de2;
font-size: 12pt;
font-weight: 700;
margin: 0px 0px 8px 0px;
}
.button .name .icon {
position: relative;
fill: #726de2;
width: 18px;
height: 18px;
top: 3px;
left: 3px;
}
.button .description {
color: #d9d9d9;
font-size: 10.5pt;
font-weight: 400;
}
</style>
20 changes: 10 additions & 10 deletions src/attack_flow_builder/src/components/Elements/AppTitleBar.vue
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
<template>
<TitleBar class="app-title-bar-element" :menus="menus" @select="onItemSelect">
<template v-slot:icon>
<span class="logo">AFB</span>
<img alt="Logo" title="Logo" class="logo" :src="menuIcon">
</template>
</TitleBar>
</template>

<script lang="ts">
const Images = require.context("../../assets", false);
import Configuration from "@/assets/builder.config"
// Dependencies
import { ContextMenu } from "@/assets/scripts/ContextMenuTypes";
import { CommandEmitter } from "@/store/Commands/Command";
Expand All @@ -17,6 +19,11 @@ import TitleBar from "@/components/Controls/TitleBar.vue";
export default defineComponent({
name: "AppTitleBar",
data() {
return {
menuIcon: Images(Configuration.menu_icon),
};
},
computed: {
/**
Expand Down Expand Up @@ -77,17 +84,10 @@ export default defineComponent({
</script>

<style scoped>
/** === App Logo === */
.logo {
margin: 0px 8px;
padding: 2px 4px;
color: #f0f1f2;
font-size: 7pt;
font-weight: 600;
border-radius: 3px;
background: #726de2;
margin: 2px 8px 0px 10px;
height: 16px;
}
</style>
12 changes: 12 additions & 0 deletions src/attack_flow_builder/src/components/Icons/Link.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"
viewBox="0 0 640 512"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path
d="M579.8 267.7c56.5-56.5 56.5-148 0-204.5c-50-50-128.8-56.5-186.3-15.4l-1.6 1.1c-14.4 10.3-17.7 30.3-7.4 44.6s30.3 17.7 44.6 7.4l1.6-1.1c32.1-22.9 76-19.3 103.8 8.6c31.5 31.5 31.5 82.5 0 114L422.3 334.8c-31.5 31.5-82.5 31.5-114 0c-27.9-27.9-31.5-71.8-8.6-103.8l1.1-1.6c10.3-14.4 6.9-34.4-7.4-44.6s-34.4-6.9-44.6 7.4l-1.1 1.6C206.5 251.2 213 330 263 380c56.5 56.5 148 56.5 204.5 0L579.8 267.7zM60.2 244.3c-56.5 56.5-56.5 148 0 204.5c50 50 128.8 56.5 186.3 15.4l1.6-1.1c14.4-10.3 17.7-30.3 7.4-44.6s-30.3-17.7-44.6-7.4l-1.6 1.1c-32.1 22.9-76 19.3-103.8-8.6C74 372 74 321 105.5 289.5L217.7 177.2c31.5-31.5 82.5-31.5 114 0c27.9 27.9 31.5 71.8 8.6 103.9l-1.1 1.6c-10.3 14.4-6.9 34.4 7.4 44.6s34.4 6.9 44.6-7.4l1.1-1.6C433.5 260.8 427 182 377 132c-56.5-56.5-148-56.5-204.5 0L60.2 244.3z" />
</svg>
</template>

<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({ name: "Link" });
</script>
12 changes: 12 additions & 0 deletions src/attack_flow_builder/src/components/Icons/NewFlow.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"
viewBox="0 0 384 512"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path
d="M0 64C0 28.7 28.7 0 64 0H224V128c0 17.7 14.3 32 32 32H384V448c0 35.3-28.7 64-64 64H64c-35.3 0-64-28.7-64-64V64zm384 64H256V0L384 128z" />
</svg>
</template>

<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({ name: "NewFlow" });
</script>
12 changes: 12 additions & 0 deletions src/attack_flow_builder/src/components/Icons/OpenFlow.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<template>
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20"
viewBox="0 0 576 512"><!--! Font Awesome Free 6.4.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license (Commercial License) Copyright 2023 Fonticons, Inc. -->
<path
d="M384 480h48c11.4 0 21.9-6 27.6-15.9l112-192c5.8-9.9 5.8-22.1 .1-32.1S555.5 224 544 224H144c-11.4 0-21.9 6-27.6 15.9L48 357.1V96c0-8.8 7.2-16 16-16H181.5c4.2 0 8.3 1.7 11.3 4.7l26.5 26.5c21 21 49.5 32.8 79.2 32.8H416c8.8 0 16 7.2 16 16v32h48V160c0-35.3-28.7-64-64-64H298.5c-17 0-33.3-6.7-45.3-18.7L226.7 50.7c-12-12-28.3-18.7-45.3-18.7H64C28.7 32 0 60.7 0 96V416c0 35.3 28.7 64 64 64H87.7 384z" />
</svg>
</template>

<script lang="ts">
import { defineComponent } from "vue";
export default defineComponent({ name: "OpenFlow" });
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { AppCommand } from "../AppCommand";
import { ApplicationStore } from "@/store/StoreTypes";

export class HideSplashMenu extends AppCommand {

/**
* Display the find dialog
* @param context
* The application context.
*/
constructor(context: ApplicationStore) {
super(context);
}

/**
* Executes the command.
*/
public execute(): void {
this._context.splashIsVisible = false;
}

}
Loading

0 comments on commit ecb1175

Please sign in to comment.