diff --git a/package-lock.json b/package-lock.json
index 1389a32..202c062 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -2194,6 +2194,11 @@
}
}
},
+ "classnames": {
+ "version": "2.2.6",
+ "resolved": "https://registry.npmjs.org/classnames/-/classnames-2.2.6.tgz",
+ "integrity": "sha512-JR/iSQOSt+LQIWwrwEzJ9uk0xfN3mTVYMwt1Ir5mUcSN6pU+V4zQFFaJsclJbPuAUQH+yfWef6tm7l1quW3C8Q=="
+ },
"clean-css": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/clean-css/-/clean-css-4.2.1.tgz",
@@ -3058,6 +3063,14 @@
"utila": "~0.4"
}
},
+ "dom-helpers": {
+ "version": "3.4.0",
+ "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-3.4.0.tgz",
+ "integrity": "sha512-LnuPJ+dwqKDIyotW1VzmOZ5TONUN7CwkCR5hrgawTUbkBGYdeoNLZo6nNfGkCrjtE1nXXaj7iMMpDa8/d9WoIA==",
+ "requires": {
+ "@babel/runtime": "^7.1.2"
+ }
+ },
"dom-serializer": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.1.1.tgz",
@@ -7715,6 +7728,11 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.8.6.tgz",
"integrity": "sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA=="
},
+ "react-lifecycles-compat": {
+ "version": "3.0.4",
+ "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
+ "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
+ },
"react-router": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/react-router/-/react-router-5.0.1.tgz",
@@ -7761,6 +7779,28 @@
"tiny-warning": "^1.0.0"
}
},
+ "react-toastify": {
+ "version": "5.3.2",
+ "resolved": "https://registry.npmjs.org/react-toastify/-/react-toastify-5.3.2.tgz",
+ "integrity": "sha512-YHTTey7JWqXVkkBIeJ34PAvQELmGfLEGCx9bu68aIZYd+kRU2u9k/nG3AydgbX/uevIb4QNpeeE98DjkooMs5w==",
+ "requires": {
+ "@babel/runtime": "^7.4.2",
+ "classnames": "^2.2.6",
+ "prop-types": "^15.7.2",
+ "react-transition-group": "^2.6.1"
+ }
+ },
+ "react-transition-group": {
+ "version": "2.9.0",
+ "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-2.9.0.tgz",
+ "integrity": "sha512-+HzNTCHpeQyl4MJ/bdE0u6XRMe9+XG/+aL4mCxVN4DnPBQ0/5bfHWPDuOZUzYdMj94daZaZdCCc1Dzt9R/xSSg==",
+ "requires": {
+ "dom-helpers": "^3.4.0",
+ "loose-envify": "^1.4.0",
+ "prop-types": "^15.6.2",
+ "react-lifecycles-compat": "^3.0.4"
+ }
+ },
"read-pkg": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz",
diff --git a/package.json b/package.json
index caedf60..95c66c0 100644
--- a/package.json
+++ b/package.json
@@ -44,6 +44,7 @@
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-router-dom": "^5.0.1",
+ "react-toastify": "^5.3.2",
"rxjs": "^6.5.2",
"rxjs-hooks": "^0.5.1"
}
diff --git a/src/main.tsx b/src/main.tsx
index e76f69d..812be3d 100644
--- a/src/main.tsx
+++ b/src/main.tsx
@@ -2,5 +2,8 @@ import React from 'react'
import App from './modules/core/components/App'
import { render } from 'react-dom'
+import { handleErrors } from './modules/errors/helpers/handlErrors'
render(, document.getElementById('app'))
+
+handleErrors()
diff --git a/src/modules/core/components/App.scss b/src/modules/core/components/App.scss
index 0dbfb1a..e8f96f4 100644
--- a/src/modules/core/components/App.scss
+++ b/src/modules/core/components/App.scss
@@ -8,4 +8,5 @@ body {
canvas {
background-color: #222222;
+ z-index: -1;
}
diff --git a/src/modules/core/components/App.tsx b/src/modules/core/components/App.tsx
index 75544d2..301cb28 100644
--- a/src/modules/core/components/App.tsx
+++ b/src/modules/core/components/App.tsx
@@ -1,10 +1,26 @@
import React from 'react'
import '../styles/reset'
import './App.scss'
+import 'react-toastify/dist/ReactToastify.css'
import Canvas from './Canvas'
+import { ToastContainer } from 'react-toastify'
const App = () => {
- return
+ return (
+ <>
+
+
+ >
+ )
}
export default App
diff --git a/src/modules/errors/classes/SimulationError.ts b/src/modules/errors/classes/SimulationError.ts
new file mode 100644
index 0000000..1285904
--- /dev/null
+++ b/src/modules/errors/classes/SimulationError.ts
@@ -0,0 +1,9 @@
+export class SimulationError extends Error {
+ public constructor(public mesagge: string = '') {
+ super()
+ }
+
+ public toString() {
+ return `SimulationError: ${this.mesagge}`
+ }
+}
diff --git a/src/modules/errors/helpers/handlErrors.ts b/src/modules/errors/helpers/handlErrors.ts
new file mode 100644
index 0000000..8d3f0f5
--- /dev/null
+++ b/src/modules/errors/helpers/handlErrors.ts
@@ -0,0 +1,14 @@
+import { toast } from 'react-toastify'
+import { createToastArguments } from '../../toasts/helpers/createToastArguments'
+
+export const handleErrors = () => {
+ window.onerror = (a, b, c, d, error) => {
+ if (error) {
+ const args = createToastArguments(error.toString())
+
+ toast.error(...args)
+ }
+
+ console.log(a)
+ }
+}
diff --git a/src/modules/saving/helpers/getState.ts b/src/modules/saving/helpers/getState.ts
index 83550a5..582f2d0 100644
--- a/src/modules/saving/helpers/getState.ts
+++ b/src/modules/saving/helpers/getState.ts
@@ -1,15 +1,18 @@
import { SimulationRenderer } from '../../simulationRenderer/classes/SimulationRenderer'
-import { Gate } from '../../simulation/classes/Gate'
+import { Gate, PinWrapper } from '../../simulation/classes/Gate'
import {
GateState,
TransformState,
RendererState,
CameraState,
- SimulationState
+ SimulationState,
+ WireState,
+ WireLimit
} from '../types/SimulationSave'
import { Transform } from '../../../common/math/classes/Transform'
import { Camera } from '../../simulationRenderer/classes/Camera'
import { Simulation } from '../../simulation/classes/Simulation'
+import { Wire } from '../../simulation/classes/Wire'
export const getTransformState = (transform: Transform): TransformState => {
return {
@@ -25,9 +28,26 @@ export const getCameraState = (camera: Camera): CameraState => {
}
}
+export const getWireLimit = (pin: PinWrapper): WireLimit => {
+ return {
+ id: pin.value.gate.id,
+ index: pin.index
+ }
+}
+
+export const getWireState = (wire: Wire): WireState => {
+ return {
+ from: getWireLimit(wire.start),
+ to: getWireLimit(wire.end),
+ id: wire.id
+ }
+}
+
export const getSimulationState = (simulation: Simulation): SimulationState => {
return {
- gates: Array.from(simulation.gates).map(getGateState)
+ gates: Array.from(simulation.gates).map(getGateState),
+ wires: simulation.wires.map(getWireState),
+ mode: simulation.mode
}
}
diff --git a/src/modules/saving/types/SimulationSave.ts b/src/modules/saving/types/SimulationSave.ts
index cfa9ef9..2d88e5f 100644
--- a/src/modules/saving/types/SimulationSave.ts
+++ b/src/modules/saving/types/SimulationSave.ts
@@ -1,5 +1,7 @@
import { vector2 } from '../../../common/math/classes/Transform'
+export type simulationMode = 'ic' | 'project'
+
export interface TransformState {
position: vector2
scale: vector2
@@ -16,8 +18,22 @@ export interface CameraState {
transform: TransformState
}
+export interface WireLimit {
+ id: number
+ index: number
+}
+
+export interface WireState {
+ from: WireLimit
+ to: WireLimit
+ id: number
+}
+
export interface SimulationState {
gates: GateState[]
+ wires: WireState[]
+
+ mode: simulationMode
}
export interface RendererState {
diff --git a/src/modules/simulation/classes/Gate.ts b/src/modules/simulation/classes/Gate.ts
index 3c0663d..f23d4dc 100644
--- a/src/modules/simulation/classes/Gate.ts
+++ b/src/modules/simulation/classes/Gate.ts
@@ -3,6 +3,7 @@ import { Pin } from './Pin'
import merge from 'deepmerge'
import { GateTemplate, PinCount } from '../types/GateTemplate'
import { DefaultGateTemplate } from '../constants'
+import { idStore } from '../stores/idStore'
export interface GatePins {
inputs: Pin[]
@@ -16,22 +17,30 @@ export interface PinWrapper {
}
export class Gate {
- public static lastId = 0
-
public transform = new Transform()
- public id = Gate.lastId++
public _pins: GatePins = {
inputs: [],
outputs: []
}
+ public id: number
public template: GateTemplate
- public constructor(template: DeepPartial = {}) {
+ public constructor(template: DeepPartial = {}, id?: number) {
this.template = merge(DefaultGateTemplate, template) as GateTemplate
- this._pins.inputs = Gate.generatePins(this.template.pins.inputs, 1)
- this._pins.outputs = Gate.generatePins(this.template.pins.outputs, 2)
+ this._pins.inputs = Gate.generatePins(
+ this.template.pins.inputs,
+ 1,
+ this
+ )
+ this._pins.outputs = Gate.generatePins(
+ this.template.pins.outputs,
+ 2,
+ this
+ )
+
+ this.id = id !== undefined ? id : idStore.generate()
}
private wrapPins(pins: Pin[]) {
@@ -58,7 +67,9 @@ export class Gate {
return result
}
- private static generatePins(options: PinCount, type: number) {
- return [...Array(options.count)].fill(true).map(() => new Pin(type))
+ private static generatePins(options: PinCount, type: number, gate: Gate) {
+ return [...Array(options.count)]
+ .fill(true)
+ .map(() => new Pin(type, gate))
}
}
diff --git a/src/modules/simulation/classes/Pin.ts b/src/modules/simulation/classes/Pin.ts
index e097b78..7d61728 100644
--- a/src/modules/simulation/classes/Pin.ts
+++ b/src/modules/simulation/classes/Pin.ts
@@ -1,5 +1,6 @@
import { SubscriptionData } from '../types/SubscriptionData'
import { BehaviorSubject } from 'rxjs'
+import { Gate } from './Gate'
/* Types:
@@ -9,24 +10,25 @@ Second bit = output
*/
export class Pin {
public state = new BehaviorSubject(false)
- public connectedTo = new Set()
+ public pairs = new Set()
- private pairs = new Set()
private subscriptions: SubscriptionData[] = []
- public constructor(public type = 0b01) {}
+ public constructor(public type = 0b01, public gate: Gate) {}
- public addPair(pin: Pin) {
+ public addPair(pin: Pin, subscribe = false) {
this.pairs.add(pin)
- const rawSubscription = pin.state.subscribe(state => {
- this.state.next(state)
- })
+ if (subscribe) {
+ const rawSubscription = pin.state.subscribe(state => {
+ this.state.next(state)
+ })
- this.subscriptions.push({
- data: pin,
- subscription: rawSubscription
- })
+ this.subscriptions.push({
+ data: pin,
+ subscription: rawSubscription
+ })
+ }
}
public removePair(pin: Pin) {
diff --git a/src/modules/simulation/classes/Simulation.ts b/src/modules/simulation/classes/Simulation.ts
index 8da8de7..543d6c1 100644
--- a/src/modules/simulation/classes/Simulation.ts
+++ b/src/modules/simulation/classes/Simulation.ts
@@ -1,9 +1,14 @@
import { Gate } from './Gate'
import { GateStorage } from './GateStorage'
import { LruCacheNode } from '@eix-js/utils'
+import { Wire } from './Wire'
+import { simulationMode } from '../../saving/types/SimulationSave'
export class Simulation {
public gates = new GateStorage()
+ public wires: Wire[] = []
+
+ public constructor(public mode: simulationMode = 'project') {}
public push(...gates: Gate[]) {
for (const gate of gates) {
diff --git a/src/modules/simulation/classes/Wire.ts b/src/modules/simulation/classes/Wire.ts
new file mode 100644
index 0000000..5a008fb
--- /dev/null
+++ b/src/modules/simulation/classes/Wire.ts
@@ -0,0 +1,27 @@
+import { idStore } from '../stores/idStore'
+import { PinWrapper } from './Gate'
+import { SimulationError } from '../../errors/classes/SimulationError'
+
+export class Wire {
+ public id: number
+
+ public constructor(
+ public start: PinWrapper,
+ public end: PinWrapper,
+ id?: number
+ ) {
+ if (end.value.pairs.size !== 0) {
+ throw new SimulationError('An input pin can only have 1 pair')
+ }
+
+ end.value.addPair(start.value, true)
+ start.value.addPair(end.value)
+
+ this.id = id !== undefined ? id : idStore.generate()
+ }
+
+ public dispose() {
+ this.end.value.removePair(this.start.value)
+ this.start.value.removePair(this.end.value)
+ }
+}
diff --git a/src/modules/simulation/stores/idStore.ts b/src/modules/simulation/stores/idStore.ts
new file mode 100644
index 0000000..354db10
--- /dev/null
+++ b/src/modules/simulation/stores/idStore.ts
@@ -0,0 +1,12 @@
+import { LocalStore } from '../../storage/classes/LocalStore'
+
+const store = new LocalStore('id')
+
+export const idStore = {
+ generate() {
+ const current = store.get()
+ store.set(current + 1)
+
+ return current + 1
+ }
+}
diff --git a/src/modules/simulationRenderer/classes/SimulationRenderer.ts b/src/modules/simulationRenderer/classes/SimulationRenderer.ts
index 00a80aa..088a83d 100644
--- a/src/modules/simulationRenderer/classes/SimulationRenderer.ts
+++ b/src/modules/simulationRenderer/classes/SimulationRenderer.ts
@@ -14,6 +14,7 @@ import { getPinPosition } from '../helpers/pinPosition'
import { pointInCircle } from '../../../common/math/helpers/pointInCircle'
import { SelectedPins } from '../types/SelectedPins'
import { getRendererState } from '../../saving/helpers/getState'
+import { Wire } from '../../simulation/classes/Wire'
export class SimulationRenderer {
public mouseDownOutput = new Subject()
@@ -104,11 +105,21 @@ export class SimulationRenderer {
}
}
- if (this.selectedPins.start && this.selectedPins.end) {
- console.log('Connecting!')
- console.log(getRendererState(this))
+ if (
+ this.selectedPins.start &&
+ this.selectedPins.end &&
+ this.selectedPins.end.wrapper.value.pairs.size === 0
+ ) {
+ this.simulation.wires.push(
+ new Wire(
+ this.selectedPins.start.wrapper,
+ this.selectedPins.end.wrapper
+ )
+ )
this.selectedPins.start = null
this.selectedPins.end = null
+
+ console.log(getRendererState(this))
}
}
}
diff --git a/src/modules/simulationRenderer/constants.ts b/src/modules/simulationRenderer/constants.ts
index 7098fa4..cc1359b 100644
--- a/src/modules/simulationRenderer/constants.ts
+++ b/src/modules/simulationRenderer/constants.ts
@@ -8,9 +8,14 @@ export const defaultSimulationRendererOptions: SimulationRendererOptions = {
connectionLength: 30,
pinRadius: 10,
pinStrokeColor: '#888888',
- pinStrokeWidth: 3
+ pinStrokeWidth: 3,
+ pinFill: {
+ open: 'rgb(255,216,20)',
+ closed: 'rgb(90,90,90)'
+ }
},
wires: {
- temporaryWireColor: `rgba(128,128,128,0.5)`
+ temporaryWireColor: `rgba(128,128,128,0.5)`,
+ curvePointOffset: 100
}
}
diff --git a/src/modules/simulationRenderer/helpers/pinFill.ts b/src/modules/simulationRenderer/helpers/pinFill.ts
index 0134344..52d5677 100644
--- a/src/modules/simulationRenderer/helpers/pinFill.ts
+++ b/src/modules/simulationRenderer/helpers/pinFill.ts
@@ -1,13 +1,14 @@
import { Pin } from '../../simulation/classes/Pin'
+import { SimulationRenderer } from '../classes/SimulationRenderer'
-export const pinFill = (pin: Pin) => {
+export const pinFill = (renderer: SimulationRenderer, pin: Pin) => {
let color = 'rgba(0,0,0,0)'
- if (pin.connectedTo.size) {
- if (pin.state) {
- color = 'yellow'
+ if (pin.pairs.size) {
+ if (pin.state.value) {
+ color = renderer.options.gates.pinFill.open
} else {
- color = 'grey'
+ color = renderer.options.gates.pinFill.closed
}
}
diff --git a/src/modules/simulationRenderer/helpers/renderPins.ts b/src/modules/simulationRenderer/helpers/renderPins.ts
index 8bff60b..c872069 100644
--- a/src/modules/simulationRenderer/helpers/renderPins.ts
+++ b/src/modules/simulationRenderer/helpers/renderPins.ts
@@ -23,7 +23,7 @@ export const renderPins = (
ctx.lineWidth = pinStrokeWidth
for (const pin of gate.pins) {
- ctx.fillStyle = pinFill(pin.value)
+ ctx.fillStyle = pinFill(renderer, pin.value)
// render little connection
const start = calculatePinStart(
diff --git a/src/modules/simulationRenderer/helpers/renderSimulation.ts b/src/modules/simulationRenderer/helpers/renderSimulation.ts
index 3d74aef..f73c092 100644
--- a/src/modules/simulationRenderer/helpers/renderSimulation.ts
+++ b/src/modules/simulationRenderer/helpers/renderSimulation.ts
@@ -3,6 +3,7 @@ import { invert } from '../../vector2/helpers/basic'
import { renderGate } from './renderGate'
import { clearCanvas } from '../../../common/canvas/helpers/clearCanvas'
import { renderClickedPins } from './renderClickedPins'
+import { renderWires } from './renderWires'
export const renderSimulation = (
ctx: CanvasRenderingContext2D,
@@ -12,7 +13,10 @@ export const renderSimulation = (
ctx.translate(...renderer.camera.transform.position)
- // render gates
+ for (const wire of renderer.simulation.wires) {
+ renderWires(ctx, renderer, wire)
+ }
+
for (const gate of renderer.simulation.gates) {
renderGate(ctx, renderer, gate)
}
diff --git a/src/modules/simulationRenderer/helpers/renderWires.ts b/src/modules/simulationRenderer/helpers/renderWires.ts
new file mode 100644
index 0000000..d1f91a5
--- /dev/null
+++ b/src/modules/simulationRenderer/helpers/renderWires.ts
@@ -0,0 +1,94 @@
+import { SimulationRenderer } from '../classes/SimulationRenderer'
+import { pinFill } from './pinFill'
+import { getPinPosition } from './pinPosition'
+import { Wire } from '../../simulation/classes/Wire'
+import { wireRadius } from './wireRadius'
+import { clamp } from '../../simulation/helpers/clamp'
+
+export const renderWires = (
+ ctx: CanvasRenderingContext2D,
+ renderer: SimulationRenderer,
+ wire: Wire
+) => {
+ const { start, end } = wire
+ const startPosition = getPinPosition(
+ renderer,
+ start.value.gate.transform,
+ start
+ )
+ const endPosition = getPinPosition(renderer, end.value.gate.transform, end)
+ const length = renderer.options.wires.curvePointOffset
+ const centerY = (startPosition[1] + endPosition[1]) / 2
+ const controlPostions = [startPosition[0] + length, endPosition[0] - length]
+
+ ctx.strokeStyle = pinFill(renderer, start.value)
+ ctx.lineWidth = wireRadius(renderer)
+ ctx.lineCap = 'round'
+
+ ctx.beginPath()
+ ctx.moveTo(...startPosition)
+
+ if (startPosition[0] < endPosition[0]) {
+ ctx.bezierCurveTo(
+ controlPostions[0],
+ startPosition[1],
+ controlPostions[1],
+ endPosition[1],
+ ...endPosition
+ )
+ } else {
+ const { abs, PI } = Math
+
+ const baseFactor = startPosition[1] < endPosition[1] ? 1 : -1
+ const factors = [baseFactor, baseFactor]
+
+ const radiuses = [...Array(2)].fill(
+ abs((centerY - startPosition[1]) / 2)
+ )
+
+ const limit = 70
+ if (radiuses[0] < limit) {
+ factors[0] *= -1
+ radiuses[0] = limit
+ radiuses[1] = abs(
+ (startPosition[1] +
+ factors[0] * 2 * radiuses[0] -
+ endPosition[1]) /
+ 2
+ )
+ // radiuses[0] =
+ }
+
+ const centerPosition = [
+ startPosition[1] + factors[0] * radiuses[0],
+ endPosition[1] - factors[1] * radiuses[1]
+ ]
+
+ ctx.arc(
+ controlPostions[0],
+ centerPosition[0],
+ radiuses[0],
+ (-factors[0] * PI) / 2,
+ (factors[0] * PI) / 2,
+ factors[0] !== 1
+ )
+
+ ctx.lineTo(
+ controlPostions[1],
+ endPosition[1] - factors[1] * 2 * radiuses[1]
+ )
+
+ ctx.arc(
+ controlPostions[1],
+ centerPosition[1],
+ radiuses[1],
+ (-factors[1] * PI) / 2,
+ (factors[1] * PI) / 2,
+ factors[1] === 1
+ )
+
+ ctx.lineTo(...endPosition)
+ }
+
+ ctx.stroke()
+}
diff --git a/src/modules/simulationRenderer/helpers/wireRadius.ts b/src/modules/simulationRenderer/helpers/wireRadius.ts
new file mode 100644
index 0000000..cbaa565
--- /dev/null
+++ b/src/modules/simulationRenderer/helpers/wireRadius.ts
@@ -0,0 +1,9 @@
+import { SimulationRenderer } from '../classes/SimulationRenderer'
+
+export const wireRadius = (renderer: SimulationRenderer) => {
+ return (
+ 2 *
+ (renderer.options.gates.pinRadius -
+ renderer.options.gates.pinStrokeWidth)
+ )
+}
diff --git a/src/modules/simulationRenderer/types/SimulationRendererOptions.ts b/src/modules/simulationRenderer/types/SimulationRendererOptions.ts
index e013453..1df581a 100644
--- a/src/modules/simulationRenderer/types/SimulationRendererOptions.ts
+++ b/src/modules/simulationRenderer/types/SimulationRendererOptions.ts
@@ -7,8 +7,13 @@ export interface SimulationRendererOptions {
pinRadius: number
pinStrokeColor: string
pinStrokeWidth: number
+ pinFill: {
+ open: string
+ closed: string
+ }
}
wires: {
temporaryWireColor: string
+ curvePointOffset: number
}
}
diff --git a/src/modules/storage/classes/LocalStore.ts b/src/modules/storage/classes/LocalStore.ts
new file mode 100644
index 0000000..942ad60
--- /dev/null
+++ b/src/modules/storage/classes/LocalStore.ts
@@ -0,0 +1,56 @@
+import { CacheInstancesByKey } from '@eix-js/utils'
+
+@CacheInstancesByKey(Infinity)
+export class LocalStore {
+ public constructor(public name: string) {
+ if (!localStorage.getItem(name)) {
+ localStorage.setItem(name, '{}')
+ }
+ }
+
+ public getAll(): Record {
+ const raw = localStorage.getItem(this.name)
+
+ if (!raw)
+ throw new Error(
+ `An error occured when accesing ${
+ this.name
+ } in the local storage!`
+ )
+ else {
+ return JSON.parse(raw)
+ }
+ }
+
+ public ls(): string[] {
+ return Object.keys(this.getAll())
+ }
+
+ public *[Symbol.iterator](): Iterable {
+ for (const item of this.ls()) {
+ return this.get(item)
+ }
+ }
+
+ public get(key = 'index') {
+ return this.getAll()[key]
+ }
+
+ public set(key: string | T = 'index', value?: T) {
+ if (typeof key !== 'string' || value === undefined) {
+ localStorage.setItem(
+ this.name,
+ JSON.stringify({
+ index: key
+ })
+ )
+ } else {
+ localStorage.setItem(
+ this.name,
+ JSON.stringify({
+ [key]: value
+ })
+ )
+ }
+ }
+}
diff --git a/src/modules/toasts/helpers/createToastArguments.ts b/src/modules/toasts/helpers/createToastArguments.ts
new file mode 100644
index 0000000..0b6592c
--- /dev/null
+++ b/src/modules/toasts/helpers/createToastArguments.ts
@@ -0,0 +1,15 @@
+import { ToastOptions } from 'react-toastify'
+
+export const createToastArguments = (
+ message: string
+): [string, ToastOptions] => [
+ message,
+ {
+ position: 'top-left',
+ autoClose: 5000,
+ hideProgressBar: false,
+ closeOnClick: true,
+ pauseOnHover: true,
+ draggable: true
+ }
+]
diff --git a/webpack.config.js b/webpack.config.js
index 970293f..33d3c29 100644
--- a/webpack.config.js
+++ b/webpack.config.js
@@ -17,18 +17,27 @@ const babelRule = {
use: 'babel-loader'
}
+const cssAndSass = [
+ isProduction
+ ? MiniCssExtractPlugin.loader
+ : {
+ loader: 'style-loader',
+ options: {
+ singleton: true
+ }
+ },
+ 'css-loader'
+]
+
+const cssRule = {
+ test: /\.css$/,
+ use: cssAndSass
+}
+
const sassRule = {
test: /\.scss$/,
use: [
- isProduction
- ? MiniCssExtractPlugin.loader
- : {
- loader: 'style-loader',
- options: {
- singleton: true
- }
- },
- { loader: 'css-loader' },
+ ...cssAndSass,
{
loader: 'sass-loader',
options: {
@@ -47,7 +56,7 @@ const baseConfig = {
publicPath: '/'
},
module: {
- rules: [babelRule, sassRule]
+ rules: [babelRule, sassRule, cssRule]
},
resolve: {
extensions: ['.js', '.ts', '.tsx', '.scss']