svelte-micro-components
v0.6.2
Published
`svelte-micro-components` is a JavaScript library for creating small Svelte components that don't deserve their own file.
Downloads
26
Readme
svelte-micro-components
svelte-micro-components
is a JavaScript library for creating small Svelte components that don't deserve their own file.
Currently supports:
- Text props (e.g.
<h1>Hello ${'name'}!</h1>
) - Attribute props (
<input class={name} />
)- Note: not
class="your-class {propClass}"
- Note: not
- Event forwarding (optionally under an alias)
<button ${on`${'click'}`} />
<button ${on`${'click'}=${'otherClick'}`} />
- Slots
- Default:
<div class="wrapper">${slot()}</div>
- Named:
<div class="wrapper">${slot('slotName')}</div>
- Default:
- Actions, including:
- with events:
<div ${use`${outclick}`} />
(the component will emit a fully typedoutclick
event when the user clicks outside of the element) - with props:
<div ${use`${longclick}=${'duration'}`} />
- with events:
- Nested components
<div class="wrapper">${component`<${Nested} />`}</div>
- with props:
<div class="wrapper">${component`<${Nested} prop=${'nested_prop'} />`}</div>
(Nested
will be passed the propnested_prop
) - events are not currently supported
Usage
<script>
import m from 'svelte-micro-components';
const Greeting = m`<h1>Hello ${'name'}!</h1>`;
let name = '';
</script>
<Greeting {name} />
<input class={name} bind:value={name} />
- Micro component props are typesafe, so trying
<Greeting propThatIsntName="world" />
will throw an error. - They also support SSR, so you can use them easily in your SvelteKit app.
- Also note that for attributes, you have to do
class={
your-class ${propClass}}
instead ofclass="your-class {propClass}"
, and that there can't be spaces padding the equals sign.
Examples
Basic text interpolation: Link to REPL: REPL
<script lang="ts">
import m from 'svelte-micro-components';
const Greeting = m`<h1>Hello ${'name'}</h1>`;
</script>
<Greeting name="world" />
Dynamic attributes: REPL
<script lang="ts">
import m from 'svelte-micro-components';
const src = 'https://svelte.dev/tutorial/image.gif';
const name = 'Rick Astley';
const Image = m`<img src=${'src'} alt=${'alt'}>`;
</script>
<Image {src} alt="{name} dancing" />
Nested components: REPL
<script lang="ts">
import m, { component } from 'svelte-micro-components';
import Nested from './Nested.svelte';
const Wrapper = m`<div class="wrapper">${component`<${Nested} />`}</div>`;
</script>
<Wrapper />
DOM events: REPL
<script lang="ts">
let pos = { x: 0, y: 0 };
function handleMousemove(event: MouseEvent) {
pos.x = event.clientX;
pos.y = event.clientY;
}
import m, { on } from 'svelte-micro-components';
const Tracker = m`
<div ${on`${'mousemove'}`}>
The mouse position is ${'x'} x ${'y'}
</div>
`;
</script>
<Tracker x={pos.x} y={pos.y} on:mousemove={handleMousemove} />
Event forwarding: REPL
<script lang="ts">
let pos = { x: 0, y: 0 };
function handleMousemove(event: MouseEvent) {
pos.x = event.clientX;
pos.y = event.clientY;
}
import m, { on } from 'svelte-micro-components';
const Tracker = m`
<div ${on`${'mousemove'}=${'moved'}`}>
The mouse position is ${'x'} x ${'y'}
</div>
`;
</script>
<Tracker x={pos.x} y={pos.y} on:moved={handleMousemove} />
Basic action: REPL
<script context="module" lang="ts">
import type { Action } from 'svelte/action';
const changeColor: Action<HTMLElement, string> = (node, param = 'red') => {
node.style.color = param;
return {
update(parameter) {
node.style.color = parameter;
}
};
};
</script>
<script lang="ts">
import m, { on, use } from 'svelte-micro-components';
const Box = m`
<div ${use`${changeColor}=${'color'}`} ${on`${'click'}`}>
I start off as red, but when you click me I turn green.
</div>
`;
let color = 'rgb(255, 0, 0)';
</script>
<Box on:click={() => (color = 'rgb(0, 255, 0)')} {color} />
Parameterized action (longpress from this official example): REPL
<script context="module" lang="ts">
import type { Action } from 'svelte/action';
declare const longpress: Action<
HTMLElement,
number,
{ 'on:longpress': (e: CustomEvent) => void }
>;
</script>
<script lang="ts">
import m, { on, use } from 'svelte-micro-components';
let pressed = false;
let duration = 2000;
const Button = m`
<button
${use`${longpress}=${'duration'}`}
${on`${'longpress'}`}
${on`${'mouseenter'}`}
>
press and hold
</button>
`;
const Message = m`<p>congratulations, you pressed and held for ${'duration'}ms</p>`;
</script>
<label>
<input type="range" bind:value={duration} max={2000} step={100} />
{duration}ms
</label>
<Button {duration} on:longpress={() => (pressed = true)} on:mouseenter={() => (pressed = false)} />
{#if pressed}
<Message {duration} />
{/if}
Complex action: REPL
<script lang="ts" context="module">
import type { Action } from 'svelte/action';
declare const pannable: Action<
HTMLElement,
null,
{
'on:panstart': CustomEvent<{ x: number; y: number }>;
'on:panmove': CustomEvent<{ x: number; y: number; dx: number; dy: number }>;
'on:panend': CustomEvent<{ x: number; y: number }>;
}
>;
</script>
<script lang="ts">
import { spring } from 'svelte/motion';
const coords = spring({ x: 0, y: 0 }, { stiffness: 0.2, damping: 0.4 });
function handlePanStart() {
coords.stiffness = coords.damping = 1;
}
function handlePanMove(event: CustomEvent<{ x: number; y: number; dx: number; dy: number }>) {
coords.update(($coords) => ({
x: $coords.x + event.detail.dx,
y: $coords.y + event.detail.dy
}));
}
function handlePanEnd() {
coords.stiffness = 0.2;
coords.damping = 0.4;
coords.set({ x: 0, y: 0 });
}
import m, { on, use } from 'svelte-micro-components';
const Box = m`
<div class="box"
${use`${pannable}`}
${on`${'panstart'}`}
${on`${'panmove'}`}
${on`${'panend'}`}
style=${'style'}
></div>
`;
</script>
<Box
on:panstart={handlePanStart}
on:panmove={handlePanMove}
on:panend={handlePanEnd}
style="transform:
translate({$coords.x}px,{$coords.y}px)
rotate({$coords.x * 0.2}deg)"
/>
Slots: REPL
<script>
import m, { slot } from 'svelte-micro-components';
const Box = m`
<div class="box">
${slot()}
</div>
`;
</script>
<Box>
<h2>Hello!</h2>
<p>This is a box. It can contain anything.</p>
</Box>
Named slots: REPL
<script>
import m, { slot } from 'svelte-micro-components';
const ContactCard = m`
<article class="contact-card">
<h2>
${slot('name')}
</h2>
<div class="address">
${slot('address')}
</div>
<div class="email">
${slot('email')}
</div>
</article>
`;
</script>
<ContactCard>
<span slot="name"> P. Sherman </span>
<span slot="address">
42 Wallaby Way<br />
Sydney
</span>
</ContactCard>
Modal: REPL
<script>
let showModal = false;
function dialogShow(node) {
return {
update(showModal) {
if (showModal) node.showModal();
else node.close();
}
}
}
import m, { slot, on, use } from "svelte-micro-components";
const Modal = m`
<dialog
${on`${"close"}`}
${on`${"click"}`}
${use`${dialogShow}=${"showmodal"}`}
>
<div ${on`${"click"}=${'divClick'}`}>
${slot("header")}
<hr />
${slot()}
<hr />
<!-- svelte-ignore a11y-autofocus -->
<button autofocus ${on`${"click"}`}>close modal</button>
</div>
</dialog>
`;
function hide() { showModal = false }
function show() { showModal = true }
function stopPropagation(e) { e.stopPropagation(); }
</script>
<button on:click={show}>
show modal
</button>
<Modal on:close={hide} on:click={hide} on:divClick={stopPropagation} showmodal={showModal}>
<h2 slot="header">
modal
<small><em>adjective</em> mod·al \ˈmō-dəl\</small>
</h2>
<ol class="definition-list">
<li>of or relating to modality in logic</li>
<li>
containing provisions as to the mode of procedure or the manner of taking effect —used of a contract or legacy
</li>
<li>of or relating to a musical mode</li>
<li>of or relating to structure as opposed to substance</li>
<li>of, relating to, or constituting a grammatical form or category characteristically indicating predication</li>
<li>of or relating to a statistical mode</li>
</ol>
<a href="https://www.merriam-webster.com/dictionary/modal">merriam-webster.com</a>
</Modal>