@foxui/visual 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +20 -0
- package/dist/components/confetti/index.d.ts +1 -0
- package/dist/components/confetti/index.js +50 -0
- package/dist/components/excalidraw/Excalidraw.svelte +70 -0
- package/dist/components/excalidraw/Excalidraw.svelte.d.ts +11 -0
- package/dist/components/excalidraw/index.d.ts +1 -0
- package/dist/components/excalidraw/index.js +1 -0
- package/dist/components/gradient/Gradient.svelte +8 -0
- package/dist/components/gradient/Gradient.svelte.d.ts +26 -0
- package/dist/components/gradient/index.d.ts +1 -0
- package/dist/components/gradient/index.js +1 -0
- package/dist/components/image-masonry/ImageMasonry.svelte +94 -0
- package/dist/components/image-masonry/ImageMasonry.svelte.d.ts +18 -0
- package/dist/components/image-masonry/index.d.ts +1 -0
- package/dist/components/image-masonry/index.js +1 -0
- package/dist/components/index.d.ts +7 -0
- package/dist/components/index.js +7 -0
- package/dist/components/phone/Phone.svelte +30 -0
- package/dist/components/phone/Phone.svelte.d.ts +8 -0
- package/dist/components/phone/index.d.ts +1 -0
- package/dist/components/phone/index.js +1 -0
- package/dist/components/quote/Quote.svelte +69 -0
- package/dist/components/quote/Quote.svelte.d.ts +19 -0
- package/dist/components/quote/index.d.ts +1 -0
- package/dist/components/quote/index.js +1 -0
- package/dist/components/undraw/Undraw.svelte +92 -0
- package/dist/components/undraw/Undraw.svelte.d.ts +13 -0
- package/dist/components/undraw/index.d.ts +1 -0
- package/dist/components/undraw/index.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/types.d.ts +1 -0
- package/package.json +76 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License Copyright (c) 2025 flo-bit
|
|
2
|
+
|
|
3
|
+
Permission is hereby granted, free of
|
|
4
|
+
charge, to any person obtaining a copy of this software and associated
|
|
5
|
+
documentation files (the "Software"), to deal in the Software without
|
|
6
|
+
restriction, including without limitation the rights to use, copy, modify, merge,
|
|
7
|
+
publish, distribute, sublicense, and/or sell copies of the Software, and to
|
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to the
|
|
9
|
+
following conditions:
|
|
10
|
+
|
|
11
|
+
The above copyright notice and this permission notice
|
|
12
|
+
(including the next paragraph) shall be included in all copies or substantial
|
|
13
|
+
portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
|
|
16
|
+
ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
|
|
18
|
+
EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
|
|
19
|
+
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
|
20
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
|
21
|
+
THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# 🦊 fox ui
|
|
2
|
+
|
|
3
|
+
svelte 5 + tailwind 4 ui kit, visual components
|
|
4
|
+
|
|
5
|
+
- [Confetti](https://flo-bit.dev/ui-kit/components/visual/confetti)
|
|
6
|
+
- [Excalidraw](https://flo-bit.dev/ui-kit/components/visual/excalidraw)
|
|
7
|
+
- [Image Masonry](https://flo-bit.dev/ui-kit/components/visual/image-masonry)
|
|
8
|
+
- [Phone](https://flo-bit.dev/ui-kit/components/visual/phone)
|
|
9
|
+
- [Quote](https://flo-bit.dev/ui-kit/components/visual/quote)
|
|
10
|
+
- [Undraw](https://flo-bit.dev/ui-kit/components/visual/undraw)
|
|
11
|
+
|
|
12
|
+
> **This is a public alpha release. Expect bugs and breaking changes.**
|
|
13
|
+
|
|
14
|
+
[See all components here](https://flo-bit.dev/ui-kit)
|
|
15
|
+
|
|
16
|
+
For a guide on how to use this ui kit, see the [Quickstart Guide](https://flo-bit.dev/ui-kit/docs/quick-start).
|
|
17
|
+
|
|
18
|
+
Read more about [the philosophy/aim of this project here](https://flo-bit.dev/ui-kit/docs/philosophy).
|
|
19
|
+
|
|
20
|
+
For more information about development, contributing and the like, see the main [README](https://github.com/flo-bit/ui-kit/blob/main/README.md).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export declare const launchConfetti: (color?: string, brightnesses?: (50 | 100 | 200 | 300 | 400 | 500 | 600 | 700 | 800 | 900 | 950)[]) => void;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import confetti from 'canvas-confetti';
|
|
2
|
+
import { convertCSSToHex } from '@foxui/colors';
|
|
3
|
+
function getCSSVar(variable) {
|
|
4
|
+
return getComputedStyle(document.documentElement).getPropertyValue(variable).trim();
|
|
5
|
+
}
|
|
6
|
+
export const launchConfetti = (color = 'accent', brightnesses = [
|
|
7
|
+
300, 400, 500, 800
|
|
8
|
+
]) => {
|
|
9
|
+
const count = 200;
|
|
10
|
+
const defaults = {
|
|
11
|
+
origin: { y: 0.7 }
|
|
12
|
+
};
|
|
13
|
+
const colors = [];
|
|
14
|
+
for (const b of brightnesses) {
|
|
15
|
+
const c = getCSSVar(`--color-${color}-${b}`);
|
|
16
|
+
if (c) {
|
|
17
|
+
colors.push(convertCSSToHex(c));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function fire(particleRatio, opts) {
|
|
21
|
+
confetti({
|
|
22
|
+
...defaults,
|
|
23
|
+
...opts,
|
|
24
|
+
particleCount: Math.floor(count * particleRatio),
|
|
25
|
+
colors
|
|
26
|
+
});
|
|
27
|
+
}
|
|
28
|
+
fire(0.25, {
|
|
29
|
+
spread: 26,
|
|
30
|
+
startVelocity: 55
|
|
31
|
+
});
|
|
32
|
+
fire(0.2, {
|
|
33
|
+
spread: 60
|
|
34
|
+
});
|
|
35
|
+
fire(0.35, {
|
|
36
|
+
spread: 100,
|
|
37
|
+
decay: 0.91,
|
|
38
|
+
scalar: 0.8
|
|
39
|
+
});
|
|
40
|
+
fire(0.1, {
|
|
41
|
+
spread: 120,
|
|
42
|
+
startVelocity: 25,
|
|
43
|
+
decay: 0.92,
|
|
44
|
+
scalar: 1.2
|
|
45
|
+
});
|
|
46
|
+
fire(0.1, {
|
|
47
|
+
spread: 120,
|
|
48
|
+
startVelocity: 45
|
|
49
|
+
});
|
|
50
|
+
};
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { WithElementRef, WithoutChildrenOrChild } from 'bits-ui';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { cn } from '@foxui/core';
|
|
5
|
+
|
|
6
|
+
import { load } from 'cheerio';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
ref = $bindable(null),
|
|
10
|
+
svg,
|
|
11
|
+
alt,
|
|
12
|
+
caption,
|
|
13
|
+
class: className,
|
|
14
|
+
captionClass,
|
|
15
|
+
...restProps
|
|
16
|
+
}: WithElementRef<WithoutChildrenOrChild<HTMLAttributes<HTMLElement>>> & {
|
|
17
|
+
svg: string;
|
|
18
|
+
alt: string;
|
|
19
|
+
caption?: string;
|
|
20
|
+
captionClass?: string;
|
|
21
|
+
} = $props();
|
|
22
|
+
|
|
23
|
+
function modifySvg(svgString: string): string {
|
|
24
|
+
const loadedSvg = load(svgString, { xmlMode: true });
|
|
25
|
+
|
|
26
|
+
const svg = loadedSvg('svg');
|
|
27
|
+
svg.attr('width', '100%');
|
|
28
|
+
svg.attr('height', '100%');
|
|
29
|
+
svg.addClass('w-full h-auto');
|
|
30
|
+
svg.removeAttr('filter');
|
|
31
|
+
|
|
32
|
+
loadedSvg('text').each((_, el) => {
|
|
33
|
+
loadedSvg(el).removeAttr('fill');
|
|
34
|
+
loadedSvg(el).addClass('fill-base-800 dark:fill-base-100');
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
loadedSvg('rect').each((_, el) => {
|
|
38
|
+
loadedSvg(el).removeAttr('fill');
|
|
39
|
+
loadedSvg(el).addClass('fill-accent-600 dark:fill-accent-500');
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
loadedSvg('path').each((_, el) => {
|
|
43
|
+
loadedSvg(el).removeAttr('stroke');
|
|
44
|
+
loadedSvg(el).addClass('stroke-accent-600 dark:stroke-accent-500');
|
|
45
|
+
if (loadedSvg(el).attr('fill') !== 'none') {
|
|
46
|
+
loadedSvg(el).addClass('fill-accent-600 dark:fill-accent-500');
|
|
47
|
+
loadedSvg(el).removeAttr('fill');
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
return loadedSvg.html();
|
|
52
|
+
}
|
|
53
|
+
</script>
|
|
54
|
+
|
|
55
|
+
<figure
|
|
56
|
+
bind:this={ref}
|
|
57
|
+
class={cn('excalidraw-container mx-auto w-full max-w-full overflow-hidden', className)}
|
|
58
|
+
{...restProps}
|
|
59
|
+
>
|
|
60
|
+
<div class="excalidraw-svg w-full" aria-label={alt}>
|
|
61
|
+
{@html modifySvg(svg)}
|
|
62
|
+
</div>
|
|
63
|
+
{#if caption}
|
|
64
|
+
<figcaption
|
|
65
|
+
class={cn('text-base-700 dark:text-base-300 mt-4 text-center text-xs', captionClass)}
|
|
66
|
+
>
|
|
67
|
+
{caption}
|
|
68
|
+
</figcaption>
|
|
69
|
+
{/if}
|
|
70
|
+
</figure>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { WithElementRef, WithoutChildrenOrChild } from 'bits-ui';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type $$ComponentProps = WithElementRef<WithoutChildrenOrChild<HTMLAttributes<HTMLElement>>> & {
|
|
4
|
+
svg: string;
|
|
5
|
+
alt: string;
|
|
6
|
+
caption?: string;
|
|
7
|
+
captionClass?: string;
|
|
8
|
+
};
|
|
9
|
+
declare const Excalidraw: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
10
|
+
type Excalidraw = ReturnType<typeof Excalidraw>;
|
|
11
|
+
export default Excalidraw;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Excalidraw } from './Excalidraw.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Excalidraw } from './Excalidraw.svelte';
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
export default Gradient;
|
|
2
|
+
type Gradient = SvelteComponent<{
|
|
3
|
+
[x: string]: never;
|
|
4
|
+
}, {
|
|
5
|
+
[evt: string]: CustomEvent<any>;
|
|
6
|
+
}, {}> & {
|
|
7
|
+
$$bindings?: string | undefined;
|
|
8
|
+
};
|
|
9
|
+
declare const Gradient: $$__sveltets_2_IsomorphicComponent<{
|
|
10
|
+
[x: string]: never;
|
|
11
|
+
}, {
|
|
12
|
+
[evt: string]: CustomEvent<any>;
|
|
13
|
+
}, {}, {}, string>;
|
|
14
|
+
interface $$__sveltets_2_IsomorphicComponent<Props extends Record<string, any> = any, Events extends Record<string, any> = any, Slots extends Record<string, any> = any, Exports = {}, Bindings = string> {
|
|
15
|
+
new (options: import("svelte").ComponentConstructorOptions<Props>): import("svelte").SvelteComponent<Props, Events, Slots> & {
|
|
16
|
+
$$bindings?: Bindings;
|
|
17
|
+
} & Exports;
|
|
18
|
+
(internal: unknown, props: {
|
|
19
|
+
$$events?: Events;
|
|
20
|
+
$$slots?: Slots;
|
|
21
|
+
}): Exports & {
|
|
22
|
+
$set?: any;
|
|
23
|
+
$on?: any;
|
|
24
|
+
};
|
|
25
|
+
z_$$bindings?: Bindings;
|
|
26
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Gradient } from './Gradient.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Gradient } from './Gradient.svelte';
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { Badge, cn } from '@foxui/core';
|
|
3
|
+
|
|
4
|
+
type Props = {
|
|
5
|
+
images: {
|
|
6
|
+
src: string;
|
|
7
|
+
|
|
8
|
+
name: string;
|
|
9
|
+
href?: string;
|
|
10
|
+
width?: number;
|
|
11
|
+
height?: number;
|
|
12
|
+
onclick?: () => void;
|
|
13
|
+
alt?: string;
|
|
14
|
+
}[];
|
|
15
|
+
interactive?: boolean;
|
|
16
|
+
maxColumns?: number;
|
|
17
|
+
showNames?: boolean;
|
|
18
|
+
class?: string;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const { images, interactive = true, maxColumns = 4, showNames = true, class: className }: Props = $props();
|
|
22
|
+
</script>
|
|
23
|
+
|
|
24
|
+
{#snippet backgroundTitle(name?: string)}
|
|
25
|
+
{#if showNames}
|
|
26
|
+
<div
|
|
27
|
+
class={cn(
|
|
28
|
+
'from-base-950/80 absolute inset-0 z-10 h-full w-full bg-gradient-to-t via-transparent opacity-80',
|
|
29
|
+
interactive
|
|
30
|
+
? 'transition-opacity duration-200 group-hover:opacity-90 group-focus:opacity-100 [@media(pointer:fine)]:opacity-0'
|
|
31
|
+
: ''
|
|
32
|
+
)}
|
|
33
|
+
></div>
|
|
34
|
+
|
|
35
|
+
<div
|
|
36
|
+
class={cn(
|
|
37
|
+
'pointer-events-none absolute inset-0 z-20 flex h-full w-full items-end overflow-hidden px-3 py-2 text-xl font-semibold leading-6 tracking-tight text-white md:px-4 md:py-3',
|
|
38
|
+
interactive
|
|
39
|
+
? 'transition-translation duration-200 group-hover:translate-y-0 [@media(pointer:fine)]:translate-y-20'
|
|
40
|
+
: ''
|
|
41
|
+
)}
|
|
42
|
+
>
|
|
43
|
+
<div class="mt-1 text-sm sm:text-lg">{name}</div>
|
|
44
|
+
</div>
|
|
45
|
+
{:else}
|
|
46
|
+
<div class="absolute inset-0 z-10 h-full w-full"></div>
|
|
47
|
+
<span class="sr-only">{name}</span>
|
|
48
|
+
{/if}
|
|
49
|
+
{/snippet}
|
|
50
|
+
|
|
51
|
+
<div
|
|
52
|
+
class={cn(
|
|
53
|
+
'not-prose gap-x-4',
|
|
54
|
+
maxColumns === 2 ? 'columns-2' : '',
|
|
55
|
+
maxColumns === 3 ? 'columns-2 sm:columns-3' : '',
|
|
56
|
+
maxColumns === 4 ? 'columns-2 sm:columns-3 md:columns-4' : '',
|
|
57
|
+
className
|
|
58
|
+
)}
|
|
59
|
+
>
|
|
60
|
+
{#each images as image}
|
|
61
|
+
<div
|
|
62
|
+
class="group relative mb-4 w-full break-inside-avoid-column overflow-hidden rounded-2xl border border-white/15"
|
|
63
|
+
style={`aspect-ratio: ${image.width ?? 1} / ${image.height ?? 1}`}
|
|
64
|
+
>
|
|
65
|
+
<img
|
|
66
|
+
src={image.src}
|
|
67
|
+
alt={image.alt}
|
|
68
|
+
class={cn(
|
|
69
|
+
'absolute inset-0 h-full w-full rounded-2xl object-cover',
|
|
70
|
+
image.onclick || image.href ? 'transition-all duration-300 group-hover:scale-105' : ''
|
|
71
|
+
)}
|
|
72
|
+
loading="lazy"
|
|
73
|
+
width="270"
|
|
74
|
+
/>
|
|
75
|
+
|
|
76
|
+
{#if image.href}
|
|
77
|
+
<a href={image.href} class="z-20">
|
|
78
|
+
{@render backgroundTitle(image.name)}
|
|
79
|
+
</a>
|
|
80
|
+
{:else if image.onclick}
|
|
81
|
+
<button
|
|
82
|
+
class="z-20 cursor-pointer"
|
|
83
|
+
onclick={() => {
|
|
84
|
+
image.onclick?.();
|
|
85
|
+
}}
|
|
86
|
+
>
|
|
87
|
+
{@render backgroundTitle(image.name)}
|
|
88
|
+
</button>
|
|
89
|
+
{:else}
|
|
90
|
+
{@render backgroundTitle(image.name)}
|
|
91
|
+
{/if}
|
|
92
|
+
</div>
|
|
93
|
+
{/each}
|
|
94
|
+
</div>
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
type Props = {
|
|
2
|
+
images: {
|
|
3
|
+
src: string;
|
|
4
|
+
name: string;
|
|
5
|
+
href?: string;
|
|
6
|
+
width?: number;
|
|
7
|
+
height?: number;
|
|
8
|
+
onclick?: () => void;
|
|
9
|
+
alt?: string;
|
|
10
|
+
}[];
|
|
11
|
+
interactive?: boolean;
|
|
12
|
+
maxColumns?: number;
|
|
13
|
+
showNames?: boolean;
|
|
14
|
+
class?: string;
|
|
15
|
+
};
|
|
16
|
+
declare const ImageMasonry: import("svelte").Component<Props, {}, "">;
|
|
17
|
+
type ImageMasonry = ReturnType<typeof ImageMasonry>;
|
|
18
|
+
export default ImageMasonry;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ImageMasonry } from './ImageMasonry.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as ImageMasonry } from './ImageMasonry.svelte';
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import { cn } from '@foxui/core';
|
|
3
|
+
import type { Snippet } from 'svelte';
|
|
4
|
+
|
|
5
|
+
const { children, class: className }: { children: Snippet; class?: string } = $props();
|
|
6
|
+
</script>
|
|
7
|
+
|
|
8
|
+
<div
|
|
9
|
+
class={cn(
|
|
10
|
+
'bg-accent-500 relative isolate aspect-[350/712] h-[712px] w-[350px] max-w-full rounded-[60px]',
|
|
11
|
+
className
|
|
12
|
+
)}
|
|
13
|
+
>
|
|
14
|
+
<div class="bg-accent-500 absolute top-20 -left-0.5 h-8 w-3 rounded-xs"></div>
|
|
15
|
+
<div class="bg-accent-500 absolute top-[140px] -left-[3px] h-14 w-3 rounded-xs"></div>
|
|
16
|
+
<div class="bg-accent-500 absolute top-[210px] -left-[3px] h-14 w-3 rounded-xs"></div>
|
|
17
|
+
<div class="bg-accent-500 absolute top-[180px] -right-[3px] h-20 w-3 rounded-xs"></div>
|
|
18
|
+
|
|
19
|
+
<div class="absolute top-[18px] z-10 flex w-full justify-center">
|
|
20
|
+
<div class="h-7 w-24 rounded-full bg-black"></div>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="bg-accent-500 absolute top-[180px] -right-[3px] h-20 w-3 rounded-xs"></div>
|
|
24
|
+
|
|
25
|
+
<div class="absolute inset-0 h-full w-full p-3">
|
|
26
|
+
<div class="bg-accent-200 relative h-full w-full overflow-hidden rounded-[50px]">
|
|
27
|
+
{@render children?.()}
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Phone } from './Phone.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Phone } from './Phone.svelte';
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import {
|
|
3
|
+
type WithElementRef,
|
|
4
|
+
type WithoutChildrenOrChild
|
|
5
|
+
} from 'bits-ui';
|
|
6
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
7
|
+
import { cn, Avatar } from '@foxui/core';
|
|
8
|
+
|
|
9
|
+
let {
|
|
10
|
+
quote,
|
|
11
|
+
author,
|
|
12
|
+
role,
|
|
13
|
+
|
|
14
|
+
ref = $bindable(null),
|
|
15
|
+
|
|
16
|
+
useThemeColor = false,
|
|
17
|
+
|
|
18
|
+
class: className,
|
|
19
|
+
...restProps
|
|
20
|
+
}: WithoutChildrenOrChild<WithElementRef<HTMLAttributes<HTMLDivElement>>> & {
|
|
21
|
+
quote?: string;
|
|
22
|
+
author?: {
|
|
23
|
+
src?: string;
|
|
24
|
+
alt?: string;
|
|
25
|
+
fallback?: string;
|
|
26
|
+
|
|
27
|
+
role?: string;
|
|
28
|
+
name?: string;
|
|
29
|
+
|
|
30
|
+
avatarClass?: string;
|
|
31
|
+
fallbackClass?: string;
|
|
32
|
+
imageClass?: string;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
useThemeColor?: boolean;
|
|
36
|
+
} = $props();
|
|
37
|
+
</script>
|
|
38
|
+
|
|
39
|
+
<div
|
|
40
|
+
class={cn(
|
|
41
|
+
'flex flex-col items-start justify-between gap-4 md:flex-row md:items-center md:gap-6',
|
|
42
|
+
className
|
|
43
|
+
)}
|
|
44
|
+
bind:this={ref}
|
|
45
|
+
{...restProps}
|
|
46
|
+
>
|
|
47
|
+
<div class="h-24 w-24 shrink-0 md:h-32 md:w-32">
|
|
48
|
+
<Avatar
|
|
49
|
+
{useThemeColor}
|
|
50
|
+
fallback={author?.fallback}
|
|
51
|
+
src={author?.src}
|
|
52
|
+
alt={author?.alt}
|
|
53
|
+
class="size-24 rounded-2xl object-cover text-3xl md:size-32"
|
|
54
|
+
/>
|
|
55
|
+
</div>
|
|
56
|
+
<div class="flex flex-col gap-2">
|
|
57
|
+
<blockquote class="text-base-900 dark:text-base-50 text-lg font-medium">
|
|
58
|
+
<p>
|
|
59
|
+
"{quote}"
|
|
60
|
+
</p>
|
|
61
|
+
</blockquote>
|
|
62
|
+
|
|
63
|
+
<div class="flex items-center gap-2 text-sm">
|
|
64
|
+
<div class="text-accent-600 dark:text-accent-500 font-medium">{author?.name}</div>
|
|
65
|
+
|
|
66
|
+
<div class="text-base-600 dark:text-base-400">{author?.role}</div>
|
|
67
|
+
</div>
|
|
68
|
+
</div>
|
|
69
|
+
</div>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { type WithElementRef, type WithoutChildrenOrChild } from 'bits-ui';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type $$ComponentProps = WithoutChildrenOrChild<WithElementRef<HTMLAttributes<HTMLDivElement>>> & {
|
|
4
|
+
quote?: string;
|
|
5
|
+
author?: {
|
|
6
|
+
src?: string;
|
|
7
|
+
alt?: string;
|
|
8
|
+
fallback?: string;
|
|
9
|
+
role?: string;
|
|
10
|
+
name?: string;
|
|
11
|
+
avatarClass?: string;
|
|
12
|
+
fallbackClass?: string;
|
|
13
|
+
imageClass?: string;
|
|
14
|
+
};
|
|
15
|
+
useThemeColor?: boolean;
|
|
16
|
+
};
|
|
17
|
+
declare const Quote: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
18
|
+
type Quote = ReturnType<typeof Quote>;
|
|
19
|
+
export default Quote;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Quote } from './Quote.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Quote } from './Quote.svelte';
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
<script lang="ts">
|
|
2
|
+
import type { WithElementRef, WithoutChildrenOrChild } from 'bits-ui';
|
|
3
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
4
|
+
import { cn } from '@foxui/core';
|
|
5
|
+
|
|
6
|
+
import { load, type CheerioAPI } from 'cheerio';
|
|
7
|
+
|
|
8
|
+
let {
|
|
9
|
+
ref = $bindable(null),
|
|
10
|
+
svg,
|
|
11
|
+
alt,
|
|
12
|
+
caption,
|
|
13
|
+
class: className,
|
|
14
|
+
captionClass,
|
|
15
|
+
autoInvert = false,
|
|
16
|
+
colorMap = {},
|
|
17
|
+
...restProps
|
|
18
|
+
}: WithElementRef<WithoutChildrenOrChild<HTMLAttributes<HTMLElement>>> & {
|
|
19
|
+
svg: string;
|
|
20
|
+
alt: string;
|
|
21
|
+
caption?: string;
|
|
22
|
+
captionClass?: string;
|
|
23
|
+
autoInvert?: boolean;
|
|
24
|
+
colorMap?: Record<string, string>;
|
|
25
|
+
} = $props();
|
|
26
|
+
|
|
27
|
+
function applyClasses(loadedSvg: CheerioAPI, el: any) {
|
|
28
|
+
let fill = loadedSvg(el).attr('fill');
|
|
29
|
+
if (!fill) return;
|
|
30
|
+
|
|
31
|
+
if (fill === '#6c63ff') {
|
|
32
|
+
loadedSvg(el).removeAttr('fill');
|
|
33
|
+
loadedSvg(el).addClass('fill-accent-600 dark:fill-accent-500');
|
|
34
|
+
} else if (fill.startsWith('#') && !fill.includes('url') && autoInvert) {
|
|
35
|
+
loadedSvg(el).addClass('dark:invert dark:hue-rotate-180');
|
|
36
|
+
} else if (colorMap[fill]) {
|
|
37
|
+
loadedSvg(el).removeAttr('fill');
|
|
38
|
+
loadedSvg(el).addClass(colorMap[fill]);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function modifySvg(svgString: string): string {
|
|
43
|
+
const loadedSvg = load(svgString, { xmlMode: true });
|
|
44
|
+
|
|
45
|
+
const svg = loadedSvg('svg');
|
|
46
|
+
svg.attr('width', '100%');
|
|
47
|
+
svg.attr('height', '100%');
|
|
48
|
+
svg.addClass('w-full h-auto');
|
|
49
|
+
svg.removeAttr('filter');
|
|
50
|
+
|
|
51
|
+
loadedSvg('path').each((_, el) => {
|
|
52
|
+
applyClasses(loadedSvg, el);
|
|
53
|
+
});
|
|
54
|
+
loadedSvg('text').each((_, el) => {
|
|
55
|
+
applyClasses(loadedSvg, el);
|
|
56
|
+
});
|
|
57
|
+
loadedSvg('rect').each((_, el) => {
|
|
58
|
+
applyClasses(loadedSvg, el);
|
|
59
|
+
});
|
|
60
|
+
loadedSvg('circle').each((_, el) => {
|
|
61
|
+
applyClasses(loadedSvg, el);
|
|
62
|
+
});
|
|
63
|
+
loadedSvg('ellipse').each((_, el) => {
|
|
64
|
+
applyClasses(loadedSvg, el);
|
|
65
|
+
});
|
|
66
|
+
loadedSvg('polygon').each((_, el) => {
|
|
67
|
+
applyClasses(loadedSvg, el);
|
|
68
|
+
});
|
|
69
|
+
loadedSvg('line').each((_, el) => {
|
|
70
|
+
applyClasses(loadedSvg, el);
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
return loadedSvg.html();
|
|
74
|
+
}
|
|
75
|
+
</script>
|
|
76
|
+
|
|
77
|
+
<figure
|
|
78
|
+
bind:this={ref}
|
|
79
|
+
class={cn('excalidraw-container mx-auto w-full max-w-full overflow-hidden', className)}
|
|
80
|
+
{...restProps}
|
|
81
|
+
>
|
|
82
|
+
<div class="excalidraw-svg w-full" aria-label={alt}>
|
|
83
|
+
{@html modifySvg(svg)}
|
|
84
|
+
</div>
|
|
85
|
+
{#if caption}
|
|
86
|
+
<figcaption
|
|
87
|
+
class={cn('text-base-700 dark:text-base-300 mt-4 text-center text-xs', captionClass)}
|
|
88
|
+
>
|
|
89
|
+
{caption}
|
|
90
|
+
</figcaption>
|
|
91
|
+
{/if}
|
|
92
|
+
</figure>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { WithElementRef, WithoutChildrenOrChild } from 'bits-ui';
|
|
2
|
+
import type { HTMLAttributes } from 'svelte/elements';
|
|
3
|
+
type $$ComponentProps = WithElementRef<WithoutChildrenOrChild<HTMLAttributes<HTMLElement>>> & {
|
|
4
|
+
svg: string;
|
|
5
|
+
alt: string;
|
|
6
|
+
caption?: string;
|
|
7
|
+
captionClass?: string;
|
|
8
|
+
autoInvert?: boolean;
|
|
9
|
+
colorMap?: Record<string, string>;
|
|
10
|
+
};
|
|
11
|
+
declare const Undraw: import("svelte").Component<$$ComponentProps, {}, "ref">;
|
|
12
|
+
type Undraw = ReturnType<typeof Undraw>;
|
|
13
|
+
export default Undraw;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Undraw } from './Undraw.svelte';
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default as Undraw } from './Undraw.svelte';
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './components';
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './components';
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './index';
|
package/package.json
ADDED
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@foxui/visual",
|
|
3
|
+
"private": false,
|
|
4
|
+
"version": "0.4.0",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist"
|
|
8
|
+
],
|
|
9
|
+
"sideEffects": [
|
|
10
|
+
"**/*.css"
|
|
11
|
+
],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/types.d.ts",
|
|
15
|
+
"svelte": "./dist/index.js"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"types": "./dist/types.d.ts",
|
|
19
|
+
"svelte": "./dist/index.js",
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"@eslint/compat": "^1.2.5",
|
|
22
|
+
"@eslint/js": "^9.18.0",
|
|
23
|
+
"@sveltejs/adapter-auto": "^6.0.0",
|
|
24
|
+
"@sveltejs/adapter-static": "^3.0.8",
|
|
25
|
+
"@sveltejs/kit": "^2.16.0",
|
|
26
|
+
"@sveltejs/package": "^2.3.11",
|
|
27
|
+
"@sveltejs/vite-plugin-svelte": "^5.0.0",
|
|
28
|
+
"@tailwindcss/forms": "^0.5.9",
|
|
29
|
+
"@tailwindcss/typography": "^0.5.15",
|
|
30
|
+
"@tailwindcss/vite": "^4.1.5",
|
|
31
|
+
"@types/canvas-confetti": "^1.9.0",
|
|
32
|
+
"eslint": "^9.18.0",
|
|
33
|
+
"eslint-config-prettier": "^10.0.1",
|
|
34
|
+
"eslint-plugin-svelte": "^3.0.0",
|
|
35
|
+
"globals": "^16.0.0",
|
|
36
|
+
"prettier": "^3.4.2",
|
|
37
|
+
"prettier-plugin-svelte": "^3.3.3",
|
|
38
|
+
"prettier-plugin-tailwindcss": "^0.6.11",
|
|
39
|
+
"svelte": "^5.0.0",
|
|
40
|
+
"svelte-check": "^4.0.0",
|
|
41
|
+
"tailwindcss": "^4.1.5",
|
|
42
|
+
"typescript": "^5.0.0",
|
|
43
|
+
"typescript-eslint": "^8.20.0",
|
|
44
|
+
"vite": "^6.2.6"
|
|
45
|
+
},
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"bits-ui": "^1.4.3",
|
|
48
|
+
"canvas-confetti": "^1.9.3",
|
|
49
|
+
"cheerio": "^1.0.0",
|
|
50
|
+
"@foxui/core": "0.4.0",
|
|
51
|
+
"@foxui/colors": "0.4.0"
|
|
52
|
+
},
|
|
53
|
+
"peerDependencies": {
|
|
54
|
+
"svelte": ">=5",
|
|
55
|
+
"tailwindcss": ">=3"
|
|
56
|
+
},
|
|
57
|
+
"description": "ui kit - svelte 5 + tailwind 4 - visual components",
|
|
58
|
+
"homepage": "https://flo-bit.dev/ui-kit",
|
|
59
|
+
"repository": {
|
|
60
|
+
"type": "git",
|
|
61
|
+
"url": "git+https://github.com/flo-bit/ui-kit.git"
|
|
62
|
+
},
|
|
63
|
+
"author": "flo-bit (http://flo-bit.dev/)",
|
|
64
|
+
"bugs": "https://github.com/flo-bit/ui-kit/issues",
|
|
65
|
+
"license": "MIT",
|
|
66
|
+
"scripts": {
|
|
67
|
+
"dev": "vite dev",
|
|
68
|
+
"build": "vite build && npm run prepack",
|
|
69
|
+
"build:package": "vite build && npm run prepack",
|
|
70
|
+
"preview": "vite preview",
|
|
71
|
+
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
|
72
|
+
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
|
73
|
+
"format": "prettier --write .",
|
|
74
|
+
"lint": "prettier --check . && eslint ."
|
|
75
|
+
}
|
|
76
|
+
}
|