new changes implemented ui
This commit is contained in:
+277
-94
@@ -6,7 +6,7 @@
|
||||
[pageIndex]="queryState.pageIndex()"
|
||||
[pageSize]="queryState.pageSize()"
|
||||
[pageSizeOptions]="[5, 10, 20, 50]"
|
||||
tableTitle="Currencies"
|
||||
tableTitle="Currency Management"
|
||||
buttonTitle="Add"
|
||||
[showSearch]="true"
|
||||
[showAddButton]="true"
|
||||
@@ -49,6 +49,7 @@
|
||||
/>
|
||||
|
||||
<modal
|
||||
class="modern-modal"
|
||||
[open]="showCurrencyModal()"
|
||||
[title]="currencyModalTitle()"
|
||||
size="md"
|
||||
@@ -59,105 +60,287 @@
|
||||
(closed)="closeCurrencyModal()"
|
||||
(submitted)="saveCurrency()"
|
||||
>
|
||||
<form [formGroup]="currencyForm" (ngSubmit)="saveCurrency()" autocomplete="off">
|
||||
<div class="grid grid-cols-12 gap-x-5 gap-y-5">
|
||||
<div class="col-span-12 md:col-span-6">
|
||||
<app-form-input
|
||||
<form [formGroup]="currencyForm" (ngSubmit)="saveCurrency()" autocomplete="off">
|
||||
|
||||
<div class="grid grid-cols-12 gap-x-5 gap-y-5">
|
||||
|
||||
<!-- Currency Name -->
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
<div class="custom-floating-field"
|
||||
[class.has-error]="currencyForm.get('name')?.invalid && currencySubmitAttempted()"
|
||||
[class.is-empty]="isEmptyField('name')">
|
||||
|
||||
<input
|
||||
id="currency-name"
|
||||
formControlName="name"
|
||||
inputId="currency-name"
|
||||
label="Currency Name"
|
||||
placeholder="Name"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
[required]="true"
|
||||
[maxLength]="100"
|
||||
[validationMessages]="{
|
||||
required: 'Currency Name is required.',
|
||||
maxlength: 'Currency Name cannot exceed 100 characters.'
|
||||
}"
|
||||
[submitAttempted]="currencySubmitAttempted()"
|
||||
maxlength="100"
|
||||
required
|
||||
placeholder=" "
|
||||
class="custom-floating-input"
|
||||
[class.is-invalid]="currencyForm.get('name')?.invalid && currencySubmitAttempted()"
|
||||
[attr.aria-invalid]="currencyForm.get('name')?.invalid && currencySubmitAttempted() ? true : null"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-6">
|
||||
<app-form-input
|
||||
formControlName="code"
|
||||
inputId="currency-code"
|
||||
label="Currency Code"
|
||||
placeholder="e.g.: AED"
|
||||
autocomplete="off"
|
||||
[required]="true"
|
||||
[minLength]="3"
|
||||
[maxLength]="3"
|
||||
[validationMessages]="{
|
||||
required: 'Currency Code is required.',
|
||||
minlength: 'Currency Code must contain exactly 3 letters.',
|
||||
maxlength: 'Currency Code must contain exactly 3 letters.',
|
||||
pattern: 'Currency Code can contain letters only.'
|
||||
}"
|
||||
[submitAttempted]="currencySubmitAttempted()"
|
||||
/>
|
||||
</div>
|
||||
<label for="currency-name" class="custom-floating-label">
|
||||
Currency Name
|
||||
<span class="ms-0.5 text-danger" aria-hidden="true">*</span>
|
||||
<span class="sr-only">Required</span>
|
||||
</label>
|
||||
|
||||
<div class="col-span-12 md:col-span-6">
|
||||
<app-form-input
|
||||
formControlName="symbol"
|
||||
inputId="currency-symbol"
|
||||
label="Currency Symbol"
|
||||
placeholder="e.g.: د.إ"
|
||||
autocomplete="off"
|
||||
[required]="true"
|
||||
[maxLength]="8"
|
||||
[validationMessages]="{
|
||||
required: 'Currency Symbol is required.',
|
||||
maxlength: 'Currency Symbol cannot exceed 8 characters.'
|
||||
}"
|
||||
[submitAttempted]="currencySubmitAttempted()"
|
||||
/>
|
||||
</div>
|
||||
@if (getCurrencyFieldError('name', {
|
||||
required: 'Currency Name is required.',
|
||||
maxlength: 'Currency Name cannot exceed 100 characters.'
|
||||
})) {
|
||||
<span class="custom-floating-error">
|
||||
{{ getCurrencyFieldError('name', {
|
||||
required: 'Currency Name is required.',
|
||||
maxlength: 'Currency Name cannot exceed 100 characters.'
|
||||
}) }}
|
||||
</span>
|
||||
}
|
||||
|
||||
<div class="col-span-12 md:col-span-6">
|
||||
<app-form-input
|
||||
formControlName="numericCode"
|
||||
inputId="currency-numeric-code"
|
||||
label="Numeric Code"
|
||||
type="number"
|
||||
inputMode="numeric"
|
||||
placeholder="e.g.: 784"
|
||||
autocomplete="off"
|
||||
[required]="false"
|
||||
[min]="1"
|
||||
[max]="999"
|
||||
[step]="1"
|
||||
[validationMessages]="{
|
||||
required: 'Numeric Code is required.',
|
||||
min: 'Numeric Code must be at least 1.',
|
||||
max: 'Numeric Code cannot exceed 999.'
|
||||
}"
|
||||
[submitAttempted]="currencySubmitAttempted()"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div class="col-span-12 md:col-span-6">
|
||||
<app-form-input
|
||||
formControlName="decimalDigits"
|
||||
inputId="currency-decimal-digits"
|
||||
label="Decimal Digits"
|
||||
type="number"
|
||||
inputMode="numeric"
|
||||
placeholder="e.g.: 2"
|
||||
autocomplete="off"
|
||||
[required]="true"
|
||||
[min]="0"
|
||||
[max]="4"
|
||||
[step]="1"
|
||||
[validationMessages]="{
|
||||
required: 'Decimal Digits is required.',
|
||||
min: 'Decimal Digits cannot be less than 0.',
|
||||
max: 'Decimal Digits cannot exceed 4.'
|
||||
}"
|
||||
[submitAttempted]="currencySubmitAttempted()"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
|
||||
|
||||
<!-- Currency Code -->
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
|
||||
<div class="custom-floating-field"
|
||||
[class.has-error]="currencyForm.get('code')?.invalid && currencySubmitAttempted()"
|
||||
[class.is-empty]="isEmptyField('code')">
|
||||
|
||||
<input
|
||||
id="currency-code"
|
||||
formControlName="code"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
minlength="3"
|
||||
maxlength="3"
|
||||
pattern="[A-Za-z]{3}"
|
||||
required
|
||||
placeholder=" "
|
||||
class="custom-floating-input"
|
||||
[class.is-invalid]="currencyForm.get('code')?.invalid && currencySubmitAttempted()"
|
||||
[attr.aria-invalid]="currencyForm.get('code')?.invalid && currencySubmitAttempted() ? true : null"
|
||||
/>
|
||||
|
||||
<label for="currency-code" class="custom-floating-label">
|
||||
Currency Code
|
||||
<span class="ms-0.5 text-danger" aria-hidden="true">*</span>
|
||||
<span class="sr-only">Required</span>
|
||||
</label>
|
||||
|
||||
@if (getCurrencyFieldError('code', {
|
||||
required: 'Currency Code is required.',
|
||||
minlength: 'Currency Code must contain exactly 3 letters.',
|
||||
maxlength: 'Currency Code must contain exactly 3 letters.',
|
||||
pattern: 'Currency Code can contain letters only.'
|
||||
})) {
|
||||
|
||||
<span class="custom-floating-error">
|
||||
{{ getCurrencyFieldError('code', {
|
||||
required: 'Currency Code is required.',
|
||||
minlength: 'Currency Code must contain exactly 3 letters.',
|
||||
maxlength: 'Currency Code must contain exactly 3 letters.',
|
||||
pattern: 'Currency Code can contain letters only.'
|
||||
}) }}
|
||||
</span>
|
||||
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Currency Symbol -->
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
|
||||
<div class="custom-floating-field"
|
||||
[class.has-error]="currencyForm.get('symbol')?.invalid && currencySubmitAttempted()"
|
||||
[class.is-empty]="isEmptyField('symbol')">
|
||||
|
||||
|
||||
<input
|
||||
id="currency-symbol"
|
||||
formControlName="symbol"
|
||||
type="text"
|
||||
autocomplete="off"
|
||||
maxlength="8"
|
||||
required
|
||||
placeholder=" "
|
||||
class="custom-floating-input"
|
||||
[class.is-invalid]="currencyForm.get('symbol')?.invalid && currencySubmitAttempted()"
|
||||
[attr.aria-invalid]="currencyForm.get('symbol')?.invalid && currencySubmitAttempted() ? true : null"
|
||||
/>
|
||||
|
||||
|
||||
<label for="currency-symbol" class="custom-floating-label">
|
||||
|
||||
Currency Symbol
|
||||
|
||||
<span class="ms-0.5 text-danger" aria-hidden="true">*</span>
|
||||
|
||||
<span class="sr-only">
|
||||
Required
|
||||
</span>
|
||||
|
||||
</label>
|
||||
|
||||
|
||||
@if (getCurrencyFieldError('symbol', {
|
||||
required: 'Currency Symbol is required.',
|
||||
maxlength: 'Currency Symbol cannot exceed 8 characters.'
|
||||
})) {
|
||||
|
||||
<span class="custom-floating-error">
|
||||
|
||||
{{ getCurrencyFieldError('symbol', {
|
||||
required: 'Currency Symbol is required.',
|
||||
maxlength: 'Currency Symbol cannot exceed 8 characters.'
|
||||
}) }}
|
||||
|
||||
</span>
|
||||
|
||||
}
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Numeric Code -->
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
|
||||
<div class="custom-floating-field"
|
||||
[class.has-error]="currencyForm.get('numericCode')?.invalid && currencySubmitAttempted()"
|
||||
[class.is-empty]="isEmptyField('numericCode')">
|
||||
|
||||
|
||||
<input
|
||||
id="currency-numeric-code"
|
||||
formControlName="numericCode"
|
||||
type="number"
|
||||
inputmode="numeric"
|
||||
autocomplete="off"
|
||||
min="1"
|
||||
max="999"
|
||||
step="1"
|
||||
placeholder=" "
|
||||
class="custom-floating-input"
|
||||
[class.is-invalid]="currencyForm.get('numericCode')?.invalid && currencySubmitAttempted()"
|
||||
[attr.aria-invalid]="currencyForm.get('numericCode')?.invalid && currencySubmitAttempted() ? true : null"
|
||||
/>
|
||||
|
||||
|
||||
<label for="currency-numeric-code" class="custom-floating-label">
|
||||
Numeric Code
|
||||
</label>
|
||||
|
||||
|
||||
@if (getCurrencyFieldError('numericCode', {
|
||||
required:'Numeric Code is required.',
|
||||
min:'Numeric Code must be at least 1.',
|
||||
max:'Numeric Code cannot exceed 999.'
|
||||
})) {
|
||||
|
||||
<span class="custom-floating-error">
|
||||
|
||||
{{ getCurrencyFieldError('numericCode', {
|
||||
required:'Numeric Code is required.',
|
||||
min:'Numeric Code must be at least 1.',
|
||||
max:'Numeric Code cannot exceed 999.'
|
||||
}) }}
|
||||
|
||||
</span>
|
||||
|
||||
}
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<!-- Decimal Digits -->
|
||||
<div class="col-span-12 md:col-span-4">
|
||||
|
||||
|
||||
<div class="custom-floating-field"
|
||||
[class.has-error]="currencyForm.get('decimalDigits')?.invalid && currencySubmitAttempted()"
|
||||
[class.is-empty]="isEmptyField('decimalDigits')">
|
||||
|
||||
|
||||
<input
|
||||
id="currency-decimal-digits"
|
||||
formControlName="decimalDigits"
|
||||
type="number"
|
||||
inputmode="numeric"
|
||||
autocomplete="off"
|
||||
min="0"
|
||||
max="4"
|
||||
step="1"
|
||||
required
|
||||
placeholder=" "
|
||||
class="custom-floating-input"
|
||||
[class.is-invalid]="currencyForm.get('decimalDigits')?.invalid && currencySubmitAttempted()"
|
||||
[attr.aria-invalid]="currencyForm.get('decimalDigits')?.invalid && currencySubmitAttempted() ? true : null"
|
||||
/>
|
||||
|
||||
|
||||
<label for="currency-decimal-digits" class="custom-floating-label">
|
||||
|
||||
Decimal Digits
|
||||
|
||||
<span class="ms-0.5 text-danger" aria-hidden="true">*</span>
|
||||
|
||||
<span class="sr-only">
|
||||
Required
|
||||
</span>
|
||||
|
||||
</label>
|
||||
|
||||
|
||||
@if (getCurrencyFieldError('decimalDigits', {
|
||||
required:'Decimal Digits is required.',
|
||||
min:'Decimal Digits cannot be less than 0.',
|
||||
max:'Decimal Digits cannot exceed 4.'
|
||||
})) {
|
||||
|
||||
<span class="custom-floating-error">
|
||||
|
||||
{{ getCurrencyFieldError('decimalDigits', {
|
||||
required:'Decimal Digits is required.',
|
||||
min:'Decimal Digits cannot be less than 0.',
|
||||
max:'Decimal Digits cannot exceed 4.'
|
||||
}) }}
|
||||
|
||||
</span>
|
||||
|
||||
}
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
|
||||
</form>
|
||||
</modal>
|
||||
|
||||
@@ -0,0 +1,117 @@
|
||||
:host-context(.dark) .custom-floating-input {
|
||||
background-color: var(--color-bodybg) !important;
|
||||
border-color: color-mix(in srgb, #fff 10%, transparent) !important;
|
||||
}
|
||||
|
||||
:host-context(.dark) .custom-floating-label {
|
||||
background-color: var(--color-bodybg) !important;
|
||||
}
|
||||
|
||||
:host-context(.dark) .custom-floating-input:focus {
|
||||
border-color: var(--color-blue-400) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
:host-context(.dark) .custom-floating-input:not(:placeholder-shown) {
|
||||
border-color: var(--color-primary) !important;
|
||||
box-shadow: none !important;
|
||||
}
|
||||
|
||||
.custom-floating-field {
|
||||
position: relative;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.custom-floating-input {
|
||||
display: block;
|
||||
width: 100%;
|
||||
min-height: 2.5rem;
|
||||
padding: 0.75rem 0.75rem 0.5rem;
|
||||
border: 1px solid var(--color-inputborder);
|
||||
border-radius: 0.5rem;
|
||||
background-color: var(--color-white);
|
||||
color: var(--color-defaulttextcolor);
|
||||
font-size: 0.8125rem;
|
||||
line-height: 1.25;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.custom-floating-input:focus {
|
||||
border-color: var(--color-primary) !important;
|
||||
outline: none;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.custom-floating-input:not(:placeholder-shown) {
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.custom-floating-input::placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
|
||||
.custom-floating-label {
|
||||
position: absolute;
|
||||
inset-inline-start: 0.75rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
transform-origin: left top;
|
||||
z-index: 10;
|
||||
padding-inline: 0.25rem;
|
||||
background-color: var(--color-white);
|
||||
color: #8c9097;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1;
|
||||
pointer-events: none;
|
||||
transition: all 0.2s ease-out;
|
||||
max-width: calc(100% - 1.75rem);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.custom-floating-input:focus~.custom-floating-label,
|
||||
.custom-floating-input:not(:placeholder-shown)~.custom-floating-label {
|
||||
top: 0;
|
||||
transform: translateY(-50%) scale(0.9);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Center label on invalid empty fields (validation state) */
|
||||
.custom-floating-field.has-error.is-empty .custom-floating-input:not(:focus)~.custom-floating-label {
|
||||
top: 35% !important;
|
||||
transform: translateY(-50%) !important;
|
||||
}
|
||||
|
||||
.custom-floating-field.has-error .custom-floating-input,
|
||||
.custom-floating-field.has-error .custom-floating-input:focus,
|
||||
.custom-floating-field.has-error .custom-floating-input:not(:placeholder-shown) {
|
||||
border-color: var(--color-danger) !important;
|
||||
}
|
||||
|
||||
.custom-floating-field.has-error .custom-floating-label,
|
||||
.custom-floating-field.has-error .custom-floating-input:focus~.custom-floating-label,
|
||||
.custom-floating-field.has-error .custom-floating-input:not(:placeholder-shown)~.custom-floating-label,
|
||||
.custom-floating-field.has-error.is-empty .custom-floating-input~.custom-floating-label {
|
||||
color: var(--color-danger) !important;
|
||||
}
|
||||
|
||||
.custom-floating-error {
|
||||
display: block;
|
||||
margin-top: 0.25rem;
|
||||
color: var(--color-danger);
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
/* Remove number spinner buttons for cleaner look */
|
||||
.custom-floating-input[type='number']::-webkit-inner-spin-button,
|
||||
.custom-floating-input[type='number']::-webkit-outer-spin-button {
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.custom-floating-input[type='number'] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
|
||||
@@ -27,7 +27,6 @@ import {
|
||||
} from '../../../../../shared/components/data-table/data-table.types';
|
||||
import { DataTable } from '../../../../../shared/components/data-table/data-table';
|
||||
import { DataTableCellDirective } from '../../../../../shared/directives/data-table-cell.directive';
|
||||
import { FormInput } from '../../../../../shared/components/form/form-input/form-input';
|
||||
import { Modal } from '../../../../../shared/components/modal/modal';
|
||||
import { ConfirmDialog } from '../../../../../shared/components/confirm-dialog/confirm-dialog';
|
||||
|
||||
@@ -48,7 +47,7 @@ interface CurrencyTableRow extends DataTableRecord {
|
||||
@Component({
|
||||
selector: 'currency-list',
|
||||
standalone: true,
|
||||
imports: [DataTable, DataTableCellDirective, Modal, ReactiveFormsModule, FormInput, ConfirmDialog],
|
||||
imports: [DataTable, DataTableCellDirective, Modal, ReactiveFormsModule, ConfirmDialog],
|
||||
templateUrl: './currency-list.html',
|
||||
styleUrl: './currency-list.scss',
|
||||
})
|
||||
@@ -390,7 +389,7 @@ export class CurrencyList {
|
||||
name: currencyDetails.name ?? '',
|
||||
code: currencyDetails.code ?? '',
|
||||
symbol: currencyDetails.symbol ?? '',
|
||||
numericCode: currencyDetails.numericCode ?? 0,
|
||||
numericCode: currencyDetails.numericCode ?? '0',
|
||||
decimalDigits: currencyDetails.decimalDigits ?? 2
|
||||
});
|
||||
this.resetCurrencyFormState();
|
||||
@@ -482,7 +481,7 @@ export class CurrencyList {
|
||||
queueMicrotask(() => {
|
||||
const firstInvalidControl =
|
||||
this.elementRef.nativeElement.querySelector<HTMLElement>(
|
||||
'modal .form-control.is-invalid, modal [aria-invalid="true"]'
|
||||
'modal .custom-floating-input.is-invalid, modal [aria-invalid="true"]'
|
||||
);
|
||||
|
||||
firstInvalidControl?.focus();
|
||||
@@ -515,6 +514,31 @@ export class CurrencyList {
|
||||
? `https://flagcdn.com/24x18/${code}.png`
|
||||
: '';
|
||||
}
|
||||
isEmptyField(fieldName: string): boolean {
|
||||
const value = this.currencyForm.get(fieldName)?.value;
|
||||
return value === '' || value === null || value === undefined;
|
||||
}
|
||||
|
||||
getCurrencyFieldError(fieldName: string, messages: Record<string, string>): string | null {
|
||||
const control = this.currencyForm.get(fieldName);
|
||||
|
||||
if (!control || control.valid || !this.currencySubmitAttempted()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const errors = control.errors ?? {};
|
||||
|
||||
for (const key of Object.keys(errors)) {
|
||||
const message = messages[key];
|
||||
|
||||
if (message) {
|
||||
return message;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
onFlagError(event: Event): void {
|
||||
const image = event.target as HTMLImageElement;
|
||||
image.style.display = 'none';
|
||||
|
||||
@@ -1,16 +1,16 @@
|
||||
<!-- Start::row-1 -->
|
||||
<div class="grid grid-cols-12">
|
||||
<div class="grid grid-cols-12 data-table-modern">
|
||||
<div class="xl:col-span-12 col-span-12">
|
||||
<div class="box overflow-visible">
|
||||
<div class="box-header justify-between">
|
||||
<div class="box-title">
|
||||
<div class="box-header justify-between items-center gap-3">
|
||||
<h5 class="box-title">
|
||||
{{ tableTitle () }}
|
||||
</div>
|
||||
<div class="flex flex-wrap gap-2">
|
||||
</h5>
|
||||
<div class="flex flex-wrap items-center gap-3">
|
||||
@if(showAddButton()){
|
||||
<div>
|
||||
<app-button action="add" [label]="buttonTitle()" size="sm" iconClass="!text-[1rem]"
|
||||
(buttonClicked)="onAddClick($event)" />
|
||||
className="!rounded-full shadow-sm" (buttonClicked)="onAddClick($event)" />
|
||||
|
||||
</div>
|
||||
}
|
||||
@@ -20,17 +20,18 @@
|
||||
</div>
|
||||
} -->
|
||||
@if (showSearch()) {
|
||||
<div>
|
||||
<input #searchInput class="form-control form-control-sm" type="text" [placeholder]="searchPlaceholder()"
|
||||
aria-label="table search" (input)="onSearch(searchInput.value)">
|
||||
<div class="search-wrapper">
|
||||
<i class="ri-search-line search-icon"></i>
|
||||
<input #searchInput class="form-control form-control-sm brdr-clr" type="text"
|
||||
[placeholder]="searchPlaceholder()" aria-label="table search" (input)="onSearch(searchInput.value)">
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="box-body !p-0">
|
||||
<div class="table-responsive overflow-x-auto overflow-y-visible">
|
||||
<div class="box-body !p-0 modern-table-body">
|
||||
<div class="table-responsive overflow-x-auto overflow-y-visible modern-table-wrapper">
|
||||
<table [class]="tableClass()">
|
||||
<thead>
|
||||
<tr [class]="trHeadClass()">
|
||||
@@ -67,7 +68,8 @@
|
||||
<tr class="border-b border-defaultborder bg-light/30 dark:bg-black/10">
|
||||
<td [attr.colspan]="totalVisibleColumns()" class="p-0">
|
||||
<div class="flex min-h-[140px] flex-col items-center justify-center px-4 py-8 text-center">
|
||||
<div class="mb-3 inline-flex h-11 w-11 items-center justify-center rounded-full bg-primary/10 text-primary dark:bg-primary/15">
|
||||
<div
|
||||
class="mb-3 inline-flex h-11 w-11 items-center justify-center rounded-full bg-primary/10 text-primary dark:bg-primary/15">
|
||||
<i class="ti ti-database-off text-[1.25rem]" aria-hidden="true"></i>
|
||||
</div>
|
||||
|
||||
@@ -109,10 +111,10 @@
|
||||
<button type="button" cdkOverlayOrigin #actionMenuOrigin="cdkOverlayOrigin" data-action-menu-trigger
|
||||
class="
|
||||
ti-btn ti-btn-icon ti-btn-sm ti-btn-primary
|
||||
!m-0 !h-8 !w-8 !p-0
|
||||
!m-0 !h-6 !w-6 !p-0
|
||||
" aria-label="Open row actions" aria-haspopup="menu" [attr.aria-controls]="getActionMenuId(row)"
|
||||
[attr.aria-expanded]="isActionMenuOpen(row)" (click)="toggleActionMenu($event, row)">
|
||||
<i class="ri-more-2-fill text-lg"></i>
|
||||
<i class="ri-more-2-fill text-sm"></i>
|
||||
</button>
|
||||
|
||||
<ng-template cdkConnectedOverlay [cdkConnectedOverlayOrigin]="actionMenuOrigin"
|
||||
@@ -167,7 +169,7 @@
|
||||
</div>
|
||||
</div>
|
||||
@if (showPaginator()) {
|
||||
<div class="box-footer border-t-0">
|
||||
<div class="box-footer border-t-0 modern-table-footer">
|
||||
<div class="flex items-center justify-between flex-wrap gap-3">
|
||||
|
||||
<!-- Record details and page-size selection -->
|
||||
|
||||
@@ -1,59 +1,238 @@
|
||||
// :host {
|
||||
// display: block;
|
||||
// }
|
||||
// :host .table tbody td,
|
||||
// :host .table tbody th {
|
||||
// line-height: 1.25;
|
||||
// vertical-align: middle;
|
||||
// padding: 0.1rem !important;
|
||||
// }
|
||||
|
||||
// .table-responsive {
|
||||
// width: 100%;
|
||||
// overflow-x: auto;
|
||||
// }
|
||||
|
||||
// .table th,
|
||||
// .table td {
|
||||
// vertical-align: middle;
|
||||
// }
|
||||
|
||||
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
:host .table {
|
||||
thead {
|
||||
background-color: color-mix(in srgb, var(--color-primary) 8%, transparent);
|
||||
}
|
||||
|
||||
thead th {
|
||||
padding-top: 0.5rem;
|
||||
padding-bottom: 0.5rem;
|
||||
line-height: 1.2;
|
||||
vertical-align: middle;
|
||||
color: var(--color-defaulttextcolor);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
tbody td,
|
||||
tbody th {
|
||||
padding-top: 0.2rem;
|
||||
padding-bottom: 0.2rem;
|
||||
line-height: 1.2;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
tbody tr {
|
||||
line-height: 1.1;
|
||||
}
|
||||
/* Modern card wrapper */
|
||||
.data-table-modern .box {
|
||||
border-radius: 1rem;
|
||||
border: 1px solid var(--color-defaultborder);
|
||||
background-color: var(--color-white);
|
||||
box-shadow: 0 4px 24px rgba(0, 0, 0, 0.04);
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
[data-theme-mode='dark'] :host .table thead {
|
||||
background-color: color-mix(in srgb, var(--color-primary) 14%, transparent);
|
||||
:host-context(.dark) .data-table-modern .box {
|
||||
border-color: color-mix(in srgb, #fff 10%, transparent);
|
||||
background-color: var(--color-bodybg);
|
||||
}
|
||||
|
||||
.data-table-modern .box-header {
|
||||
// padding: 1rem 1.25rem;
|
||||
padding: 7px 20px;
|
||||
border-bottom: 1px solid var(--color-defaultborder);
|
||||
}
|
||||
|
||||
:host-context(.dark) .data-table-modern .box-header {
|
||||
border-color: color-mix(in srgb, #fff 10%, transparent);
|
||||
}
|
||||
|
||||
.data-table-modern .box-title {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
color: var(--color-defaulttextcolor);
|
||||
}
|
||||
|
||||
/* Search input */
|
||||
.search-wrapper {
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.brdr-clr {
|
||||
border: 1.5px solid #7c3deb !important;
|
||||
|
||||
}
|
||||
|
||||
|
||||
.search-icon {
|
||||
position: absolute;
|
||||
left: 0.75rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
color: var(--color-textmuted, #8c9097);
|
||||
font-size: 0.8125rem;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.data-table-modern .search-wrapper .form-control-sm {
|
||||
min-width: 180px;
|
||||
padding-left: 2.25rem !important;
|
||||
border-radius: 9999px;
|
||||
border-color: var(--color-defaultborder);
|
||||
background-color: var(--color-white);
|
||||
font-size: 0.8125rem;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
}
|
||||
|
||||
.data-table-modern .search-wrapper .form-control-sm:focus {
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-primary) 15%, transparent);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
:host-context(.dark) .data-table-modern .search-wrapper .form-control-sm {
|
||||
background-color: var(--color-bodybg);
|
||||
border-color: color-mix(in srgb, #fff 10%, transparent);
|
||||
color: var(--color-defaulttextcolor);
|
||||
}
|
||||
|
||||
/* Table body / wrapper */
|
||||
.modern-table-body {
|
||||
padding: 0 !important;
|
||||
}
|
||||
|
||||
.modern-table-wrapper {
|
||||
border-radius: 0 0 0.75rem 0.75rem;
|
||||
}
|
||||
|
||||
/* Table */
|
||||
.data-table-modern .table {
|
||||
width: 100%;
|
||||
margin-bottom: 0;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.data-table-modern .table.table-bordered,
|
||||
.data-table-modern .table.table-bordered thead th,
|
||||
.data-table-modern .table.table-bordered tbody td {
|
||||
border: none;
|
||||
}
|
||||
|
||||
.data-table-modern .table thead {
|
||||
// background: linear-gradient(
|
||||
// 135deg,
|
||||
// #020617 0%,
|
||||
// #172554 40%,
|
||||
// #4338CA 75%,
|
||||
// #7C3AED 100%
|
||||
// );
|
||||
background: #7C3AED;
|
||||
color: var(--color-white);
|
||||
}
|
||||
|
||||
.data-table-modern .table thead th {
|
||||
padding: 0.5rem 0.75rem;
|
||||
font-weight: 600;
|
||||
font-size: 0.6875rem;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
color: var(--color-white);
|
||||
background: transparent;
|
||||
border: none;
|
||||
}
|
||||
|
||||
.data-table-modern .table tbody td {
|
||||
padding: 0.2rem 0.75rem;
|
||||
vertical-align: middle;
|
||||
color: var(--color-defaulttextcolor);
|
||||
border: none;
|
||||
font-size: 0.8125rem;
|
||||
}
|
||||
|
||||
.data-table-modern .table tbody tr:hover {
|
||||
// background-color: var(--color-light);
|
||||
|
||||
background-color: #e1d0ff;
|
||||
}
|
||||
|
||||
:host-context(.dark) .data-table-modern .table tbody tr:hover {
|
||||
background-color: color-mix(in srgb, #fff 6%, transparent);
|
||||
}
|
||||
|
||||
/* Inactive row action buttons border */
|
||||
.data-table-modern .inactive-actions button {
|
||||
border: 1.5px solid #7c3deb !important;
|
||||
}
|
||||
|
||||
/* Footer */
|
||||
.data-table-modern .box-footer {
|
||||
padding: 0.625rem 1rem;
|
||||
border-top: 1px solid var(--color-defaultborder);
|
||||
background-color: color-mix(in srgb, var(--color-primary) 3%, transparent);
|
||||
}
|
||||
|
||||
:host-context(.dark) .data-table-modern .box-footer {
|
||||
border-color: color-mix(in srgb, #fff 10%, transparent);
|
||||
background-color: color-mix(in srgb, var(--color-primary) 6%, transparent);
|
||||
}
|
||||
|
||||
/* Pagination */
|
||||
.data-table-modern .ti-pagination {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.25rem;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.data-table-modern .ti-pagination li.page-item {
|
||||
display: inline-flex;
|
||||
border: 1px solid var(--color-primary);
|
||||
border-radius: 9999px;
|
||||
}
|
||||
|
||||
.data-table-modern .ti-pagination li.page-item.disabled {
|
||||
opacity: 0.4;
|
||||
}
|
||||
|
||||
.data-table-modern .ti-pagination li .page-link {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
min-width: 1.5rem;
|
||||
height: 1.5rem;
|
||||
padding: 0 0.5rem;
|
||||
border-radius: 9999px;
|
||||
border: none;
|
||||
background-color: transparent;
|
||||
color: var(--color-defaulttextcolor);
|
||||
font-size: 0.75rem;
|
||||
font-weight: 500;
|
||||
line-height: 1;
|
||||
transition: all 0.15s ease;
|
||||
}
|
||||
|
||||
.data-table-modern .ti-pagination li .page-link:hover:not(:disabled) {
|
||||
background-color: color-mix(in srgb, var(--color-primary) 10%, transparent);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
.data-table-modern .ti-pagination li.active .page-link,
|
||||
.data-table-modern .ti-pagination li .page-link.active {
|
||||
background-color: var(--color-primary);
|
||||
color: var(--color-white);
|
||||
box-shadow: 0 2px 6px color-mix(in srgb, var(--color-primary) 30%, transparent);
|
||||
}
|
||||
|
||||
.data-table-modern .ti-pagination li.disabled .page-link {
|
||||
cursor: not-allowed;
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
:host-context(.dark) .data-table-modern .ti-pagination li .page-link {
|
||||
background-color: transparent;
|
||||
color: color-mix(in srgb, var(--color-defaulttextcolor) 80%, #fff);
|
||||
}
|
||||
|
||||
:host-context(.dark) .data-table-modern .ti-pagination li .page-link:hover:not(:disabled) {
|
||||
background-color: color-mix(in srgb, var(--color-primary) 15%, transparent);
|
||||
color: var(--color-primary);
|
||||
}
|
||||
|
||||
:host-context(.dark) .data-table-modern .ti-pagination li.active .page-link {
|
||||
color: var(--color-white);
|
||||
background-color: var(--color-primary);
|
||||
}
|
||||
|
||||
/* Responsive scroll */
|
||||
.table-responsive {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
@@ -64,3 +243,14 @@
|
||||
.table td {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
/* Remove number spinner buttons for cleaner look (kept from original) */
|
||||
.data-table-modern .custom-floating-input[type='number']::-webkit-inner-spin-button,
|
||||
.data-table-modern .custom-floating-input[type='number']::-webkit-outer-spin-button {
|
||||
appearance: none;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.data-table-modern .custom-floating-input[type='number'] {
|
||||
-moz-appearance: textfield;
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
<div [class]="resolvedWrapperClass()">
|
||||
@if (showLabel()) {
|
||||
@if (showLabel() && !floating()) {
|
||||
<label [for]="inputId()" [class]="resolvedLabelClass()">
|
||||
{{ label() }}
|
||||
@if (required()) {
|
||||
@@ -32,7 +32,25 @@
|
||||
</p>
|
||||
}
|
||||
|
||||
@if (floating()) {
|
||||
<div class="floating-label" [class.floating-label--float]="hasValue()" [class.floating-label--invalid]="hasVisibleError()">
|
||||
<ng-content />
|
||||
<label [for]="inputId()" [class]="resolvedLabelClass()">
|
||||
{{ label() }}
|
||||
@if (required()) {
|
||||
<span [class]="requiredClass()" aria-hidden="true">
|
||||
*
|
||||
</span>
|
||||
|
||||
<span class="sr-only">
|
||||
Required
|
||||
</span>
|
||||
}
|
||||
</label>
|
||||
</div>
|
||||
} @else {
|
||||
<ng-content />
|
||||
}
|
||||
|
||||
@if (showHint()) {
|
||||
<p [id]="hintId()" [class]="hintClass()">
|
||||
@@ -46,4 +64,4 @@
|
||||
[submitAttempted]="submitAttempted()" />
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
:host {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.floating-label {
|
||||
position: relative;
|
||||
display: block;
|
||||
|
||||
::ng-deep .form-control {
|
||||
padding-block: 1.125rem 0.375rem;
|
||||
min-height: 3.25rem;
|
||||
border-radius: 0.5rem;
|
||||
transition: border-color 0.2s ease, box-shadow 0.2s ease;
|
||||
|
||||
&::placeholder {
|
||||
color: transparent;
|
||||
}
|
||||
}
|
||||
|
||||
::ng-deep .form-control:focus {
|
||||
border-color: var(--color-primary);
|
||||
box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--color-primary) 20%, transparent);
|
||||
}
|
||||
|
||||
> label {
|
||||
position: absolute;
|
||||
inset-inline-start: 0.75rem;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
transform-origin: left top;
|
||||
z-index: 10;
|
||||
padding-inline: 0.25rem;
|
||||
background: var(--color-white) !important;
|
||||
color: #8c9097 !important;
|
||||
font-size: 0.875rem !important;
|
||||
line-height: 1;
|
||||
transition: all 0.2s ease-out;
|
||||
pointer-events: none;
|
||||
max-width: calc(100% - 1.75rem);
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
&:focus-within > label,
|
||||
&.floating-label--float > label {
|
||||
top: 0;
|
||||
transform: translateY(-50%) scale(0.85);
|
||||
color: var(--color-primary) !important;
|
||||
}
|
||||
|
||||
&.floating-label--invalid > label {
|
||||
color: var(--color-danger) !important;
|
||||
}
|
||||
}
|
||||
|
||||
:host-context(.dark) .floating-label > label,
|
||||
:host-context(.dark) .floating-label:focus-within > label,
|
||||
:host-context(.dark) .floating-label.floating-label--float > label {
|
||||
background: var(--color-bodybg) !important;
|
||||
}
|
||||
|
||||
:host-context(.dark) .floating-label ::ng-deep .form-control:focus {
|
||||
box-shadow: 0 0 0 0.2rem color-mix(in srgb, var(--color-primary) 25%, transparent);
|
||||
}
|
||||
|
||||
@@ -21,6 +21,7 @@ export type FormLabelPosition = 'top' | 'left' | 'hidden';
|
||||
standalone: true,
|
||||
imports: [FormValidationMessage, TooltipDirective],
|
||||
templateUrl: './form-field.html',
|
||||
styleUrl: './form-field.scss',
|
||||
changeDetection: ChangeDetectionStrategy.OnPush
|
||||
})
|
||||
export class FormField {
|
||||
@@ -51,6 +52,8 @@ export class FormField {
|
||||
|
||||
readonly hideLabel = input(false);
|
||||
|
||||
readonly floating = input(false);
|
||||
|
||||
readonly hideValidation = input(false);
|
||||
|
||||
readonly showValidationWhenDirty = input(false);
|
||||
@@ -135,6 +138,20 @@ export class FormField {
|
||||
);
|
||||
});
|
||||
|
||||
readonly hasValue = computed(() => {
|
||||
this.controlStateVersion();
|
||||
|
||||
const control = this.control();
|
||||
|
||||
if (!control) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const value = control.value;
|
||||
|
||||
return value !== null && value !== undefined && value !== '';
|
||||
});
|
||||
|
||||
readonly resolvedWrapperClass = computed(() => {
|
||||
return [
|
||||
this.labelPosition() === 'left'
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<app-form-field
|
||||
<!-- <app-form-field
|
||||
[label]="label()"
|
||||
[inputId]="inputId()"
|
||||
[control]="control()"
|
||||
@@ -11,6 +11,7 @@
|
||||
[showValidationWhenDirty]="showValidationWhenDirty()"
|
||||
[submitAttempted]="submitAttempted()"
|
||||
[validationMessages]="validationMessages()"
|
||||
[floating]="floating()"
|
||||
[wrapperClass]="wrapperClass()"
|
||||
[labelClass]="labelClass()"
|
||||
[contentClass]="fieldContentClass()"
|
||||
@@ -114,4 +115,144 @@
|
||||
</span>
|
||||
}
|
||||
</div>
|
||||
</app-form-field>
|
||||
</app-form-field> -->
|
||||
|
||||
|
||||
<app-form-field
|
||||
[label]="label()"
|
||||
[inputId]="inputId()"
|
||||
[control]="control()"
|
||||
[required]="required()"
|
||||
[disabled]="isDisabled()"
|
||||
[description]="description()"
|
||||
[hint]="hint()"
|
||||
[help]="help()"
|
||||
[hideValidation]="hideValidation()"
|
||||
[showValidationWhenDirty]="showValidationWhenDirty()"
|
||||
[submitAttempted]="submitAttempted()"
|
||||
[validationMessages]="validationMessages()"
|
||||
[floating]="floating()"
|
||||
[wrapperClass]="wrapperClass()"
|
||||
[labelClass]="labelClass()"
|
||||
[contentClass]="fieldContentClass()"
|
||||
>
|
||||
<div class="relative custom-floating-field">
|
||||
|
||||
<div class="floating-input-wrapper">
|
||||
|
||||
@if (prefixIcon()) {
|
||||
<span
|
||||
class="pointer-events-none absolute inset-y-0 start-0 z-[1] flex w-10 items-center justify-center text-textmuted floating-prefix-icon"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<i
|
||||
[class]="
|
||||
prefixIcon() +
|
||||
' text-[1rem] leading-none ' +
|
||||
prefixIconClass()
|
||||
"
|
||||
></i>
|
||||
</span>
|
||||
}
|
||||
|
||||
|
||||
<input
|
||||
[id]="inputId()"
|
||||
[name]="name()"
|
||||
[type]="resolvedType()"
|
||||
|
||||
[class]="resolvedInputClass() + ' custom-floating-input'"
|
||||
|
||||
[value]="value() ?? ''"
|
||||
[placeholder]="resolvedPlaceholder()"
|
||||
[required]="required()"
|
||||
[readOnly]="readonly()"
|
||||
[disabled]="isDisabled()"
|
||||
|
||||
[attr.autocomplete]="autocomplete()"
|
||||
[attr.inputmode]="inputMode()"
|
||||
[attr.spellcheck]="spellcheck()"
|
||||
|
||||
[attr.minlength]="minLength()"
|
||||
[attr.maxlength]="maxLength()"
|
||||
[attr.pattern]="pattern()"
|
||||
|
||||
[attr.min]="min()"
|
||||
[attr.max]="max()"
|
||||
[attr.step]="step()"
|
||||
|
||||
[attr.aria-label]="ariaLabel() || label()"
|
||||
[attr.aria-description]="ariaDescription()"
|
||||
[attr.aria-describedby]="describedBy()"
|
||||
[attr.aria-invalid]="control()?.invalid ? true : null"
|
||||
[attr.aria-required]="required() ? true : null"
|
||||
[attr.aria-busy]="loading() ? true : null"
|
||||
|
||||
(input)="onInput($event)"
|
||||
(blur)="onBlur()"
|
||||
/>
|
||||
|
||||
|
||||
@if (loading()) {
|
||||
<span
|
||||
class="pointer-events-none absolute inset-y-0 end-0 flex w-10 items-center justify-center floating-loader"
|
||||
aria-hidden="true"
|
||||
>
|
||||
<span
|
||||
class="inline-block size-4 animate-spin rounded-full border-2 border-current border-t-transparent text-primary"
|
||||
></span>
|
||||
</span>
|
||||
|
||||
} @else if (
|
||||
type() === 'password' &&
|
||||
showPasswordToggle()
|
||||
) {
|
||||
|
||||
<button
|
||||
type="button"
|
||||
class="absolute inset-y-0 end-0 flex w-10 cursor-pointer items-center justify-center text-textmuted transition-colors hover:text-primary disabled:cursor-not-allowed disabled:opacity-50 floating-action"
|
||||
[disabled]="isDisabled()"
|
||||
[attr.aria-label]="
|
||||
passwordVisible()
|
||||
? 'Hide ' + label()
|
||||
: 'Show ' + label()
|
||||
"
|
||||
(click)="togglePasswordVisibility()"
|
||||
>
|
||||
<i
|
||||
[class]="
|
||||
passwordVisible()
|
||||
? 'ri-eye-off-line'
|
||||
: 'ri-eye-line'
|
||||
"
|
||||
class="text-[1rem] leading-none"
|
||||
aria-hidden="true"
|
||||
></i>
|
||||
|
||||
</button>
|
||||
|
||||
|
||||
} @else if (suffixIcon()) {
|
||||
|
||||
<span
|
||||
class="pointer-events-none absolute inset-y-0 end-0 flex w-10 items-center justify-center text-textmuted floating-suffix-icon"
|
||||
aria-hidden="true"
|
||||
>
|
||||
|
||||
<i
|
||||
[class]="
|
||||
suffixIcon() +
|
||||
' text-[1rem] leading-none ' +
|
||||
suffixIconClass()
|
||||
"
|
||||
></i>
|
||||
|
||||
</span>
|
||||
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</app-form-field>
|
||||
@@ -0,0 +1,272 @@
|
||||
/* =========================================
|
||||
Floating Input
|
||||
========================================= */
|
||||
|
||||
:host ::ng-deep .custom-floating-field {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
:host ::ng-deep .floating-input-wrapper {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
/* =========================================
|
||||
Input
|
||||
========================================= */
|
||||
|
||||
:host ::ng-deep .custom-floating-input {
|
||||
|
||||
width: 100%;
|
||||
height: 56px;
|
||||
|
||||
padding: 0 16px;
|
||||
|
||||
border-radius: 12px;
|
||||
|
||||
border: 1px solid #d1d5db;
|
||||
|
||||
background: #ffffff;
|
||||
|
||||
font-size: 16px;
|
||||
|
||||
color: #111827;
|
||||
|
||||
outline: none;
|
||||
|
||||
transition:
|
||||
border-color .25s ease,
|
||||
box-shadow .25s ease;
|
||||
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
|
||||
/* =========================================
|
||||
Focus
|
||||
========================================= */
|
||||
|
||||
:host ::ng-deep .custom-floating-input:focus {
|
||||
|
||||
border-color: #2563eb;
|
||||
|
||||
box-shadow:
|
||||
0 0 0 3px rgba(37,99,235,.12);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* =========================================
|
||||
Floating Label
|
||||
========================================= */
|
||||
|
||||
|
||||
:host ::ng-deep .floating-label {
|
||||
|
||||
position: absolute;
|
||||
|
||||
left: 16px;
|
||||
|
||||
top: 50%;
|
||||
|
||||
transform: translateY(-50%);
|
||||
|
||||
background: #ffffff;
|
||||
|
||||
padding: 0 6px;
|
||||
|
||||
color: #64748b;
|
||||
|
||||
font-size: 16px;
|
||||
|
||||
pointer-events: none;
|
||||
|
||||
transition: .25s ease;
|
||||
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* =========================================
|
||||
Floating Animation
|
||||
========================================= */
|
||||
|
||||
|
||||
:host ::ng-deep .custom-floating-input:focus + .floating-label,
|
||||
:host ::ng-deep .custom-floating-input:not(:placeholder-shown) + .floating-label {
|
||||
|
||||
|
||||
top: 0;
|
||||
|
||||
transform: translateY(-50%);
|
||||
|
||||
font-size: 13px;
|
||||
|
||||
color: #2563eb;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* =========================================
|
||||
Prefix Icon
|
||||
========================================= */
|
||||
|
||||
|
||||
:host ::ng-deep .floating-prefix-icon {
|
||||
|
||||
position: absolute;
|
||||
|
||||
left: 14px;
|
||||
|
||||
top: 50%;
|
||||
|
||||
transform: translateY(-50%);
|
||||
|
||||
z-index: 2;
|
||||
|
||||
color: #94a3b8;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
:host ::ng-deep .floating-prefix-icon ~ .custom-floating-input {
|
||||
|
||||
padding-left: 42px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* =========================================
|
||||
Suffix / Action / Loader
|
||||
========================================= */
|
||||
|
||||
|
||||
:host ::ng-deep .floating-suffix-icon,
|
||||
:host ::ng-deep .floating-action,
|
||||
:host ::ng-deep .floating-loader {
|
||||
|
||||
|
||||
position: absolute;
|
||||
|
||||
right: 14px;
|
||||
|
||||
top: 50%;
|
||||
|
||||
transform: translateY(-50%);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
:host ::ng-deep .floating-action {
|
||||
|
||||
|
||||
border: none;
|
||||
|
||||
background: transparent;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
color: #64748b;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
/* =========================================
|
||||
Disabled
|
||||
========================================= */
|
||||
|
||||
|
||||
:host ::ng-deep .custom-floating-input:disabled {
|
||||
|
||||
|
||||
background: #f8fafc;
|
||||
|
||||
cursor: not-allowed;
|
||||
|
||||
opacity: .8;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* =========================================
|
||||
Validation States
|
||||
========================================= */
|
||||
|
||||
|
||||
:host ::ng-deep .custom-floating-input.ng-invalid.ng-touched {
|
||||
|
||||
|
||||
border-color:#ef4444;
|
||||
|
||||
}
|
||||
|
||||
|
||||
:host ::ng-deep .custom-floating-input.ng-valid.ng-touched {
|
||||
|
||||
|
||||
border-color:#22c55e;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* =========================================
|
||||
Dark Mode
|
||||
========================================= */
|
||||
|
||||
|
||||
:host ::ng-deep .dark .custom-floating-input {
|
||||
|
||||
|
||||
background:#1f2937;
|
||||
|
||||
border-color:#475569;
|
||||
|
||||
color:#ffffff;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
:host ::ng-deep .dark .floating-label {
|
||||
|
||||
|
||||
background:#1f2937;
|
||||
|
||||
color:#94a3b8;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
:host ::ng-deep .dark .custom-floating-input:focus {
|
||||
|
||||
|
||||
border-color:#60a5fa;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
:host ::ng-deep .dark
|
||||
.custom-floating-input:focus + .floating-label,
|
||||
:host ::ng-deep .dark
|
||||
.custom-floating-input:not(:placeholder-shown) + .floating-label {
|
||||
|
||||
|
||||
color:#60a5fa;
|
||||
|
||||
}
|
||||
@@ -79,6 +79,7 @@ export class FormInput implements ControlValueAccessor {
|
||||
readonly showPasswordToggle = input(true);
|
||||
readonly loading = input(false);
|
||||
|
||||
readonly floating = input(false);
|
||||
|
||||
readonly wrapperClass = input('');
|
||||
readonly fieldContentClass = input('');
|
||||
@@ -151,10 +152,14 @@ export class FormInput implements ControlValueAccessor {
|
||||
|
||||
const configuredPlaceholder = this.placeholder().trim();
|
||||
|
||||
if (configuredPlaceholder) {
|
||||
if (configuredPlaceholder && !this.floating()) {
|
||||
return configuredPlaceholder;
|
||||
}
|
||||
|
||||
if (this.floating()) {
|
||||
return '\u00A0';
|
||||
}
|
||||
|
||||
return this.useLabelAsPlaceholder()
|
||||
? this.label()
|
||||
: null;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
@if (open()) {
|
||||
<!-- @if (open()) {
|
||||
<div
|
||||
class="hs-overlay open hs-overlay-backdrop-open:!bg-[#32325180] dark:hs-overlay-backdrop-open:!bg-[#323251cc] ti-modal pointer-events-none"
|
||||
(mousedown)="onBackdropClick($event)">
|
||||
@@ -51,4 +51,126 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
}
|
||||
} -->
|
||||
|
||||
@if (open()) {
|
||||
<div
|
||||
class="hs-overlay modal-overlay"
|
||||
(mousedown)="onBackdropClick($event)">
|
||||
|
||||
<!-- Backdrop -->
|
||||
<div
|
||||
class="modal-backdrop"
|
||||
aria-hidden="true"
|
||||
(mousedown)="onBackdropClick($event)">
|
||||
</div>
|
||||
|
||||
<!-- Modal -->
|
||||
<div
|
||||
[class]="modalBoxClass() + ' modern-modal-wrapper'"
|
||||
role="dialog"
|
||||
aria-modal="true"
|
||||
[attr.aria-label]="title()"
|
||||
tabindex="-1"
|
||||
(mousedown)="$event.stopPropagation()">
|
||||
|
||||
<div class="modern-modal">
|
||||
|
||||
@if (showHeader()) {
|
||||
<div class="modern-modal-header">
|
||||
|
||||
<!-- Decorative Background -->
|
||||
<div class="modal-header-bg"></div>
|
||||
|
||||
<div class="modal-header-content">
|
||||
|
||||
<div class="modal-header-left">
|
||||
|
||||
<!-- Header Icon -->
|
||||
<!-- <div class="modal-icon">
|
||||
|
||||
<div class="modal-icon-inner">
|
||||
<i class="ri-layout-grid-fill"></i>
|
||||
</div>
|
||||
|
||||
</div> -->
|
||||
|
||||
<!-- Title -->
|
||||
<div class="modal-title-wrapper">
|
||||
|
||||
<h6 class="modern-modal-title">
|
||||
{{ title() }}
|
||||
</h6>
|
||||
|
||||
@if (subtitle()) {
|
||||
<p class="modern-modal-subtitle">
|
||||
{{ subtitle() }}
|
||||
</p>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
@if (showCloseButton()) {
|
||||
<button
|
||||
type="button"
|
||||
class="modern-close-btn"
|
||||
aria-label="Close modal"
|
||||
[disabled]="loading()"
|
||||
(click)="close()">
|
||||
|
||||
<i class="ri-close-line"></i>
|
||||
|
||||
</button>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
<!-- Body -->
|
||||
<div class="modern-modal-body">
|
||||
|
||||
<div class="modal-content-wrapper">
|
||||
|
||||
<ng-content />
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
@if (showFooter()) {
|
||||
<div class="modern-modal-footer">
|
||||
|
||||
@if (showCancelButton()) {
|
||||
<app-button
|
||||
action="cancel"
|
||||
[label]="cancelLabel()"
|
||||
[showIcon]="true"
|
||||
[disabled]="loading()"
|
||||
(buttonClicked)="close()" />
|
||||
}
|
||||
|
||||
@if (showSubmitButton()) {
|
||||
<app-button
|
||||
[action]="submitAction()"
|
||||
[label]="submitLabel()"
|
||||
[loadingLabel]="loadingLabel()"
|
||||
[showIcon]="true"
|
||||
[loading]="loading()"
|
||||
[disabled]="submitDisabled()"
|
||||
(buttonClicked)="submit()" />
|
||||
}
|
||||
|
||||
</div>
|
||||
}
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
}
|
||||
@@ -1,3 +1,750 @@
|
||||
:host {
|
||||
display: contents;
|
||||
// :host {
|
||||
// display: contents;
|
||||
// }
|
||||
|
||||
// :host(.modern-modal) .ti-modal-content {
|
||||
// border-radius: 1rem !important;
|
||||
// box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.25) !important;
|
||||
// border-color: var(--color-defaultborder) !important;
|
||||
|
||||
// &:where(.dark, .dark *) {
|
||||
// box-shadow: 0 25px 50px -12px rgba(0, 0, 0, 0.5) !important;
|
||||
// }
|
||||
// }
|
||||
|
||||
// :host(.modern-modal) .ti-modal-header {
|
||||
// padding-inline: 1.5rem !important;
|
||||
// padding-block: 1.25rem !important;
|
||||
// border-color: var(--color-defaultborder) !important;
|
||||
// }
|
||||
|
||||
// :host(.modern-modal) .ti-modal-title {
|
||||
// font-size: 1.125rem !important;
|
||||
// font-weight: 600 !important;
|
||||
// color: var(--color-gray-800) !important;
|
||||
|
||||
// &:where(.dark, .dark *) {
|
||||
// color: var(--color-white) !important;
|
||||
// }
|
||||
// }
|
||||
|
||||
// :host(.modern-modal) .ti-modal-close-btn {
|
||||
// border-radius: 0.5rem !important;
|
||||
// color: var(--color-gray-500) !important;
|
||||
// transition: all 0.2s ease !important;
|
||||
|
||||
// &:hover {
|
||||
// color: var(--color-primary) !important;
|
||||
// background-color: color-mix(in srgb, var(--color-primary) 10%, transparent) !important;
|
||||
// }
|
||||
// }
|
||||
|
||||
// :host(.modern-modal) .ti-modal-body {
|
||||
// padding: 1.5rem !important;
|
||||
// }
|
||||
|
||||
// :host(.modern-modal) .ti-modal-footer {
|
||||
// padding-inline: 1.5rem !important;
|
||||
// padding-block: 1rem !important;
|
||||
// gap: 0.75rem !important;
|
||||
// border-color: var(--color-defaultborder) !important;
|
||||
// }
|
||||
|
||||
|
||||
/* ==========================================================
|
||||
Overlay
|
||||
========================================================== */
|
||||
|
||||
.modal-overlay {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9999;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
padding: 1.5rem;
|
||||
|
||||
animation: modalFade .22s ease;
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
Backdrop
|
||||
========================================================== */
|
||||
|
||||
.modal-backdrop {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
|
||||
background: rgba(15, 23, 42, .42);
|
||||
|
||||
backdrop-filter: blur(1px);
|
||||
-webkit-backdrop-filter: blur(1px);
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
Wrapper
|
||||
========================================================== */
|
||||
|
||||
.modern-modal-wrapper {
|
||||
|
||||
position: relative;
|
||||
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
pointer-events: auto;
|
||||
|
||||
animation: modalSlide .28s ease;
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
Modal
|
||||
========================================================== */
|
||||
|
||||
.modern-modal {
|
||||
|
||||
position: relative;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
||||
width: 100%;
|
||||
max-height: 90vh;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
border-radius: 20px;
|
||||
|
||||
background: #ffffff;
|
||||
|
||||
// border: 1px solid #edf2f7;
|
||||
|
||||
box-shadow:
|
||||
0 15px 40px rgba(15, 23, 42, .10),
|
||||
0 30px 80px rgba(15, 23, 42, .14);
|
||||
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
Header
|
||||
========================================================== */
|
||||
|
||||
.modern-modal-header {
|
||||
|
||||
position: relative;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
border-bottom: 1px solid rgba(255, 255, 255, .08);
|
||||
|
||||
// background: linear-gradient(135deg,
|
||||
// #020617 0%,
|
||||
// #172554 40%,
|
||||
// #4338CA 75%,
|
||||
// #7C3AED 100%);
|
||||
// background: linear-gradient(135deg,
|
||||
// #111C43 0%,
|
||||
// #16285c 55%,
|
||||
// #1d3578 100%);
|
||||
background-color: #7c3deb;
|
||||
|
||||
isolation: isolate;
|
||||
}
|
||||
|
||||
/* Decorative Background */
|
||||
.modal-header-bg {
|
||||
|
||||
position: absolute;
|
||||
|
||||
inset: 0;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
/* Left Glow */
|
||||
|
||||
.modal-header-bg::before {
|
||||
|
||||
content: "";
|
||||
|
||||
position: absolute;
|
||||
|
||||
width: 280px;
|
||||
|
||||
height: 280px;
|
||||
|
||||
left: -120px;
|
||||
|
||||
top: -120px;
|
||||
|
||||
border-radius: 50%;
|
||||
|
||||
background:
|
||||
radial-gradient(circle,
|
||||
rgba(255, 255, 255, .14),
|
||||
transparent 72%);
|
||||
|
||||
filter: blur(8px);
|
||||
}
|
||||
|
||||
/* Right Glow */
|
||||
|
||||
.modal-header-bg::after {
|
||||
|
||||
content: "";
|
||||
|
||||
position: absolute;
|
||||
|
||||
width: 240px;
|
||||
|
||||
height: 240px;
|
||||
|
||||
right: -80px;
|
||||
|
||||
top: -90px;
|
||||
|
||||
border-radius: 50%;
|
||||
|
||||
background:
|
||||
radial-gradient(circle,
|
||||
rgba(91, 140, 255, .25),
|
||||
transparent 72%);
|
||||
|
||||
filter: blur(10px);
|
||||
}
|
||||
|
||||
.modern-modal-header::after {
|
||||
|
||||
content: "";
|
||||
|
||||
position: absolute;
|
||||
|
||||
left: 0;
|
||||
|
||||
bottom: 0;
|
||||
|
||||
width: 100%;
|
||||
|
||||
height: 1px;
|
||||
|
||||
background:
|
||||
linear-gradient(90deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, .18),
|
||||
transparent);
|
||||
}
|
||||
|
||||
/* Header Layout */
|
||||
|
||||
.modal-header-content {
|
||||
|
||||
position: relative;
|
||||
|
||||
z-index: 2;
|
||||
|
||||
display: flex;
|
||||
|
||||
justify-content: space-between;
|
||||
|
||||
align-items: center;
|
||||
|
||||
gap: 20px;
|
||||
|
||||
padding: 10px 2rem;
|
||||
}
|
||||
|
||||
.modal-header-left {
|
||||
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
gap: 18px;
|
||||
|
||||
min-width: 0;
|
||||
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
Icon
|
||||
========================================================== */
|
||||
|
||||
.modal-icon {
|
||||
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.modal-icon-inner {
|
||||
|
||||
width: 40px;
|
||||
|
||||
height: 40px;
|
||||
|
||||
border-radius: 12px;
|
||||
|
||||
display: flex;
|
||||
|
||||
justify-content: center;
|
||||
|
||||
align-items: center;
|
||||
|
||||
color: #ffffff;
|
||||
|
||||
background:
|
||||
linear-gradient(145deg,
|
||||
rgba(255, 255, 255, .18),
|
||||
rgba(255, 255, 255, .06));
|
||||
|
||||
border: 1px solid rgba(255, 255, 255, .18);
|
||||
|
||||
backdrop-filter: blur(12px);
|
||||
|
||||
box-shadow:
|
||||
inset 0 1px 1px rgba(255, 255, 255, .15),
|
||||
0 10px 30px rgba(0, 0, 0, .25);
|
||||
}
|
||||
|
||||
.modal-icon-inner i {
|
||||
|
||||
font-size: 17px;
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
Title
|
||||
========================================================== */
|
||||
|
||||
.modal-title-wrapper {
|
||||
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.modern-modal-title {
|
||||
|
||||
margin: 0;
|
||||
|
||||
color: #ffffff;
|
||||
|
||||
font-size: 17px;
|
||||
|
||||
font-weight: 700;
|
||||
|
||||
letter-spacing: -.4px;
|
||||
|
||||
line-height: 1.3;
|
||||
}
|
||||
|
||||
.modern-modal-subtitle {
|
||||
|
||||
margin-top: .45rem;
|
||||
|
||||
color: rgba(255, 255, 255, .72);
|
||||
|
||||
font-size: .9rem;
|
||||
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
Close
|
||||
========================================================== */
|
||||
|
||||
.modern-close-btn {
|
||||
|
||||
width: 25px;
|
||||
|
||||
height: 25px;
|
||||
|
||||
border-radius: 14px;
|
||||
|
||||
display: flex;
|
||||
|
||||
align-items: center;
|
||||
|
||||
justify-content: center;
|
||||
|
||||
cursor: pointer;
|
||||
|
||||
color: #ffffff;
|
||||
|
||||
background: rgba(255, 255, 255, .08);
|
||||
|
||||
// border: 1px solid rgba(255, 255, 255, .12);
|
||||
|
||||
backdrop-filter: blur(8px);
|
||||
|
||||
transition: .28s;
|
||||
}
|
||||
|
||||
.modern-close-btn:hover {
|
||||
|
||||
background: rgba(255, 255, 255, .18);
|
||||
|
||||
transform: rotate(90deg) scale(1.05);
|
||||
|
||||
border-color: rgba(255, 255, 255, .25);
|
||||
}
|
||||
|
||||
.modern-close-btn i {
|
||||
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
Body
|
||||
========================================================== */
|
||||
|
||||
.modern-modal-body {
|
||||
|
||||
flex: 1;
|
||||
|
||||
overflow: auto;
|
||||
|
||||
padding: 2rem;
|
||||
}
|
||||
|
||||
.modal-content-wrapper {
|
||||
|
||||
display: flex;
|
||||
|
||||
flex-direction: column;
|
||||
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
/* Scroll */
|
||||
|
||||
.modern-modal-body::-webkit-scrollbar {
|
||||
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.modern-modal-body::-webkit-scrollbar-thumb {
|
||||
|
||||
background: #d4dbe6;
|
||||
|
||||
border-radius: 20px;
|
||||
}
|
||||
|
||||
.modern-modal-body::-webkit-scrollbar-thumb:hover {
|
||||
|
||||
background: #b8c3d3;
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
Footer
|
||||
========================================================== */
|
||||
/* Footer */
|
||||
|
||||
.modern-modal-footer {
|
||||
|
||||
display: flex;
|
||||
|
||||
justify-content: flex-end;
|
||||
|
||||
align-items: center;
|
||||
|
||||
gap: 12px;
|
||||
|
||||
padding: 5px 2rem;
|
||||
|
||||
border-top: 1px solid #edf2f7;
|
||||
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
|
||||
/* ============================
|
||||
Cancel Button
|
||||
============================ */
|
||||
|
||||
|
||||
::ng-deep .modern-modal-footer app-button:first-child button {
|
||||
|
||||
|
||||
height: 36px;
|
||||
|
||||
padding: 0 16px;
|
||||
|
||||
border-radius: 10px;
|
||||
|
||||
border: 1px solid #dbe3ef;
|
||||
|
||||
background: #ffffff;
|
||||
|
||||
color: #475569;
|
||||
|
||||
font-weight: 600;
|
||||
|
||||
font-size: 13px;
|
||||
|
||||
transition: .3s ease;
|
||||
|
||||
}
|
||||
|
||||
|
||||
::ng-deep .modern-modal-footer app-button:first-child button:hover {
|
||||
|
||||
|
||||
background: #f8fafc;
|
||||
|
||||
border-color: #cbd5e1;
|
||||
|
||||
transform: translateY(-2px);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ============================
|
||||
Save Button
|
||||
============================ */
|
||||
|
||||
|
||||
::ng-deep .modern-modal-footer app-button:last-child button {
|
||||
|
||||
|
||||
position: relative;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
height: 36px;
|
||||
|
||||
padding: 0 16px;
|
||||
|
||||
border-radius: 10px;
|
||||
|
||||
border: none;
|
||||
|
||||
|
||||
color: #ffffff;
|
||||
|
||||
|
||||
// background:
|
||||
|
||||
// linear-gradient(135deg,
|
||||
// #111C43,
|
||||
// #2563eb);
|
||||
background: #7c3deb;
|
||||
|
||||
box-shadow:
|
||||
|
||||
0 8px 20px rgba(125, 61, 235, 0.468);
|
||||
|
||||
|
||||
font-weight: 600;
|
||||
|
||||
font-size: 13px;
|
||||
|
||||
|
||||
transition: .35s ease;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
::ng-deep .modern-modal-footer app-button:last-child button:hover {
|
||||
|
||||
|
||||
transform: translateY(-3px);
|
||||
|
||||
|
||||
box-shadow:
|
||||
|
||||
0 12px 28px rgba(37, 99, 235, .45);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
/* ============================
|
||||
Shine Animation
|
||||
============================ */
|
||||
|
||||
|
||||
::ng-deep .modern-modal-footer app-button:last-child button::before {
|
||||
|
||||
|
||||
content: "";
|
||||
|
||||
|
||||
position: absolute;
|
||||
|
||||
|
||||
top: 0;
|
||||
|
||||
left: -120%;
|
||||
|
||||
|
||||
width: 70%;
|
||||
|
||||
|
||||
height: 100%;
|
||||
|
||||
|
||||
background:
|
||||
|
||||
linear-gradient(120deg,
|
||||
transparent,
|
||||
rgba(255, 255, 255, .55),
|
||||
transparent);
|
||||
|
||||
|
||||
transform: skewX(-25deg);
|
||||
|
||||
|
||||
animation: saveButtonShine 2s infinite;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@keyframes saveButtonShine {
|
||||
|
||||
|
||||
0% {
|
||||
|
||||
left: -120%;
|
||||
|
||||
}
|
||||
|
||||
|
||||
40% {
|
||||
|
||||
left: 130%;
|
||||
|
||||
}
|
||||
|
||||
|
||||
100% {
|
||||
|
||||
left: 130%;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/* Disabled */
|
||||
|
||||
::ng-deep .modern-modal-footer button:disabled {
|
||||
|
||||
|
||||
opacity: .55;
|
||||
|
||||
cursor: not-allowed;
|
||||
|
||||
transform: none !important;
|
||||
|
||||
box-shadow: none !important;
|
||||
|
||||
}
|
||||
|
||||
::ng-deep .modern-modal-footer app-button button {
|
||||
|
||||
height: 36px;
|
||||
|
||||
padding: 0 16px;
|
||||
|
||||
border-radius: 10px;
|
||||
|
||||
font-size: 13px;
|
||||
|
||||
font-weight: 600;
|
||||
|
||||
transition: .3s ease;
|
||||
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
Mobile
|
||||
========================================================== */
|
||||
|
||||
@media (max-width:768px) {
|
||||
|
||||
.modal-overlay {
|
||||
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
.modern-modal {
|
||||
|
||||
border-radius: 18px;
|
||||
|
||||
max-height: 95vh;
|
||||
}
|
||||
|
||||
.modal-header-content {
|
||||
|
||||
padding: 1em;
|
||||
}
|
||||
|
||||
.modal-icon-inner {
|
||||
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.modern-modal-title {
|
||||
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.modern-modal-body {
|
||||
|
||||
padding: 1.2rem;
|
||||
}
|
||||
|
||||
.modern-modal-footer {
|
||||
|
||||
padding: 1rem 1.2rem;
|
||||
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* ==========================================================
|
||||
Animation
|
||||
========================================================== */
|
||||
|
||||
@keyframes modalFade {
|
||||
|
||||
from {
|
||||
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
to {
|
||||
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@keyframes modalSlide {
|
||||
|
||||
from {
|
||||
|
||||
opacity: 0;
|
||||
|
||||
transform: translateY(20px) scale(.98);
|
||||
}
|
||||
|
||||
to {
|
||||
|
||||
opacity: 1;
|
||||
|
||||
transform: translateY(0) scale(1);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
@if(childTitle){
|
||||
<div class="block justify-between page-header md:flex">
|
||||
<div>
|
||||
<h3 class="!text-defaulttextcolor dark:!text-defaulttextcolor/70 dark:text-white dark:hover:text-white text-[1.125rem]! font-semibold">
|
||||
{{childTitle}}</h3>
|
||||
<!-- <h3 class="!text-defaulttextcolor dark:!text-defaulttextcolor/70 dark:text-white dark:hover:text-white text-[1.125rem]! font-semibold">
|
||||
{{childTitle}}</h3> -->
|
||||
</div>
|
||||
<!-- <ol class="flex items-center whitespace-nowrap min-w-0">
|
||||
@if(parentTitle){
|
||||
|
||||
@@ -13,6 +13,28 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@500;600&display=swap');
|
||||
|
||||
|
||||
/* Etihad Altis Text (local font) */
|
||||
@font-face {
|
||||
font-family: 'EtihadAltis-Text';
|
||||
src: url('/assets/fonts/EtihadAltis-Text_V3.ttf') format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: swap;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'EtihadAltis-Text', 'Inter', 'Noto Sans', sans-serif !important;
|
||||
}
|
||||
|
||||
.app-sidebar,
|
||||
.app-header {
|
||||
font-family: 'EtihadAltis-Text', 'Inter', 'Noto Sans', sans-serif !important;
|
||||
}
|
||||
|
||||
.main-header-container .header-element {
|
||||
align-items: center !important;
|
||||
}
|
||||
|
||||
.total-mail-recepients::-webkit-scrollbar {
|
||||
@apply h-0!;
|
||||
}
|
||||
@@ -42,3 +64,17 @@
|
||||
font-size: 1rem;
|
||||
line-height: 1.25;
|
||||
}
|
||||
|
||||
/* Center currency modal floating label on empty invalid fields */
|
||||
currency-list .custom-floating-field.has-error.is-empty .custom-floating-input:not(:focus) ~ .custom-floating-label {
|
||||
top: 35% !important;
|
||||
transform: translateY(-50%) !important;
|
||||
}
|
||||
|
||||
/* Remove unwanted bottom margin from primary-full buttons */
|
||||
.ti-btn-primary-full {
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
.ti-form-select{
|
||||
border: 1px solid #7c3deb !important;
|
||||
}
|
||||
Reference in New Issue
Block a user