File

src/app/modules/certificate/components/browse-image-popup/browse-image-popup.component.ts

Implements

OnInit

Metadata

Index

Properties
Methods
Inputs
Outputs

Constructor

constructor(uploadCertificateService: UploadCertificateService, toasterService: ToasterService, resourceService: ResourceService, userService: UserService)
Parameters :
Name Type Optional
uploadCertificateService UploadCertificateService No
toasterService ToasterService No
resourceService ResourceService No
userService UserService No

Inputs

enableUploadSignature
Type : boolean
Default value : false
logoType
Type : any
showSelectImageModal
Type : boolean
Default value : false
showUploadUserModal
Type : boolean
Default value : false

Outputs

assetData
Type : EventEmitter
close
Type : EventEmitter

Methods

back
back()
Returns : void
browseImages
browseImages()
Returns : void
closeModel
closeModel()
Returns : void
dimentionCheck
dimentionCheck(image)
Parameters :
Name Optional
image No
Returns : boolean
Async fileChange
fileChange(ev)
Parameters :
Name Optional
ev No
Returns : any
getAllImages
getAllImages()
Returns : void
getAssetList
getAssetList()
Returns : void
getImageProperties
getImageProperties(ev)
Parameters :
Name Optional
ev No
Returns : any
getImageURLs
getImageURLs()

converting images files as data URLs

Returns : void
ngOnInit
ngOnInit()
Returns : void
searchImage
searchImage(type?)
Parameters :
Name Optional
type Yes
Returns : void
selectAndUseLogo
selectAndUseLogo()
Returns : void
Async selectLogo
selectLogo(logo)
Parameters :
Name Optional
logo No
Returns : any
showUploadSignature
showUploadSignature()
Returns : void
upload
upload()
Returns : void
uploadBlob
uploadBlob(data)
Parameters :
Name Optional
data No
Returns : void

Properties

allImagesList
Type : []
Default value : []
fileObj
Type : any
imageDimensions
Type : object
Default value : { 'LOGO': { type: 'PNG', dimensions: '88px X 88px' }, 'SIGN': { type: 'PNG', dimensions: '112px X 46px' } }
imageName
imagesList
Type : []
Default value : []
Public resourceService
Type : ResourceService
selectedLogo
Type : any
sign
Type : string
Default value : 'SIGN'
Public toasterService
Type : ToasterService
Public uploadCertificateService
Type : UploadCertificateService
uploadForm
Type : UntypedFormGroup
Public userService
Type : UserService
import { Component, OnInit, Input, Output, EventEmitter } from '@angular/core';
import { UploadCertificateService } from '../../services/upload-certificate/upload-certificate.service';
import { ToasterService, ResourceService } from '@sunbird/shared';
import * as _ from 'lodash-es';
import { UserService } from '@sunbird/core';
import { UntypedFormGroup, UntypedFormControl, Validators } from '@angular/forms';


@Component({
  selector: 'browse-image-popup',
  templateUrl: './browse-image-popup.component.html',
  styleUrls: ['./browse-image-popup.component.scss']
})
export class BrowseImagePopupComponent implements OnInit {

  @Input() showSelectImageModal = false;
  @Input() logoType;
  @Input() enableUploadSignature = false;
  @Output() assetData = new EventEmitter();
  @Output() close = new EventEmitter();
  @Input() showUploadUserModal = false;
  imageName;
  imagesList = [];
  uploadForm: UntypedFormGroup;
  fileObj: any;
  selectedLogo: any;
  sign = 'SIGN';
  imageDimensions = {
    'LOGO': { type: 'PNG', dimensions: '88px X 88px' },
    'SIGN': { type: 'PNG', dimensions: '112px X 46px' }
  };
  allImagesList = [];

  constructor(public uploadCertificateService: UploadCertificateService,
    public toasterService: ToasterService,
    public resourceService: ResourceService,
    public userService: UserService) {
    this.uploadForm = new UntypedFormGroup({
      assetCaption: new UntypedFormControl('', [Validators.required]),
      creator: new UntypedFormControl('', [Validators.required]),
      creatorId: new UntypedFormControl('', [Validators.required])
    });
  }

  ngOnInit() {
    // this.getAssetList();
  }

  getAssetList() {
    this.imageName = '';
    this.selectedLogo = null;
    this.uploadCertificateService.getAssetData().subscribe(res => {
      this.imagesList = res.result.content;
    }, error => {
      this.toasterService.error(_.get(this.resourceService, 'messages.fmsg.m0004'));
    });
  }

  searchImage(type?) {
    this.uploadCertificateService.getAssetData(this.imageName, type).subscribe(res => {
      if (res && res.result) {
        if (!type) {
          this.imagesList = res.result.content;
        } else {
          this.allImagesList = res.result.content;
        }
      }
    }, error => {
      this.toasterService.error(_.get(this.resourceService, 'messages.fmsg.m0004'));
    });
  }

  async fileChange(ev) {
    this.uploadForm.reset();
    const imageProperties = await this.getImageProperties(ev.target.files[0]);
    const isDimensionMatched = this.dimentionCheck(imageProperties);
    const isTypeMatched = _.get(imageProperties, 'type').includes('png');
    const isSizeMatched = _.get(imageProperties, 'size') < 1;
    if (imageProperties && isSizeMatched && isTypeMatched && isDimensionMatched) {
      this.fileObj = ev.target.files[0];
      const fileName = _.get(this.fileObj, 'name').split('.')[0];
      const userName = `${_.get(this.userService, 'userProfile.firstName') || ''} ${_.get(this.userService, 'userProfile.lastName') || ''}`;
      this.uploadForm.patchValue({
        'assetCaption': fileName,
        'creator': userName,
        'creatorId': _.get(this.userService, 'userProfile.id')
      });
    } else {
      this.toasterService.error(_.get(this.resourceService, 'frmelmnts.cert.lbl.imageErrorMsg'));

    }
  }

  getAllImages() {
    this.imageName = '';
    this.selectedLogo = null;
    this.uploadCertificateService.getAssetData(null, 'all').subscribe(res => {
      this.allImagesList = res.result.content;
    }, error => {
      this.toasterService.error(_.get(this.resourceService, 'messages.fmsg.m0004'));
    });
  }

  dimentionCheck(image) {
    let flag = false;
    if (image) {
      const dimension = `${_.get(image, 'width')}px X ${_.get(image, 'height')}px`;
      const logoType = _.get(this.logoType, 'type');
      const requiredDimensions = this.imageDimensions[logoType]['dimensions'];
      flag = _.isEqual(dimension, requiredDimensions);
    }
    return flag;
  }

  getImageProperties(ev) {
    return new Promise((resolve, reject) => {
      let imageData;
      const file = ev;
      const img = new Image();
      if (file.url) {
        img.src = file.url;
      } else {
        img.src = window.URL.createObjectURL(file);
      }
      img.onload = () => {
        const width = img.naturalWidth;
        const height = img.naturalHeight;
        imageData = {
          'height': height,
          'width': width,
          'size': _.toNumber((file.size / (1024 * 1024)).toFixed(2)),
          'type': file.type
        };
        resolve(imageData);
      };
    });
  }

  browseImages() {
    this.showUploadUserModal = true;
    this.selectedLogo = null;
  }

  upload() {
    // TODO: have to make more dynamic (use input variable autoUpload)
    if (this.logoType.type !== this.sign) {
      this.uploadCertificateService.createAsset(this.uploadForm.value, this.logoType.type).subscribe(res => {
        if (res && res.result) {
          this.uploadBlob(res);
        }
      }, error => {
        this.toasterService.error(_.get(this.resourceService, 'messages.fmsg.m0004'));
      });
    } else {
      this.getImageURLs();
    }
  }

  /**
  *  converting images files as data URLs
  */
  getImageURLs() {
    const file = this.fileObj;
    const reader = new FileReader();
    if (file) {
      reader.readAsDataURL(file);
      reader.onload = () => {
        const imageURL = reader.result as string;
        const image = {
          'name': this.uploadForm.controls.assetCaption.value,
          'url': imageURL,
          'type': this.logoType.type,
          'key': this.logoType.key,
          'index': this.logoType.index
        };
        this.assetData.emit(image);
        this.uploadForm.reset();
        this.closeModel();
      };
    }
  }

  uploadBlob(data) {
    if (data) {
      const identifier = _.get(data, 'result.content_id');
      this.uploadCertificateService.storeAsset(this.fileObj, identifier).subscribe(imageData => {
        if (imageData.result) {
          this.showUploadUserModal = false;
          this.showSelectImageModal = false;
          const image = {
            'name': this.uploadForm.controls.assetCaption.value,
            'url': imageData.result.artifactUrl,
            'type': this.logoType.type,
            'key': this.logoType.key,
            'index': this.logoType.index
          };
          this.assetData.emit(image);
          this.uploadForm.reset();
          this.closeModel();
        }
      }, error => {
        this.toasterService.error(_.get(this.resourceService, 'messages.fmsg.m0004'));
        this.closeModel();
        this.uploadForm.reset();
      });
    }
  }

  async selectLogo(logo) {
    const file = {
      url: logo.artifactUrl,
      type: logo.mimeType,
      size: logo.size
    };
    const imageProperties = await this.getImageProperties(file);
    const isDimensionMatched = this.dimentionCheck(imageProperties);
    const isTypeMatched = _.get(imageProperties, 'type').includes('png');
    const isSizeMatched = _.get(imageProperties, 'size') < 1;
    if (imageProperties && isSizeMatched && isTypeMatched && isDimensionMatched) {
      this.selectedLogo = logo;
    } else {
      this.toasterService.error(_.get(this.resourceService, 'frmelmnts.cert.lbl.imageErrorMsg'));
    }
  }
  back() {
    if (this.logoType.type === this.sign) {
      this.closeModel();
    } else {
      this.showUploadUserModal = false;
      this.showSelectImageModal = true;
      this.uploadForm.reset();
      // this.close.emit();
      this.selectedLogo = null;
    }

  }

  closeModel() {
    this.uploadForm.reset();
    this.showUploadUserModal = false;
    this.showSelectImageModal = false;
    this.selectedLogo = null;
    this.close.emit();
  }

  selectAndUseLogo() {
    const image = {
      'name': this.selectedLogo.name,
      'url': this.selectedLogo.artifactUrl,
      'type': this.logoType.type,
      'key': this.logoType.key,
      'index': this.logoType.index
    };
    this.assetData.emit(image);
    this.selectedLogo = null;
    this.closeModel();
  }

  showUploadSignature() {
    this.logoType = { type: 'SIGN', index: 0, key: 'SIGN1' };
    this.showSelectImageModal = false;
    this.showUploadUserModal = true;
  }
}
<app-modal-wrapper *ngIf="showSelectImageModal" [config]="{disableClose: true, size: 'large'}" (dismiss)="closeModel()"
  #modal>
  <ng-template sbModalContent>
    <div class="sb-modal">
      <div class="transition ui dimmer page modals active visible">
        <div class="ui modal transition active visible large">
          <div class="d-flex flex-ai-center sb-modal-header">
            <img src="assets/images/image.svg" width="24px" height="20px" class="mr-8">
            {{resourceService.frmelmnts?.lbl?.selectImage}}
          </div>
          <div class="sb-modal-content">
            <div class="image-tabs px-8">
              <input class="image-input" id="tab1" type="radio" name="tabs" checked>
              <label for="tab1" (click)="getAssetList()" tabindex="0"
                class="image-label mr-8">{{resourceService.frmelmnts?.lbl?.myImages}}</label>

              <input class="image-input" id="tab2" type="radio" name="tabs">
              <label (click)="getAllImages()" tabindex="0" for="tab2"
                class="image-label">{{resourceService.frmelmnts?.lbl?.allImages}}</label>

              <section id="content1">
                <div class="twelve wide column">
                  <div class="sb-search-box mb-16">
                    <div class="input-div relative">
                      <input class="sb-search-input" [(ngModel)]="imageName" (keyup.enter)="searchImage()" type="text"
                        [placeholder]="resourceService?.frmelmnts?.cert?.prmpt?.searchImage" />
                      <i (click)="getAssetList()" tabindex="0" class="close icon"></i>
                      <i class="search icon"></i>
                    </div>
                  </div>
                  <div class="ui grid m-0">
                    <div class="three wide column p-8" *ngFor="let image of imagesList">
                      <div class="ui card text-center common-contribution-card active">
                        <div class="content">
                          <img [src]="image?.artifactUrl" tabindex="0" (click)="selectLogo(image)" rel="placeholder">
                        </div>
                        <div *ngIf="image?.versionKey === selectedLogo?.versionKey" class="selected-mark"><img
                            src="assets/images/checkbox-icon.svg" width="24px" height="24px"></div>
                      </div>
                    </div>
                  </div>
                </div>
              </section>

              <section id="content2">
                <div class="twelve wide column">
                  <div class="sb-search-box mb-16">
                    <div class="input-div relative">
                      <input class="sb-search-input" [(ngModel)]="imageName" (keyup.enter)="searchImage('all')"
                        type="text" [placeholder]="resourceService?.frmelmnts?.cert?.prmpt?.searchImage" />
                      <i (click)="getAllImages()" tabindex="0" class="close icon"></i>
                      <i class="search icon"></i>
                    </div>
                  </div>
                  <div class="ui grid m-0">
                    <div class="three wide column p-8" *ngFor="let image of allImagesList">
                      <div class="ui card text-center common-contribution-card active">
                        <div class="content">
                          <img [src]="image?.artifactUrl" tabindex="0" (click)="selectLogo(image)" rel="placeholder">
                        </div>
                        <div *ngIf="image?.versionKey === selectedLogo?.versionKey" class="selected-mark"><img
                            src="assets/images/checkbox-icon.svg" width="24px" height="24px"></div>
                      </div>
                    </div>
                  </div>
                </div>
              </section>
            </div>
          </div>

          <div class="sb-modal-actions d-flex flex-jc-space-between flex-ai-center">
            <div class="d-flex flex-ai-center relative">
              <button class="sb-btn sb-btn-normal sb-btn-error mr-4" tabindex="0" (click)="closeModel()">
                {{resourceService?.frmelmnts?.btn?.cancel}}
              </button>
              <div class="or-toggle">
                <span>{{resourceService.frmelmnts?.lbl?.or}}</span>
              </div>
              <button class="sb-btn sb-btn-normal sb-btn-primary" [disabled]="!selectedLogo"
                [ngClass]="{'sb-btn-disabled' : !selectedLogo}" tabindex="0" (click)="selectAndUseLogo()">
                {{resourceService?.frmelmnts?.lbl?.Select}}
              </button>
            </div>
           <div class="d-flex flex-ai-center relative">
             <button class="sb-btn sb-btn-normal sb-btn-primary mr-8" tabindex="0" (click)="browseImages()">
               {{resourceService.frmelmnts?.lbl?.uploadAndUse}}
             </button>
             <button *ngIf="enableUploadSignature" class="sb-btn sb-btn-normal sb-btn-primary" tabindex="1" (click)="showUploadSignature()">
               {{resourceService.frmelmnts?.lbl?.uploadSignature}}
              </button>
           </div>
          </div>
        </div>
      </div>
    </div>
  </ng-template>
</app-modal-wrapper>

<app-modal-wrapper *ngIf="showUploadUserModal" [config]="{disableClose: true, size: 'large'}" (dismiss)="closeModel()" #modal>
  <ng-template sbModalContent>
    <div class="sb-modal">
      <div class="transition ui dimmer page modals active visible">
        <div class="ui modal transition active visible large">

          <div class="d-flex flex-ai-center sb-modal-header">
            <img src="assets/images/upload.svg" width="24px" height="20px" class="mr-8">
            {{resourceService.frmelmnts?.lbl?.uploadAndUse}}
          </div>
          <div class="sb-modal-content px-24 sb-bg-color-white">
            <div class="twelve wide column">
              <div class='ui stackable grid mt-0'>
                <div class="six wide column">
                  <label>{{resourceService?.frmelmnts?.lbl?.chooseImageNote}}</label>
                  <div class="choose-img-box"><input (change)="fileChange($event)"
                      class="sb-btn-upload upload-btn fnormal" name="file" type="file"></div>
                  <div class="ui info message">
                    <ol class="ui list">
                      <li class="fsmall">{{resourceService?.frmelmnts?.lbl?.chooseImageType}}
                        {{imageDimensions[logoType.type]?.type}}</li>
                      <li class="fsmall">{{resourceService?.frmelmnts?.lbl?.chooseImageSize}}</li>
                      <li class="fsmall">{{resourceService?.frmelmnts?.lbl?.chooseImagedimensions}}
                        {{imageDimensions[logoType.type]?.dimensions}}</li>
                    </ol>
                  </div>
                  <div>
                    <label>{{resourceService?.frmelmnts?.lbl?.copyrights}}<span class="asterik">*</span></label>
                    <p class="fsmall">{{resourceService?.frmelmnts?.lbl?.uploadlicense}}</p>
                  </div>
                </div>
                <div class="six wide column pl-0">
                  <div [formGroup]="uploadForm" class="border-line">
                    <div class="ui info message p-16">
                      <div class="d-flex">
                        <img src="assets/images/information-icon.svg" width="24px" height="22px" class="mr-8">
                        <div class="fsmall" class="drop-img-text fsmall">
                          {{resourceService?.frmelmnts?.lbl?.imageDrapAndDrop}}
                        </div>
                      </div>
                    </div>

                    <div class="sb-field mb-16">
                      <label>{{resourceService?.frmelmnts?.lbl?.assetCaption}} <span class="asterik">*</span></label>
                      <div class="sb-field">
                        <input formControlName="assetCaption" class="sb-form-control is-valid"
                          placeholder="Enter asset caption">
                      </div>
                    </div>
                    <div class="sb-field">
                      <label>{{resourceService?.frmelmnts?.lbl?.creator}}</label>
                      <div class="sb-field">
                        <input formControlName="creator" class="sb-form-control is-valid" placeholder="Enter Name">
                      </div>
                    </div>
                  </div>

                </div>
              </div>
            </div>

          </div>
          <div class="sb-modal-actions d-flex flex-jc-space-between flex-ai-center">
            <div class="d-flex flex-ai-center relative">
              <button class="sb-btn sb-btn-normal sb-btn-error mr-4" (click)="back()" tabindex="0">
                {{resourceService?.frmelmnts?.btn?.cancel}}
              </button>
              <div class="or-toggle">
                <span>{{resourceService.frmelmnts?.lbl?.or}}</span>
              </div>
              <button class="sb-btn sb-btn-normal sb-btn-primary"
                [ngClass]="{'sb-btn-disabled': (!uploadForm.valid || !fileObj)}"
                [disabled]="(!uploadForm.valid || !fileObj)" (click)="upload()">
                {{resourceService.frmelmnts?.lbl?.uploadAndUse}}
              </button>
            </div>
            <button class="sb-btn sb-btn-normal sb-btn-gray" (click)="back()" tabindex="0">
              {{resourceService?.frmelmnts?.btn?.back }}
            </button>
          </div>
        </div>
      </div>
    </div>
  </ng-template>
</app-modal-wrapper>

./browse-image-popup.component.scss

@use "@project-sunbird/sb-styles/assets/mixins/mixins" as *;

// image modal css //
.sb-imagemodal {
  .sb-modal-content {
    .asterik{
      color: var(--red-400);
    }

    .image-tabs {
      margin: 0 auto;
      background: var(--white);
    }

    .image-input {
      display: none;
    }

    .image-label {
      display: inline-block;
      margin: 0 0 -0.0625rem;
      padding: calculateRem(14px) calculateRem(24px);
      font-weight: 600;
      text-align: center;
      color: var(--gray-200);
      border: calculateRem(1px) solid transparent;
    }

    .image-label:before {
      font-family: fontawesome;
      font-weight: normal;
      margin-right: calculateRem(10px);
    }

    .image-label:hover {
      color: var(--gray-200);
      cursor: pointer;
    }

    .image-input:checked+.image-label {
      color: var(--gray-800);
      border-bottom: calculateRem(3px) solid var(--primary-400);
    }

    #tab1:checked~#content1,
    #tab2:checked~#content2 {
      display: block;
    }

    section {
      display: none;
      padding: calculateRem(16px) 0 0;
      border-top: calculateRem(1px) solid var(--rc-dddddd);

      .sb-search-box .sb-search-input {
        padding: 0.75rem 2.75rem;
      }

      .sb-search-box .icon {
        position: absolute;
        margin: 0;
        height: calculateRem(24px);
        width: calculateRem(24px);
        border-radius: 50%;
        background: var(--gray-200);
      }

      i.icon.search:before {
        content: "\f002";
        background: var(--gray-200);
        position: relative;
        top:calculateRem(2px);
        color: var(--white);
        font-size: calculateRem(12px);
      }

      i.icon.close:before {
        content: "\f00d";
        position: relative;
        top: calculateRem(3px);
      }

      .ui.card:last-child {
        margin-bottom: 0;
      }

      .ui.card:first-child {
        margin-top: 0;
      }

      .common-contribution-card {
        box-sizing: border-box;
        border: calculateRem(0.5px) solid rgba(123, 134, 244, .39);
        border-radius: .25rem;
        background-color: var(--white);
        box-shadow: none;
        height: 100%;
      }

      .ui.card {
        margin: 1em 0;
      }

      .ui.card,
      .ui.cards>.card {
        max-width: 100%;
        position: relative;
        display: flex;
        flex-direction: column;
        width: calculateRem(292px);
        min-height: 0;
        background: var(--white);
        padding: 0;
        border: none;
        border-radius: .28571429rem;
        box-shadow: 0 calculateRem(1px) calculateRem(3px) 0 var(--gray-100), 0 0 0 calculateRem(1px) var(--gray-100);
        transition: box-shadow .1s ease, transform .1s ease;
        z-index: '';
        position: relative;
        .selected-mark{
          display: none;
        }
        &.active {
          transform: scale(1.03, 1.03);
          .selected-mark{
            position: absolute;
            right:calculateRem(4px);
            top:0;
            display: block;
          }
        }
        &:hover {
          transform: scale(1.03, 1.03);
       }
      }

      .ui.card>.content,
      .ui.cards>.card>.content {
        flex-grow: 1;
        border: none;
        border-top: calculateRem(1px) solid rgba(var(--rc-rgba-black), .1);
        background: 0 0;
        margin: 0;
        padding: 0;
        box-shadow: none;
        font-size: 1em;
        border-radius: 0;
      }

      .ui.card .content img,
      .ui.cards>.card .content img {
        display: inline-block;
        vertical-align: middle;
        width: 100%;
        height: calculateRem(150px);
      }
    }

  }

  .sb-modal-actions {
    .or-toggle {
      background: var(--white);
      border-radius: 50%;
      width: calculateRem(24px);
      height: calculateRem(24px);
      position: absolute;
      left: 42%;
      z-index: 2;

      span {
        position: relative;
        top: calculateRem(3px);
        left: calculateRem(5px);
        font-size: calculateRem(14px);
      }
    }
  }
}



.sb-uploadmodal {
  .sb-modal-content {
    .asterik{
      color: var(--red-400);
    }
    .choose-img-box{
      border: calculateRem(3px) solid var(--black);
      padding: calculateRem(24px);
      border-style: dashed;
      border-radius: calculateRem(4px);
    }
    .border-line{
      border-left: calculateRem(2px) solid var(--rc-dddddd);
      height: 100%;
      padding-left: calculateRem(16px);
      html[dir='rtl'] & {
        border-right: calculateRem(2px) solid var(--rc-dddddd);
        height: 100%;
        padding-right: calculateRem(16px);
        }
    }
    .drop-img-text{
      line-height: 1.4em;
    }
    .sb-field {
      .tags {
        list-style: none;
        margin: 0;
        overflow: hidden;
        padding: 0;
        border-bottom: calculateRem(1px) solid var(--gray-200);
      }
      .tags li {
        float: left;
        margin-bottom: 0;
        position: relative;
        img{
          position: absolute;
          right: calculateRem(10px);
        }
      }
      .tag {
        background: var(--primary);
        border-radius: calculateRem(3px) 0 0 calculateRem(3px);
        color: var(--white);
        display: inline-block;
        height: calculateRem(26px);
        line-height: calculateRem(26px);
        padding: 0 calculateRem(20px) 0 calculateRem(23px);
        position: relative;
        margin: 0 calculateRem(10px) calculateRem(10px) 0;
        text-decoration: none;
        -webkit-transition: color 0.2s;
      }
      .tag::before {
        background: var(--white);
        border-radius: calculateRem(10px);
        box-shadow: inset 0 calculateRem(1px) rgba(var(--rc-rgba-black), 0.25);
        content: '';
        height: 0.375rem;
        left: 0.625rem;
        position: absolute;
        width: 0.375rem;
        top: 0.625rem;
      }
      .tag::after {
        background: var(--white);
        border-bottom: 0.8125rem solid transparent;
        border-left: 0.625rem solid 0.625rem solid var(--primary);;
        border-top: 0.8125rem solid transparent;
        content: '';
        position: absolute;
        right: 0;
        top: 0;
      }
    }
  }
  .sb-modal-actions {
    .or-toggle {
      background: var(--white);
      border-radius: 50%;
      width: 1.5rem;
      height: 1.5rem;
      position: absolute;
      left: 31%;
      z-index: 2;
      span {
        position: relative;
        top: 0.1875rem;
        left: 0.3125rem;
        font-size: 0.875rem;
      }
    }
  }
}
Legend
Html element
Component
Html element with directive

results matching ""

    No results matching ""