-
-
Save unlocomqx/4789da779287f894000a8e605545a5f6 to your computer and use it in GitHub Desktop.
A helper function for setting Svelte 5 $state() runes on objects
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script lang="ts"> | |
import { gettable, settable, runed } from './runes.js' | |
const createFruit = (obj) => { | |
// An optional object of default values if the provided prop is nullish | |
const defaults = { | |
color: 'purple', | |
consumed: true, | |
count: 0 | |
} | |
// An optional array of keys, indicating which properties should be readonly | |
const readonly_props = [] | |
/* This function iterates through all *provided* keys on the object and establishes | |
writable or read-only properties (i.e. getters with or without setters). If you | |
provide an optional array of keys, it will treat those as readonly properties (no setters) */ | |
const runified = runed(obj, defaults, readonly_props) | |
/* And of course, we can still place methods on it before we return: */ | |
runified.log = () => console.log(JSON.stringify(runified)) | |
runified.increment = () => runified.count++ | |
runified.decrement = () => runified.count-- | |
return runified | |
} | |
/* Note that these objects don't all contain the same properties. We can handle that! */ | |
let data = [ | |
{name: 'apple', color: 'red', consumed: true}, | |
{name: 'orange', color: 'orange', consumed: false}, | |
{name: 'pear', color: 'green'}, | |
{name: 'grape'} | |
] | |
let fruits = $state(data.map(f => createFruit(f))) | |
const addFruit = (name) => { | |
const fruit_to_add = createFruit({name}) // Create a fruit with our helper function, passing only part of the full object in | |
fruits = [...fruits, fruit_to_add] // Push the new stateful fruit to the array, and reassign to trigger reactive updates | |
fruit = '' // Reset the input field | |
} | |
const removeFruit = (name) => { | |
fruits = fruits.filter(f => f.name !== name) | |
} | |
</script> | |
<input type="text" bind:value={fruit} /> | |
<button on:click={()=>addFruit(fruit)}>Add</button> | |
{#each fruits as fruit (fruit)} | |
<p> | |
<span style:background={fruit.color}>{fruit.name}</span> | |
<input type="text" bind:value={fruit.name} /> | |
<input type="text" bind:value={fruit.color} /> | |
<input type="checkbox" bind:checked={fruit.consumed} /> | |
<button on:click={()=>removeFruit(fruit.name)}>Remove</button> | |
</p> | |
{/each} | |
<style> | |
span{ | |
padding: 0.5rem 1rem; | |
} | |
</style> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
export const settable = (key, initial_value = undefined, obj = {}) => { | |
let value = $state(initial_value); | |
Object.defineProperty(obj, key, { | |
get(){return value}, | |
set(new_val){ | |
value = new_val | |
}, | |
enumerable: true, | |
}); | |
return obj | |
} | |
export const gettable = (key, initial_value = undefined, obj = {}) => { | |
let value = $state(initial_value); | |
Object.defineProperty(obj, key, { | |
get(){return value}, | |
enumerable: true, | |
}); | |
return obj | |
} | |
export const runed = (obj,defaults={}, readonly = []) => { | |
let defaulted = {...defaults, ...obj} | |
iterate_over_properties(defaulted, readonly) | |
return defaulted | |
} | |
const iterate_over_properties = (obj, readonly=[]) => { | |
for(const property in obj){ | |
if(readonly.includes(property)){ | |
gettable(property, obj[property], obj) | |
}else{ | |
settable(property, obj[property], obj) | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment