Run formatter

This commit is contained in:
Wout De Puysseleir 2023-03-30 15:51:24 -07:00
parent 45a7f0fe78
commit 9b837bcd28
No known key found for this signature in database
GPG key ID: 3DE9371B50FEC46A
29 changed files with 513 additions and 533 deletions

View file

@ -16,19 +16,19 @@ Render Svelte directly into Phoenix LiveView with E2E reactivity.
## Resources
- [HexDocs](https://hexdocs.pm/live_svelte)
- [HexPackage](https://hex.pm/packages/live_svelte)
- [Phoenix LiveView](https://github.com/phoenixframework/phoenix_live_view)
- [Blog Post](https://wout.space/notes/live-svelte)
- [HexDocs](https://hexdocs.pm/live_svelte)
- [HexPackage](https://hex.pm/packages/live_svelte)
- [Phoenix LiveView](https://github.com/phoenixframework/phoenix_live_view)
- [Blog Post](https://wout.space/notes/live-svelte)
## Features
- ⚡ **End-To-End Reactivity** with LiveView
- 🔋 **Server-Side Rendered** (SSR) Svelte
- ⭐ **Svelte Preprocessing** Support with [svelte-preprocess](https://github.com/sveltejs/svelte-preprocess)
- 🦄 **Tailwind** Support
- 💀 **Dead View** Support
- 🦥 **Slot Interoperability** *(Experimental)*
- **End-To-End Reactivity** with LiveView
- 🔋 **Server-Side Rendered** (SSR) Svelte
- **Svelte Preprocessing** Support with [svelte-preprocess](https://github.com/sveltejs/svelte-preprocess)
- 🦄 **Tailwind** Support
- 💀 **Dead View** Support
- 🦥 **Slot Interoperability** _(Experimental)_
## Demo
@ -58,11 +58,11 @@ LiveSvelte builds on top of Phoenix LiveView to allow for easy client side state
### Reasons why you'd use LiveSvelte
- You have (complex) local state
- You want to use an NPM package
- You want to take advantage of Svelte's animations
- You want scoped CSS
- You like Svelte and its DX :)
- You have (complex) local state
- You want to use an NPM package
- You want to take advantage of Svelte's animations
- You want scoped CSS
- You like Svelte and its DX :)
## Requirements
@ -99,6 +99,7 @@ end
```
3. Run the following in your terminal
```bash
mix deps.get
mix live_svelte.setup
@ -127,10 +128,10 @@ In addition we commented out some things such as the `esbuild` watcher configure
Svelte components need to go into the `assets/svelte` directory
- Set the `name` of the Svelte component.
- _Optional:_ Provide the `props` you want to use that should be reactive as a map to the props field
- _Optional:_ Provide `class` to set the class attribute on the root svelte element
- _Optional:_ Set `ssr` to false to disable server-side rendering
- Set the `name` of the Svelte component.
- _Optional:_ Provide the `props` you want to use that should be reactive as a map to the props field
- _Optional:_ Provide `class` to set the class attribute on the root svelte element
- _Optional:_ Set `ssr` to false to disable server-side rendering
e.g. If your component is named `assets/svelte/Example.svelte`:
@ -164,7 +165,7 @@ An example project can be found in the `/example_project` directory.
// This pushes the event over the websocket
// The last parameter is optional. It's a callback for when the event is finished.
// You could for example set a loading state until the event is finished if it takes a longer time.
pushEvent('set_number', { number: number + 1 }, () => {})
pushEvent("set_number", {number: number + 1}, () => {})
// Note that we actually never set the number in the frontend!
// We ONLY push the event to the server.
@ -173,7 +174,7 @@ An example project can be found in the `/example_project` directory.
}
function decrease() {
pushEvent('set_number', { number: number - 1 }, () => {})
pushEvent("set_number", {number: number - 1}, () => {})
}
</script>
@ -246,6 +247,7 @@ LiveView allows for a bunch of interoperability which you can read more about he
To use the preprocessor, install the desired preprocessor.
e.g. Typescript
```
cd assets && npm install --save-dev typescript
```
@ -293,9 +295,9 @@ mix assets.build
### Releasing
- Update the version in `README.md`
- Update the version in `package.json`
- Update the version in `mix.exs`
- Update the version in `README.md`
- Update the version in `package.json`
- Update the version in `mix.exs`
Run:
@ -304,5 +306,6 @@ mix hex.publish
```
## Credits
- [Ryan Cooke](https://dev.to/debussyman) - [E2E Reactivity using Svelte with Phoenix LiveView](https://dev.to/debussyman/e2e-reactivity-using-svelte-with-phoenix-liveview-38mf)
- [Svonix](https://github.com/nikokozak/svonix)
- [Ryan Cooke](https://dev.to/debussyman) - [E2E Reactivity using Svelte with Phoenix LiveView](https://dev.to/debussyman/e2e-reactivity-using-svelte-with-phoenix-liveview-38mf)
- [Svonix](https://github.com/nikokozak/svonix)

View file

@ -1,71 +1,71 @@
const esbuild = require('esbuild')
const sveltePlugin = require('esbuild-svelte')
const importGlobPlugin = require('esbuild-plugin-import-glob').default
const sveltePreprocess = require('svelte-preprocess')
const esbuild = require("esbuild")
const sveltePlugin = require("esbuild-svelte")
const importGlobPlugin = require("esbuild-plugin-import-glob").default
const sveltePreprocess = require("svelte-preprocess")
const args = process.argv.slice(2)
const watch = args.includes('--watch')
const deploy = args.includes('--deploy')
const watch = args.includes("--watch")
const deploy = args.includes("--deploy")
let optsClient = {
entryPoints: ['js/app.js'],
mainFields: ['svelte', 'browser', 'module', 'main'],
entryPoints: ["js/app.js"],
mainFields: ["svelte", "browser", "module", "main"],
bundle: true,
minify: false,
target: 'es2017',
outdir: '../priv/static/assets',
logLevel: 'info',
target: "es2017",
outdir: "../priv/static/assets",
logLevel: "info",
plugins: [
importGlobPlugin(),
sveltePlugin({
preprocess: sveltePreprocess(),
compilerOptions: {hydratable: true, css: true},
})
]
}),
],
}
let optsServer = {
entryPoints: ['js/server.js'],
mainFields: ['svelte', 'module', 'main'],
platform: 'node',
format: 'cjs',
entryPoints: ["js/server.js"],
mainFields: ["svelte", "module", "main"],
platform: "node",
format: "cjs",
bundle: true,
minify: false,
target: "node19.6.1",
outdir: '../priv/static/assets/server',
logLevel: 'info',
outdir: "../priv/static/assets/server",
logLevel: "info",
plugins: [
importGlobPlugin(),
sveltePlugin({
preprocess: sveltePreprocess(),
compilerOptions: {hydratable: true, generate: 'ssr', format: 'cjs'},
})
]
compilerOptions: {hydratable: true, generate: "ssr", format: "cjs"},
}),
],
}
if (watch) {
optsClient = {
...optsClient,
watch,
sourcemap: 'inline'
sourcemap: "inline",
}
optsServer = {
...optsServer,
watch,
sourcemap: 'inline'
sourcemap: "inline",
}
}
if (deploy) {
optsClient = {
...optsClient,
minify: true
minify: true,
}
optsServer = {
...optsServer,
minify: true
minify: true,
}
}
@ -74,7 +74,7 @@ const server = esbuild.build(optsServer)
if (watch) {
client.then(_result => {
process.stdin.on('close', () => {
process.stdin.on("close", () => {
process.exit(0)
})
@ -82,7 +82,7 @@ if (watch) {
})
server.then(_result => {
process.stdin.on('close', () => {
process.stdin.on("close", () => {
process.exit(0)
})

View file

@ -21,8 +21,8 @@ import "phoenix_html"
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
import {getHooks} from 'live_svelte'
import * as SvelteComponents from '../svelte/**/*'
import {getHooks} from "live_svelte"
import * as SvelteComponents from "../svelte/**/*"
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {hooks: getHooks(SvelteComponents), params: {_csrf_token: csrfToken}})
@ -40,4 +40,3 @@ liveSocket.connect()
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket

View file

@ -1,4 +1,4 @@
import * as Components from '../svelte/**/*'
import {exportSvelteComponents} from 'live_svelte'
import * as Components from "../svelte/**/*"
import {exportSvelteComponents} from "live_svelte"
module.exports = exportSvelteComponents(Components)

View file

@ -1,8 +1,8 @@
import {detach, insert, noop} from 'svelte/internal'
import {exportSvelteComponents} from './utils'
import {detach, insert, noop} from "svelte/internal"
import {exportSvelteComponents} from "./utils"
function base64ToElement(base64) {
let template = document.createElement('div')
let template = document.createElement("div")
template.innerHTML = atob(base64).trim()
return template
}
@ -18,7 +18,7 @@ function createSlots(slots, ref) {
return () => {
return {
getElement() {
return base64ToElement(dataAttributeToJson('data-slots', ref.el)[slotName])
return base64ToElement(dataAttributeToJson("data-slots", ref.el)[slotName])
},
update() {
const element = this.getElement()
@ -53,10 +53,10 @@ function createSlots(slots, ref) {
function getProps(ref) {
return {
...dataAttributeToJson('data-props', ref.el),
...dataAttributeToJson("data-props", ref.el),
pushEvent: (event, data, callback) => ref.pushEvent(event, data, callback),
$$slots: createSlots(dataAttributeToJson('data-slots', ref.el), ref),
$$scope: {}
$$slots: createSlots(dataAttributeToJson("data-slots", ref.el), ref),
$$scope: {},
}
}
@ -71,9 +71,9 @@ export function getHooks(Components) {
const SvelteHook = {
mounted() {
const componentName = this.el.getAttribute('data-name')
const componentName = this.el.getAttribute("data-name")
if (!componentName) {
throw new Error('Component name must be provided')
throw new Error("Component name must be provided")
}
const Component = components[componentName]
@ -84,7 +84,7 @@ export function getHooks(Components) {
this._instance = new Component({
target: this.el,
props: getProps(this),
hydrate: true
hydrate: true,
})
},
@ -104,10 +104,10 @@ export function getHooks(Components) {
// If we do a page navigation, this would remove the component in the DOM,
// and then it would to the transition, causing a flicker of unrendered content
// Since we're doing a page transition anyway, the component will be remove automatically
}
},
}
return {
SvelteHook
SvelteHook,
}
}

View file

@ -1,3 +1,3 @@
export {getRender} from "./render"
export {getHooks} from "./hooks"
export {exportSvelteComponents} from './utils'
export {exportSvelteComponents} from "./utils"

View file

@ -5,17 +5,14 @@ export function getRender(componentPath) {
function render(name, props = {}, slots = null) {
// remove from cache in non-production environments
// so that we can see changes
if (
process.env.NODE_ENV !== 'production' &&
require.resolve(componentPath) in require.cache
) {
if (process.env.NODE_ENV !== "production" && require.resolve(componentPath) in require.cache) {
delete require.cache[require.resolve(componentPath)]
}
const component = require(componentPath)[name].default
const $$slots = Object.fromEntries(Object.entries(slots).map(([k, v]) => [k, () => v])) || {}
return component.render(props, { $$slots, context: new Map() })
return component.render(props, {$$slots, context: new Map()})
}
return render

View file

@ -1,9 +1,7 @@
export function exportSvelteComponents(components) {
let { default: modules, filenames } = components
let {default: modules, filenames} = components
filenames = filenames
.map(name => name.replace('../svelte/', ''))
.map(name => name.replace('.svelte', ''))
filenames = filenames.map(name => name.replace("../svelte/", "")).map(name => name.replace(".svelte", ""))
return Object.assign({}, ...modules.map((m, index) => ({[filenames[index]]: m.default})))
}

View file

@ -2,8 +2,8 @@
To start your Phoenix server:
* Run `mix setup` to install and setup dependencies
* Start Phoenix endpoint with `mix phx.server` or inside IEx with `iex -S mix phx.server`
- Run `mix setup` to install and setup dependencies
- Start Phoenix endpoint with `mix phx.server` or inside IEx with `iex -S mix phx.server`
Now you can visit [`localhost:4000`](http://localhost:4000) from your browser.
@ -11,8 +11,8 @@ Ready to run in production? Please [check our deployment guides](https://hexdocs
## Learn more
* Official website: https://www.phoenixframework.org/
* Guides: https://hexdocs.pm/phoenix/overview.html
* Docs: https://hexdocs.pm/phoenix
* Forum: https://elixirforum.com/c/phoenix-forum
* Source: https://github.com/phoenixframework/phoenix
- Official website: https://www.phoenixframework.org/
- Guides: https://hexdocs.pm/phoenix/overview.html
- Docs: https://hexdocs.pm/phoenix
- Forum: https://elixirforum.com/c/phoenix-forum
- Source: https://github.com/phoenixframework/phoenix

View file

@ -1,71 +1,71 @@
const esbuild = require('esbuild')
const sveltePlugin = require('esbuild-svelte')
const importGlobPlugin = require('esbuild-plugin-import-glob').default
const sveltePreprocess = require('svelte-preprocess')
const esbuild = require("esbuild")
const sveltePlugin = require("esbuild-svelte")
const importGlobPlugin = require("esbuild-plugin-import-glob").default
const sveltePreprocess = require("svelte-preprocess")
const args = process.argv.slice(2)
const watch = args.includes('--watch')
const deploy = args.includes('--deploy')
const watch = args.includes("--watch")
const deploy = args.includes("--deploy")
let optsClient = {
entryPoints: ['js/app.js'],
mainFields: ['svelte', 'browser', 'module', 'main'],
entryPoints: ["js/app.js"],
mainFields: ["svelte", "browser", "module", "main"],
bundle: true,
minify: false,
target: 'es2017',
outdir: '../priv/static/assets',
logLevel: 'info',
target: "es2017",
outdir: "../priv/static/assets",
logLevel: "info",
plugins: [
importGlobPlugin(),
sveltePlugin({
preprocess: sveltePreprocess(),
compilerOptions: {hydratable: true, css: true},
})
]
}),
],
}
let optsServer = {
entryPoints: ['js/server.js'],
mainFields: ['svelte', 'module', 'main'],
platform: 'node',
format: 'cjs',
entryPoints: ["js/server.js"],
mainFields: ["svelte", "module", "main"],
platform: "node",
format: "cjs",
bundle: true,
minify: false,
target: "node19.6.1",
outdir: '../priv/static/assets/server',
logLevel: 'info',
outdir: "../priv/static/assets/server",
logLevel: "info",
plugins: [
importGlobPlugin(),
sveltePlugin({
preprocess: sveltePreprocess(),
compilerOptions: {hydratable: true, generate: 'ssr', format: 'cjs'},
})
]
compilerOptions: {hydratable: true, generate: "ssr", format: "cjs"},
}),
],
}
if (watch) {
optsClient = {
...optsClient,
watch,
sourcemap: 'inline'
sourcemap: "inline",
}
optsServer = {
...optsServer,
watch,
sourcemap: 'inline'
sourcemap: "inline",
}
}
if (deploy) {
optsClient = {
...optsClient,
minify: true
minify: true,
}
optsServer = {
...optsServer,
minify: true
minify: true,
}
}
@ -74,7 +74,7 @@ const server = esbuild.build(optsServer)
if (watch) {
client.then(_result => {
process.stdin.on('close', () => {
process.stdin.on("close", () => {
process.exit(0)
})
@ -82,7 +82,7 @@ if (watch) {
})
server.then(_result => {
process.stdin.on('close', () => {
process.stdin.on("close", () => {
process.exit(0)
})

View file

@ -21,8 +21,8 @@ import "phoenix_html"
import {Socket} from "phoenix"
import {LiveSocket} from "phoenix_live_view"
import topbar from "../vendor/topbar"
import {getHooks} from 'live_svelte'
import * as SvelteComponents from '../svelte/**/*'
import {getHooks} from "live_svelte"
import * as SvelteComponents from "../svelte/**/*"
let csrfToken = document.querySelector("meta[name='csrf-token']").getAttribute("content")
let liveSocket = new LiveSocket("/live", Socket, {hooks: getHooks(SvelteComponents), params: {_csrf_token: csrfToken}})
@ -40,4 +40,3 @@ liveSocket.connect()
// >> liveSocket.enableLatencySim(1000) // enabled for duration of browser session
// >> liveSocket.disableLatencySim()
window.liveSocket = liveSocket

View file

@ -1,4 +1,4 @@
import * as Components from '../svelte/**/*'
import {exportSvelteComponents} from 'live_svelte'
import * as Components from "../svelte/**/*"
import {exportSvelteComponents} from "live_svelte"
module.exports = exportSvelteComponents(Components)

View file

@ -1,41 +1,41 @@
<script>
import { slide, fly } from 'svelte/transition'
import { Marquee } from 'dynamic-marquee'
import { onMount } from 'svelte'
import {slide, fly} from "svelte/transition"
import {Marquee} from "dynamic-marquee"
import {onMount} from "svelte"
export let news = []
export let pushEvent
let newItem = ""
let marquee
let marqueeEl
let index = 0
let speed = -150
// Function to add a new item to the news feed
function addItem() {
if (newItem === "") return
if (news.length === 0) marquee.appendItem(createItem(newItem))
pushEvent("add_news_item", { body: newItem })
pushEvent("add_news_item", {body: newItem})
newItem = ""
}
// Function to remove an item from the news feed
function removeItem(id) {
pushEvent("remove_news_item", { id: id })
pushEvent("remove_news_item", {id: id})
}
// Run this code when the component is mounted to the DOM
onMount(async () => {
// Create a new Marquee instance
marquee = new Marquee(marqueeEl, {
rate: speed,
startOnScreen: false,
});
})
// Add the first item to the Marquee, if there is one
if (news.length > 0) marquee.appendItem(createItem(news[0].body))
// Add an item to the Marquee whenever it is required
marquee.onItemRequired(() => {
if (news.length === 0) return
@ -44,16 +44,16 @@
return createItem(news[index].body)
})
})
// Function to create a new Marquee item
function createItem(text) {
const item = document.createElement('span');
item.classList.add("mx-8");
item.classList.add("hover:text-black");
const item = document.createElement("span")
item.classList.add("mx-8")
item.classList.add("hover:text-black")
item.textContent = text
return item
}
// Set the Marquee speed whenever it is changed
$: marquee && marquee.setRate(speed)
</script>
@ -62,12 +62,12 @@
<div>
<div class="flex items-center">
<form>
<input class="rounded" type="text" bind:value={newItem}/>
<input class="rounded" type="text" bind:value={newItem} />
<button class="bg-black text-white rounded px-2 py-1" on:click|preventDefault={addItem} type="submit">Add Item</button>
</form>
<div class="ml-4">
<button class="bg-black text-white rounded px-2 py-1 active:opacity-95" on:click={() => speed -= 20}> Faster</button>
<button class="bg-black text-white rounded px-2 py-1 active:opacity-95" on:click={() => speed += 20}>Slower →</button>
<button class="bg-black text-white rounded px-2 py-1 active:opacity-95" on:click={() => (speed -= 20)}> Faster</button>
<button class="bg-black text-white rounded px-2 py-1 active:opacity-95" on:click={() => (speed += 20)}>Slower →</button>
</div>
</div>
@ -76,19 +76,16 @@
<div in:fly={{y: 20}} out:slide={{y: -20}} class="mb-1">
<button class="bg-[#F00] px-2 py-1 rounded" on:click={() => removeItem(item.id)}>Remove</button>
{item.body}
</div>
</div>
{/each}
</div>
</div>
</div>
<div
class="fixed bottom-0 left-0 text-white font-bold text-4xl z-20 p-4 rounded-r-lg bg-gradient-to-b from-[#f00] via-[#f77] to-[#f00]"
>
<div class="fixed bottom-0 left-0 text-white font-bold text-4xl z-20 p-4 rounded-r-lg bg-gradient-to-b from-[#f00] via-[#f77] to-[#f00]">
BREAKING NEWS
</div>
<div
bind:this={marqueeEl}
<div
bind:this={marqueeEl}
class="fixed bottom-0 w-screen text-white font-bold text-xl py-2 bg-gradient-to-b from-[#f00] via-[#f77] to-[#f00]"
>
</div>
/>

View file

@ -1,59 +1,59 @@
<script>
import {fly} from "svelte/transition"
import {elasticOut} from "svelte/easing"
import {afterUpdate} from "svelte"
import {fly} from "svelte/transition"
import {elasticOut} from "svelte/easing"
import {afterUpdate} from "svelte"
export let messages
export let name
export let pushEvent
export let messages
export let name
export let pushEvent
let body = ""
let messagesElement
let body = ""
let messagesElement
$: charCount = body.length
$: charCount = body.length
afterUpdate(() => messagesElement.scroll({ top: messagesElement.scrollHeight, behavior: "smooth" }))
afterUpdate(() => messagesElement.scroll({top: messagesElement.scrollHeight, behavior: "smooth"}))
function submitMessage() {
if (body === "") return
pushEvent("send_message", {body})
body = ""
}
function submitMessage() {
if (body === "") return
pushEvent("send_message", {body})
body = ""
}
</script>
<div class="flex flex-col justify-between items-between sm:border sm:rounded-lg w-full h-full sm:w-[360px] sm:h-[600px]">
<ul bind:this={messagesElement} class="flex flex-col gap-2 h-full overflow-x-clip overflow-y-auto p-2">
{#each messages as message (message.id)}
{@const me = message.name === name}
<li
in:fly={{x: 100 * (me ? 1 : -1), y: -20, duration: 1000, easing: elasticOut}}
class="
<ul bind:this={messagesElement} class="flex flex-col gap-2 h-full overflow-x-clip overflow-y-auto p-2">
{#each messages as message (message.id)}
{@const me = message.name === name}
<li
in:fly={{x: 100 * (me ? 1 : -1), y: -20, duration: 1000, easing: elasticOut}}
class="
rounded-[1em] px-4 py-2 flex flex-col
{me ? 'rounded-tr-none ml-10 bg-[#0A80FE] text-white' : 'rounded-tl-none mr-10 bg-[#E9E8EB] text-black'}
"
>
<span in:fly={{y: 10}} class="text-xs font-bold">{message.name}</span>
<span>{message.body}</span>
</li>
{/each}
</ul>
>
<span in:fly={{y: 10}} class="text-xs font-bold">{message.name}</span>
<span>{message.body}</span>
</li>
{/each}
</ul>
<form on:submit|preventDefault={submitMessage} class="bg-gray-100 p-2 flex gap-2 justify-center items-center">
<div class="relative flex justify-center items-center">
<!-- svelte-ignore a11y-autofocus -->
<input
type="text"
name="message"
bind:value={body}
placeholder="Message..."
autofocus
autocomplete="off"
class="flex-grow rounded-full bg-transparent text-black pr-10"
/>
<span class="absolute right-0 px-4 text-gray-500 text-xs">
{charCount}
</span>
</div>
<button class="bg-black text-white rounded px-4 py-2">Send</button>
</form>
<form on:submit|preventDefault={submitMessage} class="bg-gray-100 p-2 flex gap-2 justify-center items-center">
<div class="relative flex justify-center items-center">
<!-- svelte-ignore a11y-autofocus -->
<input
type="text"
name="message"
bind:value={body}
placeholder="Message..."
autofocus
autocomplete="off"
class="flex-grow rounded-full bg-transparent text-black pr-10"
/>
<span class="absolute right-0 px-4 text-gray-500 text-xs">
{charCount}
</span>
</div>
<button class="bg-black text-white rounded px-4 py-2">Send</button>
</form>
</div>

View file

@ -1,5 +1,5 @@
<script>
import {slide, fly} from 'svelte/transition'
import {slide, fly} from "svelte/transition"
export let pushEvent
export let items = []
@ -9,26 +9,27 @@
function addItem() {
if (!newItemName) return
pushEvent('add_item', {name: newItemName})
newItemName = ''
pushEvent("add_item", {name: newItemName})
newItemName = ""
}
</script>
<div class="flex flex-col">
<label>
<input type="checkbox" bind:checked={showItems}>
<input type="checkbox" bind:checked={showItems} />
show list
</label>
<label>
<input type="range" bind:value={i} max={items.length}>
<input type="range" bind:value={i} max={items.length} />
{i}
</label>
<div class="mb-2">
<form>
<input type="test" bind:value={newItemName} class="border rounded px-2 py-1"/>
<button type="submit" on:click|preventDefault={addItem} class="bg-black rounded text-white px-2 py-1 font-bold">Add item</button>
<input type="test" bind:value={newItemName} class="border rounded px-2 py-1" />
<button type="submit" on:click|preventDefault={addItem} class="bg-black rounded text-white px-2 py-1 font-bold">Add item</button
>
</form>
</div>
</div>

View file

@ -1,24 +1,24 @@
<script>
export let number
let other = 1
export let number
let other = 1
</script>
<svelte:head>
<title>Simple Counter</title>
<title>Simple Counter</title>
</svelte:head>
<div class="flex flex-col justify-center items-center gap-4 p-4">
<div class="flex flex-row items-center justify-center gap-10">
<div class="flex flex-col justify-center items-center">
Server
<span class="text-xl">{number}</span>
<button class="plus" phx-click="increment">+1</button>
</div>
<div class="flex flex-row items-center justify-center gap-10">
<div class="flex flex-col justify-center items-center">
Server
<span class="text-xl">{number}</span>
<button class="plus" phx-click="increment">+1</button>
</div>
<div class="flex flex-col justify-center items-center">
Client
<span class="text-xl">{other}</span>
<button class="plus" on:click={() => other += 1}>+1</button>
<div class="flex flex-col justify-center items-center">
Client
<span class="text-xl">{other}</span>
<button class="plus" on:click={() => (other += 1)}>+1</button>
</div>
</div>
</div>
</div>

View file

@ -7,9 +7,9 @@
<div class="flex flex-col justify-center items-center gap-4">
<div class="flex flex-row items-center justify-center gap-10">
<button class="minus" on:click={() => number -= amount}>-{amount}</button>
<button class="minus" on:click={() => (number -= amount)}>-{amount}</button>
<span class="text-xl">{number}</span>
<button class="plus" on:click={() => number += amount}>+{amount}</button>
<button class="plus" on:click={() => (number += amount)}>+{amount}</button>
</div>
<label>
Amount:

View file

@ -6,63 +6,64 @@ const fs = require("fs")
const path = require("path")
module.exports = {
content: [
"./js/**/*.js",
"../lib/*_web.ex",
"../lib/*_web/**/*.*ex",
"./svelte/**/*.svelte"
],
theme: {
extend: {
colors: {
brand: "#FD4F00",
}
content: ["./js/**/*.js", "../lib/*_web.ex", "../lib/*_web/**/*.*ex", "./svelte/**/*.svelte"],
theme: {
extend: {
colors: {
brand: "#FD4F00",
},
},
},
},
plugins: [
require("@tailwindcss/forms"),
// Allows prefixing tailwind classes with LiveView classes to add rules
// only when LiveView classes are applied, for example:
//
// <div class="phx-click-loading:animate-ping">
//
plugin(({addVariant}) => addVariant("phx-no-feedback", [".phx-no-feedback&", ".phx-no-feedback &"])),
plugin(({addVariant}) => addVariant("phx-click-loading", [".phx-click-loading&", ".phx-click-loading &"])),
plugin(({addVariant}) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])),
plugin(({addVariant}) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"])),
plugins: [
require("@tailwindcss/forms"),
// Allows prefixing tailwind classes with LiveView classes to add rules
// only when LiveView classes are applied, for example:
//
// <div class="phx-click-loading:animate-ping">
//
plugin(({addVariant}) => addVariant("phx-no-feedback", [".phx-no-feedback&", ".phx-no-feedback &"])),
plugin(({addVariant}) => addVariant("phx-click-loading", [".phx-click-loading&", ".phx-click-loading &"])),
plugin(({addVariant}) => addVariant("phx-submit-loading", [".phx-submit-loading&", ".phx-submit-loading &"])),
plugin(({addVariant}) => addVariant("phx-change-loading", [".phx-change-loading&", ".phx-change-loading &"])),
// Embeds Hero Icons (https://heroicons.com) into your app.css bundle
// See your `CoreComponents.icon/1` for more information.
//
plugin(function({matchComponents, theme}) {
let iconsDir = path.join(__dirname, "../priv/hero_icons/optimized")
let values = {}
let icons = [
["", "/24/outline"],
["-solid", "/24/solid"],
["-mini", "/20/solid"]
]
icons.forEach(([suffix, dir]) => {
fs.readdirSync(path.join(iconsDir, dir)).map(file => {
let name = path.basename(file, ".svg") + suffix
values[name] = {name, fullPath: path.join(iconsDir, dir, file)}
})
})
matchComponents({
"hero": ({name, fullPath}) => {
let content = fs.readFileSync(fullPath).toString().replace(/\r?\n|\r/g, "")
return {
[`--hero-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
"-webkit-mask": `var(--hero-${name})`,
"mask": `var(--hero-${name})`,
"background-color": "currentColor",
"vertical-align": "middle",
"display": "inline-block",
"width": theme("spacing.5"),
"height": theme("spacing.5")
}
}
}, {values})
})
]
// Embeds Hero Icons (https://heroicons.com) into your app.css bundle
// See your `CoreComponents.icon/1` for more information.
//
plugin(function ({matchComponents, theme}) {
let iconsDir = path.join(__dirname, "../priv/hero_icons/optimized")
let values = {}
let icons = [
["", "/24/outline"],
["-solid", "/24/solid"],
["-mini", "/20/solid"],
]
icons.forEach(([suffix, dir]) => {
fs.readdirSync(path.join(iconsDir, dir)).map(file => {
let name = path.basename(file, ".svg") + suffix
values[name] = {name, fullPath: path.join(iconsDir, dir, file)}
})
})
matchComponents(
{
hero: ({name, fullPath}) => {
let content = fs
.readFileSync(fullPath)
.toString()
.replace(/\r?\n|\r/g, "")
return {
[`--hero-${name}`]: `url('data:image/svg+xml;utf8,${content}')`,
"-webkit-mask": `var(--hero-${name})`,
mask: `var(--hero-${name})`,
"background-color": "currentColor",
"vertical-align": "middle",
display: "inline-block",
width: theme("spacing.5"),
height: theme("spacing.5"),
}
},
},
{values}
)
}),
],
}

View file

@ -4,162 +4,149 @@
* https://buunguyen.github.io/topbar
* Copyright (c) 2021 Buu Nguyen
*/
(function (window, document) {
"use strict";
;(function (window, document) {
"use strict"
// https://gist.github.com/paulirish/1579671
(function () {
var lastTime = 0;
var vendors = ["ms", "moz", "webkit", "o"];
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame =
window[vendors[x] + "RequestAnimationFrame"];
window.cancelAnimationFrame =
window[vendors[x] + "CancelAnimationFrame"] ||
window[vendors[x] + "CancelRequestAnimationFrame"];
}
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime();
var timeToCall = Math.max(0, 16 - (currTime - lastTime));
var id = window.setTimeout(function () {
callback(currTime + timeToCall);
}, timeToCall);
lastTime = currTime + timeToCall;
return id;
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function (id) {
clearTimeout(id);
};
})();
var canvas,
currentProgress,
showing,
progressTimerId = null,
fadeTimerId = null,
delayTimerId = null,
addEvent = function (elem, type, handler) {
if (elem.addEventListener) elem.addEventListener(type, handler, false);
else if (elem.attachEvent) elem.attachEvent("on" + type, handler);
else elem["on" + type] = handler;
},
options = {
autoRun: true,
barThickness: 3,
barColors: {
0: "rgba(26, 188, 156, .9)",
".25": "rgba(52, 152, 219, .9)",
".50": "rgba(241, 196, 15, .9)",
".75": "rgba(230, 126, 34, .9)",
"1.0": "rgba(211, 84, 0, .9)",
},
shadowBlur: 10,
shadowColor: "rgba(0, 0, 0, .6)",
className: null,
},
repaint = function () {
canvas.width = window.innerWidth;
canvas.height = options.barThickness * 5; // need space for shadow
var ctx = canvas.getContext("2d");
ctx.shadowBlur = options.shadowBlur;
ctx.shadowColor = options.shadowColor;
var lineGradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
for (var stop in options.barColors)
lineGradient.addColorStop(stop, options.barColors[stop]);
ctx.lineWidth = options.barThickness;
ctx.beginPath();
ctx.moveTo(0, options.barThickness / 2);
ctx.lineTo(
Math.ceil(currentProgress * canvas.width),
options.barThickness / 2
);
ctx.strokeStyle = lineGradient;
ctx.stroke();
},
createCanvas = function () {
canvas = document.createElement("canvas");
var style = canvas.style;
style.position = "fixed";
style.top = style.left = style.right = style.margin = style.padding = 0;
style.zIndex = 100001;
style.display = "none";
if (options.className) canvas.classList.add(options.className);
document.body.appendChild(canvas);
addEvent(window, "resize", repaint);
},
topbar = {
config: function (opts) {
for (var key in opts)
if (options.hasOwnProperty(key)) options[key] = opts[key];
},
show: function (delay) {
if (showing) return;
if (delay) {
if (delayTimerId) return;
delayTimerId = setTimeout(() => topbar.show(), delay);
} else {
showing = true;
if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId);
if (!canvas) createCanvas();
canvas.style.opacity = 1;
canvas.style.display = "block";
topbar.progress(0);
if (options.autoRun) {
(function loop() {
progressTimerId = window.requestAnimationFrame(loop);
topbar.progress(
"+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2)
);
})();
}
// https://gist.github.com/paulirish/1579671
;(function () {
var lastTime = 0
var vendors = ["ms", "moz", "webkit", "o"]
for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) {
window.requestAnimationFrame = window[vendors[x] + "RequestAnimationFrame"]
window.cancelAnimationFrame = window[vendors[x] + "CancelAnimationFrame"] || window[vendors[x] + "CancelRequestAnimationFrame"]
}
},
progress: function (to) {
if (typeof to === "undefined") return currentProgress;
if (typeof to === "string") {
to =
(to.indexOf("+") >= 0 || to.indexOf("-") >= 0
? currentProgress
: 0) + parseFloat(to);
}
currentProgress = to > 1 ? 1 : to;
repaint();
return currentProgress;
},
hide: function () {
clearTimeout(delayTimerId);
delayTimerId = null;
if (!showing) return;
showing = false;
if (progressTimerId != null) {
window.cancelAnimationFrame(progressTimerId);
progressTimerId = null;
}
(function loop() {
if (topbar.progress("+.1") >= 1) {
canvas.style.opacity -= 0.05;
if (canvas.style.opacity <= 0.05) {
canvas.style.display = "none";
fadeTimerId = null;
return;
if (!window.requestAnimationFrame)
window.requestAnimationFrame = function (callback, element) {
var currTime = new Date().getTime()
var timeToCall = Math.max(0, 16 - (currTime - lastTime))
var id = window.setTimeout(function () {
callback(currTime + timeToCall)
}, timeToCall)
lastTime = currTime + timeToCall
return id
}
}
fadeTimerId = window.requestAnimationFrame(loop);
})();
},
};
if (!window.cancelAnimationFrame)
window.cancelAnimationFrame = function (id) {
clearTimeout(id)
}
})()
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = topbar;
} else if (typeof define === "function" && define.amd) {
define(function () {
return topbar;
});
} else {
this.topbar = topbar;
}
}.call(this, window, document));
var canvas,
currentProgress,
showing,
progressTimerId = null,
fadeTimerId = null,
delayTimerId = null,
addEvent = function (elem, type, handler) {
if (elem.addEventListener) elem.addEventListener(type, handler, false)
else if (elem.attachEvent) elem.attachEvent("on" + type, handler)
else elem["on" + type] = handler
},
options = {
autoRun: true,
barThickness: 3,
barColors: {
0: "rgba(26, 188, 156, .9)",
".25": "rgba(52, 152, 219, .9)",
".50": "rgba(241, 196, 15, .9)",
".75": "rgba(230, 126, 34, .9)",
"1.0": "rgba(211, 84, 0, .9)",
},
shadowBlur: 10,
shadowColor: "rgba(0, 0, 0, .6)",
className: null,
},
repaint = function () {
canvas.width = window.innerWidth
canvas.height = options.barThickness * 5 // need space for shadow
var ctx = canvas.getContext("2d")
ctx.shadowBlur = options.shadowBlur
ctx.shadowColor = options.shadowColor
var lineGradient = ctx.createLinearGradient(0, 0, canvas.width, 0)
for (var stop in options.barColors) lineGradient.addColorStop(stop, options.barColors[stop])
ctx.lineWidth = options.barThickness
ctx.beginPath()
ctx.moveTo(0, options.barThickness / 2)
ctx.lineTo(Math.ceil(currentProgress * canvas.width), options.barThickness / 2)
ctx.strokeStyle = lineGradient
ctx.stroke()
},
createCanvas = function () {
canvas = document.createElement("canvas")
var style = canvas.style
style.position = "fixed"
style.top = style.left = style.right = style.margin = style.padding = 0
style.zIndex = 100001
style.display = "none"
if (options.className) canvas.classList.add(options.className)
document.body.appendChild(canvas)
addEvent(window, "resize", repaint)
},
topbar = {
config: function (opts) {
for (var key in opts) if (options.hasOwnProperty(key)) options[key] = opts[key]
},
show: function (delay) {
if (showing) return
if (delay) {
if (delayTimerId) return
delayTimerId = setTimeout(() => topbar.show(), delay)
} else {
showing = true
if (fadeTimerId !== null) window.cancelAnimationFrame(fadeTimerId)
if (!canvas) createCanvas()
canvas.style.opacity = 1
canvas.style.display = "block"
topbar.progress(0)
if (options.autoRun) {
;(function loop() {
progressTimerId = window.requestAnimationFrame(loop)
topbar.progress("+" + 0.05 * Math.pow(1 - Math.sqrt(currentProgress), 2))
})()
}
}
},
progress: function (to) {
if (typeof to === "undefined") return currentProgress
if (typeof to === "string") {
to = (to.indexOf("+") >= 0 || to.indexOf("-") >= 0 ? currentProgress : 0) + parseFloat(to)
}
currentProgress = to > 1 ? 1 : to
repaint()
return currentProgress
},
hide: function () {
clearTimeout(delayTimerId)
delayTimerId = null
if (!showing) return
showing = false
if (progressTimerId != null) {
window.cancelAnimationFrame(progressTimerId)
progressTimerId = null
}
;(function loop() {
if (topbar.progress("+.1") >= 1) {
canvas.style.opacity -= 0.05
if (canvas.style.opacity <= 0.05) {
canvas.style.display = "none"
fadeTimerId = null
return
}
}
fadeTimerId = window.requestAnimationFrame(loop)
})()
},
}
if (typeof module === "object" && typeof module.exports === "object") {
module.exports = topbar
} else if (typeof define === "function" && define.amd) {
define(function () {
return topbar
})
} else {
this.topbar = topbar
}
}).call(this, window, document)

View file

@ -1,56 +1,58 @@
<script>
import {slide, fly, fade} from "svelte/transition"
import {elasticOut} from "svelte/easing"
import {afterUpdate} from "svelte"
import {slide, fly, fade} from "svelte/transition"
import {elasticOut} from "svelte/easing"
import {afterUpdate} from "svelte"
export let messages
export let name
export let pushEvent
export let messages
export let name
export let pushEvent
let body = ""
let messagesElement
let body = ""
let messagesElement
afterUpdate(() => { scrollToBottom(messagesElement) })
afterUpdate(() => {
scrollToBottom(messagesElement)
})
const scrollToBottom = async (node) => {
node.scroll({ top: node.scrollHeight, behavior: 'smooth' })
}
const scrollToBottom = async node => {
node.scroll({top: node.scrollHeight, behavior: "smooth"})
}
function submitMessage() {
if (body === "") return
pushEvent("send_message", {body})
body = ""
}
function submitMessage() {
if (body === "") return
pushEvent("send_message", {body})
body = ""
}
</script>
<div in:fade class="flex flex-col justify-between items-between sm:border sm:rounded-lg w-full h-full sm:w-[360px] sm:h-[600px]">
<ul bind:this={messagesElement} class="flex flex-col gap-2 h-full sm:h-[400px] overflow-x-clip overflow-y-auto p-2">
{#each messages as message (message.id)}
{@const me = message.name === name}
<li
in:fly={{x: 100 * (me ? 1 : -1), y: -20, duration: 1000, easing: elasticOut}}
class="
<ul bind:this={messagesElement} class="flex flex-col gap-2 h-full sm:h-[400px] overflow-x-clip overflow-y-auto p-2">
{#each messages as message (message.id)}
{@const me = message.name === name}
<li
in:fly={{x: 100 * (me ? 1 : -1), y: -20, duration: 1000, easing: elasticOut}}
class="
rounded-[1em] px-4 py-2 flex flex-col
{me ? 'rounded-tr-none ml-10 bg-[#0A80FE] text-white' : 'rounded-tl-none mr-10 bg-[#E9E8EB] text-black'}
"
>
<span in:fly={{y: 10}} class="text-xs font-bold">{message.name}</span>
<span in:fade>{message.body}</span>
</li>
{/each}
</ul>
>
<span in:fly={{y: 10}} class="text-xs font-bold">{message.name}</span>
<span in:fade>{message.body}</span>
</li>
{/each}
</ul>
<form on:submit|preventDefault={submitMessage} class="bg-gray-100 p-2 flex gap-2">
<!-- svelte-ignore a11y-autofocus -->
<input
type="text"
name="message"
class="flex-grow rounded-full bg-transparent text-black"
bind:value={body}
placeholder="Message..."
autofocus
autocomplete="off"
/>
<button class="bg-black text-white rounded px-4 py-2">Send</button>
</form>
<form on:submit|preventDefault={submitMessage} class="bg-gray-100 p-2 flex gap-2">
<!-- svelte-ignore a11y-autofocus -->
<input
type="text"
name="message"
class="flex-grow rounded-full bg-transparent text-black"
bind:value={body}
placeholder="Message..."
autofocus
autocomplete="off"
/>
<button class="bg-black text-white rounded px-4 py-2">Send</button>
</form>
</div>

View file

@ -1,15 +1,15 @@
<script>
import {fly} from 'svelte/transition'
import {fly} from "svelte/transition"
export let number = 1
export let pushEvent
function increase() {
pushEvent('set_number', { number: number + 1 })
pushEvent("set_number", {number: number + 1})
}
function decrease() {
pushEvent('set_number', { number: number - 1 })
pushEvent("set_number", {number: number - 1})
}
</script>
@ -21,12 +21,8 @@
{#key number}
<p class="mt-1">
The number is:
<br/>
<span
in:fly={{y: -40}}
class="absolute px-2 mt-2"
style="font-size: {number/2}rem;"
>
<br />
<span in:fly={{y: -40}} class="absolute px-2 mt-2" style="font-size: {number / 2}rem;">
{number}
</span>
</p>

View file

@ -1,9 +1,9 @@
<script>
// https://github.com/tjenkinson/dynamic-marquee
import {slide, fly} from 'svelte/transition'
import {Marquee} from 'dynamic-marquee'
import {onMount} from 'svelte'
import {slide, fly} from "svelte/transition"
import {Marquee} from "dynamic-marquee"
import {onMount} from "svelte"
export let news = []
export let pushEvent
@ -12,23 +12,23 @@
let marqueeEl
let index = 0
let speed = -150
function addItem() {
if (newItem === "") return
if (news.length === 0) marquee.appendItem(createItem(newItem))
pushEvent("add_news_item", { body: newItem })
pushEvent("add_news_item", {body: newItem})
newItem = ""
}
function removeItem(id) {
pushEvent("remove_news_item", { id: id })
pushEvent("remove_news_item", {id: id})
}
onMount(async () => {
marquee = new Marquee(marqueeEl, {
rate: speed,
startOnScreen: false,
});
})
if (news.length > 0) marquee.appendItem(createItem(news[0].body))
@ -39,15 +39,15 @@
return createItem(news[index].body)
})
})
function createItem(text) {
const item = document.createElement('span');
item.classList.add("mx-8");
item.classList.add("hover:text-black");
const item = document.createElement("span")
item.classList.add("mx-8")
item.classList.add("hover:text-black")
item.textContent = text
return item
}
$: marquee && marquee.setRate(speed)
</script>
@ -55,12 +55,12 @@
<div>
<div class="flex items-center">
<form>
<input class="rounded" type="text" bind:value={newItem}/>
<input class="rounded" type="text" bind:value={newItem} />
<button class="bg-black text-white rounded px-2 py-1" on:click|preventDefault={addItem} type="submit">Add Item</button>
</form>
<div class="ml-4">
<button class="bg-black text-white rounded px-2 py-1 active:opacity-95" on:click={() => speed -= 20}> Faster</button>
<button class="bg-black text-white rounded px-2 py-1 active:opacity-95" on:click={() => speed += 20}>Slower →</button>
<button class="bg-black text-white rounded px-2 py-1 active:opacity-95" on:click={() => (speed -= 20)}> Faster</button>
<button class="bg-black text-white rounded px-2 py-1 active:opacity-95" on:click={() => (speed += 20)}>Slower →</button>
</div>
</div>
@ -69,19 +69,16 @@
<div in:fly={{y: 20}} out:slide={{y: -20}} class="mb-1">
<button class="bg-[#F00] px-2 py-1 rounded" on:click={() => removeItem(item.id)}>Remove</button>
{item.body}
</div>
</div>
{/each}
</div>
</div>
</div>
<div
class="fixed bottom-0 left-0 text-white font-bold text-4xl z-20 p-4 rounded-r-lg bg-gradient-to-b from-[#f00] via-[#f77] to-[#f00]"
>
<div class="fixed bottom-0 left-0 text-white font-bold text-4xl z-20 p-4 rounded-r-lg bg-gradient-to-b from-[#f00] via-[#f77] to-[#f00]">
BREAKING NEWS
</div>
<div
bind:this={marqueeEl}
<div
bind:this={marqueeEl}
class="fixed bottom-0 w-screen text-white font-bold text-xl py-2 bg-gradient-to-b from-[#f00] via-[#f77] to-[#f00]"
>
</div>
/>

View file

@ -1,5 +1,5 @@
<script>
import {slide, fly} from 'svelte/transition'
import {slide, fly} from "svelte/transition"
export let pushEvent
export let items = []
@ -10,26 +10,27 @@
function addItem() {
if (!newItemName) return
pushEvent('add_item', {name: newItemName})
newItemName = ''
pushEvent("add_item", {name: newItemName})
newItemName = ""
}
</script>
<div class="flex flex-col">
<label>
<input type="checkbox" bind:checked={showItems}>
<input type="checkbox" bind:checked={showItems} />
show list
</label>
<label>
<input type="range" bind:value={i} max={items.length}>
<input type="range" bind:value={i} max={items.length} />
{i}
</label>
<div class="mb-2">
<form>
<input type="test" bind:value={newItemName} class="border rounded px-2 py-1"/>
<button type="submit" on:click|preventDefault={addItem} class="bg-black rounded text-white px-2 py-1 font-bold">Add item</button>
<input type="test" bind:value={newItemName} class="border rounded px-2 py-1" />
<button type="submit" on:click|preventDefault={addItem} class="bg-black rounded text-white px-2 py-1 font-bold">Add item</button
>
</form>
</div>
</div>
@ -45,4 +46,3 @@
{/each}
</div>
{/if}

View file

@ -3,11 +3,11 @@
export let pushEvent
function increase() {
pushEvent('set_number', { number: number + 1 })
pushEvent("set_number", {number: number + 1})
}
function decrease() {
pushEvent('set_number', { number: number - 1 })
pushEvent("set_number", {number: number - 1})
}
</script>

View file

@ -18,14 +18,15 @@
<ul class="flex flex-col gap-2">
{#each messages as message (message.id)}
<li in:slide class="bg-[#eee] rounded-full px-4 py-2 rounded-bl-none">
<i>{message.name}:</i> {message.body}
<i>{message.name}:</i>
{message.body}
</li>
{/each}
</ul>
<form on:submit|preventDefault={submitMessage}>
<input type="text" name="name" class="rounded" bind:value={name} placeholder="Your Name" />
<input type="text" name="message" class="rounded" bind:value={message} placeholder="Message..."/>
<input type="text" name="message" class="rounded" bind:value={message} placeholder="Message..." />
<button class="bg-black text-white rounded px-4 py-2">Send</button>
</form>
</div>
</div>

View file

@ -1,19 +1,19 @@
<script>
import {fly} from 'svelte/transition'
import {fly} from "svelte/transition"
export let show = true
</script>
<button class="bg-black text-white p-2 rounded" on:click={() => show = !show}>Show/Hide</button>
<button class="bg-black text-white p-2 rounded" on:click={() => (show = !show)}>Show/Hide</button>
{#if show}
<div transition:fly|local={{y: 20}} class="flex flex-col gap-4">
<div class="flex gap-2">
<p>Inner Block: </p>
<p>Inner Block:</p>
<slot>Default</slot>
</div>
<div class="flex gap-2">
<p>Updating named slot: </p>
<p>Updating named slot:</p>
<slot name="the-slot-name">Another default</slot>
</div>
</div>

View file

@ -1,7 +1,8 @@
<script>
import store from './store'
import store from "./store"
</script>
<label>
<input type="checkbox" bind:checked={$store}/> {$store}
</label>
<input type="checkbox" bind:checked={$store} />
{$store}
</label>

View file

@ -1,7 +1,8 @@
<script>
import store from './store'
import store from "./store"
</script>
<label>
<input type="checkbox" bind:checked={$store}/> {$store}
<input type="checkbox" bind:checked={$store} />
{$store}
</label>

View file

@ -1,13 +1,13 @@
import {writable} from 'svelte/store'
import {writable} from "svelte/store"
function createStore() {
return writable(true)
}
function getStore() {
if (typeof window === 'undefined') return createStore()
if (typeof window === "undefined") return createStore()
window.store = window.store || createStore()
return window.store
}
export default store = getStore()
export default store = getStore()