-loading class added when image is being uploaded
upon completion, -uploadComplete is added, -loading is removed
Add the is-invalid class to the component, include the invalid-feedback after the component and wrap it in a form-group
<div class="form-group">
<div class="sds-imgUpload -box -small is-invalid">
<div class="sds-imgUpload__content">
<input type="file" id="imgUpload" class="sr-only">
<div class="sds-imgUpload__iconWrapper">
<span class="sds-iconCircle -secondary10 sds-imgUpload__icon" aria-hidden="true">
<span class="sds-icon sds-icon-documentupload"></span>
</span>
<div class="sds-imgUpload__progress">
<div class="sds-circularProgress">
<svg viewBox="0 0 36 36" class="">
<path class="sds-circularProgress__shape" stroke-dasharray="50, 100" d="M18 2.0845
a 15.9155 15.9155 0 0 1 0 31.831
a 15.9155 15.9155 0 0 1 0 -31.831" />
</svg>
</div>
</div>
</div>
<div class="sds-stackXxs">
<div class="sds-imgUpload__title">
<label for="imgUpload" class="sds-imgUpload__label stretched-link">Upload file(s)</label>
</div>
<div class="sds-imgUpload__meta sds-textHelper">{file_name.format (Weight)}</div>
</div>
</div>
<button type="button" class="sds-btn -iconBtn -btnSecondary sds-imgUpload__reset">
<span class="sds-icon sds-icon-trash"></span>
<div class="sr-only">Delete uploaded image</div>
</button>
</div>
<div class="invalid-feedback">Error message</div>
</div>
<div class="form-group">
{% render "@snet-img-upload--box-small",{classes: ["is-invalid"]},true %}
<div class="invalid-feedback">Error message</div>
</div>
/* variables specific to current element */
$img-upload-dimensions: map-deep-get($token-sizes-component-map, "image-upload");
$img-upload-box-dimensions: 280px;
$img-upload-box-max-dimensions: calc(80vh - #{$nav-topbar-height-global} - #{map-deep-get($token-sizes-component-map, "panel-header-height")} - #{$frame-content-bottom-bar-min-height-global});
$img-upload-box-big-dimensions: 466px;
.#{$namespace}imgUpload {
/* Save root element context for easy access if nesting is needed */
$self: &;
/* properties of current element + media queries */
width: $img-upload-dimensions;
height: $img-upload-dimensions;
border: $border-width*2 dashed;
@include custom-prop-fallback("border-color", "sys-color-text-primary-muted");
border-radius: map-deep-get($token-radius-map, "circle");
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
text-align: center;
position: relative;
/* Pseudo Elements */
&::before {
}
&::after {
}
/*
Include elements that are linked to the current element but have to reside at the root level of the stylesheet
(e.g: keyframes)
*/
@at-root {
}
/* children - write selector in full in comments in order to facilitate search */
// imgUpload__meta
&__meta {
// follows same logic as parent
@include custom-prop-fallback("color", "sys-color-text-primary-muted");
}
&__iconWrapper {
position: relative;
pointer-events: none;
margin-bottom: map-deep-get($token-spacer-unit-map, "8");
}
&__progress {
width: 46px; // so loading bar doesn't overlap icon circle
display: none;
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
}
&__action {
cursor: pointer;
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
&::after {
content: "";
position: absolute;
z-index: z("zero");
top: 0;
left: 0;
width: 100%;
height: 100%;
border-radius: map-deep-get($token-radius-map, "circle");
}
}
&__reset#{$self}__reset {
display: none;
position: absolute;
z-index: z("low");
top: 100%;
left: 50%;
transform: translate(-50%,-50%);
}
&__title {
font-weight: map-deep-get($token-font-weight-map, "semi-bold");
}
&__label {
cursor: pointer;
&::after {
border-radius: map-deep-get($token-radius-map, "circle");
}
}
/* modifiers */
// imgUpload -uploadComplete
&.-uploadComplete {
// follows same logic as base element
@include centered-bgi;
background-clip: content-box;
#{$self}__content {
display: none;
}
#{$self}__reset {
display: block;
}
}
&.-loading {
position: relative;
z-index: z("low");
&::before {
content: "";
position: absolute;
z-index: z("negative");
top: 0;
left: 0;
width: 100%;
height: 100%;
@extend %skeletonUnit;
border-radius: inherit;
}
#{$self}__action {
display: none;
}
#{$self}__label {
&::after {
pointer-events: none;
}
}
}
&.-box {
width: auto;
height: $img-upload-box-dimensions;
border-radius: map-deep-get($token-radius-map, "16");
&:not(.-small) {
#{$self}__icon {
width: map-deep-get($token-spacer-unit-map, "64");
height: map-deep-get($token-spacer-unit-map, "64");
}
}
#{$self}__meta {
@include custom-prop-fallback("color", "sys-color-text-neutral-contrast");
}
#{$self}__content {
display: block;
}
#{$self}__reset {
position: static;
transform: none;
margin-top: map-deep-get($token-spacer-stack-max-map, "md");
}
#{$self}__iconWrapper {
margin-bottom: map-deep-get($token-spacer-unit-map, "16");
}
#{$self}__label {
&::after {
border-radius: map-deep-get($token-radius-map, "16");
}
}
&.-loading {
@include custom-prop-fallback("background-color", "sys-color-background-primary-10");
@include custom-prop-fallback("border-color", "sys-color-border-primary-strong");
#{$self}__icon {
@include custom-prop-fallback("background-color", "sys-color-elevation-surface-flat");
}
#{$self}__progress {
width: 76px;
}
}
&.-uploadComplete {
@include custom-prop-fallback("background", "sys-color-background-primary-6");
#{$self}__icon {
@include custom-prop-fallback("background-color", "sys-color-elevation-surface-flat");
@include custom-prop-fallback("color", "sys-color-border-primary-strong");
}
}
&.-big {
min-height: $img-upload-box-dimensions;
height: $img-upload-box-big-dimensions;
max-height: $img-upload-box-max-dimensions;
}
&.-small {
height: auto;
@include spacer-component-inset("md");
@include custom-prop-fallback("border-color", "sys-color-background-primary-30");
align-items: flex-start;
justify-content: flex-start;
max-height: none;
#{$self}__iconWrapper {
margin-bottom: 0;
margin-right: map-deep-get($token-spacer-inline-map, "md");
}
#{$self}__content {
display: flex;
text-align: left;
align-items: flex-start;
}
&:hover {
@media (hover: hover) {
@include custom-prop-fallback("border-color", "sys-color-border-secondary-strong");
}
}
&.-loading {
@include custom-prop-fallback("border-color", "sys-color-border-primary-strong");
#{$self}__progress {
width: map-deep-get($token-spacer-unit-map, "48");
}
}
&.-uploadComplete {
flex-direction: row;
align-items: flex-start;
justify-content: space-between;
border: 0;
#{$self}__reset {
margin-top: 0;
}
}
}
}
/* random parent element */
/*
*
* Syntax : .randomParentElt & {}
*
*/
/* Pseudo Classes */
&:hover {
@media (hover: hover) {
@include custom-prop-fallback("background-color", "sys-color-background-primary-10");
@include custom-prop-fallback("border-color", "sys-color-border-secondary-strong");
#{$self}__icon {
@include custom-prop-fallback("background-color", "sys-color-elevation-surface-flat");
}
}
}
&:focus {
}
&:active {
}
&:focus,
&:active {
}
}