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