128 lines
2.8 KiB
Plaintext
128 lines
2.8 KiB
Plaintext
// Image -- a framed image with a reserved aspect ratio.
|
|
//
|
|
// <Image src="/hero.jpg" alt="Dashboard" size="video" rounded={true} />
|
|
//
|
|
// The frame keeps its ratio whether or not the image has loaded, so the page
|
|
// does not jump when it arrives. Without a src it renders a labelled
|
|
// placeholder of the same shape rather than collapsing.
|
|
//
|
|
// NOTE: the style block uses /* */ comments only -- // is not a CSS comment
|
|
// and silently swallows the rule that follows it.
|
|
component Image {
|
|
props {
|
|
size: string = "default"
|
|
color: string = "primary"
|
|
src: string = ""
|
|
alt: string = ""
|
|
width: string = ""
|
|
height: string = ""
|
|
loading: string = "lazy"
|
|
fit: string = "cover"
|
|
rounded: boolean = false
|
|
class: string = ""
|
|
}
|
|
|
|
view {
|
|
<figure
|
|
{...attrs}
|
|
data-ui-component="Image"
|
|
class='wrn-image {class}'
|
|
data-size='{size}'
|
|
data-color='{color}'
|
|
data-fit='{fit}'
|
|
data-rounded='{rounded}'
|
|
>
|
|
<img
|
|
class="wrn-image__img"
|
|
data-show="src"
|
|
src='{src}'
|
|
alt='{alt}'
|
|
width='{width}'
|
|
height='{height}'
|
|
loading='{loading}'
|
|
decoding="async"
|
|
/>
|
|
|
|
<div
|
|
class="wrn-image__placeholder"
|
|
data-show="!src"
|
|
role="img"
|
|
aria-label='{alt || "Image placeholder"}'
|
|
>
|
|
<span class="icon-[lucide--image] wrn-image__placeholder-icon" aria-hidden="true"></span>
|
|
</div>
|
|
|
|
<slot></slot>
|
|
</figure>
|
|
}
|
|
|
|
style {
|
|
.wrn-image {
|
|
position: relative;
|
|
margin: 0;
|
|
overflow: hidden;
|
|
width: 100%;
|
|
min-width: 0;
|
|
background: var(--wrn-color-surface-soft);
|
|
}
|
|
|
|
.wrn-image[data-rounded="true"] {
|
|
border-radius: var(--wrn-radius-lg);
|
|
}
|
|
|
|
/*
|
|
* The ratio sits on the frame rather than the image, so the space is
|
|
* reserved before the file arrives and the page does not jump.
|
|
*/
|
|
.wrn-image[data-size="square"] {
|
|
aspect-ratio: 1 / 1;
|
|
}
|
|
|
|
.wrn-image[data-size="video"] {
|
|
aspect-ratio: 16 / 9;
|
|
}
|
|
|
|
.wrn-image[data-size="landscape"] {
|
|
aspect-ratio: 4 / 3;
|
|
}
|
|
|
|
.wrn-image[data-size="portrait"] {
|
|
aspect-ratio: 3 / 4;
|
|
}
|
|
|
|
.wrn-image__img {
|
|
display: block;
|
|
width: 100%;
|
|
height: 100%;
|
|
object-fit: cover;
|
|
}
|
|
|
|
.wrn-image[data-fit="contain"] .wrn-image__img {
|
|
object-fit: contain;
|
|
}
|
|
|
|
.wrn-image[data-fit="fill"] .wrn-image__img {
|
|
object-fit: fill;
|
|
}
|
|
|
|
.wrn-image[data-size="default"] .wrn-image__img {
|
|
height: auto;
|
|
}
|
|
|
|
.wrn-image__placeholder {
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
width: 100%;
|
|
min-height: 12rem;
|
|
height: 100%;
|
|
color: var(--wrn-color-text-muted);
|
|
}
|
|
|
|
.wrn-image__placeholder-icon {
|
|
width: 2rem;
|
|
height: 2rem;
|
|
}
|
|
}
|
|
}
|