story for ShipItem
This commit is contained in:
parent
0c0351cbb3
commit
b18017d753
6
histoire.config.js
Normal file
6
histoire.config.js
Normal file
@ -0,0 +1,6 @@
|
||||
import { defineConfig } from "histoire";
|
||||
import { HstSvelte } from "@histoire/plugin-svelte";
|
||||
|
||||
export default defineConfig({
|
||||
plugins: [HstSvelte()],
|
||||
});
|
@ -11,6 +11,7 @@
|
||||
"format": "prettier --write ."
|
||||
},
|
||||
"devDependencies": {
|
||||
"@histoire/plugin-svelte": "^0.15.9",
|
||||
"@sveltejs/adapter-static": "^1.0.0-next.28",
|
||||
"@sveltejs/kit": "^1.10.0",
|
||||
"@sveltejs/vite-plugin-svelte": "^1.0.0-next.38",
|
||||
@ -28,11 +29,13 @@
|
||||
"vitest-svelte-kit": "^0.0.6"
|
||||
},
|
||||
"dependencies": {
|
||||
"@picocss/pico": "^1.5.7",
|
||||
"@reduxjs/toolkit": "^1.9.3",
|
||||
"@sveltejs/adapter-node": "^1.0.0-next.0",
|
||||
"@yanick/updeep-remeda": "^2.1.0",
|
||||
"chota": "^0.8.0",
|
||||
"effector": "^22.5.2",
|
||||
"histoire": "^0.15.9",
|
||||
"lodash": "^4.17.21",
|
||||
"redux": "^4.1.2",
|
||||
"remeda": "^1.1.0",
|
||||
|
9
src/lib/components/CostMass/index.story.svelte
Normal file
9
src/lib/components/CostMass/index.story.svelte
Normal file
@ -0,0 +1,9 @@
|
||||
<Hst.Story title="CostMass">
|
||||
<CostMass mass={12} cost={21} />
|
||||
</Hst.Story>
|
||||
|
||||
<script>
|
||||
export let Hst;
|
||||
|
||||
import CostMass from "./index.svelte";
|
||||
</script>
|
@ -1,8 +1,8 @@
|
||||
<div class="mass">{mass}<img src="{base}/mass.svg" alt="mass"/></div>
|
||||
<div class="mass">{mass}<img src="{base}/mass.svg" alt="mass" /></div>
|
||||
<div class="cost">{cost}</div>
|
||||
|
||||
<script>
|
||||
import { base } from '$app/paths';
|
||||
import { base } from "$app/paths";
|
||||
export let mass;
|
||||
export let cost;
|
||||
</script>
|
@ -27,8 +27,8 @@
|
||||
$: if (shipTypes.length > 0 && !shipTypes.includes(shipType))
|
||||
shipType = shipTypes[0];
|
||||
|
||||
$: ship.dispatch.setShipType(shipType);
|
||||
$: ship.dispatch.setShipClass(shipClass);
|
||||
$: ship.dispatch(ship.actions.setShipType(shipType));
|
||||
$: ship.dispatch(ship.actions.setShipClass(shipClass));
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -1,7 +1,14 @@
|
||||
<ShipItem {...reqs}>
|
||||
<div>
|
||||
<Field label="thrust rating">
|
||||
<input class="short" type="number" bind:value={rating} min="0" max="20" step="1" />
|
||||
<input
|
||||
class="short"
|
||||
type="number"
|
||||
bind:value={rating}
|
||||
min="0"
|
||||
max="20"
|
||||
step="1"
|
||||
/>
|
||||
</Field>
|
||||
|
||||
<label><input type="checkbox" bind:checked={advanced} /> advanced</label>
|
||||
@ -19,7 +26,7 @@
|
||||
|
||||
const ship = getContext("ship");
|
||||
|
||||
$: ship.dispatch.setDrive({ rating, advanced });
|
||||
$: ship.dispatch(ship.actions.setDrive({ rating, advanced }));
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
@ -1,87 +1,87 @@
|
||||
<div class="mass">
|
||||
<Field label="ship tonnage">
|
||||
<input class="short" bind:value={mass} type="number" min="10" max="300" />
|
||||
<img class="mass_symbol" src="{base}/mass.svg" alt="mass"/>
|
||||
<Field label="ship tonnage">
|
||||
<input class="short" bind:value={mass} type="number" min="10" max="300" />
|
||||
<img class="mass_symbol" src="{base}/mass.svg" alt="mass" />
|
||||
|
||||
<div class="note" class:warning={!withinBudget}>
|
||||
{#if withinBudget}
|
||||
mass unused: {massUnused}
|
||||
{:else}
|
||||
excessive mass: {-massUnused}
|
||||
{/if}
|
||||
</div>
|
||||
</Field>
|
||||
<div class="note" class:warning={!withinBudget}>
|
||||
{#if withinBudget}
|
||||
mass unused: {massUnused}
|
||||
{:else}
|
||||
excessive mass: {-massUnused}
|
||||
{/if}
|
||||
</div>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<div class="cost">
|
||||
<Field label="cost">
|
||||
<span class="cost">{cost}</span>
|
||||
</Field>
|
||||
<Field label="cost">
|
||||
<span class="cost">{cost}</span>
|
||||
</Field>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
import { base } from '$app/paths';
|
||||
import { getContext } from "svelte";
|
||||
import Field from "$lib/components/Field/index.svelte";
|
||||
import { base } from "$app/paths";
|
||||
import { getContext } from "svelte";
|
||||
import Field from "$lib/components/Field/index.svelte";
|
||||
|
||||
export let ship = getContext("ship");
|
||||
export let ship = getContext("ship");
|
||||
|
||||
export let mass = 10;
|
||||
export let cost = 10;
|
||||
export let usedMass = 5;
|
||||
export let mass = 10;
|
||||
export let cost = 10;
|
||||
export let usedMass = 5;
|
||||
|
||||
$: massUnused = mass - usedMass;
|
||||
$: withinBudget = massUnused >= 0;
|
||||
$: massUnused = mass - usedMass;
|
||||
$: withinBudget = massUnused >= 0;
|
||||
|
||||
$: ship.dispatch.setShipMass(mass);
|
||||
$: ship.dispatch(ship.actions.setShipMass(mass));
|
||||
|
||||
/* const change_tonnage = ({ target: { value } }) => */
|
||||
/* ship.dispatch(ship.actions.set_ship_mass(parseInt(value))); */
|
||||
/* const change_tonnage = ({ target: { value } }) => */
|
||||
/* ship.dispatch(ship.actions.set_ship_mass(parseInt(value))); */
|
||||
|
||||
/* let mass_unused; */
|
||||
/* $: mass_unused = $ship.general.mass - $ship.general.used_mass; */
|
||||
/* let mass_unused; */
|
||||
/* $: mass_unused = $ship.general.mass - $ship.general.used_mass; */
|
||||
|
||||
/* let within_budget = true; */
|
||||
/* let within_budget = true; */
|
||||
|
||||
/* $: within_budget = mass_unused >= 0; */
|
||||
/* $: within_budget = mass_unused >= 0; */
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.ship_cost {
|
||||
display: flex;
|
||||
grid-column: span 3;
|
||||
justify-content: space-around;
|
||||
}
|
||||
input {
|
||||
width: 5em;
|
||||
display: inline !important;
|
||||
}
|
||||
.mass_symbol {
|
||||
width: 0.75em;
|
||||
display: inline-block;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
.warning {
|
||||
color: red;
|
||||
}
|
||||
.note {
|
||||
font-size: smaller;
|
||||
}
|
||||
.ship_cost {
|
||||
display: flex;
|
||||
grid-column: span 3;
|
||||
justify-content: space-around;
|
||||
}
|
||||
input {
|
||||
width: 5em;
|
||||
display: inline !important;
|
||||
}
|
||||
.mass_symbol {
|
||||
width: 0.75em;
|
||||
display: inline-block;
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
.warning {
|
||||
color: red;
|
||||
}
|
||||
.note {
|
||||
font-size: smaller;
|
||||
}
|
||||
|
||||
.mass,
|
||||
div.cost {
|
||||
padding: 0px 2em;
|
||||
justify-self: right;
|
||||
}
|
||||
.mass,
|
||||
div.cost {
|
||||
padding: 0px 2em;
|
||||
justify-self: right;
|
||||
}
|
||||
|
||||
.mass {
|
||||
width: 15em;
|
||||
}
|
||||
div.cost {
|
||||
grid-column: 3;
|
||||
}
|
||||
span.cost:after {
|
||||
content: "\00A4";
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
.mass {
|
||||
width: 15em;
|
||||
}
|
||||
div.cost {
|
||||
grid-column: 3;
|
||||
}
|
||||
span.cost:after {
|
||||
content: "\00A4";
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
</style>
|
||||
|
14
src/lib/components/ShipItem.story.svelte
Normal file
14
src/lib/components/ShipItem.story.svelte
Normal file
@ -0,0 +1,14 @@
|
||||
<Hst.Story>
|
||||
<ShipItem {mass} {cost}>Thingy</ShipItem>
|
||||
<svelte:fragment slot="controls">
|
||||
<Hst.Number bind:value={mass} title="Mass" />
|
||||
<Hst.Number bind:value={cost} title="Cost" />
|
||||
</svelte:fragment>
|
||||
</Hst.Story>
|
||||
|
||||
<script>
|
||||
export let Hst;
|
||||
import ShipItem from "./ShipItem.svelte";
|
||||
let mass = 1;
|
||||
let cost = 3;
|
||||
</script>
|
@ -2,14 +2,15 @@
|
||||
<div><slot /></div>
|
||||
|
||||
<div class="reqs">
|
||||
<div class="mass" bind:this={mass_el}>{mass} <img src="{base}/mass.svg"
|
||||
alt="mass"/></div>
|
||||
<div class="mass" bind:this={mass_el}>
|
||||
{mass} <img src="{base}/mass.svg" alt="mass" />
|
||||
</div>
|
||||
<div class="cost" bind:this={cost_el}>{cost}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
import { base } from '$app/paths';
|
||||
import { base } from "$app/paths";
|
||||
import { tick } from "svelte";
|
||||
|
||||
export let mass;
|
@ -5,36 +5,38 @@ import u from "updeep";
|
||||
import { Reqs, reqs } from "./reqs.js";
|
||||
|
||||
const initialState = {
|
||||
rating: 1,
|
||||
advanced: false,
|
||||
reqs,
|
||||
rating: 1,
|
||||
advanced: false,
|
||||
reqs,
|
||||
};
|
||||
|
||||
const engine = createSlice({
|
||||
name: "engine",
|
||||
initialState,
|
||||
reducers: {
|
||||
setDriveRating(state, action: PayloadAction<number>) {
|
||||
state.rating = action.payload;
|
||||
},
|
||||
setDriveAdvanced(state, action: PayloadAction<boolean>) {
|
||||
state.advanced = action.payload;
|
||||
},
|
||||
setDriverReqs(state, action: PayloadAction<Reqs>) {
|
||||
state.reqs = action.payload;
|
||||
},
|
||||
name: "engine",
|
||||
initialState,
|
||||
reducers: {
|
||||
setDriveRating(state, action: PayloadAction<number>) {
|
||||
state.rating = action.payload;
|
||||
},
|
||||
setDriveAdvanced(state, action: PayloadAction<boolean>) {
|
||||
state.advanced = action.payload;
|
||||
},
|
||||
setDriverReqs(state, action: PayloadAction<Reqs>) {
|
||||
state.reqs = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export function calcDriveReqs(
|
||||
shipMass: number,
|
||||
rating: number,
|
||||
advanced = false
|
||||
) {
|
||||
const mass = Math.ceil(rating * 0.05 * shipMass);
|
||||
const cost = mass * (advanced ? 3 : 2);
|
||||
export const { actions, reducer } = engine;
|
||||
|
||||
return { mass, cost };
|
||||
export function calcDriveReqs(
|
||||
shipMass: number,
|
||||
rating: number,
|
||||
advanced = false
|
||||
) {
|
||||
const mass = Math.ceil(rating * 0.05 * shipMass);
|
||||
const cost = mass * (advanced ? 3 : 2);
|
||||
|
||||
return { mass, cost };
|
||||
}
|
||||
|
||||
export default engine;
|
||||
|
@ -1,28 +1,32 @@
|
||||
import { Updux } from "updux";
|
||||
import u from "updeep";
|
||||
import { createSlice, type PayloadAction } from "@reduxjs/toolkit";
|
||||
import * as carrier from "./carrier.js";
|
||||
|
||||
import carrier from "./carrier.js";
|
||||
const initialState = {
|
||||
shipType: "",
|
||||
shipClass: "",
|
||||
isCarrier: false,
|
||||
mass: 10,
|
||||
};
|
||||
|
||||
const dux = new Updux({
|
||||
actions: {
|
||||
setShipType: null,
|
||||
setShipClass: null,
|
||||
setCarrierBays: carrier.actions.setCarrierBays,
|
||||
const identification = createSlice({
|
||||
name: "identification",
|
||||
initialState,
|
||||
reducers: {
|
||||
setShipType(state, action: PayloadAction<string>) {
|
||||
state.shipType = action.payload;
|
||||
},
|
||||
setShipClass(state, action: PayloadAction<string>) {
|
||||
state.shipClass = action.payload;
|
||||
},
|
||||
},
|
||||
initial: {
|
||||
shipType: "",
|
||||
shipClass: "",
|
||||
isCarrier: false,
|
||||
mass: 10,
|
||||
extraReducers(builder) {
|
||||
builder.addCase(
|
||||
carrier.actions.setCarrierBays,
|
||||
(state, action: PayloadAction<number>) => {
|
||||
state.isCarrier = action.payload > 0;
|
||||
}
|
||||
);
|
||||
},
|
||||
});
|
||||
|
||||
dux.setMutation("setShipType", (shipType) => u({ shipType }));
|
||||
dux.setMutation("setShipClass", (shipClass) => u({ shipClass }));
|
||||
dux.setMutation("setCarrierBays", (bays) =>
|
||||
u({
|
||||
isCarrier: bays > 0,
|
||||
})
|
||||
);
|
||||
|
||||
export default dux;
|
||||
export const { actions, reducer } = identification;
|
||||
|
@ -5,6 +5,9 @@ import * as propulsion from "./propulsion";
|
||||
import * as structure from "./structure";
|
||||
import * as weaponry from "./weaponry/index.js";
|
||||
import * as carrier from "./carrier";
|
||||
import * as identification from "./identification.js";
|
||||
import * as shipReqs from "./shipReqs";
|
||||
import * as engine from "./engine";
|
||||
|
||||
const shipSlice = createSlice({
|
||||
name: "ship",
|
||||
@ -14,10 +17,12 @@ const shipSlice = createSlice({
|
||||
builder.addMatcher(
|
||||
() => true,
|
||||
combineReducers({
|
||||
identification: identification.reducer,
|
||||
propulsion: propulsion.reducer,
|
||||
structure: structure.reducer,
|
||||
weaponry: weaponry.reducer,
|
||||
carrier: carrier.reducer,
|
||||
engine: engine.reducer,
|
||||
})
|
||||
);
|
||||
},
|
||||
@ -29,11 +34,16 @@ export function createStore() {
|
||||
});
|
||||
}
|
||||
|
||||
export const actions = shipSlice.actions;
|
||||
export const actions = {
|
||||
...shipReqs.actions,
|
||||
...shipSlice.actions,
|
||||
...identification.actions,
|
||||
...engine.actions,
|
||||
...propulsion.actions,
|
||||
};
|
||||
|
||||
/**
|
||||
import * as propulsion from "./propulsion/index.js";
|
||||
import * as identification from "./identification.js";
|
||||
import { calculateDriveReqs } from "./propulsion/drive.js";
|
||||
import { ftlReqsReaction } from "./propulsion/ftl.js";
|
||||
import * as structure from "./structure/index.js";
|
||||
|
@ -11,3 +11,8 @@ export const initialState = {
|
||||
drive: drive.initialState,
|
||||
ftl: ftl.initialState,
|
||||
};
|
||||
|
||||
export const actions = {
|
||||
...drive.actions,
|
||||
...ftl.actions,
|
||||
};
|
||||
|
@ -22,3 +22,5 @@ const shipReqs = createSlice({
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { actions, reducer } = shipReqs;
|
||||
|
@ -2,39 +2,38 @@ import { browser, dev } from "$app/environment";
|
||||
import { readable, get, derived } from "svelte/store";
|
||||
import { compose, applyMiddleware } from "redux";
|
||||
|
||||
import shipDux from "../shipDux/index";
|
||||
import { createStore, actions } from "../shipDux/index";
|
||||
import { initial } from "lodash";
|
||||
|
||||
let composeEnhancers = compose;
|
||||
|
||||
if (dev && browser && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) {
|
||||
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;
|
||||
composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__;
|
||||
}
|
||||
|
||||
export default (initialState = undefined) => {
|
||||
if (browser) {
|
||||
const i = localStorage.getItem("ship");
|
||||
if (browser) {
|
||||
const i = localStorage.getItem("ship");
|
||||
|
||||
if (i) initialState = JSON.parse(localStorage.getItem("ship"));
|
||||
}
|
||||
if (i) initialState = JSON.parse(localStorage.getItem("ship"));
|
||||
}
|
||||
|
||||
const duxStore = shipDux.createStore(initialState, (mw) =>
|
||||
composeEnhancers(applyMiddleware(mw))
|
||||
);
|
||||
const duxStore = createStore();
|
||||
|
||||
let previous;
|
||||
const state = readable(duxStore.getState(), (set) => {
|
||||
duxStore.subscribe(() => {
|
||||
if (previous === duxStore.getState()) return;
|
||||
previous = duxStore.getState();
|
||||
set(previous);
|
||||
if (browser) localStorage.setItem("ship", JSON.stringify(previous));
|
||||
});
|
||||
let previous;
|
||||
const state = readable(duxStore.getState(), (set) => {
|
||||
duxStore.subscribe(() => {
|
||||
if (previous === duxStore.getState()) return;
|
||||
previous = duxStore.getState();
|
||||
set(previous);
|
||||
if (browser) localStorage.setItem("ship", JSON.stringify(previous));
|
||||
});
|
||||
});
|
||||
|
||||
return {
|
||||
dispatch: duxStore.dispatch,
|
||||
state,
|
||||
shipMass: derived(state, (state) => state.reqs.mass),
|
||||
};
|
||||
return {
|
||||
dispatch: duxStore.dispatch,
|
||||
state,
|
||||
actions,
|
||||
shipMass: derived(state, (state) => state.reqs.mass),
|
||||
};
|
||||
};
|
||||
|
@ -3,10 +3,10 @@ import { sveltekit } from "@sveltejs/kit/vite";
|
||||
|
||||
/** @type {import('vite').UserConfig} */
|
||||
const config = {
|
||||
plugins: [sveltekit()],
|
||||
|
||||
ssr: {},
|
||||
optimizeDeps: {},
|
||||
plugins: [sveltekit()],
|
||||
publicDir: "./static",
|
||||
ssr: {},
|
||||
optimizeDeps: {},
|
||||
};
|
||||
|
||||
export default config;
|
||||
|
Loading…
Reference in New Issue
Block a user