Added first parts of auth editor
This commit is contained in:
parent
5d2ef81610
commit
5208c36d8e
11 changed files with 453 additions and 14 deletions
20
frontend/src/app/apitypes/Record.apitype.ts
Normal file
20
frontend/src/app/apitypes/Record.apitype.ts
Normal file
|
@ -0,0 +1,20 @@
|
|||
export class RecordApitype {
|
||||
|
||||
public id = 0;
|
||||
|
||||
public name = '';
|
||||
|
||||
public type = '';
|
||||
|
||||
public content = '';
|
||||
|
||||
public priority = 0;
|
||||
|
||||
public ttl = 0;
|
||||
|
||||
public domain = 0;
|
||||
|
||||
constructor(init: Object) {
|
||||
Object.assign(this, init);
|
||||
}
|
||||
}
|
|
@ -1,3 +1,5 @@
|
|||
import { EditAuthLineComponent } from './pages/edit-auth/edit-auth-line.component';
|
||||
import { RecordsOperation } from './operations/records.operations';
|
||||
import { LoggedOutGuard } from './services/logged-out-guard.service';
|
||||
import { NativeGuard } from './services/native-guard.service';
|
||||
import { SearchComponent } from './partials/search/search.component';
|
||||
|
@ -64,7 +66,8 @@ import { UsersComponent } from './pages/users/users.component';
|
|||
UsersComponent,
|
||||
EditUserComponent,
|
||||
CreateUserComponent,
|
||||
SearchComponent
|
||||
SearchComponent,
|
||||
EditAuthLineComponent
|
||||
],
|
||||
imports: [
|
||||
BrowserModule,
|
||||
|
@ -79,6 +82,7 @@ import { UsersComponent } from './pages/users/users.component';
|
|||
PasswordOperation,
|
||||
DomainsOperation,
|
||||
UsersOperation,
|
||||
RecordsOperation,
|
||||
AuthGuard,
|
||||
AdminGuard,
|
||||
NativeGuard,
|
||||
|
|
99
frontend/src/app/operations/records.operations.ts
Normal file
99
frontend/src/app/operations/records.operations.ts
Normal file
|
@ -0,0 +1,99 @@
|
|||
import { RecordApitype } from './../apitypes/Record.apitype';
|
||||
import { SoaApitype } from './../apitypes/Soa.apitype';
|
||||
import { DomainApitype } from './../apitypes/Domain.apitype';
|
||||
import { ListApitype } from './../apitypes/List.apitype';
|
||||
import { Injectable } from '@angular/core';
|
||||
import { HttpService } from '../services/http.service';
|
||||
import { StateService } from '../services/state.service';
|
||||
import { SessionApitype } from '../apitypes/Session.apitype';
|
||||
|
||||
@Injectable()
|
||||
export class RecordsOperation {
|
||||
|
||||
constructor(private http: HttpService, private gs: StateService) { }
|
||||
|
||||
public async getListForDomain(domainId: number, page?: number, pageSize?: number, queryName?: string,
|
||||
type?: Array<string>, queryContent?: string, sort?: Array<String> | string, ): Promise<ListApitype<RecordApitype>> {
|
||||
try {
|
||||
return new ListApitype<RecordApitype>(await this.http.get('/records', {
|
||||
domain: domainId,
|
||||
page: page,
|
||||
pagesize: pageSize,
|
||||
queryName: queryName,
|
||||
type: type,
|
||||
queryContent: queryContent,
|
||||
sort: sort
|
||||
}));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return new ListApitype<RecordApitype>({ paging: {}, results: [] });
|
||||
}
|
||||
}
|
||||
|
||||
public async delete(recordId: number): Promise<boolean> {
|
||||
try {
|
||||
await this.http.delete(['/records', recordId.toString()]);
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async getSingle(recordId: number): Promise<RecordApitype> {
|
||||
try {
|
||||
return new RecordApitype(await this.http.get(['/records', recordId.toString()]));
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return new RecordApitype({});
|
||||
}
|
||||
}
|
||||
|
||||
public async updateRecord(recordId: number, name?: string, type?: string, content?: string,
|
||||
priority?: number, ttl?: number): Promise<boolean> {
|
||||
const data = {};
|
||||
if (name !== null && name !== undefined) {
|
||||
data['name'] = name;
|
||||
}
|
||||
if (type !== null && type !== undefined) {
|
||||
data['type'] = type;
|
||||
}
|
||||
if (content !== null && content !== undefined) {
|
||||
data['content'] = content;
|
||||
}
|
||||
if (priority !== null && priority !== undefined) {
|
||||
data['priority'] = priority;
|
||||
}
|
||||
if (ttl !== null && ttl !== undefined) {
|
||||
data['ttl'] = ttl;
|
||||
}
|
||||
|
||||
try {
|
||||
await this.http.put(['/records', recordId.toString()], data);
|
||||
|
||||
return true;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public async create(domainId: number, name: string, type: string, content: string,
|
||||
priority: number, ttl: number): Promise<RecordApitype> {
|
||||
try {
|
||||
const result = new RecordApitype(await this.http.post('/records', {
|
||||
name: name,
|
||||
type: type,
|
||||
content: content,
|
||||
priority: priority,
|
||||
ttl: ttl,
|
||||
domain: domainId
|
||||
}));
|
||||
|
||||
return result;
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
return new RecordApitype({});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,48 @@
|
|||
<td>
|
||||
<span>{{ entry.id }}</span>
|
||||
</td>
|
||||
<td class="align-middle py-0">
|
||||
<div class="text-nowrap text-truncate" *ngIf="!editMode">{{ fullName() }}</div>
|
||||
<div class="input-group input-group-sm" *ngIf="editMode">
|
||||
<input class="form-control" type="text" [formControl]="inputName">
|
||||
<div class="input-group-append">
|
||||
<span class="input-group-text">{{ domainPart() }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-middle py-0">
|
||||
<span *ngIf="!editMode">{{ inputType.value }}</span>
|
||||
<app-select *ngIf="editMode" [options]="gs.recordTypes" notNull [formControl]="inputType"></app-select>
|
||||
</td>
|
||||
<td class="align-middle py-0">
|
||||
<div class="text-nowrap text-truncate" *ngIf="!editMode">{{ inputContent.value }}</div>
|
||||
<input *ngIf="editMode" class="form-control form-control-sm" type="text" [formControl]="inputContent">
|
||||
</td>
|
||||
<td class="align-middle py-0">
|
||||
<span *ngIf="!editMode">{{ inputPriority.value }}</span>
|
||||
<div *ngIf="editMode" class="form-group m-0 position-relative">
|
||||
<input class="form-control form-control-sm auto-invalid" type="text" [formControl]="inputPriority">
|
||||
<div class="invalid-tooltip w-200 mw-200">
|
||||
Must be positive integer.
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-middle py-0">
|
||||
<span *ngIf="!editMode">{{ inputTtl.value }}</span>
|
||||
<div *ngIf="editMode" class="form-group m-0 position-relative">
|
||||
<input class="form-control form-control-sm auto-invalid" type="text" [formControl]="inputTtl">
|
||||
<div class="invalid-tooltip w-200 mw-200">
|
||||
Must be positive integer.
|
||||
</div>
|
||||
</div>
|
||||
</td>
|
||||
<td class="align-middle text-center py-0">
|
||||
<span *ngIf="!editMode">
|
||||
<app-fa-icon class="cursor-pointer mx-1" icon="edit" appStopPropagateClick (click)="onEditClick()"></app-fa-icon>
|
||||
<app-fa-icon class="cursor-pointer mx-1" icon="trash" appStopPropagateClick></app-fa-icon>
|
||||
<app-fa-icon class="cursor-pointer mx-1" icon="key" appStopPropagateClick></app-fa-icon>
|
||||
</span>
|
||||
<span *ngIf="editMode">
|
||||
<button class="btn btn-primary btn-sm w-100" (click)="onSave()">Save</button>
|
||||
</span>
|
||||
</td>
|
81
frontend/src/app/pages/edit-auth/edit-auth-line.component.ts
Normal file
81
frontend/src/app/pages/edit-auth/edit-auth-line.component.ts
Normal file
|
@ -0,0 +1,81 @@
|
|||
import { RecordsOperation } from './../../operations/records.operations';
|
||||
import { StateService } from './../../services/state.service';
|
||||
import { DomainApitype } from './../../apitypes/Domain.apitype';
|
||||
import { FormControl, FormBuilder, Validators } from '@angular/forms';
|
||||
import { RecordApitype } from './../../apitypes/Record.apitype';
|
||||
import { Component, OnInit, Input, OnChanges, SimpleChanges, EventEmitter, Output } from '@angular/core';
|
||||
|
||||
@Component({
|
||||
// tslint:disable-next-line:component-selector
|
||||
selector: '[app-edit-auth-line]',
|
||||
templateUrl: './edit-auth-line.component.html'
|
||||
})
|
||||
export class EditAuthLineComponent implements OnInit, OnChanges {
|
||||
|
||||
@Input() entry: RecordApitype;
|
||||
@Input() domain: DomainApitype;
|
||||
|
||||
@Output() recordUpdated = new EventEmitter<void>();
|
||||
|
||||
public editMode = false;
|
||||
|
||||
public inputName: FormControl;
|
||||
public inputType: FormControl;
|
||||
public inputContent: FormControl;
|
||||
public inputPriority: FormControl;
|
||||
public inputTtl: FormControl;
|
||||
|
||||
constructor(private fb: FormBuilder, public gs: StateService, private records: RecordsOperation) {
|
||||
this.setupFormControls();
|
||||
}
|
||||
|
||||
ngOnInit(): void {
|
||||
}
|
||||
|
||||
ngOnChanges(changes: SimpleChanges): void {
|
||||
this.editMode = false;
|
||||
|
||||
this.inputName.reset(this.recordPart());
|
||||
this.inputType.reset(this.entry.type);
|
||||
this.inputContent.reset(this.entry.content);
|
||||
this.inputPriority.reset(this.entry.priority);
|
||||
this.inputTtl.reset(this.entry.ttl);
|
||||
}
|
||||
|
||||
public async setupFormControls() {
|
||||
this.inputName = this.fb.control('');
|
||||
this.inputType = this.fb.control('');
|
||||
this.inputContent = this.fb.control('');
|
||||
this.inputPriority = this.fb.control('', [Validators.required, Validators.pattern(/^[0-9]+$/)]);
|
||||
this.inputTtl = this.fb.control('', [Validators.required, Validators.pattern(/^[0-9]+$/)]);
|
||||
}
|
||||
|
||||
public async onEditClick() {
|
||||
this.editMode = true;
|
||||
}
|
||||
|
||||
public domainPart(): string {
|
||||
return '.' + this.domain.name;
|
||||
}
|
||||
|
||||
public recordPart(): string {
|
||||
const pos = this.entry.name.lastIndexOf(this.domain.name);
|
||||
return this.entry.name.substr(0, pos).replace(/\.$/, '');
|
||||
}
|
||||
|
||||
public fullName(): string {
|
||||
if (this.inputName.value !== '') {
|
||||
return this.inputName.value + '.' + this.domain.name;
|
||||
} else {
|
||||
return this.domain.name;
|
||||
}
|
||||
}
|
||||
|
||||
public async onSave() {
|
||||
await this.records.updateRecord(this.entry.id, this.fullName(),
|
||||
this.inputType.value, this.inputContent.value, this.inputPriority.value, this.inputTtl.value);
|
||||
|
||||
this.editMode = false;
|
||||
this.recordUpdated.emit();
|
||||
}
|
||||
}
|
|
@ -1,9 +1,9 @@
|
|||
<div class="row">
|
||||
<div class="col-12">
|
||||
<p class="font-weight-bold">Update SOA data for {{ domainName }}</p>
|
||||
<p class="font-weight-bold">Update SOA data for {{ domain.name }}</p>
|
||||
</div>
|
||||
</div>
|
||||
<form [formGroup]="soaForm" (submit)="onSubmit()">
|
||||
<form [formGroup]="soaForm" (submit)="onSoaSubmit()">
|
||||
<div class="row">
|
||||
<div class="col-12 col-md-4 col-lg-3">
|
||||
<div class="form-group">
|
||||
|
@ -67,4 +67,59 @@
|
|||
<button type="submit" class="btn btn-primary float-right float-md-left" [disabled]="!soaForm.valid || soaForm.pristine">Save</button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</form>
|
||||
<div class="row justify-content-end">
|
||||
<div class="col-12 col-md-6 mt-2 mt-md-0">
|
||||
<app-pagesize class="float-md-right" [pagesizes]="gs.pageSizes" [currentPagesize]="gs.pageSize" (pagesizeChange)="onPagesizeChange($event)"></app-pagesize>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-12">
|
||||
<div class="table-responsive-lg">
|
||||
<table class="table table-hover table-layout-fixed table-triple">
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="w-6 align-middle">
|
||||
<span>ID</span>
|
||||
<app-sort field="id" [activeFields]="sortField" (sort)="onSortEvent($event)"></app-sort>
|
||||
</th>
|
||||
<th class="w-25 align-middle">
|
||||
<div class="form-inline">
|
||||
<span>Name</span>
|
||||
<app-sort field="name" [activeFields]="sortField" (sort)="onSortEvent($event)"></app-sort>
|
||||
<input class="form-control form-control-sm no-shadow" type="text" placeholder="Search" [formControl]="queryNameInput">
|
||||
</div>
|
||||
</th>
|
||||
<th class="w-15 align-middle">
|
||||
<div class="form-inline">
|
||||
<span>Type</span>
|
||||
<app-sort field="type" [activeFields]="sortField" (sort)="onSortEvent($event)"></app-sort>
|
||||
<app-select class="w-60" [options]="gs.recordTypes" [formControl]="typeFilter" multiple></app-select>
|
||||
</div>
|
||||
</th>
|
||||
<th class="align-middle">
|
||||
<div class="form-inline">
|
||||
<span>Content</span>
|
||||
<app-sort field="content" [activeFields]="sortField" (sort)="onSortEvent($event)"></app-sort>
|
||||
<input class="form-control form-control-sm no-shadow" type="text" placeholder="Search" [formControl]="queryContentInput">
|
||||
</div>
|
||||
</th>
|
||||
<th class="w-10 align-middle">
|
||||
<span>Priority</span>
|
||||
<app-sort field="priority" [activeFields]="sortField" (sort)="onSortEvent($event)"></app-sort>
|
||||
</th>
|
||||
<th class="w-8 align-middle">
|
||||
<span>TTL</span>
|
||||
<app-sort field="ttl" [activeFields]="sortField" (sort)="onSortEvent($event)"></app-sort>
|
||||
</th>
|
||||
<th class="w-9 align-middle"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr app-edit-auth-line *ngFor="let record of recordList" [entry]="record" [domain]="domain" (recordUpdated)="updateSerial()"></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<app-paging [pagingInfo]="pagingInfo" [pageWidth]="3" (pageChange)="onPageChange($event)"></app-paging>
|
|
@ -1,9 +1,16 @@
|
|||
import { EditAuthLineComponent } from './edit-auth-line.component';
|
||||
import { RecordApitype } from './../../apitypes/Record.apitype';
|
||||
import { StateService } from './../../services/state.service';
|
||||
import { RecordsOperation } from './../../operations/records.operations';
|
||||
import { DomainApitype } from './../../apitypes/Domain.apitype';
|
||||
import { SoaApitype } from './../../apitypes/Soa.apitype';
|
||||
import { DomainsOperation } from './../../operations/domains.operations';
|
||||
import { FormGroup, FormBuilder, Validators } from '@angular/forms';
|
||||
import { FormGroup, FormBuilder, Validators, FormControl } from '@angular/forms';
|
||||
import { ActivatedRoute, ParamMap } from '@angular/router';
|
||||
import { Component, OnInit } from '@angular/core';
|
||||
import { PagingApitype } from '../../apitypes/Paging.apitype';
|
||||
import { SortEventDatatype } from '../../datatypes/sort-event.datatype';
|
||||
import 'rxjs/add/operator/filter';
|
||||
|
||||
@Component({
|
||||
selector: 'app-edit-auth',
|
||||
|
@ -15,11 +22,24 @@ export class EditAuthComponent implements OnInit {
|
|||
|
||||
public type = '';
|
||||
|
||||
public domainName = '';
|
||||
public domain: DomainApitype = new DomainApitype({});
|
||||
|
||||
public domainId = 0;
|
||||
|
||||
constructor(private route: ActivatedRoute, private fb: FormBuilder, private domains: DomainsOperation) { }
|
||||
public pagingInfo = new PagingApitype({});
|
||||
public pageRequested = 1;
|
||||
|
||||
public recordList: RecordApitype[] = [];
|
||||
|
||||
public sortField = '';
|
||||
public sortOrder = 'asc';
|
||||
|
||||
public queryNameInput: FormControl;
|
||||
public queryContentInput: FormControl;
|
||||
public typeFilter: FormControl;
|
||||
|
||||
constructor(private route: ActivatedRoute, private fb: FormBuilder, public gs: StateService,
|
||||
private domains: DomainsOperation, private records: RecordsOperation) { }
|
||||
|
||||
ngOnInit() {
|
||||
this.createForm();
|
||||
|
@ -33,7 +53,7 @@ export class EditAuthComponent implements OnInit {
|
|||
this.domainId = +params.get('domainId');
|
||||
|
||||
this.domains.getSingle(this.domainId).then((domain: DomainApitype) => {
|
||||
this.domainName = domain.name;
|
||||
this.domain = domain;
|
||||
});
|
||||
|
||||
this.domains.getSoa(this.domainId).then((soa: SoaApitype) => {
|
||||
|
@ -47,6 +67,12 @@ export class EditAuthComponent implements OnInit {
|
|||
serial: soa.serial
|
||||
});
|
||||
});
|
||||
|
||||
this.queryNameInput.reset();
|
||||
this.queryContentInput.reset();
|
||||
|
||||
// this triggers also a reload of the records, therefore this function is ommited here
|
||||
this.typeFilter.reset();
|
||||
}
|
||||
|
||||
private async updateSerial() {
|
||||
|
@ -66,13 +92,62 @@ export class EditAuthComponent implements OnInit {
|
|||
ttl: ['', [Validators.required, Validators.pattern(/^[0-9]+$/)]],
|
||||
serial: ['']
|
||||
});
|
||||
|
||||
this.queryNameInput = new FormControl('');
|
||||
this.queryNameInput.valueChanges.filter((d) => d !== null).debounceTime(500).subscribe(() => this.loadRecords());
|
||||
|
||||
this.queryContentInput = new FormControl('');
|
||||
this.queryContentInput.valueChanges.filter((d) => d !== null).debounceTime(500).subscribe(() => this.loadRecords());
|
||||
|
||||
this.typeFilter = new FormControl(null);
|
||||
this.typeFilter.valueChanges.subscribe(() => this.loadRecords());
|
||||
}
|
||||
|
||||
public async onSubmit() {
|
||||
public async onSoaSubmit() {
|
||||
const v = this.soaForm.value;
|
||||
await this.domains.setSoa(this.domainId, v.primary, v.email, v.refresh, v.retry, v.expire, v.ttl);
|
||||
this.soaForm.markAsPristine();
|
||||
await this.updateSerial();
|
||||
}
|
||||
|
||||
public async loadRecords() {
|
||||
const sortStr = this.sortField !== '' ? this.sortField + '-' + this.sortOrder : null;
|
||||
const queryName = this.queryNameInput.value !== '' ? this.queryNameInput.value : null;
|
||||
const queryContent = this.queryContentInput.value !== '' ? this.queryContentInput.value : null;
|
||||
const typeFilter = this.typeFilter.value;
|
||||
|
||||
const res = await this.records.getListForDomain(this.domainId, this.pageRequested,
|
||||
this.gs.pageSize, queryName, typeFilter, queryContent, sortStr);
|
||||
|
||||
this.pagingInfo = res.paging;
|
||||
this.recordList = res.results;
|
||||
if (res.paging.total < this.pageRequested && res.paging.total > 1) {
|
||||
this.pageRequested = Math.max(1, res.paging.total);
|
||||
await this.loadRecords();
|
||||
}
|
||||
}
|
||||
|
||||
public async onPageChange(newPage: number) {
|
||||
this.pageRequested = newPage;
|
||||
await this.loadRecords();
|
||||
}
|
||||
|
||||
public async onPagesizeChange(pagesize: number) {
|
||||
this.gs.pageSize = pagesize;
|
||||
this.pageRequested = 1;
|
||||
await this.loadRecords();
|
||||
}
|
||||
|
||||
public async onSortEvent(sortEvent: SortEventDatatype) {
|
||||
if (sortEvent.order === 0) {
|
||||
this.sortField = '';
|
||||
this.sortOrder = 'asc';
|
||||
} else {
|
||||
this.sortField = sortEvent.field;
|
||||
this.sortOrder = sortEvent.order === 1 ? 'asc' : 'desc';
|
||||
}
|
||||
|
||||
await this.loadRecords();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,13 @@
|
|||
<div class="dropdown">
|
||||
<a class="btn btn-sm dropdown-toggle no-shadow" (click)="toggleOpen()" [class.disabled]="!enabled">
|
||||
<a class="btn btn-sm dropdown-toggle-left no-shadow mw-100 text-truncate" (click)="toggleOpen()" [class.disabled]="!enabled">
|
||||
{{ buttonText() }}
|
||||
</a>
|
||||
<div class="dropdown-menu" [class.show]="open">
|
||||
<div class="dropdown-menu" [class.show]="open" style="overflow-y: auto;">
|
||||
<span class="dropdown-item cursor-pointer" *ngIf="notNull === false" (click)="reset()">Clear</span>
|
||||
<div class="dropdown-divider" *ngIf="notNull === false"></div>
|
||||
<span *ngFor="let option of options" [class.active]="isActive(option)" class="dropdown-item cursor-pointer"
|
||||
(click)="onClick(option)">{{ option }}</span>
|
||||
<div class="scrolling">
|
||||
<span *ngFor="let option of options" [class.active]="isActive(option)" class="dropdown-item cursor-pointer"
|
||||
(click)="onClick(option)">{{ option }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
.scrolling {
|
||||
overflow-y: auto;
|
||||
max-height: 20em;
|
||||
}
|
|
@ -54,6 +54,18 @@ export class StateService {
|
|||
return this._pageSizes;
|
||||
}
|
||||
|
||||
private _recordTypes = [
|
||||
'A', 'A6', 'AAAA', 'AFSDB', 'ALIAS', 'CAA', 'CDNSKEY', 'CDS', 'CERT', 'CNAME', 'DHCID',
|
||||
'DLV', 'DNAME', 'DNSKEY', 'DS', 'EUI48', 'EUI64', 'HINFO',
|
||||
'IPSECKEY', 'KEY', 'KX', 'LOC', 'MAILA', 'MAILB', 'MINFO', 'MR',
|
||||
'MX', 'NAPTR', 'NS', 'NSEC', 'NSEC3', 'NSEC3PARAM', 'OPENPGPKEY',
|
||||
'OPT', 'PTR', 'RKEY', 'RP', 'RRSIG', 'SIG', 'SPF',
|
||||
'SRV', 'TKEY', 'SSHFP', 'TLSA', 'TSIG', 'TXT', 'WKS', 'MBOXFW', 'URL'
|
||||
];
|
||||
get recordTypes(): Array<string> {
|
||||
return this._recordTypes;
|
||||
}
|
||||
|
||||
constructor() {
|
||||
this.loadLocalStorage();
|
||||
}
|
||||
|
|
|
@ -3,7 +3,15 @@ $table-cell-padding: .60rem !default;
|
|||
|
||||
$sizes: (
|
||||
0: 0%,
|
||||
1: 1%,
|
||||
2: 2%,
|
||||
3: 3%,
|
||||
4: 4%,
|
||||
5: 5%,
|
||||
6: 6%,
|
||||
7: 7%,
|
||||
8: 8%,
|
||||
9: 9%,
|
||||
10: 10%,
|
||||
15: 15%,
|
||||
20: 20%,
|
||||
|
@ -22,7 +30,11 @@ $sizes: (
|
|||
85: 85%,
|
||||
90: 90%,
|
||||
95: 95%,
|
||||
100: 100%
|
||||
100: 100%,
|
||||
150: 150%,
|
||||
200: 200%,
|
||||
250: 250%,
|
||||
300: 300%
|
||||
);
|
||||
|
||||
@import '~bootstrap/scss/bootstrap.scss';
|
||||
|
@ -31,6 +43,12 @@ $sizes: (
|
|||
@extend .dropdown-item:hover;
|
||||
}
|
||||
|
||||
@each $prop, $abbrev in (max-width: w, max-height: h) {
|
||||
@each $size, $length in $sizes {
|
||||
.m#{$abbrev}-#{$size} { #{$prop}: $length !important; }
|
||||
}
|
||||
}
|
||||
|
||||
/* Add font awesome */
|
||||
$fa-font-path: "~font-awesome/fonts";
|
||||
@import '~font-awesome/scss/font-awesome';
|
||||
|
@ -68,3 +86,24 @@ $fa-font-path: "~font-awesome/fonts";
|
|||
box-shadow: none!important;
|
||||
border-color: #CCC!important;
|
||||
}
|
||||
|
||||
/* Table with fixed layout */
|
||||
.table-layout-fixed {
|
||||
table-layout: fixed;
|
||||
}
|
||||
|
||||
/* Dropdown with toggle carret left */
|
||||
.dropdown-toggle-left::before {
|
||||
@extend .dropdown-toggle::after;
|
||||
margin-left: unset;
|
||||
margin-right: 0.255em;
|
||||
}
|
||||
|
||||
/* Make table wider for small screens */
|
||||
@include media-breakpoint-down(sm) {
|
||||
.table-triple {
|
||||
min-width: 100%;
|
||||
max-width: 300%;
|
||||
width: auto;
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue