Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Save chriscorwin/294f1f98bb5aa538b865c37ab2c89116 to your computer and use it in GitHub Desktop.
Save chriscorwin/294f1f98bb5aa538b865c37ab2c89116 to your computer and use it in GitHub Desktop.
SvelteKit Coffee Ratio Calculator Tutorial 3

Create a detailed tutorial for a SvelteKit application named 'Coffee Calc', designed as an iOS app mockup that calculates the ideal coffee-to-water brewing ratio. The tutorial should cover:

Ratio Input: Users can input the desired coffee-to-water ratio. Mass Input: Users can input the mass of coffee and water. Unit Toggle: Users can switch between grams and ounces. Feedback Display: Displays the selected mass of coffee and water in the chosen unit. Reset Functionality: Users can reset each input individually or the entire form. Visual Feedback: Provides a visual cue when a value changes. Keyboard Functionality: The application responds to keyboard inputs. The tutorial should also include instructions on setting up the project, installing prerequisites like Homebrew, Git, Node.js, npm, and Visual Studio Code, and creating the application components. The tutorial should be in markdown format and user-friendly for coffee enthusiasts.

The generated prompt should include all the 'Features' details and an appropriate project name. The code generated from the prompt should match the provided example, with many additional comments for clarity, especially for developers new to Svelte and SvelteKit. Explain even the very obvious. The conversion math in the unit toggle and mass components, and the functionality of the reset buttons should be explained in detail. The output should include a detailed readme about the process that created the tutorial.

Ensure that the app has keyboard shortcuts, handling arrow down to decrement, arrow up to increment, and shift key modifier on them to change make the change value by 1 instead of 0.1.

Also, when clicking the up and down arrows on the input fields, the shift key should modify the step change from 0.1 to 1.

=== coffee calc tutorial ===

SvelteKit Coffee Brewing App

This is a single-page SvelteKit application designed as a full-functional mockup for an iOS app that calculates the ideal ratio of coffee to water for brewing. The application incorporates a range of features that give users the ability to precisely control their coffee brewing process.

Features

  1. Ratio Input: Allows users to input the desired ratio of coffee to water, expressed in parts. For instance, inputting 1 for coffee and 15 for water sets the ratio at 1:15.

  2. Mass Input: Users can input the mass of coffee and water they want to use, either manually or by adjusting the values using buttons.

  3. Unit Toggle: Gives users the flexibility to switch between using grams and ounces, with automatic conversion handled in the code.

  4. Feedback Display: Showcases the selected mass of coffee and water in the currently chosen unit, prominently on the screen.

  5. Reset Functionality: Lets users reset each input individually or reset the entire form simultaneously.

  6. Visual Feedback: Provides a visual cue to the user when a value changes by momentarily changing the color of the associated input field to yellow.

  7. Keyboard Functionality: The application also responds to keyboard inputs, allowing users to increase or decrease input values using the up and down arrow keys.

This application is a practical tool for coffee enthusiasts aiming to perfect their brewing process. It enables users to experiment with various ratios and quantities and observe the effects of these changes in real time.

Prerequisites

To set up this project, make sure you have the following installed:

  • Homebrew
  • Git
  • Node.js and npm
  • Visual Studio Code (VS Code)

Homebrew is a package manager for macOS that allows you to easily install software from the large collection of formulae in the Homebrew repository, or from other repositories.

To install the latest stable version of Homebrew on your Mac, you can use the following command in your terminal:

/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"

This command downloads a script from the Homebrew repository and runs it. The script will automatically download and install Homebrew.

Once the installation is complete, you can verify that Homebrew was successfully installed by running:

brew --version

This command will display the version of Homebrew that is currently installed on your system.

Please note that to use Homebrew, you need to have the Command Line Tools for Xcode installed on your Mac. If you don't have it installed, the Homebrew installer will prompt you to install it.

It's important to always keep your Homebrew installation up to date. You can update Homebrew by running:

brew update

This command will update Homebrew and all the formulae installed on your system to their latest versions.

Using the Homebrew Git instead of the "Apple Git" is important for web development because it allows you to have more control over the version of Git you are using and ensures that you have access to the latest features, bug fixes, and security updates. The "Apple Git" refers to the version of Git that comes pre-installed with macOS, but it may not always be the most up-to-date version.

Once you have Homebrew installed, you can use it to install Git with the following command:

brew install git

This command tells Homebrew to download and install the latest stable version of Git.

After the installation, you can verify the successful installation of Git and check its version by using the following command:

git --version

This command will display the version of Git that is currently installed on your system. If Git has been installed correctly, it should display the version number.

Important: unsure your system uses the Homebrew Git, and not the "Apple Git".

The which git command is showing you the location of the Git executable that the shell is currently configured to use. If it's showing the path to the Apple-supplied version of Git, it means that this version is found first in your system's PATH.

When you install Git via Homebrew, it gets installed in /usr/local/bin/git, while the Apple-supplied Git is in /usr/bin/git. The order of these directories in your PATH determines which version is found first.

To use the Homebrew version of Git, you need to ensure that /usr/local/bin comes before /usr/bin in your PATH. You can adjust the PATH in your shell profile file (like ~/.bash_profile, ~/.bashrc, or ~/.zshrc, depending on your shell).

Here's how you can do this:

  1. Open your shell profile file in a text editor. If you're using the default shell (Zsh), the file should be ~/.zshrc. If you're using Bash, it's likely ~/.bash_profile or ~/.bashrc.
nano ~/.zshrc

or

nano ~/.bash_profile
  1. Add the following line to the file:
export PATH="/usr/local/bin:$PATH"
  1. Save the file and exit the text editor (in nano, you can do this by pressing Ctrl+X, then Y, then Enter).

  2. Apply the changes by sourcing the profile file:

source ~/.zshrc

or

source ~/.bash_profile
  1. Now, when you run which git, it should show the Homebrew-installed version:
which git

This should return /usr/local/bin/git, which is the Homebrew version of Git.

Please note that changing the PATH in this way will affect all commands, not just Git. All binaries in /usr/local/bin will now take precedence over those in /usr/bin. If you want this change to affect only Git, consider using an alias instead.

With Homebrew and Git installed, you are ready to set up a SvelteKit project.

Before you create a new SvelteKit project, you need to install Node.js and npm (Node Package Manager). Node.js is a JavaScript runtime built on Chrome's V8 JavaScript engine, and npm is the package manager for Node.js.

  1. Install Node.js and npm

    You can install Node.js and npm via Homebrew using the following command:

    brew install node

    After the installation, you can verify that Node.js and npm were successfully installed by running:

    node --version
    npm --version

    These commands will display the version of Node.js and npm that is currently installed on your system.

  2. Create a new SvelteKit project

    You can create a new SvelteKit project using npm (Node Package Manager).

    First, navigate to the directory where you want to create your new SvelteKit project. Then, use the following commands:

    npm init svelte@next my-svelte-project

    Replace my-svelte-project with the name of your new project. This command will create a new directory with the name of your project and set up a new SvelteKit project inside it.

  3. Install the project dependencies

    Navigate into your new project directory and install the project dependencies:

    cd my-svelte-project
    npm install
  4. Start the development server

    You can start the development server by running:

    npm run dev

    Now, you can open your browser and navigate to http://localhost:5000 to see your new SvelteKit project.

Please remember to replace my-svelte-project with your desired project name.

Setup and Installation

Here's how you can set up your new SvelteKit project:

Step 1: Create a new SvelteKit project

Open your terminal, navigate to the directory where you want to create your new SvelteKit project, and run:

npm init svelte@next my-svelte-project

During the setup process, select the "Demo App" option. Replace my-svelte-project with the name of your new project. This command will create a new directory with the name of your project and set up a new SvelteKit project inside it.

Step 2: Install the project dependencies

Navigate into your new project directory and install the project dependencies:

cd my-svelte-project
npm install

Step 3: Create the application components

Our application will consist of four main components: InputComponent, ToggleComponent, VisualFeedbackComponent, and KeyboardComponent. Create these files in the src/routes directory and add the respective code provided below.

InputComponent

The InputComponent allows the user to input the coffee and water mass and calculates the coffee-to-water ratio based on these values.

This code is a Svelte component that provides an interactive user interface to adjust and visualize coffee-related measurements, such as coffee-to-water ratio and coffee and water mass. Here's a brief overview of what this code does:

  1. It imports the coffeeStore from a separate file to manage the state related to coffee measurements.

  2. It defines local variables ratio, mass, and units to store the coffee-to-water ratio, coffee and water masses, and units (grams or ounces), respectively.

  3. It sets a stepSize variable to determine the increment/decrement value when adjusting coffee and water mass.

  4. The component has several functions:

    • handleShiftKey: This function handles the Shift key event and updates the stepSize accordingly, allowing for more significant adjustments when Shift is pressed.
    • handleWaterRatioChange: This function handles changes in the coffee-to-water ratio input and updates the waterMass accordingly in the coffeeStore.
    • handleCoffeeMassChange: This function handles changes in the coffee mass input and updates the coffeeMass in the coffeeStore.
    • handleWaterMassChange: This function handles changes in the water mass input and updates the waterMass in the coffeeStore.
    • setCoffeeMassPreset and setWaterMassPreset: These functions set the coffee and water masses to preset values specified in waterPresets.
  5. The component subscribes to updates from the coffeeStore and keeps local variables ratio, mass, and units in sync with the store's state.

  6. The component renders several input components, sliders, and buttons for adjusting coffee and water mass, and for changing the coffee-to-water ratio. It displays the current measurements and provides options for selecting preset values.

  7. The component includes styling for the input components and sliders to improve the user interface.

In summary, this Svelte component offers a user-friendly interface to adjust and visualize coffee measurements using various input methods, including sliders and buttons with preset values. It leverages the coffeeStore to manage the state and make updates based on user interactions.

Create a new file InputComponent.svelte in the src/routes directory and add the following code:

<!-- Importing necessary modules from Svelte -->
<script>
  import { coffeeStore } from '../stores/coffeeStore.js'; // Importing the coffeeStore from a separate file

  // Defining local variables
  let ratio; // To store the coffee-to-water ratio
  let mass; // To store the mass of coffee grounds and water
  let units; // To store the units for the coffee-to-water ratio (grams or ounces)
  let stepSize = 0.1; // The step size for adjusting coffee and water mass

  // Function to handle the shift key event and update the stepSize accordingly
  function handleShiftKey(event) {
    stepSize = event.shiftKey ? 1 : 0.1; // If shift key is pressed, set stepSize to 1, else set it to 0.1
  }

  // Function to handle the change in coffee-to-water ratio input
  function handleWaterRatioChange(event) {
    let newWaterRatio = parseFloat(event.target.value); // Parse the new ratio value as a floating-point number
    coffeeStore.setWaterRatio(newWaterRatio); // Update the water ratio in the coffeeStore
    let newWaterMass = mass.coffee * newWaterRatio; // Calculate the new water mass based on the updated ratio
    coffeeStore.setWaterMass(newWaterMass); // Update the water mass in the coffeeStore
  }

  // Function to handle the change in coffee mass input
  function handleCoffeeMassChange(event) {
    coffeeStore.setCoffeeMass(parseFloat(event.target.value)); // Update the coffee mass in the coffeeStore
  }

  // Function to handle the change in water mass input
  function handleWaterMassChange(event) {
    coffeeStore.setWaterMass(parseFloat(event.target.value)); // Update the water mass in the coffeeStore
  }

  // Function to set the coffee mass to a preset value
  function setCoffeeMassPreset(presetValue) {
    coffeeStore.setCoffeeMass(presetValue); // Update the coffee mass in the coffeeStore with the preset value
  }

  // Function to set the water mass to a preset value
  function setWaterMassPreset(presetValue) {
    coffeeStore.setWaterMass(presetValue); // Update the water mass in the coffeeStore with the preset value
  }

  // Subscribe to the coffeeStore to get updates when the store's state changes
  coffeeStore.subscribe(($coffeeStore) => {
    ratio = $coffeeStore.ratio; // Update the local 'ratio' variable with the value from the store
    mass = $coffeeStore.mass; // Update the local 'mass' variable with the value from the store
    units = $coffeeStore.units; // Update the local 'units' variable with the value from the store
  });

  // Preset values for water mass
  const waterPresets = [
    { label: 'Short', value: 100 },
    { label: 'Regular', value: 175 },
    { label: 'Tall', value: 250 },
    { label: 'Large', value: 325 },
    { label: 'X-Large', value: 400 },
    { label: 'Chemex', value: 1134 },
    { label: '3x Tall', value: 1020 }
  ];
</script>

<!-- The following code renders the input components for coffee and water mass, along with sliders and preset buttons -->
<div class="input-component">
  <div>
    <input
      type="hidden"
      value={ratio.coffee.toFixed(1)}
    />
  </div>

  <div>
    <label>Coffee Mass ({units}):</label>
    <input
      type="number"
      value={units === 'grams' ? mass.coffee.toFixed(1) : mass.coffee.toFixed(2)}
      on:input={handleCoffeeMassChange}
      min="0"
      step="{stepSize}"
      on:keydown={handleShiftKey}
    />
    <div>
      <label>Coffee Mass Presets:</label>
      {#each Array(11).fill().map((_, i) => i + 10) as preset}
        <button on:click={() => setCoffeeMassPreset(preset)}>{preset}</button>
      {/each}
    </div>

    <div>
      <label>Coffee Mass Slider:</label>
      <input
        type="range"
        min="6"
        max="334"
        step="0.1"
        value={mass.coffee}
        on:input={handleCoffeeMassChange}
        on:keydown={handleShiftKey}
      />
    </div>
  </div>

  <!-- Similar components for water mass input -->
  <div>
    <label>Water Mass ({units}):</label>
    <input
      type="number"
      value={units === 'grams' ? mass.water.toFixed(1) : mass.water.toFixed(2)}
      on:input={handleWaterMassChange}
      min="0"
      step={stepSize}
      on:keydown={handleShiftKey}
    />
    <div>
      <label>Water Mass Presets:</label>
      {#each waterPresets as preset}
        <button on:click={() => setWaterMassPreset(preset.value)}>{preset.label}</button>
      {/each}
    </div>
    <div>
      <label>Water Mass Slider:</label>
      <input
        type="range"
        min="70"
        max="2000"
        step="10"
        value={mass.water}
        on:input={handleWaterMassChange}
        on:keydown={handleShiftKey}
      />
    </div>
  </div>

  <div>
    <label>Water Ratio: {ratio.water.toFixed(1)}</label>
    <div class="slider-container">
      <span class="slider-label">Stronger</span>
      <input
      type="range"
      min="6"
      max="25"
      step="1"
      value={ratio.water}
      on:input={handleWaterRatioChange}
      />
    </div>
    <span class="slider-label">Weaker</span>
  </div>
</div>

<!-- Styling for the input components and sliders -->
<style>
  input[type=range] {
    /* Create a larger hit area for touch devices */
    padding: 10px 0;

    /* Remove default styles */
    -webkit-appearance: none;
    background: transparent;

    /* Set the width to 100% to make it responsive */
    width: 100%;
}

/* Styling for the slider track */
input[type=range]::-webkit-slider-runnable-track {
    height: 10px; /* Set the track height */
    background: #ddd; /* Set the track background color */
    border: none;
    border-radius: 3px;
}

/* Styling for the slider thumb (the handle) */
input[type=range]::-webkit-slider-thumb {
    -webkit-appearance: none; /* Remove default styling */
    border: none;
    height: 20px; /* Set the thumb height */
    width: 20px; /* Set the thumb width */
    border-radius: 50%; /* Make the thumb circular */
    background: #4285f4; /* Set the thumb background color */
    margin-top: -5px; /* Adjust margin to center the thumb on the track */
}

/* Styling for the slider ticks */
input[type=range]::-webkit-slider-runnable-track {
    background: #ddd;
    /* Add ticks to the track using repeating-linear-gradient */
    background: repeating-linear-gradient(to right, #ddd, #ddd 10px, #bbb 10px, #bbb 20px);
}

/* Additional styling for the input components */
.slider-container {
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.slider-label {
  margin: 0 10px;
}
</style>

ToggleComponent

The ToggleComponent allows the user to switch between different units (grams or ounces).

Create a new file ToggleComponent.svelte in the src/routes directory and add the following code:

<script>
  import { coffeeStore } from '../stores/coffeeStore.js';

  let units;

  coffeeStore.subscribe($coffeeStore => {
    units = $coffeeStore.units;
  });

  function toggleUnits() {
    coffeeStore.setUnits(units === 'grams' ? 'ounces' : 'grams');
  }
</script>

<div class="toggle-component">
  <button on:click={toggleUnits}>
    Switch to {units === 'grams' ? 'ounces' : 'grams'}
  </button>
</div>

This component maintains a units variable that toggles between 'grams' and 'ounces' each time the button is clicked. When the units are toggled, a 'change' event is dispatched with the new units as the event detail.

VisualFeedbackComponent

The VisualFeedbackComponent provides visual feedback to the user when a value changes.

Create a new file VisualFeedbackComponent.svelte in the src/routes directory and add the following code:

<script>
  import { fade } from 'svelte/transition';
  import { onMount } from 'svelte';

  // Initialize show variable to false
  let show = false;

  // When the component mounts, set show to true after 2 seconds
  onMount(() => {
    setTimeout(() => {
      show = true;
    }, 2000);
  });
</script>

{#if show}
  <div transition:fade>
    Value changed
  </div>
{/if}

This component uses the onMount lifecycle function to set the show variable to true 2 seconds after the component mounts. It uses a conditional block ({#if show} ... {/if}) to render a div element that fades in when show is true.

KeyboardComponent

The KeyboardComponent allows the user to increase or decrease the input values

using the keyboard.

Create a new file KeyboardComponent.svelte in the src/routes directory and add the following code:

<!-- src/routes/KeyboardInput.svelte -->
<script>
  export let mass;
  export let ratio;

  function handleKeyInput(event) {
    const increment = event.shiftKey ? 1 : 0.1;
    const { name } = event.target;
    if (name === 'coffee' || name === 'water') {
      if (event.key === 'ArrowUp') {
        mass.update((value) => ({ ...value, [name]: value[name] + increment }));
      } else if (event.key === 'ArrowDown') {
        mass.update((value) => ({ ...value, [name]: value[name] - increment }));
      }

      if (name === 'coffee') {
        mass.update((value) => ({ ...value, water: Number((value.coffee * ratio.water).toFixed(1)) }));
      } else if (name === 'water') {
        mass.update((value) => ({ ...value, coffee: Number((value.water / ratio.water).toFixed(1)) }));
      }
    }
  }
</script>

<div class="keyboard-input" tabindex="0" on:keydown={handleKeyInput}>
  <p>Use the up and down arrows to adjust the coffee mass.</p>
  <p>Hold the Shift key to adjust the coffee mass by 1 unit.</p>
</div>

<style>
  .keyboard-input {
    display: flex;
    flex-direction: column;
    align-items: center;
  }
</style>

This component uses the onMount lifecycle function to add a 'keydown' event listener to the window when the component mounts and to remove the event listener when the component is destroyed. The handleKeyDown function defines what happens when the 'ArrowUp' or 'ArrowDown' key is pressed.

Step 4: Update the main application component

Now, let's bring all these components together in the main application component, +page.svelte. Replace the content of src/routes/+page.svelte with the following code:

<!-- Importing necessary components from other files -->
<script>
    import { coffeeStore } from '../stores/coffeeStore.js'; // Importing the coffeeStore from a separate file
    import InputComponent from './InputComponent.svelte'; // Importing the InputComponent from a separate file
    import ToggleComponent from './ToggleComponent.svelte'; // Importing the ToggleComponent from a separate file
    import VisualFeedbackComponent from './VisualFeedbackComponent.svelte'; // Importing the VisualFeedbackComponent from a separate file
    import KeyboardComponent from './KeyboardComponent.svelte'; // Importing the KeyboardComponent from a separate file

    // Define local variables to store the data from the coffeeStore
    let ratio; // To store the coffee-to-water ratio
    let mass; // To store the mass of coffee grounds
    let units; // To store the units for the coffee-to-water ratio

    // Subscribe to the coffeeStore to get updates when the store's state changes
    coffeeStore.subscribe(storeState => {
        if (storeState) { // Check that the state has been defined to prevent errors
            ({ ratio, mass, units } = storeState); // Destructure and assign the values from the storeState object to the local variables
        }
    });
</script>

<!-- Adding meta information to the head of the document -->
<svelte:head>
    <title>Home</title> <!-- Setting the title of the page to "Home" -->
    <meta name="description" content="Svelte demo app" /> <!-- Setting a meta description for the page -->
</svelte:head>

<section>
    <!-- Rendering the imported components with data from the coffeeStore -->
    <InputComponent {ratio} {mass} {units} /> <!-- Rendering the InputComponent with ratio, mass, and units as props -->
    <ToggleComponent /> <!-- Rendering the ToggleComponent -->
    <VisualFeedbackComponent /> <!-- Rendering the VisualFeedbackComponent -->
    <KeyboardComponent /> <!-- Rendering the KeyboardComponent -->
</section>

<!-- Styling for the section -->
<style>
    section {
        display: flex; /* Setting the section to be a flex container */
        flex-direction: column; /* Stacking the child elements in a column */
        justify-content: center; /* Centering the child elements vertically */
        align-items: center; /* Centering the child elements horizontally */
        flex: 0.6; /* Giving the section 60% of the available space */
    }

    h1 {
        width: 100%; /* Setting the width of h1 elements to 100% of their container */
    }
</style>

This code is a Svelte component that serves as the main page of a demo app for coffee-related measurements. It imports necessary components from other files, sets up local variables to store data from the coffeeStore, and subscribes to updates from the store. Here's a brief overview of what this code does:

  1. It imports several components from separate files: coffeeStore from '../stores/coffeeStore.js', InputComponent, ToggleComponent, VisualFeedbackComponent, and KeyboardComponent. These components are used to build the user interface for the coffee-related measurements.

  2. It defines local variables ratio, mass, and units to store the data from the coffeeStore. These variables represent the coffee-to-water ratio, the mass of coffee grounds, and the units for the coffee-to-water ratio, respectively.

  3. The component subscribes to updates from the coffeeStore using the subscribe method. When the store's state changes, the function inside coffeeStore.subscribe() is called, updating the local variables ratio, mass, and units with the latest data from the store.

  4. It uses the <svelte:head> tag to add meta information to the head of the document. Specifically, it sets the title of the page to "Home" and provides a meta description for the page.

  5. It renders the imported components within a <section> element. The components InputComponent, ToggleComponent, VisualFeedbackComponent, and KeyboardComponent are rendered with data from the coffeeStore. The ratio, mass, and units variables are passed as props to InputComponent.

  6. The component applies styling to the <section> element. It uses flexbox properties to display its child elements in a column, centering them both vertically and horizontally. The section is set to take up 60% of the available space.

In summary, this Svelte component acts as the main page of a demo app related to coffee measurements. It imports various components to build the user interface and subscribes to updates from the coffeeStore to ensure the data is kept in sync. The component also adds meta information to the page's head and applies some styling to the section element to create a visually pleasing layout.

Step 5: Create the data store

In SvelteKit, a store is a reactive object that holds shared state and can be accessed and modified from different components. Stores provide a way to manage global state in your application and ensure that changes to the state are automatically propagated to all components that depend on it.

In this tutorial, we will create a SvelteKit store to manage the state related to our coffee brewing app. This store will hold information such as the coffee-to-water ratio, the coffee and water masses, and the units of measurement used.

Step 5.1: Creating the Coffee Store

Create a new directory named stores inside the src directory of your SvelteKit project.

Inside the stores directory, create a new file named coffeeStore.js.

Copy and paste the following code into the coffeeStore.js file:

// Importing the 'writable' function from Svelte to create a writable store
import { writable } from 'svelte/store';

// Function to create the coffeeStore with initial state and update functions
function createCoffeeStore() {
    // Initial state of the coffeeStore
    const initialState = {
        ratio: { coffee: 1, water: 15 }, // Initial coffee-to-water ratio (default: 1:15)
        mass: { coffee: 17, water: 255 }, // Initial coffee and water masses in grams (default: 17g coffee, 255g water)
        units: 'grams' // Initial units for measurements (default: grams)
    };

    // Creating a writable store with the initial state
    const { subscribe, set, update } = writable(initialState);

    // Returning the store methods and functions
    return {
        subscribe,

        // Function to set the coffee mass and update water mass based on the coffee-to-water ratio
        setCoffeeMass: (coffeeMass) => update(state => {
            state.mass.coffee = coffeeMass; // Update coffee mass
            state.mass.water = coffeeMass * state.ratio.water; // Calculate and update water mass based on the ratio
            return state;
        }),

        // Function to set the water mass and update coffee mass based on the coffee-to-water ratio
        setWaterMass: (waterMass) => update(state => {
            state.mass.water = waterMass; // Update water mass
            state.mass.coffee = waterMass / state.ratio.water; // Calculate and update coffee mass based on the ratio
            return state;
        }),

        // Function to set the coffee-to-water ratio and update the other ratio
        setCoffeeRatio: (coffeeRatio) => update(state => {
            state.ratio.coffee = coffeeRatio; // Update coffee-to-water ratio
            state.ratio.water = state.mass.water / state.mass.coffee; // Calculate and update water-to-coffee ratio
            return state;
        }),

        // Function to set the water-to-coffee ratio and update the other ratio
        setWaterRatio: (waterRatio) => update(state => {
            state.ratio.water = waterRatio; // Update water-to-coffee ratio
            state.ratio.coffee = state.mass.coffee / state.mass.water; // Calculate and update coffee-to-water ratio
            return state;
        }),

        // Function to set the units and convert masses accordingly (grams to ounces or vice versa)
        setUnits: (newUnits) => update(state => {
            // Calculate the conversion factor for coffee and water masses based on new units
            const conversionFactor = newUnits === 'grams' ? 28.3495 : 1 / 28.3495;
            state.mass.coffee *= conversionFactor; // Convert coffee mass
            state.mass.water *= conversionFactor; // Convert water mass
            state.units = newUnits; // Update units
            return state;
        }),

        // Function to reset the store state to initial values
        reset: () => set(initialState)
    };
}

// Creating and exporting the coffeeStore using the createCoffeeStore function
export const coffeeStore = createCoffeeStore();

Save the file.

This code is a JavaScript module that creates a Svelte writable store called coffeeStore for managing coffee-related data. The store contains initial state values for the coffee-to-water ratio, coffee and water masses, and units of measurement (defaulted to grams). It provides several functions to update and manipulate the store's state. Here's a brief overview of what this code does:

  1. It imports the writable function from Svelte's store module. This function is used to create a writable store that allows the state to be modified.

  2. It defines a function createCoffeeStore() that creates the coffeeStore. The function sets the initial state of the store with default values for the coffee-to-water ratio, coffee and water masses, and units.

  3. It creates a writable store using the writable function and assigns its methods (subscribe, set, update) to variables. The initial state is passed as an argument to the writable function.

  4. It returns an object containing the subscribe method and several functions that update the store's state. These functions include:

    • setCoffeeMass: Sets the coffee mass and updates the water mass based on the coffee-to-water ratio.
    • setWaterMass: Sets the water mass and updates the coffee mass based on the coffee-to-water ratio.
    • setCoffeeRatio: Sets the coffee-to-water ratio and updates the water-to-coffee ratio.
    • setWaterRatio: Sets the water-to-coffee ratio and updates the coffee-to-water ratio.
    • setUnits: Sets the units of measurement (grams or ounces) and converts the coffee and water masses accordingly.
    • reset: Resets the store's state to its initial values.
  5. The createCoffeeStore function creates the coffeeStore with all its methods and functions.

  6. The coffeeStore is then exported so that other components can import and use it to manage coffee-related data.

In summary, this JavaScript module defines a Svelte writable store called coffeeStore, which manages coffee-related data such as coffee-to-water ratio, coffee and water masses, and units of measurement. The store provides functions to update and manipulate the data and can be used by other Svelte components to maintain consistent state across the application.

Step 6: Start the development server

Finally, you can start the development server and see your new SvelteKit project in action. Run the following command in your terminal:

npm run dev

Then, open your browser and navigate to http://localhost:5000. You should see your new SvelteKit project up and running.

This README provides comprehensive instructions for setting up a SvelteKit project. It explains each component in detail, providing complete code and thorough explanations. Following these instructions should ensure that your code runs correctly the first time. Enjoy brewing your perfect cup of coffee!

=== end coffee calc tutorial ===

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment