Skip to content

Commit 83fd8b2

Browse files
committed
feat: add CImage
1 parent 24491dd commit 83fd8b2

File tree

9 files changed

+179
-0
lines changed

9 files changed

+179
-0
lines changed

.changeset/twenty-forks-burn.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@casual-ui/svelte': minor
3+
---
4+
5+
feat: add CImage

packages/docs/components.d.ts

+1
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ declare global {
1414
const CExpansion: typeof import("@casual-ui/svelte")["CExpansion"]
1515
const CForm: typeof import("@casual-ui/svelte")["CForm"]
1616
const CFormItem: typeof import("@casual-ui/svelte")["CFormItem"]
17+
const CImage: typeof import("@casual-ui/svelte")["CImage"]
1718
const CInfoItem: typeof import("@casual-ui/svelte")["CInfoItem"]
1819
const CInput: typeof import("@casual-ui/svelte")["CInput"]
1920
const CList: typeof import("@casual-ui/svelte")["CList"]

packages/docs/config/sidebar.ts

+4
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@ export default {
3939
title: 'Icon',
4040
to: '/features/components/basic/icon/',
4141
},
42+
{
43+
title: 'Image',
44+
to: '/features/components/basic/image/',
45+
},
4246
],
4347
},
4448
{
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
---
2+
title: Image
3+
componentName: CImage
4+
---
5+
6+
## Basic
7+
8+
```svelte live
9+
<script>
10+
let src = 'https://picsum.photos/500/300'
11+
12+
const toggle = () => {
13+
src = `https://picsum.photos/500/300?t=${Math.random()}`
14+
}
15+
</script>
16+
<CButton label="Toggle image" on:click={toggle} />
17+
<CImage width="500px" height="300px" {src} />
18+
```
19+
20+
## Custom placeholder
21+
22+
```svelte live
23+
<script>
24+
let show = true
25+
</script>
26+
<CButton label="Toggle image" on:click={() => show = !show} />
27+
{#if show}
28+
<CImage width="500px" height="300px" src="https://picsum.photos/500/300" placeholderSrc="data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMzIgMzIiPjxwYXRoIGZpbGw9IiMyZGNjOWYiIGQ9Ik0zMCA1Ljg1MXYyMC4yOThIMlY1Ljg1MWgyOCIvPjxwYXRoIGZpbGw9IiNmZmYiIGQ9Ik0yNC4yMzIgOC41NDFhMi4yIDIuMiAwIDEgMCAxLjEyNy42MjNhMi4yMTIgMi4yMTIgMCAwIDAtMS4xMjctLjYyM00xOC4xMTEgMjAuMXEtMi43MjQtMy43ODgtNS40NS03LjU3NUw0LjU3OSAyMy43NjZoMTAuOXExLjMxNi0xLjgzMiAyLjYzNC0zLjY2M00yMi4wNTcgMTZxLTIuNzkzIDMuODgyLTUuNTg0IDcuNzY1aDExLjE2OVEyNC44NTEgMTkuODgyIDIyLjA1NyAxNloiLz48L3N2Zz4=" />
29+
{/if}
30+
```
31+
32+
## Custom loading
33+
34+
```svelte live
35+
<script>
36+
let src = 'https://picsum.photos/500/300'
37+
38+
const toggle = () => {
39+
src = `https://picsum.photos/500/300?t=${Math.random()}`
40+
}
41+
</script>
42+
<CButton label="Toggle image" on:click={toggle} />
43+
<div class="flex gap-4 mt-4">
44+
<CImage width="100px" height="100px" {src}>
45+
<div class="text-white">
46+
<CLoadingLattice slot="loading" />
47+
</div>
48+
</CImage>
49+
<CImage width="100px" height="100px" {src}>
50+
<CLoadingPuff slot="loading" />
51+
</CImage>
52+
<CImage width="100px" height="100px" {src}>
53+
<CLoadingPie slot="loading" />
54+
</CImage>
55+
</div>
56+
```
57+
58+
:::important[Img native attrs]
59+
All the props pass to `CImage` that not in prop list would directly add to `<img />` tag. You can add `decoding`, `draggable`, etc..
60+
:::

packages/docs/vite.config.ts

+1
Original file line numberDiff line numberDiff line change
@@ -149,6 +149,7 @@ export default defineConfig({
149149
'CPagination',
150150
'CTabs',
151151
'CTabItem',
152+
'CImage',
152153
],
153154
defaultImport: false,
154155
},

packages/ui/import-icon.ts

+2
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,15 @@ import CCarouselSlider from './src/components/carousel/CCarouselSlider.svelte'
5353
import CTree from './src/components/tree/CTree.svelte'
5454
import CTabItem from './src/components/tabs/CTabItem.svelte'
5555
import { attributeAtom } from './src/utils/attributeAtom'
56+
import CImage from './src/components/CImage.svelte'
5657

5758
export { useFormProps } from './src/hooks/useForm'
5859

5960
export {
6061
attributeAtom,
6162
CCarouselSlider,
6263
CTree,
64+
CImage,
6365
CTabItem,
6466
CCarousel,
6567
CTable,

packages/ui/import-style.ts

+2
Original file line numberDiff line numberDiff line change
@@ -54,11 +54,13 @@ import CCarouselSlider from './src/components/carousel/CCarouselSlider.svelte'
5454
import CTree from './src/components/tree/CTree.svelte'
5555
import { attributeAtom } from './src/utils/attributeAtom'
5656
import CTabItem from './src/components/tabs/CTabItem.svelte'
57+
import CImage from './src/components/CImage.svelte'
5758

5859
export { useFormProps } from './src/hooks/useForm'
5960

6061
export {
6162
attributeAtom,
63+
CImage,
6264
CCarouselSlider,
6365
CTree,
6466
CTabItem,
+102
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
<script>
2+
import { onMount } from 'svelte'
3+
import CLoading from './CLoading.svelte'
4+
5+
/**
6+
* The image src
7+
* @type {string}
8+
*/
9+
export let src
10+
11+
/**
12+
* The default image src
13+
* @type
14+
*/
15+
export let placeholderSrc =
16+
'data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIxZW0iIGhlaWdodD0iMWVtIiB2aWV3Qm94PSIwIDAgMTYgMTYiPjxnIGZpbGw9ImN1cnJlbnRDb2xvciI+PHBhdGggZD0iTTYuMDAyIDUuNWExLjUgMS41IDAgMSAxLTMgMGExLjUgMS41IDAgMCAxIDMgMHoiLz48cGF0aCBkPSJNMi4wMDIgMWEyIDIgMCAwIDAtMiAydjEwYTIgMiAwIDAgMCAyIDJoMTJhMiAyIDAgMCAwIDItMlYzYTIgMiAwIDAgMC0yLTJoLTEyem0xMiAxYTEgMSAwIDAgMSAxIDF2Ni41bC0zLjc3Ny0xLjk0N2EuNS41IDAgMCAwLS41NzcuMDkzbC0zLjcxIDMuNzFsLTIuNjYtMS43NzJhLjUuNSAwIDAgMC0uNjMuMDYyTDEuMDAyIDEyVjNhMSAxIDAgMCAxIDEtMWgxMnoiLz48L2c+PC9zdmc+'
17+
18+
/**
19+
* The image width
20+
* @type {string}
21+
*/
22+
export let width = '100%'
23+
24+
/**
25+
* The image height
26+
* @type {string}
27+
*/
28+
export let height = '300px'
29+
30+
/**
31+
* The alt prop
32+
* @type {string}
33+
*/
34+
export let alt = ''
35+
36+
/**
37+
* The class that apply to img tag
38+
* @type {string}
39+
*/
40+
export let imgClass = ''
41+
42+
/**
43+
* Custom image request headers
44+
* @type {object}
45+
*/
46+
export let customHeaders = {}
47+
48+
/**
49+
* The last successfully loaded image src
50+
*/
51+
let lastSrc = placeholderSrc
52+
let mounted = false
53+
54+
const fetchImage = () => {
55+
if (!mounted) return
56+
return fetch(src, {
57+
method: 'GET',
58+
headers: customHeaders,
59+
})
60+
.then(r => r.blob())
61+
.then(blob => {
62+
lastSrc = URL.createObjectURL(blob)
63+
return lastSrc
64+
})
65+
}
66+
let imagePromise
67+
68+
onMount(() => {
69+
mounted = true
70+
imagePromise = fetchImage()
71+
})
72+
73+
$: {
74+
src
75+
imagePromise = fetchImage()
76+
}
77+
</script>
78+
79+
<div
80+
class="c-image"
81+
style:width
82+
style:height
83+
style:background-image="url({lastSrc})"
84+
>
85+
{#if imagePromise}
86+
{#await imagePromise}
87+
<div class="c-image--loading">
88+
<slot name="loading">
89+
<CLoading />
90+
</slot>
91+
</div>
92+
{:then src}
93+
<img {src} {alt} {width} class="{imgClass}" {...$$restProps} />
94+
{:catch err}
95+
<slot name="err" {err}>
96+
<div>
97+
{err}
98+
</div>
99+
</slot>
100+
{/await}
101+
{/if}
102+
</div>

packages/ui/src/index.ts

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import CImage from './components/CImage.svelte'
12
import CButton from './components/CButton.svelte'
23
import CAjaxBar from './components/CAjaxBar.svelte'
34
import CExpansion from './components/CExpansion.svelte'
@@ -61,6 +62,7 @@ export { useFormProps } from './hooks/useForm'
6162
export {
6263
attributeAtom,
6364
CTabItem,
65+
CImage,
6466
CPagination,
6567
CTree,
6668
CParallax,

0 commit comments

Comments
 (0)