mirror of
https://github.com/Lapikud/lapikud.github.io.git
synced 2026-08-08 18:09:13 +00:00
components and storybook for development svg previews
This commit is contained in:
3
.gitignore
vendored
3
.gitignore
vendored
@@ -22,3 +22,6 @@ dist-ssr
|
|||||||
*.njsproj
|
*.njsproj
|
||||||
*.sln
|
*.sln
|
||||||
*.sw?
|
*.sw?
|
||||||
|
|
||||||
|
*storybook.log
|
||||||
|
storybook-static
|
||||||
|
|||||||
13
.storybook/main.js
Normal file
13
.storybook/main.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
|
||||||
|
|
||||||
|
/** @type { import('@storybook/svelte-vite').StorybookConfig } */
|
||||||
|
const config = {
|
||||||
|
"stories": [
|
||||||
|
"../src/**/*.stories.@(js|ts|svelte)"
|
||||||
|
],
|
||||||
|
"addons": [
|
||||||
|
"@storybook/addon-svelte-csf"
|
||||||
|
],
|
||||||
|
"framework": "@storybook/svelte-vite"
|
||||||
|
};
|
||||||
|
export default config;
|
||||||
13
.storybook/preview.js
Normal file
13
.storybook/preview.js
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
/** @type { import('@storybook/svelte-vite').Preview } */
|
||||||
|
const preview = {
|
||||||
|
parameters: {
|
||||||
|
controls: {
|
||||||
|
matchers: {
|
||||||
|
color: /(background|color)$/i,
|
||||||
|
date: /Date$/i,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export default preview;
|
||||||
1532
package-lock.json
generated
1532
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -6,13 +6,18 @@
|
|||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "vite",
|
"dev": "vite",
|
||||||
"build": "vite build",
|
"build": "vite build",
|
||||||
"preview": "vite preview"
|
"preview": "vite preview",
|
||||||
|
"storybook": "storybook dev -p 6006",
|
||||||
|
"build-storybook": "storybook build"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
"@sveltejs/vite-plugin-svelte": "^6.2.1",
|
||||||
"@types/js-yaml": "^4.0.9",
|
"@types/js-yaml": "^4.0.9",
|
||||||
"svelte": "^5.43.8",
|
"svelte": "^5.43.8",
|
||||||
"vite": "npm:rolldown-vite@7.2.5"
|
"vite": "npm:rolldown-vite@7.2.5",
|
||||||
|
"storybook": "^10.2.15",
|
||||||
|
"@storybook/svelte-vite": "^10.2.15",
|
||||||
|
"@storybook/addon-svelte-csf": "^5.0.11"
|
||||||
},
|
},
|
||||||
"overrides": {
|
"overrides": {
|
||||||
"vite": "npm:rolldown-vite@7.2.5"
|
"vite": "npm:rolldown-vite@7.2.5"
|
||||||
|
|||||||
@@ -1,10 +1,11 @@
|
|||||||
<!--
|
<!--
|
||||||
Multi-purpose image/SVG component with optional link wrapper,
|
Multi-purpose image component with optional link wrapper,
|
||||||
hover effects, size variants, and object-fit options.
|
hover effects, size variants, and object-fit options. Includes support for WebP format with fallback to standard formats.
|
||||||
-->
|
-->
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
export let src = "";
|
export let webpSrc = ""; // Optional WebP source for browsers that support it
|
||||||
|
export let src = ""; // Fallback (some devices don't support WebP) or primary source if webpSrc is not provided
|
||||||
export let alt = "";
|
export let alt = "";
|
||||||
export let href = null; // optional link URL
|
export let href = null; // optional link URL
|
||||||
export let target = "_blank";
|
export let target = "_blank";
|
||||||
@@ -13,6 +14,12 @@ hover effects, size variants, and object-fit options.
|
|||||||
export let hover = false;
|
export let hover = false;
|
||||||
export let className = "";
|
export let className = "";
|
||||||
|
|
||||||
|
// optional srcset for responsive images (w descriptors means width-based, x descriptors means pixel density-based)
|
||||||
|
// e.g. "image-400.jpg 400w, image-800.jpg 800w"
|
||||||
|
export let srcSet = ""; // always set src if using this
|
||||||
|
export let webpSrcSet = ""; // always set src if using this
|
||||||
|
export let pictureClass = ""; // CSS/Tailwind classes applied to the <picture> element
|
||||||
|
|
||||||
$: objectFitClass = {
|
$: objectFitClass = {
|
||||||
contain: "object-contain",
|
contain: "object-contain",
|
||||||
cover: "object-cover",
|
cover: "object-cover",
|
||||||
@@ -25,22 +32,29 @@ hover effects, size variants, and object-fit options.
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if href}
|
{#if href}
|
||||||
<a
|
<a {href} {target} {rel} class="inline-block leading-none">
|
||||||
{href}
|
<picture class={pictureClass}>
|
||||||
{target}
|
{#if webpSrc || webpSrcSet}
|
||||||
{rel}
|
<source srcset={webpSrcSet || webpSrc} type="image/webp" />
|
||||||
class="inline-block leading-none"
|
{/if}
|
||||||
>
|
|
||||||
<img
|
<img
|
||||||
{src}
|
{src}
|
||||||
{alt}
|
srcset={srcSet || undefined}
|
||||||
|
alt={alt}
|
||||||
class="block h-auto max-w-full {objectFitClass} {hoverClasses} {className}"
|
class="block h-auto max-w-full {objectFitClass} {hoverClasses} {className}"
|
||||||
/>
|
/>
|
||||||
|
</picture>
|
||||||
</a>
|
</a>
|
||||||
{:else}
|
{:else}
|
||||||
|
<picture class={pictureClass}>
|
||||||
|
{#if webpSrc || webpSrcSet}
|
||||||
|
<source srcset={webpSrcSet || webpSrc} type="image/webp" />
|
||||||
|
{/if}
|
||||||
<img
|
<img
|
||||||
{src}
|
{src}
|
||||||
{alt}
|
srcset={srcSet || undefined}
|
||||||
|
alt={alt}
|
||||||
class="block h-auto max-w-full {objectFitClass} {hoverClasses} {className}"
|
class="block h-auto max-w-full {objectFitClass} {hoverClasses} {className}"
|
||||||
/>
|
/>
|
||||||
|
</picture>
|
||||||
{/if}
|
{/if}
|
||||||
19
src/components/README.md
Normal file
19
src/components/README.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# SVG previews
|
||||||
|
|
||||||
|
use `npm run storybook` which opens a browser window with all the SVG variations. You can also edit the SVG properties in storybook controls.
|
||||||
|
|
||||||
|
# Components
|
||||||
|
|
||||||
|
under components are building blocks that are not layout related.
|
||||||
|
|
||||||
|
## details
|
||||||
|
|
||||||
|
- Button: a simple button component with some styling.
|
||||||
|
- Dropdown: a dropdown component that can be used to show/hide content.
|
||||||
|
- Slideshow: a simple slideshow component that can be used to show multiple images in a slideshow format. Includes options to enable/disable autoplay and to set the autoplay interval.
|
||||||
|
- Svg: a component that renders different SVG icons based on the `type` prop. See the comment after the `type` prop in `Svg.svelte` for the available types.
|
||||||
|
- Image: a component that renders an image with some styling. It accepts a lot of props. Main ones are `src` (the main image source that is used if anything else fails or not provided), `alt` (the alt text for the image), `srcSet` (a string of comma-separated image sources for different screen sizes, jpg or png files), `webpSrc` (the webp image source as a webp file), `webpSrcSet` (a string of comma-separated webp image sources for different screen sizes). We use 2 webp images: 400 and 800 width, and one jpg image 800 width as default src for fallback.
|
||||||
|
|
||||||
|
# Layout components
|
||||||
|
|
||||||
|
under components/layout are components that are used to structure the page, such as Container, Stack, Center, etc.
|
||||||
54
src/components/Svg.svelte
Normal file
54
src/components/Svg.svelte
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
<script>
|
||||||
|
export let type; // "connector", "mirrorV", "mirrorH", "mirrorVH", "branch"
|
||||||
|
export let className = "";
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#if type === "connector"}
|
||||||
|
<svg viewBox="0 0 182 66" class={className} fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M0 63.5H67.5L102.5 15.5H156" stroke="currentColor" stroke-width="5"/>
|
||||||
|
<circle cx="166" cy="15.5" r="10" stroke="currentColor" stroke-width="5"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{:else if type === "mirrorV"}
|
||||||
|
<svg viewBox="0 0 182 66" class={className} fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="scale(1,-1) translate(0,-66)">
|
||||||
|
<path d="M0 63.5H67.5L102.5 15.5H156" stroke="currentColor" stroke-width="5"/>
|
||||||
|
<circle cx="166" cy="15.5" r="10" stroke="currentColor" stroke-width="5"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{:else if type === "mirrorH"}
|
||||||
|
<svg viewBox="0 0 182 66" class={className} fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="scale(-1,1) translate(-182,0)">
|
||||||
|
<path d="M0 63.5H67.5L102.5 15.5H156" stroke="currentColor" stroke-width="5"/>
|
||||||
|
<circle cx="166" cy="15.5" r="10" stroke="currentColor" stroke-width="5"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{:else if type === "mirrorVH"}
|
||||||
|
<svg viewBox="0 0 182 66" class={className} fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="scale(-1,-1) translate(-182,-66)">
|
||||||
|
<path d="M0 63.5H67.5L102.5 15.5H156" stroke="currentColor" stroke-width="5"/>
|
||||||
|
<circle cx="166" cy="15.5" r="10" stroke="currentColor" stroke-width="5"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{:else if type === "branch"}
|
||||||
|
<svg viewBox="0 0 75 74" class={className} fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<path d="M0 31.4784H46L68 1.47842M45.5 30.9784L66.5 63.9784"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="5"/>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{:else if type === "branchH"}
|
||||||
|
<svg viewBox="0 0 75 74" class={className} fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g transform="scale(-1,1) translate(-75,0)">
|
||||||
|
<path d="M0 31.4784H46L68 1.47842M45.5 30.9784L66.5 63.9784"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="5"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
|
||||||
|
{:else}
|
||||||
|
<div>Unknown type: {type}</div>
|
||||||
|
{/if}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
// layout components
|
||||||
export { default as Container } from './layout/Container.svelte'
|
export { default as Container } from './layout/Container.svelte'
|
||||||
export { default as Card } from './layout/Card.svelte'
|
export { default as Card } from './layout/Card.svelte'
|
||||||
export { default as Stack } from './layout/Stack.svelte'
|
export { default as Stack } from './layout/Stack.svelte'
|
||||||
@@ -5,6 +6,9 @@ export { default as Grid } from './layout/Grid.svelte'
|
|||||||
export { default as Center } from './layout/Center.svelte'
|
export { default as Center } from './layout/Center.svelte'
|
||||||
export { default as Section } from './layout/Section.svelte'
|
export { default as Section } from './layout/Section.svelte'
|
||||||
|
|
||||||
|
// components
|
||||||
export { default as Button } from './Button.svelte'
|
export { default as Button } from './Button.svelte'
|
||||||
export { default as Dropdown } from './Dropdown.svelte'
|
export { default as Dropdown } from './Dropdown.svelte'
|
||||||
export { default as Slideshow } from './Slideshow.svelte'
|
export { default as Slideshow } from './Slideshow.svelte'
|
||||||
|
export { default as Svg } from './Svg.svelte'
|
||||||
|
export { default as Image } from './Image.svelte'
|
||||||
|
|||||||
@@ -7,6 +7,7 @@
|
|||||||
|
|
||||||
const paddingClasses = {
|
const paddingClasses = {
|
||||||
none: "py-0",
|
none: "py-0",
|
||||||
|
tight: "py-2 md:py-3",
|
||||||
small: "py-4 md:py-8",
|
small: "py-4 md:py-8",
|
||||||
normal: "py-12 max-md:py-8",
|
normal: "py-12 max-md:py-8",
|
||||||
large: "py-[clamp(3rem,8vw,6rem)] max-md:py-12",
|
large: "py-[clamp(3rem,8vw,6rem)] max-md:py-12",
|
||||||
|
|||||||
@@ -105,9 +105,9 @@
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<Section className="text-black" background="orange" padding="small">
|
<Section className="text-black" background="orange" padding="tight">
|
||||||
{#if $text && Object.keys($text).length > 0}
|
{#if $text && Object.keys($text).length > 0}
|
||||||
<div class="flex items-center w-full relative min-h-16">
|
<div class="flex items-center w-full relative min-h-12">
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
{#if currentPath !== "/"}
|
{#if currentPath !== "/"}
|
||||||
<div class="absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 md:relative md:left-auto md:translate-x-0 md:top-auto md:translate-y-0">
|
<div class="absolute left-1/2 -translate-x-1/2 top-1/2 -translate-y-1/2 md:relative md:left-auto md:translate-x-0 md:top-auto md:translate-y-0">
|
||||||
|
|||||||
51
src/stories/Icon.stories.js
Normal file
51
src/stories/Icon.stories.js
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
import Icon from '../components/Svg.svelte';
|
||||||
|
|
||||||
|
export default {
|
||||||
|
title: 'SVG/Icon',
|
||||||
|
component: Icon,
|
||||||
|
argTypes: {
|
||||||
|
type: { control: { type: 'select', options: ['connector','mirrorV','mirrorH','mirrorVH','branch','branchH'] } },
|
||||||
|
className: { control: 'text' },
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const Template = (args) => ({
|
||||||
|
Component: Icon,
|
||||||
|
props: args,
|
||||||
|
});
|
||||||
|
|
||||||
|
export const Default = Template.bind({});
|
||||||
|
Default.args = {
|
||||||
|
type: 'connector',
|
||||||
|
className: 'w-64 text-cyan-500',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MirrorV = Template.bind({});
|
||||||
|
MirrorV.args = {
|
||||||
|
type: 'mirrorV',
|
||||||
|
className: 'w-64 text-green-500',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MirrorH = Template.bind({});
|
||||||
|
MirrorH.args = {
|
||||||
|
type: 'mirrorH',
|
||||||
|
className: 'w-64 text-yellow-500',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const MirrorVH = Template.bind({});
|
||||||
|
MirrorVH.args = {
|
||||||
|
type: 'mirrorVH',
|
||||||
|
className: 'w-64 text-pink-500',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Branch = Template.bind({});
|
||||||
|
Branch.args = {
|
||||||
|
type: 'branch',
|
||||||
|
className: 'w-48 text-magenta-500',
|
||||||
|
};
|
||||||
|
|
||||||
|
export const BranchH = Template.bind({});
|
||||||
|
BranchH.args = {
|
||||||
|
type: 'branchH',
|
||||||
|
className: 'w-48 text-magenta-500',
|
||||||
|
};
|
||||||
Reference in New Issue
Block a user