From e88244bc9dd92d891ecee8db532c480a716c25e4 Mon Sep 17 00:00:00 2001
From: Matei Adriel <rafaeladriel11@gmail.com>
Date: Mon, 22 Jul 2019 19:58:26 +0300
Subject: [PATCH] logic gate button

---
 src/modules/activation/types/Context.ts       |   1 +
 src/modules/core/components/App.tsx           |   2 +
 src/modules/core/components/Language.scss     |   5 +
 src/modules/core/components/Language.tsx      |  31 +++++
 src/modules/core/components/LogicGates.tsx    |  89 +++------------
 src/modules/core/components/Sidebar.tsx       |  10 +-
 src/modules/core/styles/colors.scss           |   1 +
 src/modules/core/styles/mixins/glow.scss      |  16 +++
 src/modules/core/styles/reset.scss            |   5 +
 .../components/CreateOption.scss              |   4 +-
 src/modules/internalisation/constants.ts      |  14 ++-
 .../internalisation/helpers/nextLanguage.ts   |  17 +++
 .../internalisation/helpers/randomItem.ts     |   7 ++
 .../internalisation/stores/currentLanguage.ts |   6 +-
 .../subjects/currentLanguageSubject.ts        |   4 +-
 .../internalisation/translations/english.ts   |   3 +-
 .../translations/nederlands.ts                |  33 ++++++
 .../internalisation/translations/romanian.ts  |   7 +-
 .../types/TranslationInterface.ts             |   5 +-
 .../types/supportedLanguages.ts               |   2 +-
 .../components/LogicGateModal.scss            |  62 ++++++++++
 .../logic-gates/components/LogicGateModal.tsx |  90 +++++++++++++++
 src/modules/logic-gates/helpers/getAllIcs.ts  |  26 +++++
 .../logic-gates/subjects/LogicGateList.ts     |  37 ++++++
 src/modules/saving/constants.ts               | 107 ++++++++++++++++--
 src/modules/simulation/classes/Gate.ts        |  26 ++++-
 src/modules/simulation/constants.ts           |   8 +-
 src/modules/simulation/types/GateTemplate.ts  |   4 +-
 .../classes/SimulationRenderer.ts             |   3 +
 29 files changed, 518 insertions(+), 107 deletions(-)
 create mode 100644 src/modules/core/components/Language.scss
 create mode 100644 src/modules/core/components/Language.tsx
 create mode 100644 src/modules/core/styles/mixins/glow.scss
 create mode 100644 src/modules/internalisation/helpers/nextLanguage.ts
 create mode 100644 src/modules/internalisation/helpers/randomItem.ts
 create mode 100644 src/modules/internalisation/translations/nederlands.ts
 create mode 100644 src/modules/logic-gates/components/LogicGateModal.scss
 create mode 100644 src/modules/logic-gates/components/LogicGateModal.tsx
 create mode 100644 src/modules/logic-gates/helpers/getAllIcs.ts
 create mode 100644 src/modules/logic-gates/subjects/LogicGateList.ts

diff --git a/src/modules/activation/types/Context.ts b/src/modules/activation/types/Context.ts
index f418d27..973583c 100644
--- a/src/modules/activation/types/Context.ts
+++ b/src/modules/activation/types/Context.ts
@@ -2,4 +2,5 @@ export interface Context {
     memory: Record<string, unknown>
     get: (index: number) => boolean
     set: (index: number, state: boolean) => void
+    color: (color: string) => void
 }
diff --git a/src/modules/core/components/App.tsx b/src/modules/core/components/App.tsx
index 91c3fe8..31707f6 100644
--- a/src/modules/core/components/App.tsx
+++ b/src/modules/core/components/App.tsx
@@ -12,6 +12,7 @@ import Theme from '@material-ui/styles/ThemeProvider'
 import Sidebar from './Sidebar'
 import CreateSimulation from '../../create-simulation/components/CreateSimulation'
 import Input from '../../input/components/Input'
+import LogicGateModal from '../../logic-gates/components/LogicGateModal'
 
 const App = () => {
     return (
@@ -22,6 +23,7 @@ const App = () => {
                 <Sidebar />
                 <CreateSimulation />
                 <Input />
+                <LogicGateModal />
             </Theme>
             <CssBaseline />
             <ToastContainer
diff --git a/src/modules/core/components/Language.scss b/src/modules/core/components/Language.scss
new file mode 100644
index 0000000..4dab942
--- /dev/null
+++ b/src/modules/core/components/Language.scss
@@ -0,0 +1,5 @@
+@import '../styles/colors.scss';
+
+#language-button {
+    border: 1px solid white;
+}
diff --git a/src/modules/core/components/Language.tsx b/src/modules/core/components/Language.tsx
new file mode 100644
index 0000000..34b6f56
--- /dev/null
+++ b/src/modules/core/components/Language.tsx
@@ -0,0 +1,31 @@
+import React from 'react'
+import Icon from '@material-ui/core/Icon'
+import List from '@material-ui/core/List'
+import ListItem from '@material-ui/core/ListItem'
+import ListItemIcon from '@material-ui/core/ListItemIcon'
+import ListItemIconText from '@material-ui/core/ListItemText'
+import { useTranslation } from '../../internalisation/helpers/useLanguage'
+import { nextLanguage } from '../../internalisation/helpers/nextLanguage'
+import './Language.scss'
+
+/**
+ * The language component from the sidebar
+ */
+const Language = () => {
+    const translation = useTranslation()
+
+    return (
+        <List>
+            <ListItem button onClick={nextLanguage} id="language-button">
+                <ListItemIcon>
+                    <Icon>language</Icon>
+                </ListItemIcon>
+                <ListItemIconText>
+                    {translation.sidebar.language}: {translation.language}
+                </ListItemIconText>
+            </ListItem>
+        </List>
+    )
+}
+
+export default Language
diff --git a/src/modules/core/components/LogicGates.tsx b/src/modules/core/components/LogicGates.tsx
index dbf5f0f..7af8256 100644
--- a/src/modules/core/components/LogicGates.tsx
+++ b/src/modules/core/components/LogicGates.tsx
@@ -1,32 +1,11 @@
-import React, { useState } from 'react'
+import React from 'react'
 import ListItem from '@material-ui/core/ListItem'
 import ListItemIcon from '@material-ui/core/ListItemIcon'
 import ListItemText from '@material-ui/core/ListItemText'
-import Menu from '@material-ui/core/Menu'
-import MenuItem from '@material-ui/core/MenuItem'
 import Icon from '@material-ui/core/Icon'
-import Typography from '@material-ui/core/Typography'
-import { BehaviorSubject } from 'rxjs'
-import { useObservable } from 'rxjs-hooks'
-import { switchTo } from '../../saving/helpers/switchTo'
-import { SimulationError } from '../../errors/classes/SimulationError'
-import { templateStore } from '../../saving/stores/templateStore'
 import { useTranslation } from '../../internalisation/helpers/useLanguage'
-
-/**
- * Subject to make React update the dom when new gates are stored
- */
-const allGatesSubject = new BehaviorSubject<string[]>([])
-
-/**
- * Triggers a dom update by pushing a new value to the
- * useObservable hook inside the React component.
- *
- * It also has the side effect of sorting the template names.
- */
-const updateTemplateList = () => {
-    allGatesSubject.next(templateStore.ls().sort())
-}
+import { open } from '../../logic-gates/components/LogicGateModal'
+import { updateLogicGateList } from '../../logic-gates/subjects/LogicGateList'
 
 /**
  * Component wich contains the sidebar 'Open simulation' button
@@ -34,59 +13,21 @@ const updateTemplateList = () => {
  * @throws SimulationError if the data about a simulation cant be found in localStorage
  */
 const LogicGates = () => {
-    const [anchorEl, setAnchorEl] = useState<null | HTMLElement>(null)
-    const simulations = useObservable(() => allGatesSubject, [])
-
     const translation = useTranslation()
 
-    const handleClose = () => {
-        setAnchorEl(null)
-    }
-
     return (
-        <>
-            <ListItem
-                button
-                onClick={event => {
-                    updateTemplateList()
-                    setAnchorEl(event.currentTarget)
-                }}
-            >
-                <ListItemIcon>
-                    <Icon>memory</Icon>
-                </ListItemIcon>
-                <ListItemText>{translation.sidebar.logicGates}</ListItemText>
-            </ListItem>
-
-            <Menu
-                keepMounted
-                anchorEl={anchorEl}
-                open={Boolean(anchorEl)}
-                onClose={handleClose}
-            >
-                {simulations.map((simulationName, index) => {
-                    const simulationData = templateStore.get(simulationName)
-
-                    if (!simulationData) {
-                        throw new SimulationError(
-                            `Cannot get data for simulation ${simulationName}`
-                        )
-                    }
-
-                    return (
-                        <MenuItem
-                            key={index}
-                            onClick={() => {
-                                switchTo(simulationName)
-                                handleClose()
-                            }}
-                        >
-                            <Typography>{simulationName}</Typography>
-                        </MenuItem>
-                    )
-                })}
-            </Menu>
-        </>
+        <ListItem
+            button
+            onClick={() => {
+                updateLogicGateList()
+                open.next(true)
+            }}
+        >
+            <ListItemIcon>
+                <Icon>memory</Icon>
+            </ListItemIcon>
+            <ListItemText>{translation.sidebar.logicGates}</ListItemText>
+        </ListItem>
     )
 }
 
diff --git a/src/modules/core/components/Sidebar.tsx b/src/modules/core/components/Sidebar.tsx
index 32c356c..0880958 100644
--- a/src/modules/core/components/Sidebar.tsx
+++ b/src/modules/core/components/Sidebar.tsx
@@ -5,6 +5,7 @@ import OpenSimulation from './OpenSimulation'
 import CreateSimulationButton from './CreateSimulationButton'
 import LogicGates from './LogicGates'
 import { makeStyles, createStyles } from '@material-ui/core/styles'
+import Language from './Language'
 /**
  * The width of the sidebar
  */
@@ -39,6 +40,11 @@ const useStyles = makeStyles(
             padding: '4px',
             width: sidebarWidth,
             zIndex: sidebarZIndex
+        },
+
+        // This is the class for the main button list
+        list: {
+            flexGrow: 1
         }
     })
 )
@@ -60,11 +66,13 @@ const Sidebar = () => {
                     paper: classes.drawerPaper
                 }}
             >
-                <List component="nav">
+                <List component="nav" className={classes.list}>
                     <CreateSimulationButton />
                     <OpenSimulation />
                     <LogicGates />
                 </List>
+
+                <Language />
             </Drawer>
         </div>
     )
diff --git a/src/modules/core/styles/colors.scss b/src/modules/core/styles/colors.scss
index cf790d9..aef8a79 100644
--- a/src/modules/core/styles/colors.scss
+++ b/src/modules/core/styles/colors.scss
@@ -1,2 +1,3 @@
 $modal-bg-color: rgba(0, 0, 0, 0.7);
 $primary: #673ab7;
+$grey: #444444;
diff --git a/src/modules/core/styles/mixins/glow.scss b/src/modules/core/styles/mixins/glow.scss
new file mode 100644
index 0000000..17d7c04
--- /dev/null
+++ b/src/modules/core/styles/mixins/glow.scss
@@ -0,0 +1,16 @@
+@keyframes glow {
+    from {
+        text-shadow: 0 0 10px #fff, 0 0 20px #fff, 0 0 30px #e60073,
+            0 0 40px #e60073, 0 0 50px #e60073, 0 0 60px #e60073,
+            0 0 70px #e60073;
+    }
+    to {
+        text-shadow: 0 0 20px #fff, 0 0 30px #ff4da6, 0 0 40px #ff4da6,
+            0 0 50px #ff4da6, 0 0 60px #ff4da6, 0 0 70px #ff4da6,
+            0 0 80px #ff4da6;
+    }
+}
+
+@mixin glow {
+    animation: glow 1s ease-in-out infinite alternate;
+}
diff --git a/src/modules/core/styles/reset.scss b/src/modules/core/styles/reset.scss
index d38bf8c..942b44d 100644
--- a/src/modules/core/styles/reset.scss
+++ b/src/modules/core/styles/reset.scss
@@ -444,3 +444,8 @@ textarea {
     color: #000;
     padding: 0.2em 0;
 }
+
+a {
+    color: white;
+    text-decoration: none;
+}
diff --git a/src/modules/create-simulation/components/CreateOption.scss b/src/modules/create-simulation/components/CreateOption.scss
index b962742..062b207 100644
--- a/src/modules/create-simulation/components/CreateOption.scss
+++ b/src/modules/create-simulation/components/CreateOption.scss
@@ -1,3 +1,5 @@
+@import '../../core/styles/colors.scss';
+
 #create-options {
     width: 100%;
     display: flex;
@@ -7,7 +9,7 @@
 .create-option {
     @include flex();
 
-    background-color: #444444;
+    background-color: $grey;
     height: 17em;
     width: 17em;
     border-radius: 1em;
diff --git a/src/modules/internalisation/constants.ts b/src/modules/internalisation/constants.ts
index c7f743a..42e19b6 100644
--- a/src/modules/internalisation/constants.ts
+++ b/src/modules/internalisation/constants.ts
@@ -1,12 +1,20 @@
-import { supportedLanguages } from './types/supportedLanguages'
+import { supportedLanguage } from './types/supportedLanguages'
 import { Translation } from './types/TranslationInterface'
 import { EnglishTranslation } from './translations/english'
 import { RomanianTranslation } from './translations/romanian'
+import { DutchTranslation } from './translations/nederlands'
 
 /**
  * Object with all translations
  */
-export const translations: Record<supportedLanguages, Translation> = {
+export const translations: Record<supportedLanguage, Translation> = {
     english: EnglishTranslation,
-    ['română']: RomanianTranslation
+    ['română']: RomanianTranslation,
+    dutch: DutchTranslation
 }
+
+export const allSupportedLanguages: supportedLanguage[] = [
+    'english',
+    'română',
+    'dutch'
+]
diff --git a/src/modules/internalisation/helpers/nextLanguage.ts b/src/modules/internalisation/helpers/nextLanguage.ts
new file mode 100644
index 0000000..5802902
--- /dev/null
+++ b/src/modules/internalisation/helpers/nextLanguage.ts
@@ -0,0 +1,17 @@
+import {
+    CurrentLanguage,
+    CurrentLanguageLocalStore
+} from '../stores/currentLanguage'
+import { allSupportedLanguages } from '../constants'
+
+/**
+ * Changes the language to the next one avabile
+ */
+export const nextLanguage = () => {
+    const current = CurrentLanguage.get()
+    const index = allSupportedLanguages.indexOf(current)
+
+    CurrentLanguage.set(
+        allSupportedLanguages[(index + 1) % allSupportedLanguages.length]
+    )
+}
diff --git a/src/modules/internalisation/helpers/randomItem.ts b/src/modules/internalisation/helpers/randomItem.ts
new file mode 100644
index 0000000..7b78d03
--- /dev/null
+++ b/src/modules/internalisation/helpers/randomItem.ts
@@ -0,0 +1,7 @@
+/**
+ * Returns a random element of the aray
+ *
+ * @param arr - the array to choose from
+ */
+export const randomItem = <T>(arr: T[]): T =>
+    arr[Math.floor(Math.random() * arr.length)]
diff --git a/src/modules/internalisation/stores/currentLanguage.ts b/src/modules/internalisation/stores/currentLanguage.ts
index c2cada7..1e4b8d5 100644
--- a/src/modules/internalisation/stores/currentLanguage.ts
+++ b/src/modules/internalisation/stores/currentLanguage.ts
@@ -1,4 +1,4 @@
-import { supportedLanguages } from '../types/supportedLanguages'
+import { supportedLanguage } from '../types/supportedLanguages'
 import { LocalStore } from '../../storage/classes/LocalStore'
 import currentLanguageSubject from './../subjects/currentLanguageSubject'
 import { SimulationError } from '../../errors/classes/SimulationError'
@@ -7,7 +7,7 @@ import { translations } from '../constants'
 /**
  * Local store containing the current selected language
  */
-export const CurrentLanguageLocalStore = new LocalStore<supportedLanguages>(
+export const CurrentLanguageLocalStore = new LocalStore<supportedLanguage>(
     'language'
 )
 
@@ -22,7 +22,7 @@ currentLanguageSubject.next(CurrentLanguageLocalStore.get() || 'english')
  * The preffered interface for interacting with CurrentLanguageLocalStore
  */
 const CurrentLanguage = {
-    set(name: supportedLanguages) {
+    set(name: supportedLanguage) {
         CurrentLanguageLocalStore.set(name)
         currentLanguageSubject.next(name)
     },
diff --git a/src/modules/internalisation/subjects/currentLanguageSubject.ts b/src/modules/internalisation/subjects/currentLanguageSubject.ts
index 219fde6..3dba6e6 100644
--- a/src/modules/internalisation/subjects/currentLanguageSubject.ts
+++ b/src/modules/internalisation/subjects/currentLanguageSubject.ts
@@ -1,7 +1,7 @@
 import { BehaviorSubject } from 'rxjs'
-import { supportedLanguages } from '../types/supportedLanguages'
+import { supportedLanguage } from '../types/supportedLanguages'
 
 /**
  * Subject with the current language
  */
-export default new BehaviorSubject<supportedLanguages>('english')
+export default new BehaviorSubject<supportedLanguage>('english')
diff --git a/src/modules/internalisation/translations/english.ts b/src/modules/internalisation/translations/english.ts
index 1c784fd..e6881ed 100644
--- a/src/modules/internalisation/translations/english.ts
+++ b/src/modules/internalisation/translations/english.ts
@@ -8,7 +8,8 @@ export const EnglishTranslation: Translation = {
     sidebar: {
         createSimulation: 'Create simulation',
         logicGates: 'Logic gates',
-        openSimulation: 'Open simulations'
+        openSimulation: 'Open simulations',
+        language: 'Language'
     },
     createSimulation: {
         mode: {
diff --git a/src/modules/internalisation/translations/nederlands.ts b/src/modules/internalisation/translations/nederlands.ts
new file mode 100644
index 0000000..67e862e
--- /dev/null
+++ b/src/modules/internalisation/translations/nederlands.ts
@@ -0,0 +1,33 @@
+import { Translation } from '../types/TranslationInterface'
+
+/**
+ * The dutch translation
+ */
+export const DutchTranslation: Translation = {
+    language: 'dutch',
+    sidebar: {
+        createSimulation: 'Maak simulatie',
+        logicGates: 'Logische poorten',
+        openSimulation: 'Open simulatie',
+        language: 'Taal'
+    },
+    createSimulation: {
+        mode: {
+            question: 'Wat voor simulatie wil je maken?',
+            options: {
+                // ic: 'Geïntegreerde schakeling',
+                ic: 'IC',
+                project: 'Project'
+            }
+        },
+        name: {
+            question: 'Hoe wil je je simulatie noemen?'
+        }
+    },
+    messages: {
+        createdSimulation: name => `Simulatie '${name}' succesvol gecreerd`,
+        switchedToSimulation: name =>
+            `Succesvol veranderd naar simulatie '${name}'`,
+        savedSimulation: name => `Simulatie succesvol opgeslagen '${name}'`
+    }
+}
diff --git a/src/modules/internalisation/translations/romanian.ts b/src/modules/internalisation/translations/romanian.ts
index 333eca4..b2fa222 100644
--- a/src/modules/internalisation/translations/romanian.ts
+++ b/src/modules/internalisation/translations/romanian.ts
@@ -8,11 +8,12 @@ export const RomanianTranslation: Translation = {
     sidebar: {
         createSimulation: 'Creează o simulație',
         openSimulation: 'Deschide o simulație',
-        logicGates: 'Porți logice'
+        logicGates: 'Porți logice',
+        language: 'Limba'
     },
     createSimulation: {
         mode: {
-            question: 'Ce fel de simulație vrei să creiezi?',
+            question: 'Ce fel de simulație vrei să creezi?',
             options: {
                 ic: 'Circuit integrat',
                 project: 'Proiect'
@@ -24,7 +25,7 @@ export const RomanianTranslation: Translation = {
     },
     messages: {
         createdSimulation: name =>
-            `Simulația '${name}' a fost creiată cu succes`,
+            `Simulația '${name}' a fost creeată cu succes`,
         switchedToSimulation: name =>
             `Simulația '${name}' a fost deschisă cu succes`,
         savedSimulation: name => `Simulația '${name}' a fost salvată cu succes`
diff --git a/src/modules/internalisation/types/TranslationInterface.ts b/src/modules/internalisation/types/TranslationInterface.ts
index 4995db1..c84de13 100644
--- a/src/modules/internalisation/types/TranslationInterface.ts
+++ b/src/modules/internalisation/types/TranslationInterface.ts
@@ -1,4 +1,4 @@
-import { supportedLanguages } from './supportedLanguages'
+import { supportedLanguage } from './supportedLanguages'
 import { simulationMode } from '../../saving/types/SimulationSave'
 
 export type SentenceFactory<T extends string[]> = (...names: T) => string
@@ -8,11 +8,12 @@ export type NameSentence = SentenceFactory<[string]>
  * The interface all translations need to follow
  */
 export interface Translation {
-    language: supportedLanguages
+    language: supportedLanguage
     sidebar: {
         createSimulation: string
         openSimulation: string
         logicGates: string
+        language: string
     }
     createSimulation: {
         mode: {
diff --git a/src/modules/internalisation/types/supportedLanguages.ts b/src/modules/internalisation/types/supportedLanguages.ts
index fadf838..2e348c5 100644
--- a/src/modules/internalisation/types/supportedLanguages.ts
+++ b/src/modules/internalisation/types/supportedLanguages.ts
@@ -1,4 +1,4 @@
 /**
  * Type containing the names of all supported languages
  */
-export type supportedLanguages = 'română' | 'english'
+export type supportedLanguage = 'română' | 'english' | 'dutch'
diff --git a/src/modules/logic-gates/components/LogicGateModal.scss b/src/modules/logic-gates/components/LogicGateModal.scss
new file mode 100644
index 0000000..7ef63cd
--- /dev/null
+++ b/src/modules/logic-gates/components/LogicGateModal.scss
@@ -0,0 +1,62 @@
+@import '../../core/styles/mixins/flex.scss';
+@import '../../core/styles/mixins/visibility.scss';
+@import '../../modals/styles/mixins/modal-container.scss';
+@import '../../modals/styles/mixins/modal-title.scss';
+@import '../../core/styles/colors.scss';
+@import '../../core/styles/mixins/glow.scss';
+
+// Opacities
+$normal-opacity: 0.6;
+$hover-opacity: 0.9;
+
+// Colors
+$item-color: $grey;
+
+#logic-gate-modal-container {
+    @include modal-container();
+
+    justify-content: center;
+}
+
+.visible#logic-gate-modal-container {
+    @include visible();
+}
+
+#logic-gate-modal-container > .logic-gate-item {
+    @include flex();
+
+    justify-content: left;
+    flex-direction: row;
+
+    width: 80%;
+    height: 4em;
+
+    border: 3px solid white;
+    margin: 0.5em;
+
+    background-color: rgba($item-color, $normal-opacity);
+}
+
+#logic-gate-modal-container > .logic-gate-item:hover {
+    background-color: rgba($item-color, $hover-opacity);
+}
+
+#logic-gate-modal-container > .logic-gate-item > * {
+    font-size: 3em;
+}
+
+#logic-gate-modal-container > .logic-gate-item > .logic-gate-item-type {
+    margin: 0.5em;
+}
+
+#logic-gate-modal-container > .logic-gate-item > *:last-child {
+    margin-right: 0.5em;
+}
+
+.lgi-icon:hover:not(.logic-gate-item-type) {
+    border: 1px solid white;
+}
+
+#logic-gate-modal-container > .logic-gate-item > .logic-gate-item-name {
+    flex-grow: 1;
+}
diff --git a/src/modules/logic-gates/components/LogicGateModal.tsx b/src/modules/logic-gates/components/LogicGateModal.tsx
new file mode 100644
index 0000000..6bad1fc
--- /dev/null
+++ b/src/modules/logic-gates/components/LogicGateModal.tsx
@@ -0,0 +1,90 @@
+import './LogicGateModal.scss'
+import React from 'react'
+import { BehaviorSubject } from 'rxjs'
+import { useObservable } from 'rxjs-hooks'
+import { LogicGateList } from '../subjects/LogicGateList'
+import Icon from '@material-ui/core/Icon'
+import Typography from '@material-ui/core/Typography'
+import { addGate } from '../../simulation/helpers/addGate'
+import { rendererSubject } from '../../core/subjects/rendererSubject'
+import { SimulationError } from '../../errors/classes/SimulationError'
+import { templateStore } from '../../saving/stores/templateStore'
+import { randomItem } from '../../internalisation/helpers/randomItem'
+
+/**
+ * Subject containing the open state of the modal
+ */
+export const open = new BehaviorSubject(false)
+
+/**
+ * helper to close the modal
+ */
+export const handleClose = () => {
+    open.next(false)
+}
+
+/**
+ * The component containing the info / actions about all logic gates
+ */
+const LogicGateModal = () => {
+    const openSnapshot = useObservable(() => open, false)
+    const gates = useObservable(() => LogicGateList, [])
+
+    return (
+        <div
+            className={openSnapshot ? 'visible' : ''}
+            id="logic-gate-modal-container"
+            onClick={handleClose}
+        >
+            {gates.map((gate, index) => {
+                const renderer = rendererSubject.value
+
+                if (!renderer) {
+                    throw new SimulationError(`Renderer not found`)
+                }
+
+                const template =
+                    gate.source === 'base' ? templateStore.get(gate.name) : ''
+
+                if (gate.source === 'base' && !template) {
+                    throw new SimulationError(
+                        `Template ${gate.name} cannot be found`
+                    )
+                }
+
+                return (
+                    <div
+                        key={index}
+                        className="logic-gate-item"
+                        onClick={e => {
+                            addGate(renderer.simulation, gate.name)
+                        }}
+                    >
+                        <Icon className="lgi-icon logic-gate-item-type">
+                            {gate.source === 'base' ? 'sd_storage' : 'memory'}
+                        </Icon>
+                        <Typography className="logic-gate-item-name">
+                            {gate.name}
+                        </Typography>
+                        {template && template.info && template.info.length && (
+                            <a
+                                target="_blank"
+                                className="logic-gate-item-info"
+                                href={randomItem(template.info)}
+                            >
+                                <Icon className="lgi-icon">info</Icon>
+                            </a>
+                        )}
+                        {gate.source === 'ic' && (
+                            <Icon className="lgi-icon logic-gate-item-delete">
+                                delete
+                            </Icon>
+                        )}
+                    </div>
+                )
+            })}
+        </div>
+    )
+}
+
+export default LogicGateModal
diff --git a/src/modules/logic-gates/helpers/getAllIcs.ts b/src/modules/logic-gates/helpers/getAllIcs.ts
new file mode 100644
index 0000000..bb1a74a
--- /dev/null
+++ b/src/modules/logic-gates/helpers/getAllIcs.ts
@@ -0,0 +1,26 @@
+import { saveStore } from '../../saving/stores/saveStore'
+import { SimulationError } from '../../errors/classes/SimulationError'
+
+/**
+ * Helper to get the names of all integrated circuits
+ *
+ * @throws SimulationError if a save cannot be found in localsStorage
+ */
+export const getAllics = () => {
+    const saves = saveStore.ls()
+    const result: string[] = []
+
+    for (const save of saves) {
+        const saveState = saveStore.get(save)
+
+        if (saveState) {
+            if (saveState.simulation.mode === 'ic') {
+                result.push(saveState.simulation.name)
+            }
+        } else {
+            throw new SimulationError(`Cannot find save ${save}`)
+        }
+    }
+
+    return result
+}
diff --git a/src/modules/logic-gates/subjects/LogicGateList.ts b/src/modules/logic-gates/subjects/LogicGateList.ts
new file mode 100644
index 0000000..d141c1f
--- /dev/null
+++ b/src/modules/logic-gates/subjects/LogicGateList.ts
@@ -0,0 +1,37 @@
+import { BehaviorSubject } from 'rxjs'
+import { templateStore } from '../../saving/stores/templateStore'
+import { getAllics } from '../helpers/getAllIcs'
+
+/**
+ * The interface for the items in the list
+ */
+export interface LogicGateNameWrapper {
+    source: 'base' | 'ic'
+    name: string
+}
+
+/**
+ * Subject containing a list with the names of all logic gate templates
+ */
+export const LogicGateList = new BehaviorSubject<LogicGateNameWrapper[]>([])
+
+/**
+ * Helper method to update the list of logic gate templates.
+ */
+export const updateLogicGateList = () => {
+    const ics = getAllics().map(
+        (name): LogicGateNameWrapper => ({
+            source: 'ic',
+            name
+        })
+    )
+
+    const templates = templateStore.ls().map(
+        (name): LogicGateNameWrapper => ({
+            source: 'base',
+            name
+        })
+    )
+
+    LogicGateList.next([...ics, ...templates])
+}
diff --git a/src/modules/saving/constants.ts b/src/modules/saving/constants.ts
index 78ce138..ab956a8 100644
--- a/src/modules/saving/constants.ts
+++ b/src/modules/saving/constants.ts
@@ -3,25 +3,118 @@ import { RendererState } from './types/SimulationSave'
 
 export const defaultSimulationName = 'default'
 export const baseTemplates: DeepPartial<GateTemplate>[] = [
+    {
+        metadata: {
+            name: 'and'
+        },
+        material: {
+            value: 'green'
+        },
+        code: {
+            activation: `context.set(0, context.get(0) && context.get(1))`
+        },
+        pins: {
+            inputs: {
+                count: 2
+            }
+        },
+        info: ['https://en.wikipedia.org/wiki/AND_gate']
+    },
+    {
+        metadata: {
+            name: 'or'
+        },
+        material: {
+            value: 'yellow'
+        },
+        code: {
+            activation: `context.set(0, context.get(0) || context.get(1))`
+        },
+        pins: {
+            inputs: {
+                count: 2
+            }
+        },
+        info: ['https://en.wikipedia.org/wiki/OR_gate']
+    },
+    {
+        metadata: {
+            name: 'xor'
+        },
+        material: {
+            value: 'white'
+        },
+        code: {
+            activation: `
+            const a = context.get(0)
+            const b = context.get(1)
+            const c = (a || b) && (!a || !b)
+            
+            context.set(0, c)`
+        },
+        info: ['https://en.wikipedia.org/wiki/XOR_gate'],
+        pins: {
+            inputs: {
+                count: 2
+            }
+        }
+    },
     {
         metadata: {
             name: 'not'
         },
         material: {
-            value: 'red',
-            type: 'color'
+            value: 'red'
         },
         code: {
             activation: `context.set(0, !context.get(0))`
         },
+        info: ['https://en.wikipedia.org/wiki/Inverter_(logic_gate)']
+    },
+    {
+        metadata: {
+            name: 'button'
+        },
+        material: {
+            value: 'red'
+        },
+        code: {
+            onClick: `
+                const old = context.memory.state
+                const state = !old
+
+                context.set(0, state)    
+                context.color(old ? 'red' : '#550000')
+
+                context.memory.state = state
+            `
+        },
         pins: {
             inputs: {
-                count: 1,
-                variable: false
-            },
+                count: 0
+            }
+        },
+        info: ['https://en.wikipedia.org/wiki/Push-button']
+    },
+    {
+        metadata: {
+            name: 'light bulb'
+        },
+        shape: {
+            radius: 50
+        },
+        material: {
+            value: 'white'
+        },
+        code: {
+            activation: `
+                context.color(context.get(0) ? 'yellow' : 'white')
+            `
+        },
+        info: ['https://en.wikipedia.org/wiki/Incandescent_light_bulb'],
+        pins: {
             outputs: {
-                count: 1,
-                variable: false
+                count: 0
             }
         }
     }
diff --git a/src/modules/simulation/classes/Gate.ts b/src/modules/simulation/classes/Gate.ts
index 4049238..5bbbd11 100644
--- a/src/modules/simulation/classes/Gate.ts
+++ b/src/modules/simulation/classes/Gate.ts
@@ -22,8 +22,11 @@ export interface PinWrapper {
     value: Pin
 }
 
+export type GateFunction = null | ((ctx: Context) => void)
+
 export interface GateFunctions {
-    activation: null | ((ctx: Context) => void)
+    activation: GateFunction
+    onClick: GateFunction
 }
 
 export class Gate {
@@ -37,7 +40,8 @@ export class Gate {
     public template: GateTemplate
 
     private functions: GateFunctions = {
-        activation: null
+        activation: null,
+        onClick: null
     }
 
     private subscriptions: Subscription[] = []
@@ -53,6 +57,11 @@ export class Gate {
             'context'
         )
 
+        this.functions.onClick = toFunction(
+            this.template.code.onClick,
+            'context'
+        )
+
         this._pins.inputs = Gate.generatePins(
             this.template.pins.inputs,
             1,
@@ -77,6 +86,12 @@ export class Gate {
         }
     }
 
+    public onClick() {
+        if (this.functions.onClick) {
+            this.functions.onClick(this.getContext())
+        }
+    }
+
     public dispose() {
         for (const pin of this.pins) {
             pin.value.dispose()
@@ -104,7 +119,12 @@ export class Gate {
             set: (index: number, state: boolean = false) => {
                 return this._pins.outputs[index].state.next(state)
             },
-            memory: this.memory
+            memory: this.memory,
+            color: (color: string) => {
+                if (this.template.material.type === 'color') {
+                    this.template.material.value = color
+                }
+            }
         }
     }
 
diff --git a/src/modules/simulation/constants.ts b/src/modules/simulation/constants.ts
index 3b7d92b..bb2c815 100644
--- a/src/modules/simulation/constants.ts
+++ b/src/modules/simulation/constants.ts
@@ -10,7 +10,7 @@ export const DefaultGateTemplate: GateTemplate = {
     },
     pins: {
         inputs: {
-            count: 2,
+            count: 1,
             variable: false
         },
         outputs: {
@@ -25,8 +25,7 @@ export const DefaultGateTemplate: GateTemplate = {
     },
     code: {
         activation: 'context.set(0,true)',
-        start: '',
-        stop: ''
+        onClick: ''
     },
     simulation: {
         debounce: {
@@ -36,5 +35,6 @@ export const DefaultGateTemplate: GateTemplate = {
         throttle: {
             enabled: false
         }
-    }
+    },
+    info: []
 }
diff --git a/src/modules/simulation/types/GateTemplate.ts b/src/modules/simulation/types/GateTemplate.ts
index ed2b0cc..17af852 100644
--- a/src/modules/simulation/types/GateTemplate.ts
+++ b/src/modules/simulation/types/GateTemplate.ts
@@ -39,12 +39,12 @@ export interface GateTemplate {
         name: string
     }
     code: {
-        start: string
         activation: string
-        stop: string
+        onClick: string
     }
     simulation: {
         throttle: TimePipe
         debounce: TimePipe
     }
+    info: string[]
 }
diff --git a/src/modules/simulationRenderer/classes/SimulationRenderer.ts b/src/modules/simulationRenderer/classes/SimulationRenderer.ts
index 0acadab..b4d99f4 100644
--- a/src/modules/simulationRenderer/classes/SimulationRenderer.ts
+++ b/src/modules/simulationRenderer/classes/SimulationRenderer.ts
@@ -77,6 +77,9 @@ export class SimulationRenderer {
                 const { transform, id, pins } = gates[index]
 
                 if (pointInSquare(worldPosition, transform)) {
+                    // run function
+                    gates[index].onClick()
+
                     this.mouseManager.clear(worldPosition[0])
 
                     this.mouseState |= 1