story for ShipItem

docks66-json-schema
Yanick Champoux 2023-03-20 11:59:50 -04:00
parent 0c0351cbb3
commit b18017d753
37 changed files with 209 additions and 147 deletions

6
histoire.config.js Normal file
View File

@ -0,0 +1,6 @@
import { defineConfig } from "histoire";
import { HstSvelte } from "@histoire/plugin-svelte";
export default defineConfig({
plugins: [HstSvelte()],
});

View File

@ -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",

View 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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View File

@ -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>

View 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>

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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";

View File

@ -11,3 +11,8 @@ export const initialState = {
drive: drive.initialState,
ftl: ftl.initialState,
};
export const actions = {
...drive.actions,
...ftl.actions,
};

View File

@ -22,3 +22,5 @@ const shipReqs = createSlice({
},
},
});
export const { actions, reducer } = shipReqs;

View File

@ -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),
};
};

View File

@ -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;