Skip to content

Commit

Permalink
Merge pull request #111 from emiljohansson/feature/more-svelte
Browse files Browse the repository at this point in the history
Feature/more svelte
  • Loading branch information
emiljohansson authored Dec 27, 2023
2 parents 639e3bd + dafbb21 commit 7250e4d
Show file tree
Hide file tree
Showing 29 changed files with 990 additions and 113 deletions.
3 changes: 1 addition & 2 deletions apps/next/src/app/signals/Content.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
'use client'

import { signal, computed } from '@preact/signals-core'
import './initSignals'
import { signal, computed } from './signals'

const count = signal(0)
const double = computed(() => count.value * 2)
Expand Down
42 changes: 42 additions & 0 deletions apps/next/src/app/signals/hooks.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import { Signal, computed, effect } from '@preact/signals-core'
import { useEffect, useMemo, useRef, useState } from 'react'

export function useSignalValue<T>(signal: Signal<T>): T {
const [state, setState] = useState<T>(signal.value)
useEffect(() => {
return effect(() => setState(signal.value))
}, [signal])
return state
}

export function useSignalAndValue<T>(initialValue: T): [Signal<T>, T] {
const signal = useSignal(initialValue)
const value = useSignalValue<T>(signal)
return [signal, value]
}

export function useComputedValue<T>(compute: () => T): T {
const signal = useComputed(compute)
const value = useSignalValue(signal)
return value
}

// Following hooks taken from https://github.com/preactjs/signals/blob/main/packages/react/runtime/src/index.ts
export function useSignal<T>(initialValue: T): Signal<T> {
return useMemo(() => new Signal<T>(initialValue), [])
}

export function useComputed<T>(compute: () => T): Signal<T> {
const $compute = useRef(compute)
$compute.current = compute
return useMemo(() => computed<T>(() => $compute.current()), [])
}

export function useSignalEffect(cb: () => void | (() => void)): void {
const callback = useRef(cb)
callback.current = cb

useEffect(() => {
return effect(() => callback.current())
}, [])
}
32 changes: 0 additions & 32 deletions apps/next/src/app/signals/initSignals.ts

This file was deleted.

46 changes: 46 additions & 0 deletions apps/next/src/app/signals/signals.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// temporary solution from https://github.com/JonAbrams/signals-react-safe/tree/main

import {
Signal,
signal,
computed,
effect,
batch,
type ReadonlySignal,
untracked,
} from '@preact/signals-core'
import { useSignalValue } from './hooks'
import { ReactElement } from 'react'

export {
signal,
computed,
effect,
batch,
Signal,
type ReadonlySignal,
untracked,
}
export * from './hooks'

const ReactElemType = Symbol.for('react.element') // https://github.com/facebook/react/blob/346c7d4c43a0717302d446da9e7423a8e28d8996/packages/shared/ReactSymbols.js#L15

function SignalValue({ data }: { data: Signal }) {
return useSignalValue(data)
}

Object.defineProperties(Signal.prototype, {
$$typeof: { configurable: true, value: ReactElemType },
type: { configurable: true, value: SignalValue },
props: {
configurable: true,
get() {
return { data: this }
},
},
ref: { configurable: true, value: null },
})

declare module '@preact/signals-core' {
interface Signal extends ReactElement {}
}
22 changes: 11 additions & 11 deletions apps/svelte/.eslintrc.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,17 @@
module.exports = {
root: true,
extends: [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:svelte/recommended",
"prettier",
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:svelte/recommended',
'prettier',
],
parser: "@typescript-eslint/parser",
plugins: ["@typescript-eslint"],
parser: '@typescript-eslint/parser',
plugins: ['@typescript-eslint'],
parserOptions: {
sourceType: "module",
sourceType: 'module',
ecmaVersion: 2020,
extraFileExtensions: [".svelte"],
extraFileExtensions: ['.svelte'],
},
env: {
browser: true,
Expand All @@ -21,10 +21,10 @@ module.exports = {
},
overrides: [
{
files: ["*.svelte"],
parser: "svelte-eslint-parser",
files: ['*.svelte'],
parser: 'svelte-eslint-parser',
parserOptions: {
parser: "@typescript-eslint/parser",
parser: '@typescript-eslint/parser',
},
},
],
Expand Down
2 changes: 1 addition & 1 deletion apps/svelte/.prettierrc
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"useTabs": true,
"singleQuote": false,
"singleQuote": true,
"trailingComma": "all",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
Expand Down
20 changes: 10 additions & 10 deletions apps/svelte/src/app.css
Original file line number Diff line number Diff line change
Expand Up @@ -4,51 +4,51 @@

@layer base {
@font-face {
font-family: "Inter";
font-family: 'Inter';
font-style: normal;
font-weight: 300;
font-display: swap;
src: url(/fonts/Inter-Light.woff2) format("woff2");
src: url(/fonts/Inter-Light.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
font-family: "Inter";
font-family: 'Inter';
font-style: normal;
font-weight: 400;
font-display: swap;
src: url(/fonts/Inter-Regular.woff2) format("woff2");
src: url(/fonts/Inter-Regular.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
font-family: "Inter";
font-family: 'Inter';
font-style: normal;
font-weight: 500;
font-display: swap;
src: url(/fonts/Inter-Medium.woff2) format("woff2");
src: url(/fonts/Inter-Medium.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
font-family: "Inter";
font-family: 'Inter';
font-style: normal;
font-weight: 600;
font-display: swap;
src: url(/fonts/Inter-SemiBold.woff2) format("woff2");
src: url(/fonts/Inter-SemiBold.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

@font-face {
font-family: "Inter";
font-family: 'Inter';
font-style: normal;
font-weight: 700;
font-display: swap;
src: url(/fonts/Inter-Bold.woff2) format("woff2");
src: url(/fonts/Inter-Bold.woff2) format('woff2');
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC,
U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}
Expand Down
2 changes: 1 addition & 1 deletion apps/svelte/src/app.html
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
<link rel="manifest" href="%sveltekit.assets%/images/logo/site.webmanifest" />

<script>
document.documentElement.classList.toggle("dark", localStorage.theme === "dark")
document.documentElement.classList.toggle('dark', localStorage.theme === 'dark')
</script>

%sveltekit.head%
Expand Down
8 changes: 8 additions & 0 deletions apps/svelte/src/routes/hooks/+page.server.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
import type { PageServerLoad } from './$types'
import randomString from '@emiljohansson/random-string'

export const load: PageServerLoad<{ randomValue: string }> = () => {
return {
randomValue: randomString(),
}
}
25 changes: 25 additions & 0 deletions apps/svelte/src/routes/hooks/+page.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<script lang="ts">
import type { PageData } from './$types'
import ClickOutsideSection from './ClickOutsideSection.svelte'
import CounterSection from './CounterSection.svelte'
import DebounceSection from './DebounceSection.svelte'
import RandomStringSection from './RandomStringSection.svelte'
export let data: PageData
</script>

<svelte:head>
<title>Hooks | emiljohansson.dev</title>
<meta name="description" content="Genrates a random string" />
</svelte:head>

<header class="bg-white border-b border-slate-200 px-6 py-12">
<h1 class="text-4xl font-medium mb-0">Current Time</h1>
</header>

<main class="px-6 py-4 grid gap-4">
<RandomStringSection initValue={data.randomValue} />
<DebounceSection />
<CounterSection />
<ClickOutsideSection />
</main>
16 changes: 16 additions & 0 deletions apps/svelte/src/routes/hooks/ClickOutsideSection.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script lang="ts">
let isWithin = false
function onGlobalClick() {
isWithin = false;
}
</script>

<svelte:document on:click={onGlobalClick} />

<article>
<h2>Click Outside</h2>
<button class="btn-primary" on:click|stopPropagation={() => isWithin = true}>
{isWithin ? 'Within' : 'Outside'}
</button>
</article>
24 changes: 24 additions & 0 deletions apps/svelte/src/routes/hooks/CounterSection.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
<script lang="ts">
import { createCounter } from "./counter"
let {count, increment, decrement, reset, set} = createCounter()
</script>

<article>
<h2>Counter</h2>
<p>Count: {$count}</p>
<div class="flex">
<button class="btn-secondary" on:click={increment}>
Increment
</button>
<button class="btn-secondary" on:click={decrement}>
Decrement
</button>
<button class="btn-secondary" on:click={reset}>
Reset
</button>
<button class="btn-secondary" on:click={() => set(5)}>
Set 5
</button>
</div>
</article>
22 changes: 22 additions & 0 deletions apps/svelte/src/routes/hooks/DebounceSection.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script lang="ts">
let value = ''
let debounced = ''
let timer: ReturnType<typeof setTimeout>
const debounce = (func: () => void, delay = 300) => {
clearTimeout(timer);
timer = setTimeout(func, delay)
}
</script>

<article>
<h2>Debounce</h2>
<input
class="input"
type="text"
bind:value={value}
on:keyup={() => debounce(() => debounced = value)}
/>
<p>Value: {value}</p>
<p>Debounced: {debounced}</p>
</article>
12 changes: 12 additions & 0 deletions apps/svelte/src/routes/hooks/RandomStringSection.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<script lang="ts">
import { randomString } from "./randomString"
export let initValue: string
let {value: random, generate} = randomString(initValue)
</script>

<article>
<h2>Random String</h2>
<button on:click={generate}>{$random}</button>
</article>
20 changes: 20 additions & 0 deletions apps/svelte/src/routes/hooks/counter.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { clamp } from 'lib/utils/number'
import { writable } from 'svelte/store'

export function createCounter(
initialValue = 0,
options?: Partial<{
min: number
max: number
}>,
) {
const { min, max } = { min: -Infinity, max: Infinity, ...options }
const count = writable(clamp(initialValue, min, max))

const increment = () => count.update((v) => clamp(v + 1, min, max))
const decrement = () => count.update((v) => clamp(v - 1, min, max))
const reset = () => count.set(clamp(initialValue, min, max))
const set = (newValue: number) => count.set(clamp(newValue, min, max))

return { count, increment, decrement, reset, set } as const
}
Loading

2 comments on commit 7250e4d

@vercel
Copy link

@vercel vercel bot commented on 7250e4d Dec 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vercel
Copy link

@vercel vercel bot commented on 7250e4d Dec 27, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.